Dockerfile Common Instructions Details & Mirror Cache Features

Posted by AndrewJ1313 on Wed, 19 Feb 2020 20:03:56 +0100

Introduction to Dockerfile

Dockerfile is the configuration file used in Docker to define the mirroring automation build process.In the Dockerfile, there are commands and other operations that need to be performed during the construction of the image.Dockerfile can make the process of making a given Docker image clearer and clearer. Because it is a simple, small-size file, it can transfer quickly in media such as network and realize container migration and cluster deployment more quickly.
Dockerfile is a text file that contains instructions that build a layer, so the content of each instruction is to describe how that layer should be built.

Dockerfile has many advantages over submitting container modifications for mirroring migration:

  • Dockerfile is much smaller than a mirror package, making it easier to migrate and deploy quickly.
  • The process of environment building is documented in Dockerfile, and the order and logic of mirror building can be visualized.
  • Building mirrors with Dockerfile makes automated processes such as automatic deployment easier.
  • Modifying the Dockerfile file is easier when modifying the environment setup details.

Container submission is seldom chosen in actual development use to build mirrors, but almost always Dockerfile.

Docker performs the general process of Dockerfile:

  • (1) The docker runs a container from the underlying mirror specified by the FROM header of the Dockerfile
  • (2) Then execute an instruction to modify the container.
  • (3) Next, do something similar to docker commit to create a new mirror layer.
  • (4) Run a new container based on the image you just created.
  • (4) Execute the next instruction in the dockerfile until all instructions have been executed.

    The docker deletes the container created by the middle tier, but it does not delete the middle tier image, so you can run a middle tier container using docker run to see the state of the image built at each step so that you can debug.

Dockerfile Basic Structure

Dockerfile consists of another line of command statements and supports comment lines that begin with #.
Dockerfile is divided into four parts: basic mirror information, maintainer information, mirror operation instructions, and execution instructions at container startup.
For example, here is a complete Dockerfile:

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# Base image to use, this must be set as the first line
1,The first line must be specified, underlying mirror information
FROM ubuntu

# Maintainer: docker_user <docker_user at email.com> (@docker_user)
2,Maintainer Information
MAINTAINER docker_user docker_user@email.com

# Commands to update the image
3,Mirror Operations Instructions
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# Commands when creating a new container
4,Container Start Execution Instruction
CMD /usr/sbin/nginx

The name of the mirror on which the image is based must be indicated at first, followed by the maintainer information, followed by mirroring instructions, such as RUN instructions.Each RUN instruction is executed, a new layer is added to the mirror, and the submission is made; finally, the CMD instruction indicates the operation command when the container is run.

Common Dockerfile directives

The following common dockerfile directives contain essentially 90% of the common functionality.

Common Directives Directory:

1, FROM--Specify the base image
 2, MAINTAINER -- Specify Maintainer Information
 3, RUN -- Run the specified command
 4, CMD--Commands executed at container startup
 5, EXPOSE--Declare the service port of the container
 6, ENV -- Setting environment variables
 7, ARG--build parameters
 8, COPY -- Copy a file or directory
 9, ADD--more advanced copy files/directories
 10, ENTRYPOINT--entry point
 11, ENTRYPOINT in conjunction with CMD directives
 12, VOLUME -- Define anonymous volumes
 13, WORKDIR -- Specify working directory
 14, USER -- Specify the current user
 15, ONBUILD -- Add triggers for mirroring

1, FROM--Specify the base image
The first instruction must be a FROM instruction.
If you are not based on any mirroring, the following is written, meaning that the instructions that you write next will start as the first layer of the mirror:
FROM nginx
The FROM directive supports three formats:

FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>

Three types of writing, the second and third are optional, and if no selection is made, the default is latest.

FROM directives can occur multiple times in a Dockerfile, and when FROM occurs a second time or later, it means that when you build at this time, you want to merge the current day-to-day use of pointing out mirrors into the content of building mirrors at this time.

ARG is the only parameter that can be executed before FROM:

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

2, MAINTAINER -- Specify Maintainer Information
The format is: MAINTAINER <name>, specify maintainer information (mirror maintainer name or mailbox), optional.
For example:
MAINTAINER docker_user docker_user@email.com
3, RUN -- Run the specified command
RUN has two formats:

