Docker `COPY` files into container not persistant

4/21/2019

I have created a docker image for the an app in which i am doing a copy of folder to the image like this:

COPY extra-addons/ /mnt/extra-addons/pos_item_price/

but when i use that image using kubernetes and go to the /mnt/extra-addons folder

$ kubectl --insecure-skip-tls-verify --namespace my-app exec -it my-app-55d464dd78-7h7x7 -- /bin/bash
root@my-app-55d464dd78-7h7x7:/# cd /mnt/extra-addons/
root@my-app-55d464dd78-7h7x7:/mnt/extra-addons# ls
root@my-app-55d464dd78-7h7x7:/mnt/extra-addons#

i see nothing there

but i do see that data is being copied when i am building the image

Step 19/26 : COPY extra-addons/ /mnt/extra-addons/pos_item_price/
---> 47fda7baba98
Step 20/26 : RUN ls -la /mnt/extra-addons/*
---> Running in ab93cf423db5
total 12
drwxr-xr-x. 3 odoo root 4096 Apr 21 11:13 .
drwxr-xr-x. 3 odoo root 4096 Apr 21 11:13 ..
drwxrwxrwx. 7 root root 4096 Apr 21 11:13 pos_item_price
Removing intermediate container ab93cf423db5
---> 645bc64741e0
Step 21/26 : RUN ls -la /mnt/extra-addons/pos_item_price/*
---> Running in f6ad09d6d83c
total 44
drwxrwxrwx. 7 root root 4096 Apr 21 11:13 .
drwxr-xr-x. 3 odoo root 4096 Apr 21 11:13 ..
-rw-rw-rw-. 1 root root 77 Apr 21 11:10 .git
-rw-rw-rw-. 1 root root 579 Apr 21 11:10 .gitignore
-rw-rw-rw-. 1 root root 45 Apr 21 11:10 __init__.py
-rw-rw-rw-. 1 root root 571 Apr 21 11:10 __manifest__.py
drwxrwxrwx. 2 root root 4096 Apr 21 11:13 data
drwxrwxrwx. 2 root root 4096 Apr 21 11:13 models
drwxrwxrwx. 2 root root 4096 Apr 21 11:13 security
drwxrwxrwx. 3 root root 4096 Apr 21 11:13 static
drwxrwxrwx. 2 root root 4096 Apr 21 11:13 views
Removing intermediate container f6ad09d6d83c
---> dc35af25b2a8

I wonder why it is not persistant when i am copying it into the image, i would have expected the data to be present in the kubernetes pod?

Full Dockerfile

FROM debian:stretch

# Generate locale C.UTF-8 for postgres and general locale data
ENV LANG C.UTF-8

# Install some deps, lessc and less-plugin-clean-css, and wkhtmltopdf
RUN set -x; \
        apt-get update \
        && apt-get install -y --no-install-recommends \
            ca-certificates \
            curl \
            dirmngr \
            fonts-noto-cjk \
            gnupg \
            libssl1.0-dev \
            node-less \
            python3-pip \
            python3-pyldap \
            python3-qrcode \
            python3-renderpm \
            python3-setuptools \
            python3-vobject \
            python3-watchdog \
            xz-utils \
        && curl -o wkhtmltox.deb -sSL https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb \
        && echo '7e35a63f9db14f93ec7feeb0fce76b30c08f2057 wkhtmltox.deb' | sha1sum -c - \
        && dpkg --force-depends -i wkhtmltox.deb\
        && apt-get -y install -f --no-install-recommends \
        && rm -rf /var/lib/apt/lists/* wkhtmltox.deb

# install latest postgresql-client
RUN set -x; \
        echo 'deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main' > etc/apt/sources.list.d/pgdg.list \
        && export GNUPGHOME="$(mktemp -d)" \
        && repokey='B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8' \
        && gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "${repokey}" \
        && gpg --armor --export "${repokey}" | apt-key add - \
        && gpgconf --kill all \
        && rm -rf "$GNUPGHOME" \
        && apt-get update  \
        && apt-get install -y postgresql-client \
        && rm -rf /var/lib/apt/lists/*

# Install rtlcss (on Debian stretch)
RUN set -x;\
    echo "deb http://deb.nodesource.com/node_8.x stretch main" > /etc/apt/sources.list.d/nodesource.list \
    && export GNUPGHOME="$(mktemp -d)" \
    && repokey='9FD3B784BC1C6FC31A8A0A1C1655A0AB68576280' \
    && gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "${repokey}" \
    && gpg --armor --export "${repokey}" | apt-key add - \
    && gpgconf --kill all \
    && rm -rf "$GNUPGHOME" \
    && apt-get update \
    && apt-get install -y nodejs \
    && npm install -g rtlcss \
    && rm -rf /var/lib/apt/lists/*

# Install Odoo
ENV ODOO_VERSION 12.0
ARG ODOO_RELEASE=20190128
ARG ODOO_SHA=9e34aaed2eb1e7697aaf36767247dbf335e9fe7a
RUN set -x; \
        curl -o odoo.deb -sSL http://nightly.odoo.com/${ODOO_VERSION}/nightly/deb/odoo_${ODOO_VERSION}.${ODOO_RELEASE}_all.deb \
        && echo "${ODOO_SHA} odoo.deb" | sha1sum -c - \
        && dpkg --force-depends -i odoo.deb \
        && apt-get update \
        && apt-get -y install -f --no-install-recommends \
        && rm -rf /var/lib/apt/lists/* odoo.deb

# Copy entrypoint script and Odoo configuration file
RUN pip3 install num2words xlwt
COPY ./entrypoint.sh /
COPY ./odoo.conf /etc/odoo/
RUN chown odoo /etc/odoo/odoo.conf

# Mount /var/lib/odoo to allow restoring filestore and /mnt/extra-addons for users addons
RUN mkdir -p /mnt/extra-addons/pos_item_price \
        && chown -R odoo /mnt/extra-addons
VOLUME ["/var/lib/odoo", "/mnt/extra-addons"]

RUN ls -la /mnt/extra-addons/*
RUN echo "-------- Before LS END -----"
COPY extra-addons/ /mnt/extra-addons/pos_item_price/
RUN ls -la /mnt/extra-addons/*
RUN ls -la /mnt/extra-addons/pos_item_price/*

# Expose Odoo services
EXPOSE 8069 8071

# Set the default config file
ENV ODOO_RC /etc/odoo/odoo.conf

# Set default user when running the container
USER odoo

ENTRYPOINT ["/entrypoint.sh"]
CMD ["odoo"]
-- Qambar Raza
copy
docker
kubernetes

1 Answer

4/21/2019

I believe the issue you are facing is related to your volume, but not the one defined inside the Dockerfile (even though I personally dislike any volume defined in the Dockerfile because of issues they cause).

To explain the issues resulting from the VOLUME in the Dockerfile, you can see the following example to test COPY, ADD, and RUN:

$ cat df.vol
FROM busybox:latest

VOLUME ["/data"]
CMD find /data

COPY sample-data/file.txt /data/file.txt
COPY sample-data/dir /data/dir
ADD sample-data/tar-file.tgz /data/tar-dir
RUN echo "hello world" >/data/run.txt \
 && find /data \
 && sleep 5m

Here's the sample-data directory used for the COPY and ADD commands:

$ ls -al sample-data/
total 32 
drwxr-xr-x  3 bmitch bmitch  4096 Jan 22  2017 .
drwxr-xr-x 34 bmitch bmitch 12288 Apr 17 15:16 ..
drwxr-xr-x  2 bmitch bmitch  4096 Jan 22  2017 dir
-rw-r--r--  1 bmitch bmitch    14 Jan 22  2017 file2.txt
-rw-r--r--  1 bmitch bmitch    12 Jan 22  2017 file.txt
-rw-r--r--  1 bmitch bmitch   214 Jan 22  2017 tar-file.tgz

Lets run a build (without BUILDKIT since we want to be able to debug this):

$ DOCKER_BUILDKIT=0 docker build -f df.vol -t test-vol .
Sending build context to Docker daemon  23.04kB
Step 1/7 : FROM busybox:latest
 ---> 59788edf1f3e
Step 2/7 : VOLUME ["/data"]
 ---> Using cache
 ---> 14b4f1130806                      
Step 3/7 : CMD find /data     
 ---> Running in 75673363d1e3             
Removing intermediate container 75673363d1e3
 ---> 262714d065fc
Step 4/7 : COPY sample-data/file.txt /data/file.txt
 ---> d781519c584e
Step 5/7 : COPY sample-data/dir /data/dir
 ---> 34b5b4a83b1e
Step 6/7 : ADD sample-data/tar-file.tgz /data/tar-dir
 ---> 3fc45f2e62a4                                                
Step 7/7 : RUN echo "hello world" >/data/run.txt  && find /data  && sleep 5m
 ---> Running in d75794387274
/data
/data/dir
/data/dir/file1.txt
/data/dir/file2.txt
/data/run.txt
/data/tar-dir
/data/tar-dir/dir
/data/tar-dir/dir/file1.txt
/data/tar-dir/dir/file2.txt
/data/tar-dir/file.txt
/data/file.txt
Removing intermediate container d75794387274
 ---> 5af322be539a
Successfully built 5af322be539a
Successfully tagged test-vol:latest

Note the run.txt file above. We also see the files from the COPY and ADD commands. However, if we ran another RUN command, or any time we use the resulting image, we'll see:

$ docker run -it --rm test-vol:latest
/data
/data/dir
/data/dir/file1.txt
/data/dir/file2.txt
/data/tar-dir
/data/tar-dir/dir
/data/tar-dir/dir/file1.txt
/data/tar-dir/dir/file2.txt
/data/tar-dir/file.txt
/data/file.txt

Only the files from COPY and ADD are there. The reason for that is easier to see if we look at the temporary container that docker uses for the RUN steps (this is why I had the sleep 5m during the build). Here's the output from another window during that 5 minute sleep:

$ docker ps -l                         
CONTAINER ID        IMAGE               COMMAND                   CREATED             STATUS           PORTS               NAMES
d75794387274        3fc45f2e62a4        "/bin/sh -c 'echo \"h…"   1 second ago        Created                               brave_dubinsky

$ docker diff d75

$ docker inspect d75                                                                                                                                                                            
[                                                                                                                                                                                               
    {                                                                                                                                                                                           
        "Id": "d75794387274cc222391065c14581a29ff9fcc898ef367db64b9f145bd9325c7",                                                                                                               
        "Created": "2019-04-21T18:19:19.449392301Z",                                                                                                                                            
        "Path": "/bin/sh",                                                                                                                                                                      
        "Args": [                                                                                                                                                                               
            "-c",                                                                                                                                                                               
            "echo \"hello world\" >/data/run.txt  && find /data  && sleep 5m"                                                                                                                   
        ],                                                                                                                                                                                      
        "State": {                                                                                                                                                                              
            "Status": "running",       
            "Running": true,      
            "Paused": false,        
            "Restarting": false,                                                                    
            "OOMKilled": false,                                                                      
            "Dead": false,                     
            "Pid": 31620,                        
            "ExitCode": 0,            
            "Error": "",              
            "StartedAt": "2019-04-21T18:19:22.699031557Z",                         
            "FinishedAt": "0001-01-01T00:00:00Z"
        },                                            
        ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "07b9d30dfdfcae91b820dc6fa249030fd8d7a4ad9c50ee928aaab104c07c8a9d",
                "Source": "/home/var-docker/volumes/07b9d30dfdfcae91b820dc6fa249030fd8d7a4ad9c50ee928aaab104c07c8a9d/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        ...

What you see in the above commands is that docker runs the build with a temporary container and an anonymous volume. The diff output shows the changes to that container that will be captured by the build as a layer in the Dockerfile. In this case, nothing.

Making the change to the volume doesn't modify the container filesystem, so you never see the change, but the ADD and COPY commands run directly against the image layers so you do see those changes.

Will removing the VOLUME from the Dockerfile fix this issue? Probably not (unless your method of running the image is creating and reusing an anonymous volume from the image). Do I recommend removing the VOLUME anyway? Yes, this isn't needed to later specify a volume when you run your container, you can define a volume then for any directory, and having in the Dockerfile breaks attempts to extend the image later with a RUN command in non-intuitive ways.


So, if it's not the VOLUME command interacting with your COPY, why else would you see your changes lost? The most likely cause is defining a volume when you run the container. We need to see your yml spec to know for sure. That volume, if it's a named volume, will be initialized with the contents of your image, but only once. After that, no matter what you change in your image, the volume will be persistent and show you the files in the volume from the last time you used that volume.

If you want to update a volume based on changes to your image, then you need to configure an entrypoint in your image to copy files from a saved location in the image, to your volume location in the container. I have examples of doing this in my docker-base image. See the save-volume and load-volume scripts there.

-- BMitch
Source: StackOverflow