avatar

Derek Zeng

Loneliness is the gift of life

Using docker volume to share build across containers

I encountered a problem with docker recently.

The project I've created is run entirely using docker. The vue client and node server are in different folder. For the vue client, I'm building the statics and having a nginx container running the static files. For the node server, I'm having a node container to run it.

All my node and vue dependencies are in a single package.json.

I have a docker-compose.yml look like this.

version: '3'
services:
  node-server:
    build: .
    ports:
      - '3000:3000'

  nginx:
    image: 'nginx'
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/my_project.conf
      - ./dist:/usr/share/nginx/html/my_project
    ports:
      - '8888:8080'

And a nginx.conf like this

...
upstream node {
    server node-server:3000;
}

server {
    listen 8080;
    server_name localhost;
    root /usr/share/nginx/html/my_project;

    location /graphql {
        proxy_pass http://node;
    }

    location /graphiql {
        proxy_pass http://node;
    }

    location / {
        try_files $uri /index.html;
    }
}

It's working fine. The only issue I have is that I have to compile the vue client (yarn build) offline and check the resultant files into version control. Alternatively, I have to build it on the server and add a volume to both services in the docker-compose.yml.

I think both ways are ugly. Since I'm already having a node container and installed all the dependencies in the package.json, why can't I just use it to build dist inside container so I don't have to check in the derived files in the build step.

Turns out, I can use named volume to accomplish this. Unlike ordinary volume which is removed when container is dead and not sharable, named volume is an independent volume that is created using docker and can be mapped to multiple containers.

First I need to create a volume.

sudo docker volume create app_dist

This volume is gonna to contain my app files and be shared in both services. I update my docker-compose.yml to use this volume. Note the volumes declaration.

version: '3'
services:
  node-server:
    ...
    volumes:
      - app_dist://usr/share/nginx/html/my_project

  nginx:
    image: 'nginx'
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/my_project.conf
      - app_dist:/usr/share/nginx/html/my_project
    ports:
      - '8888:8080'
volumes:
  app_dist:

Then update the nginx.conf to point the root to a sub directory that contains the build.

...
root /usr/share/nginx/html/my_project/dist;
...

Then I can just run

sudo docker-compose run node-server yarn build

This will throw the generated files into the volume app_dist, then when I up the nginx service, it will see the built files.

docker volume inspect will show you the actual host directory that the volume pointing to, so without docker, you can poke the folder to verify the files.

This method is also used to access shared database instance. The actual database file is set as a named volume and shared. To backup, just go to the host folder and copy the database file.

Moreover, docker allows specifying storage driver for finding, accessing and writing a volume. This enables the cloud provider to provide a more scalable and fault tolerant way to store data.

(End of article)