1. 语法

1.1. 语法要求

  • 执行docker build的目录为镜像创建的工作目录,所有被Dockerfile引用的文件都必须在此目录或者其子目录中,不可超出工作目录的边界
  • Dockerfile可以引用文件也可以引用目录,支持通配符,对于引用的目录中个别不需要的文件可以将路径写入.dockerignore文件中,该文件同样支持通配符
  • 注释以 # 开头,类似于shell 脚本
  • 非注释的非空行是指令行,指令不区分大小写,但是约定都是大写,一个指令是镜像的一个层,因此需要合理编排,尽可能减少层级
  • Dockerfile支持变量的引用,引用时与shell脚本一致: $var , ${var} , ${var:-default} , ${var:+word}

    1.2. 指令

    1.2.1. # escape

  • Syntax

    1. # escape=\
    2. # escape=`
  • Introduction

The default escape is \,usually used in RUN directive.But in windows image,file path delimiter is \,so the ` is better choice in create window image.

1.2.2. FROM

  • Syntax

    FROM image[:tag] [AS name]
    FROM image[@digest] [AS name]
    
  • Introduction

Specify base image,default tag is :latest.You can speicify tag or digest.If local host doesn’t has the image,it will pull from repository.Digest is image hash.

1.2.3. MAINTAINER(deprecated)

  • Syntax

    MAINTAINER strings
    
  • Introduction

Add author information to image.The directive is deprecated,the new choice is LABEL command.

1.2.4. LABEL

  • Syntax

    LABEL key=vaule key=vaule ……
    
  • Introduction

Add label to image,such as author,version.In label,you can use a “\” to continue a single key-vaule to next line.

1.2.5. COPY

  • Syntax

    COPY [--chown=user:group] src …… dest
    COPY [--chown=user:group] ["src","src",……,"dest"]
    
  • Introduction

Chown option only worked on Linux container.
COPY instruction copy files or directory based on docker-build workdir to container destination path.The src path support wildcards which will be done using Go’s filepath match rules,such as “hom* hom?.txt”. The dest is an absolute path,or a path ralative to WORKDIR.

  • If src is a directory,the whole contents of the directory are copied,include filesystem and metadata.
  • If src is a directory,the directory is not copied,just its contents.
  • If dest is ending with “/“,it will be done an directory.Otherwise,it will be considered a regular file.
  • If specify src by wildcards or sepcify multiple src,the dest must be direcotry.
  • If dest doesn’t exits,it will be created.
  • If filename contain blank charactre,the JSON array is better choice

    1.2.6. ADD

  • Syntax

    ADD [--chown=user:group] src …… dest
    ADD [--chown=user:group] ["src","src",……,"dest"]
    
  • Introduction

The ADD instruction is like COPY instruction.But the instruction has some features:

  • ADD can sepcify local files or remote file URLs from src.
  • If src is URL,the destination will be chmod to 600.
  • If src is tar archive format(.tar,.tar.gz,.tar.bz2,.tar.xz),it will be unpacked on destination(like tar -xf src).
  • If scr is URL tar archive format,it will not be unpacked on destination.
  • If URL requires authentication,you must be download by wget or curl on COM instruction.

    1.2.7. RUN

  • Syntax

    RUN command
    RUN ["executable","param1","param2",…]
    
  • Introduction

RUN command is running when you create image by “docker build”.All commands must included in base image.

  • “RUN command … “ is shell form,the command is run in a shell,whinc by defualt is “/bin/sh -c” on Linux or “cmd /s /c” on Windows.

The default shell for shell form can be changed by “SHELL” command.

  • “RUN [“executable”,”param1”,”param2”,…]” is exec form,the command is run skep shell.
  • In shell form you can use a “\” to continue a single RUN instruction onto next line.
  • The exec form is parsed as a JSON array,so you must use double-quotes(“) rather than single-quotes(‘).
  • In windows image, RUN [“c:\windows\system32\tasklist.exe”] is syntax error.The correct syntax for this example is:RUN [“c:\windows\system32\tasklist.exe”]]

    1.2.8. CMD

  • Syntax

    CMD command param1 param2
    CMD ["executable","param1","param2"]
    CMD ["param1","param2"]
    
  • Introduction

The CMD directive used for define a default command when start a container.There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

  • Shell format:

The first format is shell format.Run command by shell,default is “/bin/sh -c”.

  • Exec format:

The second format is exec format.It is preferred format.
The exec format skip shell to run command,so some enviroment or commands can’t run success.
The exec format is JSON array,that need use double-quotes(“).

  • Entrypoint argument format

The third format is entrypoint argument format.
It define default arguments for ENTRYPOIN command.

1.2.9. ENTRYPOINT

  • Syntax

    ENTRYPOINT command param1 param2
    ENTRYPOINT ["executable","param1","param2"]
    
  • Introduction

ENTRYPOINT is container default command when container start.The exec-form and shell-form has differences:

No ENTRYPOINT ENTRYPION e_cmd e_arg ENTRYPOIN[“e_cmd”,”e_arg”]
No CMD Error /bin/sh -c e_cmd e_arg E_cmd e_arg
CMD c_cmd c_arg /bin/sh -c c_cmd c_arg /bin/sh -c e_cmd e_arg E_cmd e_arg /bin/sh -c c_cmd c_arg
CMD [“c_cmd”,”c_arg”] C_cmd c_arg /bin/sh -c e_cmd e_arg E_cmd e_arg c_cmd c_arg
CMD [“c_arg”] C_arg /bin/sh -c e_cmd e_arg E_cmd e_arg c_arg
  • When don’t specify ENTRYPOIN,CMD will be container default Cmd.
  • When specify exec-form ENTRYPOINT,CMD will become arguments for ENTRYPOIN directive.
  • When specify shell-form ENTRYPOIN,CMD will be ignore.In most time,use “exec commnad”.
  • You can specify “—entrypoint string” overwrite ENTORYPIONT when run a container.
  • The exec-from is better choice.

    1.2.10. EXPOSE

  • Syntax

    EXPOSE port[/protocol] ……
    
  • Introduction

Set default expose port and protocol when you run container with “-P” optinon,default protocol is TCP.
Use the “-p” option will publish and map one or more ports.Use the “-P” option will publish all exposed ports and map to high-order ports.

1.2.11. ENV

  • Syntax

    ENV key value
    ENV key=vaule key=value …
    
  • Introduction

Defines variable key to the value.The vaule be in the environment for all subsequent instruction in docker build,even you can change them when create and run a container by options “—env key=value”.
When you need define multiple variables,you can use second format,and you also can use quotes include values.

1.2.12. HEALTHCHECK

  • Syntax

    HEALTHCHECK [--interval=time --timeout=time --start-period=time --retries=number] CMD command
    HEALTHCHECK NONE
    
  • Introduction

Defines health check process to test container status.The first format is defined health check method.The second format is disable any healthcheck inherited from the base image.
Options:

  • —interval: Sets time between tow check.Default 30s.
  • —timeout: Defines timeout time,default is 30s.
  • —start-period: Defines seconds after container start to run health check.Default 0s.
  • —retries: Retry times.Default 3.

Status:

  • 0: Success, container is healthy.
  • 1: Failed,container is not work correctly.

    1.2.13. VOLUME

  • Syntax

    VOLUME ["mounted_point","mounted_point",……]
    VOLUME mounted_point mounted_point
    
  • Introduction

Specify volume which will mounted by container-managed when container run.
Docker will copy file to volume when the files created before volumes declared.After valume declared,any build steps change the data within the volume will be discarded.
The volume can’t specify host dirctory.

1.2.14. USER

  • Syntax

    USER user[:group]
    USER UID[:GID]
    
  • Introduction

The USER command set user name or UID and group name or GID to use when running the image and for any RUN,CMD,and ENTRYPOIN directive.
When the user doesn’t have a primary group when the image will be run with root group.

1.2.15. WORKDIR

  • Syntax

    WORKDIR path
    
  • Introduction

Specify work directory in container for any RUN,CMD,ENTRYPOIN,ADD,COPY instructions.If the path dosen’t exist,it will be create.

1.2.16. ONBULID

  • Syntax

    ONBUILD INSTRODUCTION
    
  • Introduction

Defines introduction when the image referer by others.In other word,if other people create use base image which sets ONBUILD command,it will execute the INSTRODUCTION when referer.

1.2.17. ARG

  • Syntax

    ARG key[=default_value]
    
  • Introduction

Define variable that user can pass value by “—build-arg key=value” when docker build.It use default value when user doesn’t pass value and defines default in Dockerfile.
If you need specify image version,you can set “ARG VERSION” before FROM instruction,but the ARG instruction that before FROM will not be referer after FROM instruction,you must reset ARG instruction after FROM instruction.

1.2.18. SHELL

  • Syntax

    SHELL ["executalbe","parameters"]
    
  • Introduction

Defins shell which overwrite default shell (“/bin/sh -c” or “cmd /S /C”).

1.2.19. STOPSINGLE

  • Syntax

    STOPSIGNAL signal
    
  • Introduction

Defines signal when execute “docker container stop”,default is 15.

1.3. 多阶段构建

在学习go语言过程中,存在一个镜像制作的场景:

  1. 下载 golang 官方镜像,使用Dockerfile将go代码进行编译,编译结果为一个二进制文件
  2. 下载 业务容器基础镜像(比如centos),将编译好的二进制文件通过Dockerfile拷贝到业务容器基础镜像中

为了实现上述的需求,以前仅有两种方式:分两个Dockerfile进行镜像的制作,使用shell脚本将两个制作流程合并。在 Docker 17.05 之后,Dockerfile 支持多阶段构建镜像,方式如下:

FROM golang:1.13 as builder  # 编译代码的基础镜像
WORKDIR /workspace
COPY ./ .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY=https://goproxy.cn go build -a -o test-project main.go

FROM centos:7
COPY --from=builder /workspace/test-project .
RUN chmod +x test-project
CMD ["/test-project"]

2. Examples

2.1. 常用的指令使用案例

2.1.1. LABEL & COPY

[root@centos-82 test-01]# cp -r /etc/yum.repos.d/ ./
[root@centos-82 test-01]# cp /etc/passwd ./
[root@centos-82 test-01]# head -100 Dockerfile .dockerignore
==> Dockerfile <==
# Test FROM directive
FROM busybox:latest
LABEL author="heyingsheng <hys_1992@outlook.com>" version="test-image-v1.0.1" baseimage="busybox:latest"
COPY yum.repos.d passwd /tmp/
==> .dockerignore <== ## 需要忽略的文件
yum.repos.d/CentOS-*

[root@centos-82 ~]# docker container run --name c1 --rm test-image:v1.0.2 ls /tmp/
docker-ce.repo
passwd
[root@centos-82 ~]# docker image inspect -f {{.ContainerConfig.Labels}} test-image:v1.0.2
map[author:heyingsheng <hys_1992@outlook.com> baseimage:busybox:latest version:test-image-v1.0.1]

2.1.2. ADD

[root@centos-82 test-01]# cat Dockerfile

# Test FROM directive
FROM busybox:latest
LABEL author="heyingsheng <hys_1992@outlook.com>" version="test-image-v1.0.1" baseimage="busybox:latest"
# URL 格式的文件不会解压,而直接 ADD 进去的本地 .tar[.xx] 格式文件会解压
ADD http://nginx.org/download/nginx-1.14.2.tar.gz /tmp/url_tar/ 
ADD nginx-1.14.2.tar.gz /tmp/local_tar/

[root@centos-82 test-01]# docker build -t test-image:v1.0.3 ./
[root@centos-82 ~]# docker container run —name c1 —rm test-image:v1.0.3 ls /tmp/url_tar /tmp/local_tar/

/tmp/local_tar/:
nginx-1.14.2
/tmp/url_tar:
nginx-1.14.2.tar.gz

2.1.3. VOLUME

[root@centos-82 test-01]# cat Dockerfile

# Test FROM directive
FROM busybox:latest
LABEL author="heyingsheng <hys_1992@outlook.com>" version="test-image-v1.0.1" baseimage="busybox:latest"
# COPY yum.repos.d passwd /tmp/
ADD nginx-1.14.2.tar.gz /tmp/local_tar/
## 只能指定容器中的挂载点,而不能指定宿主机上的目录
VOLUME /tmp/
ADD passwd yum.repos.d /tmp/
RUN mv /tmp/local_tar/nginx-1.14.2 /tmp/local_tar/nginx_src

[root@centos-82 test-01]# docker build -t test-image:v1.0.6 ./
[root@centos-82 ~]# docker container run —name c1 —rm test-image:v1.0.6 sh -c “ls /tmp/ /tmp/local_tar ;sleep 60 “

/tmp/:
docker-ce.repo
local_tar
passwd

/tmp/local_tar:
nginx-1.14.2
#### 此处最后的RUN并没有修改声明volume之前创建的文件

[root@centos-82 test-01]# docker container inspect -f {{.Mounts}} c1

[{volume fb376e4f9dd94ec8b4e8a2778bd15b405dda653f3773ca4cb53fd120170eb6f4 /var/lib/docker/volumes/fb376e4f9dd94ec8b4e8a2778bd15b405dda653f3773ca4cb53fd120170eb6f4/_data /tmp local  true }]

2.1.4. EXPOSE

[root@centos-82 test-01]# cat Dockerfile

# Test FROM directive
FROM busybox:latest
LABEL author="heyingsheng <hys_1992@outlook.com>" version="test-image-v1.0.1" baseimage="busybox:latest"
COPY index.html /data/web/html/
EXPOSE 80/tcp

[root@centos-82 test-01]# docker build -t test-image:v1.0.8 ./
[root@centos-82 ~]# docker container run —name c1 —rm test-image:v1.0.8 /bin/httpd -f -h /data/web/html/
[root@centos-82 test-01]# docker container port c1 ## Default case doesn’t expose 80 prot.
[root@centos-82 ~]# docker container run —name c1 -P —rm test-image:v1.0.8 /bin/httpd -f -h /data/web/html/
[root@centos-82 test-01]# docker container port c1 ## When you specify “-P”,it will expose 80

80/tcp -> 0.0.0.0:32769

[root@centos-82 test-01]# curl 192.168.1.82:32769

<h1>Httpd Server</h1>

2.1.5. CMD

2.1.5.1. exec-form CMD

[root@centos-82 test-02]# vim Dockerfile

FROM busybox:latest AS baseimage
LABEL Author="heyingsheng <123@qq.com>"
ENV WEB_ROOT="/data/html/www"
RUN mkdir -p $WEB_ROOT && \
    echo "Test index!" > $WEB_ROOT/index.html
# CMD ["/bin/httpd","-f","-h","$WEB_ROOT"]    ## The exec format can't referer shell variable.
CMD ["/bin/httpd","-f","-h","/data/html/www"]

[root@centos-82 test-02]# docker build -t test-image:v2.0.2 ./
[root@centos-82 ~]# docker image inspect -f {{.Config.Cmd}} test-image:v2.0.2

[/bin/httpd -f -h /data/html/www]

[root@centos-82 ~]# docker run —name c2 —rm -d test-image:v2.0.2
[root@centos-82 ~]# docker container inspect -f {{.NetworkSettings.IPAddress}} c2

172.17.0.2

[root@centos-82 ~]# curl 172.17.0.2

Test index!

2.1.5.2. Shell-form CMD

[root@centos-82 test-02]# cat Dockerfile

FROM busybox:latest AS baseimage
LABEL Author="heyingsheng <123@qq.com>"
ENV WEB_ROOT="/data/html/www"
RUN mkdir -p $WEB_ROOT && \
    echo "Test index!" > $WEB_ROOT/index.html
# CMD ["/bin/httpd","-f","-h","$WEB_ROOT"]
# CMD ["/bin/httpd","-f","-h","/data/html/www"]
CMD /bin/httpd -f -h /data/html/www

[root@centos-82 test-02]# docker build -t test-image:v2.0.3 ./
[root@centos-82 ~]# docker image inspect -f {{.Config.Cmd}} test-image:v2.0.3

[/bin/sh -c /bin/httpd -f -h /data/html/www]

[root@centos-82 ~]# docker container run —name c1 —rm -d test-image:v2.0.3
[root@centos-82 ~]# docker container inspect -f {{.NetworkSettings.IPAddress}} c1

172.17.0.2

[root@centos-82 ~]# curl 172.17.0.2 ## Upper success.

Test index!

[root@centos-82 ~]# docker container inspect -f {{.Config.Cmd}} c1 ## The container first process is /bin/sh.

[/bin/sh -c /bin/httpd -f -h /data/html/www]

[root@centos-82 ~]# docker container exec c1 ps ## The PID 1 process is replaced!

PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/html/www

2.1.6. ENTRYPOINT

2.1.6.1. exec-form ENTRYPOINT

[root@centos-82 test-02]# cat Dockerfile

FROM busybox:latest
LABEL Author="heyingsheng <123@qq.com>"
RUN mkdir -p /data/web/html && \
    echo "Test index!" > /data/web/html/index.html
CMD ["-h","/data/web/html"]
ENTRYPOINT ["/bin/httpd","-f"]

[root@centos-82 test-02]# docker build -t test-image:v2.0.6 ./
[root@centos-82 ~]# docker container run —name c1 —rm -d test-image:v2.0.7
[root@centos-82 ~]# docker container exec c1 ps uax ## The CMD arguments will append to ENTRYPOINT.

PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html
    6 root      0:00 ps uax

2.1.6.2. shell-form ENTRYPOINT
[root@centos-82 test-02]# cat Dockerfile

FROM busybox:latest
LABEL Author="heyingsheng <123@qq.com>"
RUN mkdir -p /data/web/html && \
    echo "Test index!" > /data/web/html/index.html
CMD ["-h","/data/web/html"]
# ENTRYPOINT ["/bin/httpd","-f"]
ENTRYPOINT /bin/httpd -f

[root@centos-82 test-02]# docker build -t test-image:v2.0.7 ./
[root@centos-82 ~]# docker container run —name c1 —rm -d test-image:v2.0.7
[root@centos-82 ~]# docker container exec c1 ps uax ## The CMD directive is ignored.

PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f
    6 root      0:00 ps uax

2.1.6.3. configuration & ENTRYPOINT
[root@centos-82 test-02]# cat Dockerfile

FROM centos:centos7
LABEL Author="heyingsheng <123@qq.com>"
ADD http://nginx.org/download/nginx-1.14.2.tar.gz /usr/local/src/
RUN yum install -y gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel && \
    useradd -M -s /sbin/nologin nginx && \
    tar -xf /usr/local/src/nginx-1.14.2.tar.gz -C /usr/local/src/ && \
    cd /usr/local/src/nginx-1.14.2 && \
    ./configure --user=nginx --group=nginx && \
    make -j 4 && make install && \
    mkdir -p /data/web/html/ && \
    echo "Vhost Test!" > /data/web/html/index.html && \
    yum clean all && \
    cd / && \
    rm -fr /var/cache/yum /usr/local/src/*
EXPOSE 80/tcp
ADD nginx_start.sh /bin/
ENV SERVER_NAME="www.heyang.com" \
    PORT=8080
CMD ["/usr/local/nginx/sbin/nginx"]
ENTRYPOINT ["/bin/nginx_start.sh"]

[root@centos-82 test-02]# cat nginx_start.sh

#!/bin/sh
cat <<EOF >/usr/local/nginx/conf/nginx.conf
user  nginx;
worker_processes  1;
events {
    worker_connections  1024;
}
daemon off;

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       ${IP:-0.0.0.0}:${PORT:-80};
        server_name  ${SERVER_NAME:localhost};
        location / {
            root   /data/web/html/;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
EOF
exec $@

[root@centos-82 ~]# docker container run —name c1 —rm —env PORT=8081 -d test-image:v2.0.11
[root@centos-82 ~]# docker container exec c1 ps uax

USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0  20548  1448 ?        Ss   23:31   0:00 nginx: master process /usr/local/nginx/sbin/nginx

[root@centos-82 ~]# curl 172.17.0.2:8081

Vhost Test!