How hash IDs are managed for each RUN statements in Dockfile? - docker

For example, I may have the following Dockfile. When I run docker build, for each RUN, there is a spearate hash (e.g., 1d9c17228a9e), and it runs very fast it had run already. I guess each hash is associated an actual file at the backend. Is it so?
If there are separate files, how they can be loaded in a single virtual machine quickly? Is there any kind of assemble upon starting a new virtual machine (docker container)? Thanks.
$ docker build -t ubtsrv .
Sending build context to Docker daemon 12.29kB
Step 1/22 : FROM ubuntu
---> 1d9c17228a9e
Step 2/22 : RUN rm -rf /etc/dpkg/dpkg.cfg.d/excludes
---> Using cache
---> eb02f606ba08
Step 3/22 : RUN apt-get -y update && dpkg -l | grep ^ii | cut -d' ' -f3 | xargs apt-get install -y --reinstall
---> Using cache
---> 7062816b0023
Step 4/22 : RUN apt-get -y install apt-utils
---> Using cache
---> b89d4cdb791c
Step 5/22 : RUN apt -y update && apt -y upgrade
---> Using cache
---> 8100af2b7f2e
Step 6/22 : RUN apt-get -y install vim
---> Using cache
---> 57c142f99935
Step 7/22 : RUN apt-get -y install man
---> Using cache
---> ddb73e4bbddc
Step 8/22 : RUN apt-get -y install gawk
---> Using cache
---> 7422b4371c16
Step 9/22 : RUN apt-get -y install mawk
---> Using cache
---> 53a01709a342
Step 10/22 : RUN apt-get -y install build-essential
---> Using cache
---> af94947e6922
Step 11/22 : RUN apt-get -y install command-not-found
---> Using cache
---> 20094698a583
Step 12/22 : RUN apt-get -y install clang
---> Using cache
---> e63570058a57
Step 13/22 : RUN apt-get -y install htop
---> Using cache
---> b09fec30dc23
Step 14/22 : RUN apt-get -y install wget
---> Using cache
---> d2794d29f9ee
Step 15/22 : RUN apt-get -y install curl
---> Using cache
---> 2b122c49f3ca
Step 16/22 : RUN wget -q ftp://ftp.gnu.org/gnu/bash/bash-4.4.18.tar.gz && tar xzvf bash-4.4.18.tar.gz && cd bash-4.4.18 && ./configure && make -j && make install && cd .. && rm -rf bash-4.4.18.tar.gz bash-4.4.18
---> Using cache
---> c4bf046aff2a
Step 17/22 : RUN apt-get install -y git
---> Using cache
---> 40ebefa7acda
Step 18/22 : RUN apt-get install -y ack
---> Using cache
---> 05cefb3f0496
Step 19/22 : RUN apt-get install -y info
---> Using cache
---> 3361e4e4e06f
Step 20/22 : RUN apt-get install -y llvm
---> Using cache
---> 50b7c75fc2f5
Step 21/22 : RUN apt-get install -y graphviz
---> Using cache
---> 80f89477930c
Step 22/22 : RUN apt-get install -y cmake
---> Using cache
---> c8320b1b2523
Successfully built c8320b1b2523
Successfully tagged ubtsrv:latest
$ cat Dockerfile
FROM ubuntu
RUN rm -rf /etc/dpkg/dpkg.cfg.d/excludes
RUN apt-get -y update && \
dpkg -l | grep ^ii | cut -d' ' -f3 | xargs apt-get install -y --reinstall
RUN apt-get -y install apt-utils
RUN apt -y update && apt -y upgrade
RUN apt-get -y install vim
RUN apt-get -y install man
RUN apt-get -y install gawk
RUN apt-get -y install mawk
RUN apt-get -y install build-essential
RUN apt-get -y install command-not-found
RUN apt-get -y install clang
RUN apt-get -y install htop
RUN apt-get -y install wget
RUN apt-get -y install curl
RUN wget -q ftp://ftp.gnu.org/gnu/bash/bash-4.4.18.tar.gz && \
tar xzvf bash-4.4.18.tar.gz && \
cd bash-4.4.18 && \
./configure && \
make -j && \
make install && \
cd .. && \
rm -rf bash-4.4.18.tar.gz bash-4.4.18
RUN apt-get install -y git
RUN apt-get install -y ack
RUN apt-get install -y info
RUN apt-get install -y llvm
RUN apt-get install -y graphviz
RUN apt-get install -y cmake