RUN <command>
RUN ["executable", "param1", "param2"]

The former runs the command at the shell terminal, that is, /bin/sh-c; the latter runs with exec.Specifying the use of other terminals can be done in a second way, such as using bash terminals: RUN ['/bin/bash','-c','echo hello'].

Note: Multiline commands should not write more than one RUN, because each command in the Dockerfile establishes one layer, and how many RUNs build how many layers of mirrors, which can lead to bulky, multi-layered mirrors, not only increase the time for component deployment, but also make errors easily.Typically, a RUN command can be followed by multiple commands to be executed (using the ampersand character), and "\" can be used to wrap lines when the command is longer.
Examples are as follows:

RUN yum -y install gcc* pcre-devel openssl-devel zlib-devel unzip make vim net-tools elinks tree \
 && groupadd nginx \
 &&  useradd  nginx -g nginx  -s /sbin/nologin 

4, CMD--Commands executed at container startup
CMD supports three formats:

CMD ["executable","param1","param2"] is exec executed, recommended method;
CMD command param1 param2 is executed in/bin/sh to provide applications that require interaction;
CMD ["param1","param2"] provides default parameters to ENTRYPOINT;

Specifies the command to execute when the container is started, and each dockerfile can have only one CMD command.If more than one command is directed, only the last one will be executed.

If the user specifies a command to run when he starts the container, the command specified by CMD is overwritten.

Additional details: If you are not using the shell (/bin/sh) method, "[]" brackets must contain double quotes for parameters, and should never be written as single quotes.The reason is that after the parameter is passed, the docker parses a JSON array
For example:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
CMD echo hello world
CMD echo hello china
[root@sqm-docker01 dfs]# docker run --rm test:v1   #Only the last CMD command will be executed
hello china    
[root@sqm-docker01 dfs]# docker run --rm test:v1 echo "hello friend"  #Specify run commands when starting containers, which will be overwritten
hello friend

In exec format, the above example changes to:

FROM centos:latest
CMD ["/bin/bash","-c","echo hello world"]
CMD ["/bin/bash","-c","echo hello china"]

There is also a case where CMD is used in conjunction with the ENTRYPOINT directive, which will be discussed later.

5, EXPOSE--Declare the service port of the container
The format is EXPOSE <port 1> [<port 2>...].

The EXPOSE directive is a declaration that the runtime container provides a service port. This is only a declaration that services on this port will not be opened at runtime because of this declaration application.
Writing such a declaration in a Dockerfile has two advantages: one is to help mirror users understand the daemon ports of the mirror service to configure the mapping easily; the other is to automatically map the EXPOSE ports randomly when a random port mapping is used at run time, that is, when docker run-P is used.
To distinguish EXPOSE from using -p <host port>: <container port> at runtime.-p is the mapping of host and container ports, in other words, exposing the container's corresponding port services to outside access, while EXPOSE simply declares what ports the container intends to use and does not automatically map ports in the host.
For example:
EXPOSE 80 443

6, ENV -- Setting environment variables
There are two formats:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

The difference between the two is that one is set at a time, and the second is set more than one at a time.

The ENV directive specifies an environment variable that will be used by subsequent RUN s and maintained while the container is running.

ENV VERSION=1.0 DEBUG=on \
    NAME="Happy Feet"

This example demonstrates how to wrap lines and enclose values containing spaces in double quotes, which is consistent with the behavior under the shell.
Examples are as follows:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM busybox:latest
ENV var1=haha var2=hehe
RUN echo $var1 > a.txt && echo $var2 > b.txt
implement dockerfile Then proceed to the container to view the file:
[root@sqm-docker01 dfs]# docker run --rm test:v2 /bin/sh
/ # cat a.txt 
haha
/ # cat b.txt 
hehe
#And the defined variables remain when the container runs:
/ # echo $var1
haha
/ # echo $var2
hehe

7, ARG--build parameters

Format:
ARG <name>[=<default value>]

Build parameters and ENV s work the same way as setting environment variables.The difference is that the environment variables set by ARG that build the environment will not exist when the container runs in the future.But don't use ARG to save information like passwords because docker history can still see all the values.

#Usage 1: Pass parameters when executing docker build s

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ARG user
ARG password
RUN echo $user > a.txt \
    && echo $password >> a.txt
