Search code examples
dockerdockerfilemkdirdocker-volume

File made in Dockerfile lost when exec docker run with "-v /path:/path" params


I am in trouble with docker volume. I want to mkdir directory and touch file in it within the Dockerfile.

When I exec docker run -it --name any_name with -v /path:/path image_name params, the directory and file made in Dockerfile are both lost.

For example:

Dockerfile for build image BB:tag.

FROM AA:tag

RUN set -ex \
    && mkdir -p /var/log/my_directory/ \
    && touch /var/log/my_directory/my_file.log

CMD ["anything_cmd"]

If I exec

docker run -it --name any_name BB:tag

my_directory and my_file.log are both there.

But if I exec

docker run -it --name any_name -v /var/log/container_logs/any_name:/var/log/ BB:tag

my_directory and my_file.log are both missing.

What happened to it with -v when docker run? and what's the best practice to do such kind of thing?


Solution

    • Steps inside the Dockerfile are used to create the image.

    • The docker run command takes the image and creates a container.

    • Mounting a volume (the -v argument) will bind mount a directory into the container in a given location. And host mounts do not have any initialization step on that volume.

    Therefore, it's expected to only see files from /var/log/container_logs/any_name on the host inside the container at /var/log/.


    If you need to initialize a volume, there are a few options:

    1. You can use a named volume rather than a host volume. This will be initialized, but only when that named volume is empty. Once it has files in the named volume, docker will not initialize it again since that could result in data loss.

    2. You can use an entrypoint script inside your container to copy files from a saved location into the volume location. This saved location inside your image cannot be overlaid with a volume, otherwise you will have the same issue.


    If you want a named volume pointing to a host directory, you can use the local volume driver (this is the default) with options to bind mount a specific directory. Various methods to implement that include:

      # create the volume in advance
      $ docker volume create --driver local \
          --opt type=none \
          --opt device=/home/user/test \
          --opt o=bind \
          test_vol
    
      # create on the fly with --mount
      $ docker run -it --rm \
        --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
        foo
    
      # inside a docker-compose file
      ...
      volumes:
        bind-test:
          driver: local
          driver_opts:
            type: none
            o: bind
            device: /home/user/test
      ...
    

    And if you want to use an entrypoint script, see the volume caching description in my base image repo.