Each hash is a docker layer. It's just a filesystem layer containing the different files added in that step. If you dip into docker internals you can actually take a look at the specific files that were added.
This section on docker caching describes how docker decides what is cached and what is not: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache
This tool is a lot of fun: https://github.com/wagoodman/dive an easy way to explore your docker images and check out the contents of each layer.
Let's talk through an example dockerfile:
FROM alpine
WORKDIR /opt/
RUN touch foo && mkdir bar && touch bar/foo
RUN rm foo && touch file.txt
RUN rm -rf bar
Here's the build output:
Building app
Step 1/5 : FROM alpine
---> 196d12cf6ab1
Step 2/5 : WORKDIR /opt/
---> Running in 2098e27c28b9
Removing intermediate container 2098e27c28b9
---> 74634b6a7dcd
Step 3/5 : RUN touch foo && mkdir bar && touch bar/foo
---> Running in f109a620ebfd
Removing intermediate container f109a620ebfd
---> dea70d465cc1
Step 4/5 : RUN rm foo && touch file.txt
---> Running in 367e61e301ba
Removing intermediate container 367e61e301ba
---> 9dcca4810268
Step 5/5 : RUN rm -rf bar
---> Running in d176de336110
Removing intermediate container d176de336110
---> 2e2eee6b9bf8
Successfully built 2e2eee6b9bf8
Successfully tagged docker-fsl_app:latest
If I run docker inspect 2e2eee6b9bf8 (the outputed hash above) docker returns a bunch of data. Included in that are two sections:
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/de87e6b38f95b44137409b5a61b498781473bc05cfd74a01dd641245219c2a1f/diff:/var/lib/docker/overlay2/02d58096fd47908c82edbc34dd0205541e525afe804e88f517ff47ccf3beeee0/diff:/var/lib/docker/overlay2/91fb3592a0da4847071a51e7dda4f48b810a5d1ff0b22e34bb38a0ee52d13d09/diff:/var/lib/docker/overlay2/2e966b19c5984548a6adb172d092dd21b2bb73f6be839baa680dc524d5221063/diff",
"MergedDir": "/var/lib/docker/overlay2/3216972ae99360398a74720226b26b61f0c04142ad6aaa519c1a9dd36f7fb945/merged",
"UpperDir": "/var/lib/docker/overlay2/3216972ae99360398a74720226b26b61f0c04142ad6aaa519c1a9dd36f7fb945/diff",
"WorkDir": "/var/lib/docker/overlay2/3216972ae99360398a74720226b26b61f0c04142ad6aaa519c1a9dd36f7fb945/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:df64d3292fd6194b7865d7326af5255db6d81e9df29f48adde61a918fbd8c332",
"sha256:b9f91d14f5d797f43eeb5b56264cc697641d50dd5e9d17bf89f33cf0694f6559",
"sha256:97195b4b7c22c7eb8720edeb93feeb6901a34018ce1f3c90dc17f861438abf21",
"sha256:0f3d56ac5865b537686b1e324dfbf54edde5afd06e644903ad6b9af42eab01df",
"sha256:5ff5ef92db130446e0af4836ffba8fbf29d06643aa05a104cb4c7a4c9e462fc7"
]
},
I'm on osx. On osx I can run screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty to access the docker vm. Within the vm, I can actually look at the filesystem layers.
These are the layers in reverse order: /var/lib/docker/overlay2/de87e6b38f95b44137409b5a61b498781473bc05cfd74a01dd641245219c2a1f/diff:/var/lib/docker/overlay2/02d58096fd47908c82edbc34dd0205541e525afe804e88f517ff47ccf3beeee0/diff:/var/lib/docker/overlay2/91fb3592a0da4847071a51e7dda4f48b810a5d1ff0b22e34bb38a0ee52d13d09/diff:/var/lib/docker/overlay2/2e966b19c5984548a6adb172d092dd21b2bb73f6be839baa680dc524d5221063/diff
If you go to those locations you'll see just the files added or removed in that layer. So if I go to /var/lib/docker/overlay2/de87e6b38f95b44137409b5a61b498781473bc05cfd74a01dd641245219c2a1f/diff/opt within the vm and run ls -lah. This is the output:
drwxr-xr-x 2 root root 4.0K Jan 14 16:15 .
drwxr-xr-x 3 root root 4.0K Jan 14 16:15 ..
-rw-r--r-- 1 root root 0 Jan 14 16:15 file.txt
c--------- 1 root root 0, 0 Jan 14 16:15 foo
file.txt has been added and foo has been deleted (I think that's why foo doesn't have permissions, the specific details of what a "deleted" file is is unclear to me).
So for every build layer the diff of files added or deleted is added as a lyaer.