#When passing two parameters, add--build-arg to each parameter.
[root@sqm-docker01 dfs]# docker build --build-arg user=sqm --build-arg password=123.com  -t arg:v1 .
#Run container, parameter passed successfully
[root@sqm-docker01 dfs]# docker run -it --rm arg:v1 /bin/bash
root@4809b0c54f8d:/# cat a.txt 
sqm
123.com
#Unlike ENV, these variables do not exist in containers
root@9383b7d3d21e:/# echo $user

root@9383b7d3d21e:/# echo $password

root@9383b7d3d21e:/# 

Note: If this parameter is specified but not used in the Dockerfile, warnings will be output during the build process.

#Usage 2: Set a default value in the dockerfile, and if the ARG directive has a default value and no parameters are passed at build time, the build will use that default value

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ARG user=zhangsan
ARG password=123456
RUN echo $user $password > a.txt
#Build a mirror without passing any parameters:
[root@sqm-docker01 dfs]# docker build -t arg:v2 .
#Run container, parameter passed successfully
[root@sqm-docker01 dfs]# docker run -it --rm arg:v2 /bin/bash
root@b52fa70086de:/# cat a.txt 
zhangsan 123456

#Each stage in a multistage build must contain an arg directive:

FROM busybox
ARG user
RUN ./run/setup $user

FROM busybox
ARG user
RUN ./run/other $user

The #ENV variable overrides the ARG variable with the same name:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ARG user=zhangsan
ENV user lisi
RUN echo $user > a.txt
[root@sqm-docker01 dfs]# docker build -t arg:v3 .
[root@sqm-docker01 dfs]# docker run -it --rm arg:v3 /bin/bash
root@a2aefd05efee:/# cat a.txt 
lisi

#ARG is used in conjunction with ENV directives: (this is a common use, not common)

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
ARG var
ENV user=${var}
RUN echo $user > a.txt
[root@sqm-docker01 dfs]# docker build --build-arg var=zhangsan -t arg:v4 .
[root@sqm-docker01 dfs]# docker run -it --rm arg:v4 /bin/bash
[root@26bf8c139a3f /]# cat a.txt 
zhangsan

8, COPY -- Copy files or directories''
Format:

COPY [--chown=<user>:<group>] <from path>... <Target Path>
COPY [--chown=<user>:<group>] ["<Source Path 1>",... "<Target Path>"]

Like RUN instructions, there are two formats, the former running on a shell terminal and the latter executed using exec.

<Source Path>Paths on the host can be mu lt iple or even wildcards whose wildcard rules satisfy Go filepath.Match rules, such as:

COPY hom* /mydir/
COPY hom?.txt /mydir/

Note: <Source Path>must be in the build context directory, that is, the directory where the Dockerfile configuration file is located and the following recursive directory. If you copy a file in a directory outside the context (not the directory where the Dockerfile is located), you cannot copy it.
The concept of build context can be referred to as: https://www.cnblogs.com/sparkdev/p/9573248.html

<Target Path>is a path in a container, either absolute or relative to the working directory (the working directory can be specified with the WORKDIR directive).The destination path does not need to be created beforehand. If the directory does not exist, the missing directory will be created before the files are copied.

In addition, it is important to note that with the COPY directive, all metadata of the source file is preserved.For example, read, write, execute permissions, file change time, and so on.You can specify using the following commands:

COPY [--chown=:] ...
COPY [--chown=:] ["",... ""] (Paths with spaces need this form)

- chown functionality is only supported on Dockerfiles used to build Linux containers
For example:
COPY --chown=nginx:nginx files* /somedir/

Give an example:

