Webpack compiled output folder not showing up in docker container

Viewed 196

I'm experiencing a confusing situation that I'd love some additional thoughts about. I'm trying to get a local dev environment set up at my company using Docker.

Goal 1 is to allow local edits to app and lib to be captured and piped into the container. I am able to accomplish this with no problem using bind mounts to those directories. This enables our ruby work to happen against a running docker cluster, perfect!

Goal 2 is to allow updates to public/assets/, generated by a webpack process running on the local filesystem, to be captured and piped into the container. The intention is for our front-end engineers to run webpack locally, but to allow their compiled local output to be served by the running docker container.

Unfortunately, something strange is happening when I try to do this. Currently, as you can see below, I'm trying to use public/ as a bind mount. This sort of works -- I can navigate into public/ and see a bunch of files. But this is where it gets weird. The public/ directory contains assets/, which holds the output of the local webpack process. When I shell into my container I can see the assets folder:

$ cd public
$ ls -la
...
drwxr-xr-x  36 root root   1152 Aug 13 13:12 assets
....

But then when I try to access it I get weird behavior:

/webapp/public # ls -la assets
ls: cannot access 'assets': No such file or directory

Sure enough, when I navigate to my web application it gives me 404's on anything in the public/assets/ folder.

Here's my Dockerfile:

FROM ruby:2.7.2-alpine

WORKDIR /webapp

RUN apk add --update --no-cache yarn nodejs npm graphviz vim postgresql-client coreutils binutils build-base readline readline-dev cmake git nodejs openssh-client openssl-dev postgresql-dev shared-mime-info tzdata; \
  mkdir -p -m 0600 ~/.ssh; \
  ssh-keyscan github.com >> ~/.ssh/known_hosts

RUN mkdir config

# this overwrites the local database.yml with some custom params we need to use docker-compose's postgres.
# this way you don't need to maintain a forked local config, the container will configure itself correctly.
COPY local/config/application.template.yml config/application.yml
COPY local/config/database.template.yml config/database.yml
COPY local/config/warehouse.template.yml config/warehouse.yml
COPY local/config/.setup_config.template.yml ./.setup_config.yml

ARG packagecloud
ARG contribsys
ENV PACKAGECLOUD_TOKEN=$packagecloud
ENV BUNDLE_GEMS__CONTRIBSYS__COM=$contribsys

# by only copying over config and gem info prior to bundle installing I'm hoping to
# get Docker to use its caching to not run this unless something above has changed.
COPY ./Gemfile ./Gemfile
COPY ./Gemfile.lock ./Gemfile.lock
RUN --mount=type=ssh bundle install

# we should be able to do the same for yarn, but it's not nearly as expensive.
# for some reason, though, the packagecloud stuff fails if we run this before
# copying everything over. Unsure why.
COPY ./package.json ./package.json
COPY ./yarn.lock ./yarn.lock
COPY ./.npmrc ./.npmrc
RUN yarn install

COPY . .


# if necessary, remove the .gitkeep files -- they keep databases from initializing. This sucks for now.
RUN mkdir -p local/data/redis/
RUN mkdir -p local/data/postgres/

EXPOSE 3000

CMD ["/bin/sh"]

Here's my docker-compose.yml with database details redacted:

version: "3"

networks:
  default:
    name: mode-net
    driver: bridge

services:
  db:
    image: postgres:11.5
    environment:
    volumes:
      - ./local/data/postgres:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: "redis:5-alpine"
    command: redis-server
    ports:
      - "6379:6379"
    volumes:
      - ./local/data/redis:/var/lib/postgresql/data
    environment:
      - REDIS_URL_SIDEKIQ=redis://redis:6379/1

  sidekiq:
    image: webapp:latest
    depends_on:
      - "db"
      - "redis"
    command: bin/sidekiq
    volumes:
      - ./app:/webapp/app
      - ./lib:/webapp/lib
    environment:
      - RAILS_ENV=development
      - DATABASE_URL=
      - REDIS_URL_SIDEKIQ=redis://redis:6379/1

  web:
    image: webapp:latest
    command: /webapp/local/scripts/start_server
    environment:
      - RAILS_ENV=development
      - DEBUG=$DEBUG
      - DATABASE_URL=
      - REDIS_URL_SIDEKIQ=redis://redis:6379/1
    ports:
      - "3000:3000"
      - "1234:1234" # used for debugger access
    volumes:
      - ./public:/webapp/public
      - ./app:/webapp/app
      - ./lib:/webapp/lib
      - ./webapp-ui:/webapp/webapp-ui
    depends_on:
      - db
      - redis

I do include certain parts of public in .dockerignore but by bind mounting the volume I think it should include it at runtime, right?

update

So, I'm not going to accept this as The Answer but I do have some more information. It seems like if I restart my computer, start a fresh docker engine, and fire it up against built static files in public/assets it works fine.

If, while it's running, I do a build that updates the static assets in public/assets, that build's output is now captured by my container. Great!

But if I start, on my filesystem, a webpack dev server? That's when everything falls apart. I suspect it's doing something to the folder on the filesystem that isn't playing nicely with Docker. What sucks, though, is that once I start that dev server even once my Docker setup is hosed until I restart my computer.

That's... really weird?

1 Answers

I couldn’t reproduce this and it would be helpful if you provide more contents for reproduce it easily.

This could be a bug in the Docker file sharing mechanism or race condition to access the assets folder.

If you are using Docker Desktop on Mac, I recommend to disable Use gRPC FUSE for file sharing feature Preferences -> General

Disable Use gRPC FUSE for file sharing

Related