Related

Dockerfile is not working properly when using WORKDIR command?

Here's my Dockerfile that I want to use for one of my web-api using python fastapi, but whenever I try to built it, I am getting the below given error.
FROM tiangolo/uvicorn-gunicorn:python3.8
RUN apt-get update && \
apt-get upgrade -y && \
apt-get dist-upgrade -y && \
apt-get autoremove -y && \
apt-get clean && \
apt-get autoclean && \
apt-get install -y gcc make apt-transport-https ca-certificates build-essential
RUN apt-get install -y curl autoconf automake libtool pkg-config git
RUN git clone https://github.com/openvenues/libpostal
WORKDIR /libpostal
RUN ./bootstrap.sh
RUN libpostal/configure --datadir=/opt
RUN libpostal/make -j $(nproc)
RUN libpostal/make install && ldconfig
ENV PORT 8000
ENV APP_MODULE app.parser:app
ENV LOG_LEVEL debug
ENV WEB_CONCURRENCY 2
COPY ./requirements/base.txt ./requirements/base.txt
RUN pip install --no-cache-dir -r requirements/base.txt
COPY ./app /app/app
Whenever I run this I am getting this below error,
Sending build context to Docker daemon 4.262GB
Step 1/18 : FROM tiangolo/uvicorn-gunicorn:python3.8
---> 524e010ef786
Step 2/18 : ENV ENVIRONMENT staging
---> Using cache
---> d3e496ea9bbe
Step 3/18 : RUN apt-get update && apt-get upgrade -y && apt-get dist-upgrade -y && apt-get autoremove -y && apt-get clean && apt-get autoclean && apt-get install -y gcc make apt-transport-https ca-certificates build-essential
---> Using cache
---> cf3c1a8556e0
Step 4/18 : RUN apt-get install -y curl autoconf automake libtool pkg-config git
---> Using cache
---> 77879c6f66e9
Step 5/18 : RUN git clone https://github.com/openvenues/libpostal
---> Using cache
---> f1f7cf06e398
Step 6/18 : WORKDIR /libpostal
---> Running in 51191c3a69cb
Removing intermediate container 51191c3a69cb
---> d98ff97331db
Step 7/18 : RUN ./bootstrap.sh
---> Running in 40fd37f4900b
/bin/sh: 1: ./bootstrap.sh: not found
The command '/bin/sh -c ./bootstrap.sh' returned a non-zero code: 127
Please tell me what am I doing wrong in the Dockerfile?
The default WORKDIR for your base image tiangolo/uvicorn-gunicorn:python3.8 is /app. I believe this is the Dockerfile for the base. When you cloned the repo, you were actually running it in /app.
You can explicitly set WORKDIR / or specify WORKDIR /app/libpostal to successfully run the bootstrap script.
You should also adjust your paths in the RUN commands after cloning since they should be relative. Here are the changes I suggest:
Option 1
# this command is run in the /app folder, a default set in the base image
RUN git clone https://github.com/openvenues/libpostal
WORKDIR /app/libpostal
RUN ./bootstrap.sh
RUN ./configure --datadir=/opt
RUN make -j $(nproc)
RUN make install && ldconfig
Option 2
# explicitly set working directory in root
WORKDIR /
RUN git clone https://github.com/openvenues/libpostal
WORKDIR /libpostal
RUN ./bootstrap.sh
RUN ./configure --datadir=/opt
RUN make -j $(nproc)
RUN make install && ldconfig

Getting Permission denied error while installing grafana in docker container