[root@sqm-docker01 dfs]# echo "hello world" > index.html
[root@sqm-docker01 dfs]# echo aaaaaa > a.txt
[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
COPY ./index.html /usr/share/nginx/html/
COPY ./a.txt /test/
[root@sqm-docker01 dfs]# docker run --rm  nginx:v1  /bin/bash
root@8a1ee4925b43:/# cat /usr/share/nginx/html/index.html 
hello world
#Create the target directory yourself beforehand, even if it doesn't exist
root@8a1ee4925b43:/# cat /test/a.txt   
aaaaaa

Copy the entire directory:
Format: COPY src WORKDIR/src
For example:

[root@sqm-docker01 dfs]# cat test/a.txt 
addddddddddddaaaaa
[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
COPY ./test  /usr/share/nginx/test/  
#You must specify the directory to copy under the target path, or you will copy only the files in the source directory, not the directory

#Enter the container to view the entire directory and the files under it
[root@sqm-docker01 dfs]# docker run --rm nginx:v2 /bin/bash
root@4eae96cbe364:/# cd /usr/share/nginx/
root@4eae96cbe364:/usr/share/nginx# ls
html  test
root@4eae96cbe364:/usr/share/nginx# cat test/a.txt 
addddddddddddaaaaa

9, ADD--more advanced copy files/directories
The format is:

ADD [--chown=<user>:<group>] <from path>... <Target Path>
ADD [--chown=<user>:<group>] ["<Source Path 1>",... "<Target Path>"]

The format and nature of ADD instructions and COPY are basically the same.However, some features have been added to COPY`: it can be a URL (automatically set permissions 600 when downloaded from a URL) or a tar file (automatically decompressed into a directory).

Give an example:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
ADD nginx-1.8.0.tar.gz  /usr/src
[root@sqm-docker01 dfs]# docker run --rm  nginx:v3 /bin/bash
[root@0c8d6789aa4c /]# cd /usr/src/
[root@0c8d6789aa4c src]# ls   
debug  kernels  nginx-1.8.0     #Auto-extract tar package
[root@0c8d6789aa4c src]# ls nginx-1.8.0/
CHANGES  CHANGES.ru  LICENSE  README  auto  conf  configure  contrib  html  man  src

#Download the link file through url and place it in the target path:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
ADD http://nginx.org/download/nginx-1.9.0.tar.gz /

[root@sqm-docker01 dfs]# docker run --rm  nginx:v4 /bin/bash
[root@b9d978e3a333 /]# ls -lh nginx-1.9.0.tar.gz 
-rw------- 1 root root 835K Apr 28  2015 nginx-1.9.0.tar.gz
#Default permissions 600

The difference between #and COPY:
Similar to COPY, except that COPY can only be a local file/directory. If src is an archive file (tar,zip,tgz,xz, etc.), the ADD command file will be automatically decompressed to dest.

Therefore, when choosing between COPY and ADD instructions, you can follow the principle that COPY is used for all file copies and ADD is used only in cases where automatic decompression is required.

10, ENTRYPOINT--entry point
Two formats:

ENTRYPOINT ["executable", "param1", "param2"](exec Format)
ENTRYPOINT command param1 param2(shell Execute in).

ENTRYPOINT serves the same purpose as CMD in specifying container startup programs and parameters.ENTRYPOINT can also be replaced at runtime, but is slightly more cumbersome than CMD and needs to be specified by the docekr run parameter, entrypoint.

#Compare with CMD:
1) Same point: only one can be written, if more than one, only the last one will take effect.
The container runs when it is started and the runtime is the same.
2) The difference: ENTRYPOINT will not be overwritten by running commands, while CMD will be overwritten.
Example:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
ENTRYPOINT echo hello world
ENTRYPOINT echo hello china
#As with CMD, only the last instruction takes effect
[root@sqm-docker01 dfs]# docker run --rm  en:v1 
hello china
#The difference is that it will not be overwritten by runtime commands
[root@sqm-docker01 dfs]# docker run ---rm  en:v1 echo "test"
hello china
#Unless the --entrypoint parameter is used
[root@sqm-docker01 dfs]# docker run --rm --entrypoint hostname  en:v1    
fa667d019ce5
#Overwrite hello china with hostname command here

11, ENTRYPOINT in conjunction with CMD directives
#If we have written both ENTRYPOINT and CMD in the Dockerfile, then the content specified by CMD will be used as the parameter of ENTRYPOINT.
Examples are as follows:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
ENTRYPOINT ["/usr/bin/ping","baidu.com","-c"]
CMD ["4"]
#Run a container:
[root@sqm-docker01 dfs]# docker run --rm en:v2
PING baidu.com (39.156.69.79): 56 data bytes
64 bytes from 39.156.69.79: seq=0 ttl=127 time=72.931 ms
64 bytes from 39.156.69.79: seq=1 ttl=127 time=62.366 ms
64 bytes from 39.156.69.79: seq=2 ttl=127 time=58.875 ms
64 bytes from 39.156.69.79: seq=3 ttl=127 time=50.662 ms

--- baidu.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 50.662/61.208/72.931 ms

The command running in the container is Ping baidu.com-c 4.

Parameters in #ENTRYPOINT are always used, and additional parameters of CMD can be dynamically replaced at container startup, for example:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
ENTRYPOINT ["/usr/bin/ping","baidu.com","-c"]
CMD ["4"]
[root@sqm-docker01 dfs]# docker run --rm en:v2 2
PING baidu.com (39.156.69.79) 56(84) bytes of data.
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=127 time=80.4 ms
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=127 time=61.5 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 61.532/70.945/80.358/9.413 ms

The life of the container is Ping baidu.com-c 2.

What's the advantage of using #together?We understand this by looking at scenarios: (examples are much the same as the above examples)

//Assuming we need a mirror to know our current public network IP, we can use CMD first:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
RUN yum  -y install curl
CMD ["/usr/bin/curl","-s","https://ip.cn"]
#Build a mirror and run the container:
[root@sqm-docker01 dfs]# docker build -t myip:v1 .
[root@sqm-docker01 dfs]# docker run --rm myip:v1 
{"ip": "117.136.60.216", "country": "Jiangxi Province", "city": "move"}

It looks like we can use it as a command, but commands always have parameters, so if we want to display HTTP header information, we need to add the -i parameter.So can we add the -i parameter directly to docker run myip?

[root@sqm-docker01 dfs]# docker run --rm myip:v1 -i
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"-i\": executable file not found in $PATH": unknown.

We can see the error that the replacement file cannot be found, executable file not found, as we said before, command follows the name above, and the runtime will be replaced with the value of CMD.So instead of adding curl-s to the original CMD, -i here replaces it https://ip.cn Behind.-i is not a command at all, so naturally it cannot be found.

//To solve this problem well, we use CMD in combination with ENTRYPOINT:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos:latest
RUN yum  -y install curl
ENTRYPOINT ["/usr/bin/curl","-s","https://ip.cn"]
CMD ["-i"]
#Build and run containers:
[root@sqm-docker01 dfs]# docker build -t myip:v2 .
[root@sqm-docker01 dfs]# docker run --rm myip:v2 
HTTP/2 200 
date: Wed, 19 Feb 2020 07:10:48 GMT
content-type: application/json; charset=UTF-8
set-cookie: __cfduid=d1903fe7e93a885c6ae4890572cda42161582096248; expires=Fri, 20-Mar-20 07:10:48 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Lax
cf-cache-status: DYNAMIC
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
alt-svc: h3-25=":443"; ma=86400, h3-24=":443"; ma=86400, h3-23=":443"; ma=86400
server: cloudflare
cf-ray: 56766c505d6fb22e-HKG

{"ip": "117.136.60.216", "country": "Jiangxi Province", "city": "move"}

You can see the success because when ENTRYPOINT exists, the content of CMD will be passed to ENTRYPOINT for use, which achieves the desired effect.

##Summarize a few rules:

  • If ENTRYPOINT uses shell mode, CMD instructions are ignored.
  • If ENTRYPOINT uses exec mode, the content specified by CMD is appended with the parameters of the command specified by ENTRYPOINT.
  • If ENTRYPOINT uses exec mode, CMD should also use exec mode

12, VOLUME -- Define anonymous volumes
The format is:

VOLUME ["<Path 1>", "<Path 2>"...]
VOLUME <Route>

In Dockerfile, we can specify some directories to mount as anonymous volumes beforehand, so that at run time, if the user does not specify a mount, the application will also run properly without writing a large amount of data to the container storage layer.

VOLUME /data

The / data directory here is automatically mounted as an anonymous volume at run time, and any information written to / data is not recorded in the container storage layer, thereby ensuring that the container storage layer is stateless.Of course, you can override this mount setting when running containers.

docker run -d -v mydata:/data xxxx

In the above command, the named volume mydata was used to mount to / data instead of the mount configuration for anonymous volumes defined in Dockerfile.

#Note: As you can see from the format supported by this directive, VOLUME only supports docker manager volume mounting, not bind mount ing.

docker manager volume: You don't need to specify a source file, you just need to specify a mount point.Map directories inside containers to local (host).

Examples are as follows:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
VOLUME /usr/share/nginx/html/
#Build and run containers:
[root@sqm-docker01 dfs]# docker build -t volume:v1 .
[root@sqm-docker01 dfs]# docker run -itd --name volume volume:v1 
5d963fb3b51ae9ddcc3b55382e289e0447235676f8893900a440f5f9500f035e

We can see the mount point information by looking at the large number of generated containers created through the docker inspect:

[root@sqm-docker01 dfs]# docker inspect volume 
        "Mounts": [
            {
                "Type": "volume",
                "Name": "190c5a22df09462a9f5fd54209b8bc5ad06fc9382f9c8b9c665c734e4bcf95e0",
                "Source": "/var/lib/docker/volumes/190c5a22df09462a9f5fd54209b8bc5ad06fc9382f9c8b9c665c734e4bcf95e0/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

From the information above, you can see that the default source path mounted locally is'/var/lib/docker/volumes/190c5a22df09462a9f5d54209b8bc5ad06fc9382f9c8b9c665c734e4bcf95e0/_data', while the target path (the path in the container) is a custom'/usr/share/nginx/html'.

13, WORKDIR -- Specify working directory
The format is:

WORKDIR <Working Directory Path>

Set up working directory, effective for RUN, CMD, ENTRYPOINT,COPY,ADD.If the directory does not exist, it can be created for you or set up several times, such as:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#The final path is/a/b/c.

##WORKDIR also resolves environment variables, such as:

ENV DIRPATH /usr/src
WORKDIR $DIRPATH
RUN pwd
#The result of pwd execution is/usr/src.

Note: Never write a dockerfile as a shell script, for example:

RUN cd /app
RUN echo "hello" > world.txt

After running the dockerfile as a build mirror, you will find that the / app/world.txt file cannot be found or that its contents are not hello.The reason is very simple: in dockerfile, these two lines of RUN commands are executed in different environments and are two completely different containers.This is the error caused by an unknown concept of building hierarchical storage for Dockerfile.
If you need to change the location of the working directory for later layers, you should use the WORKDIR directive.

14, USER -- Specify the current user

Format: USER <User Name>[: <User Group>]

The USER directive is similar to WORKDIR in that it changes the state of the environment and affects subsequent layers.
WORKDIR is to change the working directory, and USER is to change the identity of the later layer to execute commands such as RUN, CMD, and ENTRYPOINT.
Of course, like WORKDIR, USER only helps you switch to a designated user, which must be established beforehand or cannot be switched:

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]

If you execute a script as root and want to change your identity during execution, such as running a service process as an established user and not using su or sudo, these require more cumbersome configuration and often make errors in an environment where TTY is missing.gosu is commonly used.
Examples are as follows:

# Create a redis user and use gosu to execute commands for another user
RUN groupadd -r redis && useradd -r -g redis redis
# Download gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true
# Set up CMD and execute with another user (redis)
CMD [ "exec", "gosu", "redis", "redis-server" ]

15, ONBUILD -- Add triggers for mirroring

Format: ONBUILD <Other Instructions>

ONBUILD is a special instruction that is followed by other instructions, such as RUN, COPY, and so on, which are not executed during the current mirroring build.
It is only executed when the next level of mirroring is built based on the current mirror.
Other instructions in the Dockerfile are prepared to customize the current image, only ONBUILD is prepared to help others customize themselves.
Examples are as follows:
//Write a Dockerfile as follows:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM nginx:latest
ONBUILD COPY ./index.html /   #Copy files to/under containers

//Use the dockerfile above to build the mirror:
[root@sqm-docker01 dfs]# docker build -t image1 .

//Create containers from image1 mirrors:

[root@sqm-docker01 dfs]# docker run --rm -it image1 /bin/bash
root@db8a068d9f30:/# ls    
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

We found that there is no index.html file in the container/directory running as an image 1 image, which indicates that the instructions specified by ONBUILD will not be executed in its own image building.

//Write a new Dockerfile as follows:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM image1  #The underlying image used is the image image1 built above
CMD echo hello world

//Use the dockerfile above to build the mirror:

[root@sqm-docker01 dfs]# docker build -t image2 .
Sending build context to Docker daemon  102.6MB
Step 1/2 : FROM image1
# Executing 1 build trigger
 ---> 796e32308d29
Step 2/2 : CMD echo hello world
 ---> Running in 5c16f913f5e9
Removing intermediate container 5c16f913f5e9
 ---> 4c0a45374727
Successfully built 4c0a45374727
Successfully tagged image2:latest

//Create containers from image2 mirrors:

[root@sqm-docker01 dfs]# docker run -it --rm image2 /bin/bash
root@3e3c1d0fe3f6:/# ls
bin   dev  home        lib    media  opt   root  sbin  sys  usr
boot  etc  index.html  lib64  mnt    proc  run   srv   tmp  var
root@3e3c1d0fe3f6:/# cat index.html 
hello world

We found an index.html file in the root directory of the container running as an image 2 image, indicating that the trigger executed the COPY. /index.html/instruction.

Mirror Cache Features:

1) Docker caches an existing mirror layer, and when building a new one, if a mirror layer already exists, it can be used directly without re-creating it, as shown below:
//Create a new dockerfile

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
RUN yum -y install vim
//Build a mirror
[root@sqm-docker01 dfs]# docker build -t a:v1 .

#Add a little new content to the dockerfile you just created (add a COPY instruction to the mirror):

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
RUN yum -y install vim
COPY testfile /

#Rebuild the mirror with the same dockefile:

[root@sqm-docker01 dfs]# touch testfile
[root@sqm-docker01 dfs]# docker build -t a:v2 .
Sending build context to Docker daemon  102.6MB
Step 1/3 : FROM centos
 ---> 0f3e07c0138f
Step 2/3 : RUN yum -y install vim
 ---> Using cache
 ---> 6467d4675159
Step 3/3 : COPY testfile /
 ---> 6b92fe05882f
Successfully built 6b92fe05882f
Successfully tagged a:v2

(1) Ensure that the testfile file already exists
(2) Using cache, with emphasis on this: the same RUN directive has been run before, this time directly using the mirror layer 6467d4675159 in the cache.
(3) Execute COPY instructions.
The process is to start the temporary container, copy the testfile, submit a new mirror layer 6b92fe05882f, and delete the temporary container.

If you want to build the image without using the cache, you can add the --no-cache parameter to the docker build command.

2) Each instruction in the Dockerfile creates a mirror layer, and the upper layer depends on the lower layer.Whenever a layer changes, all the caches on it will fail.
That is, if we change the order in which the Dockerfile instructions are executed, or modify or add instructions, the cache will fail.
Examples include exchanging the order of RUN and COPY instructions above:

[root@sqm-docker01 dfs]# cat Dockerfile 
FROM centos
COPY testfile /
RUN yum -y install vim

Although logically this change has no effect on the content of the mirror, due to the hierarchical structure, Docker must rebuild the affected mirror layer.

//Rebuild the mirror:

[root@sqm-docker01 dfs]# docker build -t a:v3 .
Sending build context to Docker daemon  102.6MB
Step 1/3 : FROM centos
 ---> 0f3e07c0138f
Step 2/3 : COPY testfile /
 ---> 97e725434a6b
Step 3/3 : RUN yum -y install vim
 ---> Running in 5f30ff393047
......
Removing intermediate container 5f30ff393047
 ---> 90ceae8b3638
Successfully built 90ceae8b3638
Successfully tagged a:v3

From the output above, you can see that a new mirror layer, 97e725434a6b, has been generated and the cache has expired.

In addition to using the cache when building, Docker also uses it when downloading the image.For example, we download an httpd image:

[root@sqm-docker01 dfs]#docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
f17d81b4b692: Already exists 
06fe09255c64: Already exists 
0baf8127507d: Already exists 
07b9730387a3: Already exists 
6dbdee9d6fa5: Already exists 
Digest: sha256:76954e59f23aa9845ed81146ef3cad4a78f5eb3daab9625874ebca0e416367e2
Status: Image is up to date for httpd:latest

The docker pull command output shows that the first layer (base mirror) already exists and does not need to be downloaded.

A simple understanding of the concept of mirror layering
Each line of code in the Dockerfile produces a new layer, a mirror cannot exceed 127 layers, and each layer produces a separate id.
Layers that already exist in an image are read-only.When an image is run as a container, this layer is readable and writable, and when you need to manipulate the files in the mirror layer, you copy one through the container layer, and then operate on it. This mechanism is copy on write.

Topics: Docker Nginx CentOS Redis