I am trying to install grafana in docker container but getting permission denied error. Below are the details :
sudo docker build -t grafana:latest .
Sending build context to Docker daemon 5.12 kB
Step 1/8 : FROM grafana/grafana:6.3.5
---> 2017e1eb54fa
Step 2/8 : RUN apt-get update && apt-get install -y curl gettext-base && rm -rf /var/lib/apt/lists/*
---> Running in 4c3b0835bb22
Reading package lists...
E: List directory /var/lib/apt/lists/partial is missing. - Acquire (13: Permission denied)
The command '/bin/sh -c apt-get update && apt-get install -y curl gettext-base && rm -rf /var/lib/apt/lists/*' returned a non-zero code: 100
I have tried running it using sudo but that didn't work. Also I have tried
RUN sudo apt-get update && apt-get install -y curl gettext-base && rm -rf /var/lib/apt/lists/*
but this also didn't work.
Below is the dockerfile
FROM grafana/grafana:6.3.5
RUN apt-get update && apt-get install -y curl gettext-base && rm -rf /var/lib/apt/lists/*
WORKDIR /etc/grafana
COPY datasources ./datasources
WORKDIR /app
COPY entrypoint.sh ./
RUN chmod u+x entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
try this:
FROM grafana/grafana:6.3.5
USER root
RUN apt-get update && apt-get install -y curl gettext-base && rm -rf /var/lib/apt/lists/*
USER grafana
WORKDIR /etc/grafana
COPY datasources ./datasources
WORKDIR /app
COPY entrypoint.sh ./
RUN chmod u+x entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
the image use the default user: grafana
You can try docker commit to make your changes permanent. it's easier than build a new image with dockerfile.
first you should go inside container, then make your changes like upgrading or changing configs and finally commit changes.
host$ docker exec -it "container_name/id" bash
container# apt-get update
host$ docker commit "container_name/id" my-grafana
You can see image list with docker image ls

Docker-entrypoint.sh results in "not found" for ARM image with golang

My problem is that I get an error when running my container on an ARM arch system(RaspberryPI with Raspbian). Image was built on that same Raspberry.
This is my dockerfile:
FROM arm32v7/golang
COPY qemu-arm-static /usr/bin
ENV STATUSOK_VERSION 0.1.1
RUN apt-get update \
&& apt-get install -y unzip \
&& wget https://github.com/sanathp/statusok/releases/download/$STATUSOK_VERSION/statusok_linux.zip \
&& unzip statusok_linux.zip \
&& mv ./statusok_linux/statusok /go/bin/StatusOk \
&& rm -rf ./statusok_linux* \
&& apt-get remove -y unzip git \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
VOLUME /config
COPY ./docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT /docker-entrypoint.sh
I'm able to succesfully build this on a RaspberryPI running Raspbian:
root#raspberrypi:~/armstatusok# docker build . -t armstatusok
Sending build context to Docker daemon 6.656kB
Step 1/7 : FROM arm32v7/golang
---> 8bbfdfd01a06
Step 2/7 : COPY qemu-arm-static /usr/bin
---> Using cache
---> 2572fd1e03a0
Step 3/7 : ENV STATUSOK_VERSION 0.1.1
---> Using cache
---> 25d39a4c6eb5
Step 4/7 : RUN apt-get update && apt-get install -y unzip && wget https://github.com/sanathp/statusok/releases/download/$STATUSOK_VERSION/statusok_linux.zip && unzip statusok_linux.zip && mv ./statusok_linux/statusok /go/bin/StatusOk && rm -rf ./statusok_linux* && apt-get remove -y unzip git && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
---> Using cache
---> bfb1cfa9a985
Step 5/7 : VOLUME /config
---> Using cache
---> 3bfbce28329b
Step 6/7 : COPY ./docker-entrypoint.sh /docker-entrypoint.sh
---> Using cache
---> a1795ca4f40c
Step 7/7 : ENTRYPOINT /docker-entrypoint.sh
---> Using cache
---> d0ce74911ba3
Successfully built d0ce74911ba3
Successfully tagged armstatusok:latest
Next step is to run it, and where I get into trouble:
root#raspberrypi:~/armstatusok# docker run --name=armstatusok -v $PWD:/config armstatusok
/docker-entrypoint.sh: 1: /docker-entrypoint.sh: /go/bin/StatusOk: not found
I went into the container commenting line one of the docker-entrypoint.sh and checked if /go/bin/StatusOk was actually there, and it was.
My docker-entrypoint.sh:
root#raspberrypi:~/armstatusok# cat docker-entrypoint.sh
/go/bin/StatusOk --config /config/config.json
Now my question is, does anybody have a clue where to start? I also tested this dockerfile on x86 arch, and there it worked. I only changed the FROM line to the x86 flavour and removed the COPY qemu-arm-static /usr/bin since that line is there to make it work on ARM arch, according to documentation.
I copied this Dockerfile and start script verbatim and it builds and runs perfectly for me. I get
Config file not present at the given location: /config/config.json give correct file location using --config parameter
because I don't have access to the config file you're using. But the fact I get that message means that StatusOk is running. So I don't know what to suggest.
The only difference I made was to add a shebang #!/bin/sh to the start of the docker-entrypoint.sh file, and ensure it has execute permission, by running ls -al, and if it doesn't have x in the permissions, running chmod +rwx. Don't know if that made any difference as to how the script tried to access /go/bin/StatusOk.
Full docker-entrypoint.sh contents:
#!/bin/sh
/go/bin/StatusOk --config /config/config.json

Why docker layer is not cached?

I have Dockerfile which have next command:
RUN source $PERLBREW_ROOT/etc/bashrc && perlbrew install $PERL_VERSION
Here layers start to rebuild:
Step 12/27 : RUN echo -e "\nif [ -f /opt/perlbrew/etc/bashrc ]; then\n\tsource /opt/perlbrew/etc/bashrc\nfi\n" >> /root/.bash_profile
---> Using cache
---> b18437df38fb
Step 13/27 : RUN source $PERLBREW_ROOT/etc/bashrc && perlbrew install $PERL_VERSION
---> Running in 3b76e5d4ae0a
Fetching perl 5.24.1 as /opt/perlbrew/dists/perl-5.24.1.tar.bz2
Download http://www.cpan.org/authors/id/S/SH/SHAY/perl-5.24.1.tar.bz2 to /opt/perlbrew/dists/perl-5.24.1.tar.bz2
Installing /opt/perlbrew/build/perl-5.24.1/perl-5.24.1 into /opt/perlbrew/perls/perl-5.24.1
Why cached layer is not used for this command?
UPD
The docker file:
FROM centos:latest
ARG PERLBREW_ROOT=/opt/perlbrew
ARG PERL_VERSION=5.24.1
ARG MONKEYMAN_DIR=/opt/monkeyman
RUN yum -y install yum-plugin-ovl
RUN yum -y upgrade
RUN yum -y install perl
RUN yum-builddep -y perl
RUN yum install -y bzip2 zip which
RUN yum groupinstall -y 'Development Tools'
RUN curl -L https://install.perlbrew.pl | bash
RUN echo -e "\nif [ -f /opt/perlbrew/etc/bashrc ]; then\n\tsource /opt/perlbrew/etc/bashrc
\nfi\n" >> /root/.bash_profile
RUN source $PERLBREW_ROOT/etc/bashrc && perlbrew install $PERL_VERSION
RUN source $PERLBREW_ROOT/etc/bashrc && perlbrew switch $PERL_VERSION
RUN source $PERLBREW_ROOT/etc/bashrc && perlbrew install-cpanm
RUN source $PERLBREW_ROOT/etc/bashrc && cpanm Carton
no arguments provided when build
UPD
$ docker --version
Docker version 1.13.1, build 07f3374/1.13.1

I can't install specific version (1.0.2g) of openssl in docker

I want to install openssl version 1.0.2g in docker image so I wrote Dockerfile:
RUN apt-get update
RUN apt-get install -y build-essential cmake zlib1g-dev libcppunit-dev git subversion && rm -rf /var/lib/apt/lists/*
RUN wget https://www.openssl.org/source/openssl-1.0.2g.tar.gz -O - | tar -xz
WORKDIR /openssl_1.0.2g
RUN ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl
and tried to build it:
Removing intermediate container 0666b2c5021f
---> e92f7ed1e3a0
Step 11/14 : WORKDIR /openssl_1.0.2g
Removing intermediate container c8e083d9a453
---> 112f18273e8f
Step 12/14 : RUN ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl
---> Running in 4871c00e5c35
/bin/sh: 1: ./config: not found
The command '/bin/sh -c ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl' returned a non-zero code: 127
but it doesn't work...
How can I fix it?
What base image do you use to build an image?
It works pretty fine with ubuntu:16.04 base image and the same Dockerfile you provided:
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install -y build-essential cmake zlib1g-dev libcppunit-dev git subversion wget && rm -rf /var/lib/apt/lists/*
RUN wget https://www.openssl.org/source/openssl-1.0.2g.tar.gz -O - | tar -xz
WORKDIR /openssl-1.0.2g
RUN ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl && make && make install

Resources