Dockerfile reference
environment-replacement
Dockerfile是用来构建docker image的构建文件。 由一系列命令和参数构成的脚本文件,是一个文本文件。
centos7 Dockerfile示例
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20201113" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]-
编写Dockerfile文件
-
docker build构建镜像
docker build [OPTIONS] PATH | URL | -
PATH | URL: 指定build时的上下文目录,即运行docker build时的工作目录(workdir)
-: 无上下文目录。Dockerfile 从STDIN读取。将不支持ADD,COPY命令 Options:-f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile') -t, --tag list Name and optionally a tag in the 'name:tag' formatBuild with -
# Dockerfile从/opt/docker/Dockerfile读取 docker build - < /opt/docker/Dockerfile # Dockerifle从/opt/docker/context.tar.gz读取 docker build - < /opt/docker/context.tar.gz
- 示例1
cd /mydocker/jdk_tomcat docker build -f ./Dockerfile -t hanxiao2100/tomcat:10 .
- 示例1
-
docker run
-
Dockerfile指令必须大写,每个指令后需要至少有一个参数
-
指令按照Dockerfilew文件,从上到下,顺序执行
-
行注释符:
# -
连接符、默认的转义字符:
\反斜杠自定义转义符,建议写在Dockerfile的开头。
如:定义`(backtick反引号)为转义符# escape=` -
每条指令都会创建一个新的镜像层,并对新的镜像进行提交
- docker daemon读取Dockerfile文件,从上往下顺序执行
- docker daemon从基础镜像(basic image)运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似docker commit操作,提交一个新的镜像层
- docker daemon在基于上面提交的新镜像运行一个容器
- 然后再执行Dockerfile文件中的下一条指令。依此执行下去,直到所有指令都执行完成
与shell、python注释符相同
#
定义变量,用于用户执行docker build命令时,把变量传递给Dockerfile引用。
可以放在 Dockerfile任何位置。这是唯一一个可以放在 FROM 指令之前的指令
可以定义多个ARG,写多行
- syntax
ARG <name>[=<default value>] - 示例
ARG - 如何传参给ARG定义的变量
docker build -f Dockerfile_PATH --build-arg <varname1>=<value1> --build-arg <varname2>=<value2> PATH
初始化新的构建阶段(build stage),并设置基础镜像(Base Image)
FROM <base_image> 背后所做的操作就是:ADD base_image的/ /
也即使把基础镜像的 整个 RootFS 复制过来,
所有docker history查看镜像的最后一条都是 `/bin/sh -c #(nop) ADD file:xxx in /`
Dockerfile必须以一个 FORM 指令开始,即一般以 FORM 为第一行。Docker 17.05即之后支持多FROM
当然如有变量要传递,ARG可以在FROM之前。
"FROM之前定义的ARG变量,只能FROM引用",FROM之后的指令不要引用
因为FROM指令之前还没有进入构建阶段。如果FROM 之后的指令引用了FROM之前定义的ARG变量,那么其变量没有值。
-
syntax
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]或
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]- 省略
:tag,缺省为latest - AS name: 设置阶段名
- 省略
-
示例
示例1
ARG VERSION=latest FROM busybox:$VERSION
示例2
FROM alpine:3.13
设置维护作者信息,如姓名、email等
已弃用,使用LABEL,可以设置更多灵活的版本信息
-
syntax
MAINTAINER <name> -
示例
MAINTAINER "NGINX Docker Maintainers <docker-maint@nginx.com>"LABEL表示方法
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
设置环境变量,已设置的变量可在构建阶段中使用,也可以在容器中使用。
可写多个
-
syntax
ENV <key>=<value> ... -
示例
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \ MY_CAT=fluffy ENV MY_NAME="John Doe" ENV MY_CAT=fluffy
添加系统环境变量
ENV PATH=$PATH:/usr/local/siege/bin相当于在 /etc/profile 添加 export PATH=$PATH:/usr/local/siege/bin
但Dockerfile中RUN echo export PATH=$PATH:/usr/local/siege/bin >> /root/.bash_profile,在容器中不生效。需要在运行的容器中执行. /etc/profile如果非要这么干,可以RUN echo export PATH=$PATH:/usr/local/siege/bin >> /root/.bash_profile
为Dockerfile中的RUN, CMD, ENTRYPOINT, COPY, ADD指令设置"工作目录"
可写多个,建议只写一个。
多个WORKDIR相对路径将最终合并成一个.
多个WORKDIR绝对路径的,只有最后一个生效。
基础镜像中的WORKDIR值 不会被子镜像继承。
一个镜像中,WORKDIR缺省的默认值为/
- syntax
WORKDIR /path/to/workdir- 如果指定的工作目录不存在(容器中),则自动创建
docker run -w "工作目录路径"将覆盖Dockerfile中指定的WORKDIRWORKDIR可以引用ENV环境变量
- 示例
# 示例1 WORKDIR /usr/local/service # 示例2 ENV DIRPATH=/path WORKDIR $DIRPATH/service
镜像构建时需要运行的命令
RUN指令将在当前镜像的最顶层(可写层)执行命令并提交结果。每执行一次RUN将生成一个镜像,该镜像将用于Dockerfile下一步的指令。
分层运行指令并提交结果,类似于git的源码管理。
可写多个该指令
-
syntax
-
shell form
RUN <command>the command is run in a shell, which by default is
/bin/sh -con Linux
orcmd /S /Con Windows- 可以使用
SHELL指令修改exec from格式的默认shell
- 可以使用
-
exec form
RUN ["executable", "param1", "param2"]
- executable写绝对路径。不能使用env环境变量
- 尽量避免使用
/bin/sh -c ''这种shell壳来执行命令。 - executable一般为二进制可执行文件,尽量不写shell脚本
- JSON数组的元素必须使用
"双引号包裹
-
-
示例
RUN yum -y install nginx RUN groupadd -r mysql && useradd -r -g mysql mysql
-
注意事项
-
RUN指令会产生的缓存,缓存不会自动清除,即docker build时会使用到RUN产生的缓存RUN yum -y install redis会下载redis安装包并缓存
-
docker build时不使用缓存
docker build --no-cache
-
将宿主机上的文件、目录、远端的URL资源复制到镜像中。
有本地tar压缩文件提取到镜像,下载URL资源到镜像的功能
- syntax
ADD [--chown=<user>:<group>] <src>... <dest> # 路径中有空格的解决方法 ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
-
必须位于build的上下文目录内(build context)。不能超出此路径范围
-
当为目录时,则复制目录下的全部内容,包括文件系统元数据。
-
如果 资源为宿主机本地的tar压缩包文件(.gz, tar.gz, .xz, tar.xz, .gzip, .bzip2等),在镜像中将解压到目录,行为类似于 tar -x XX.tar.gz -C 即将本地的tar文件提取到镜像中
-
如果 资源为URL资源时,在镜像中将不解压,直接下载
-
当为其它任何类型的文件,且以/结尾,则镜像中将复制为/base() 示例:
ADD ./app/start.sh /data/
则
start.sh将复制到镜像的/data/app/start.sh -
当有多个时,必须以/结尾
-
--chown=<user 只对linux系统有效
-
当目录不存在时,将会自动创建该目录
-
- 示例
# pkg/etc/named/ 下的所以文件或子目录下的文件,都复制到 /usr/local/bind/etc/named/ 下,不创建子目录。 ADD pkg/etc/named/* /usr/local/bind/etc/named/ # pkg/etc/named/ 下的所以文件或子目录下的文件,都复制到 /usr/local/bind/etc/named/ 下,保持原的子目录结构 ADD pkg/etc/named/ /usr/local/bind/etc/named/ ADD test.txt /absoluteDir/ ADD hom* /mydir/ ADD hom?.txt /mydir/ ADD --chown=55:mygroup files* /somedir/ ADD --chown=1 files* /somedir/ ADD ./glibc-2.28/libc.a ./glibc-2.28/libc_pci.a ./glibc-2.28/*.so /usr/lib/ ADD ["./glibc-2.28/libc.a", "./glibc-2.28/libc_pci.a", "/usr/lib/"]
从宿主上复制文件或目录到镜像中
-
syntax
COPY [--chown=<user>:<group>] <src> ... <dest> # 路径中有空格 COPY [--chown=<user>:<group>] ["<src>", ... "<dest>"]
- <src>必须位置build的上下文目录内。不能超出此路径范围
- 当<src>为目录时,则复制目录下的全部内容,包括文件系统元数据。
- 当<dest>目录不存在时,将会自动创建该目录
- <src>不支持URL
-
示例
COPY test.txt /absoluteDir/ COPY hom* /mydir/ COPY --chown=55:mygroup files* /somedir/ COPY --chown=bin files* /somedir/ # 复制目录, 把 ./go 目录复制到镜像的 /usr/local/go/ COPY ./go /usr/local/go/ 相当于 COPY ./go/* /usr/local/go/
从build阶段中复制文件到镜像中,
或从指定的镜像中复制文件到此镜像中(把外部的镜像作为一个"build stage")
-
syntax
COPY --from=<build_stage_name> <src> <dest> # 或 COPY --from=<image> <src> <dest>
-
示例1
FROM golang:1.16 AS builder WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go ./ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /go/src/github.com/alexellis/href-counter/app ./ CMD ["./app"]
-
示例2
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf # 复制目录 COPY --from=nginx:latest /usr/local/nginx /usr/local/nginx/ # 复制多个文件 ARG glibc_dir=/usr/local/src/glibc-2.28 # 或 # ENV glibc_dir=/usr/local/src/glibc-2.28 COPY --from=cucker/golang:1.17.1-glibc-static ${glibc_dir}/*.a \ ${glibc_dir}/nptl/libpthread.a \ ${glibc_dir}/dlfcn/libdl.a \ /usr/lib64/
给镜像设置metadata元数据。
可以添加维护作者信息、项目的组织信息、licensing许可信息、版本信息等
可写多个
- syntax
LABEL <key>=<value> <key>=<value> <key>=<value> ... - 示例
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>" LABEL com.example.version="0.0.1-beta" LABEL vendor1="ACME Incorporated" LABEL vendor2=ZENITH\ Incorporated LABEL multi.label1="value1" \ multi.label2="value2" \ other="value3"
镜像运行时使用的用户和组。
为Dockerfile文件中的RUN, CMD, ENTRYPOINT指令指定运行的用户和组
- syntax
USER <user>[:<group>] # or USER <UID>[:<GID>]
- 示例
# Create a group and an user RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres # 为后续命令指定用户 USER postgres
创建容器数据卷。用于数持久化
创建具体指定名称的挂载点,将其标识为用来保存来自宿主机或其他容器的数据卷
- syntax
值可以是一个json数组 或是多个参数的字符串(空格隔开)
VOLUME ["/data", "/data2"]
- 示例
VOLUME ["/var/log", "/var/db"] # 或 VOLUME /var/log /var/db
设置容器启动时要运行的主命令(main command),或为ENTRYPOINT定义默认的参数
Dockerfile中只需要一个CMD,如果有写多个CMD,只有最后一个CMD生效,所以建议只写一个
CMD在镜像构建阶段中不会做任何事情。
而RUN是会执行的命令,并会提交结果
-
syntax(有三种格式)
- exec form,首选格式
CMD ["executable","param1","param2"]
-
执行的命令:
executable param1 param2 -
进程的PID为1
-
executable为非shell的可执行程序时,要求写完整路径
-
[]JSON数组里的元素必须以双引
"号包裹,不能是单引号' -
不能引用环境变量
不可用示例
CMD [ "echo", "$HOME" ]
如果非要使用环境变量,可以这么干
CMD [ "sh", "-c", "echo $HOME" ]
-
- 作为
ENTRYPOINT []的默认参数,这也是exec form。这也是CMD的主要用途。参考 CMD为ENTRYPOINT定义默认参数CMD ["param1","param2"]
- shell form
CMD command param1 param2- 执行的命令:
/bin/sh -c "command param1 param2" - 执行的程序为/bin/sh的子进程,PID不是1。会有类似 ENTRYPOINT shell格式的问题
- 执行的命令:
- exec form,首选格式
-
当docker run容器指定了额外的参数时,
CMD定义的命令将会被覆盖。上面三种格式都一样。
docker run <image> param1 param2相当于,新建CMD [param1, param2],覆盖原来的CMD -
示例
FROM debian:buster-slim CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
FROM ubuntu CMD ["/usr/bin/wc","--help"]
设置容器启动时要运行的主命令(main command)
ENTRYPOINT 的目的和 CMD 一样,都是指定容器启动时要运行的程序及参数
只需要一个ENTRYPOINT,如果有写多个ENTRYPOINT,只有最后一个ENTRYPOINT生效,所以建议只写一个
-
syntax(有两种格式)
-
exec form,首选格式
ENTRYPOINT ["executable", "param1", "param2"]
-
执行的命令:
executable param1 param1 -
会追加
CMD或docker run <image> param ...的参数,到ENTRYPOINT []的最后面 -
启动容器后传递参数的情况
docker run <image> param1 param2 param3
建新建一个CMD [param1, param2, param3], 覆盖原来定义的CMD,
再讲新的CMD参数追加到ENTRYPOINT的命令后面 -
启动的程序进程ID为1,PID 1
-
[]JSON数组里的元素必须以双引
"号包裹,不能是单引号' -
不能引用环境变量
不可用示例
ENTRYPOINT [ "echo", "$HOME" ]
如果非要使用环境变量,可以这么干
ENTRYPOINT [ "sh", "-c", "echo $HOME" ]
-
当docker run容器指定了额外的参数时,
CMD定义的命令将会被覆盖。
docker run <image> param1 param2相当于,新建CMD [param1, param2],覆盖原来的CMD
-
-
shell form
ENTRYPOINT command param1 param2- 执行的命令:
/bin/sh -c "command param1 param2",执行的程序是一个/bin/sh的子进程 - 不接收
CMD或docker run <image> param ...的参数 - 启动的程序进程ID不是1
如果非要把启动程序的PID弄为1(主进程),可以这么干,在原来的命令前加 exec,表示使用可执行程序来运行FROM ubuntu ENTRYPOINT exec command param1 param2
- Docker daemon与容器之间不能传递Unix signals,所以执行像
docker stop <container>时,容器中的程序不能接受到SIGTERM,会出现超时(默认10s),最后容器被强制kill
- 执行的命令:
-
-
启动容器时覆盖镜像中默认的 ENTRYPOINT
docker run --entrypoint 可执行的二进制程序 # 下列的情况将不生效 docker run --entrypoint sh -c "..." docker run --entrypoint /bin/sh -c "..."
通知Docker daemon:当前容器在运行时监听了指定的端口。还没有发布端口
主要对于发布随机端口有用(docker run -P)。
EXPOSE指定的端口与发布的端口没有直接的关系。docker run -p 本机端口8888:容器8080 是否发布成功,与容器是否监听了8080端口有关系
可写多个该指令,结果为并集
也可以只写一个该指令,EXPOSE可以同时指定多个端口
- syntax
EXPOSE <port> [<port>/<protocol>...]<port>/<protocol>,省略/<protocol>时,表示TCP
- 示例
EXPOSE 80/tcp 443/tcp EXPOSE 53/udp
- 映射容器中的端口到宿主机,并对外发布
-
语法
-p单个映射docker run -p [host_ip:]<host_port>:<container_port[/protocol]> ...
- 省略
host_ip:,表示映入主机的IP为0.0.0.0,即所有IP /protocol,可选项有:/tcp,/udp,/sctp,缺省时默认为/tcp-P批量映射(主机随机大号端口)
docker run -P <image>
一次性自动映射容器中的端口到宿主机,并对外发布。映射的主机端口号是随机的(使用比较大的端口号)
- 省略
-
示例
docker run -p 80:80 443:443/tcp 53:53/udp -d --name centos01 nginx:1.21.0
-
给镜像添加一条触发指令,当该镜像作为基础镜像(父镜像),其下游镜像(子镜像)在执行docker build时,此基础镜像就会执行已定义的触发指令。
孙及更小的后代docker build时不会在执行其原始祖先的ONBUILD指令。
因为原始祖先的ONBUILD指令已经在原始祖先的子镜像中执行了,执行的结果已经被继承下来了。
触发指令将在下游镜像build构建的上下文中执行,效果如同:在紧跟下游的DockerfileFROM指之后插入了这条触发指令
我们使用父镜像(即这个Dockerfile中含ONBUILD指令的镜像)时,ONBUILD不会生产任何作用,它只对基于它为基础镜像的下游镜像有作用。
可写多个该指令
使用场景
在实际工作中,利用ONBUILD指令,通常用于创建一个模板镜像,
后续可以根据该模板镜像创建特定的子镜像,
需要在子镜像构建过程中执行的一些通用操作
就可以在模板镜像对应的dockerfile文件中用ONBUILD指令指定。
从而减少dockerfile文件的重复内容编写。
-
syntax
ONBUILD <INSTRUCTION>
-
示例
ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src
-
注意事项
- 不能使用链式的
ONBUILD,即ONBUILD ONBUILD不支持 ONBUILD无法触发FROM、MAINTAINER- 在
ONBUILD使用ADD、COPY时,就确保引用资源在宿主机上存在,否则build构建将失败
- 不能使用链式的
定义执行docker stop发送给当前容器的stop-signal(停止信号)。
以便容器在退出前,可以先做一些事情,实现容器的平滑退出。
- syntax
STOPSIGNAL signal- signal可以是无符号的数字,该信号与kernel的syscall表的位置对应。如9,表示强制退出信号
- signal也可以是信号名,要求符合SIGNAME格式。如SIGKILL,即kill信号
- 示例(nginx Dockerfile)
FROM debian:buster-slim LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>" ... ENTRYPOINT ["/docker-entrypoint.sh"] EXPOSE 80 STOPSIGNAL SIGQUIT CMD ["nginx", "-g", "daemon off;"] - 为什么要有STOPSIGNAL
主要的目的是为了让容器内的应用程序在接收到stop-signal(停止信号)之后可以先做一些事情,实现容器的平滑退出。 默认情况下,容器将在一段时间之后强制退出,会造成业务的强制中断,这个时间默认是10s docker stop <container> 发送的默认stop-signal是SIGTERM,在docker stop的时候会给容器内PID为1的进程发送这个signal, 另外也可以通过create/run的参数--stop-signal可以设置自己需要的signal,效果与Dockerfile同定义STOPSIGNAL相同。 以上两种方式指定了 stop-signal后,执行 docker stop <container> 时就会发送指定的stop-signal停止信号
容器健康检测。
通知Docker daemon:怎样检测当前容器是一直在工作的。
例如:可以检测WEB服务器卡在无限一循环中,无法处理新的连接,但服务进程却一直运行
-
syntax
两种格式
-
通过运行容器内的命令来检测容器是否健康
HEALTHCHECK [OPTIONS] CMDOPTIONS:
--interval=DURATION(default: 30s)--timeout=DURATION(default: 30s)--start-period=DURATION(default: 0s)--retries=N(default: 3)
-
禁止从基础镜像中继承任何的健康检测
HEALTHCHECK NONE
-
-
示例
HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f http://localhost/ || exit 1
为shell form格式的命令指定新的默认shell,会覆盖shell form命令原来默认的shell。
shell form格式的指令主要有:
CMD command param1 param2、
ENTRYPOINT command param1 param2、
RUN <command>
SHELL指令可以出现多次。每一条SHELL指令都会覆盖所有以前的SHELL指令,并影响所有后续指令。
-
Linux的
shell form默认shell是["/bin/sh", "-c"] -
Windows的
shell form默认shell是["cmd", "/S", "/C"] -
syntax
SHELL ["executable", "parameters"]
-
示例
FROM microsoft/windowsservercore # Executed as cmd /S /C echo default RUN echo default # Executed as cmd /S /C powershell -command Write-Host default RUN powershell -command Write-Host default # Executed as powershell -command Write-Host hello SHELL ["powershell", "-command"] RUN Write-Host hello # Executed as cmd /S /C echo hello SHELL ["cmd", "/S", "/C"] RUN echo hello
- 只做复制的情况下,建议使用COPY,因为直接透明
- 如果需要把宿主机本地的tar压缩文件提取到镜像中,或下载URL资源到镜像中建议使用ADD
CMD和ENTRYPOINT都能定义容器启动时要执行的命令。
CMD与ENTRYPOINT组合使用的规则
- Dockerfile中至少要指定一个
CMD或ENTRYPOINT命令 - 把容器当可执行文件用,使用
ENTRYPOINT CMD用于给ENTRYPOINT命令定义默认参数,当docker run有传递参数时,该默认参数会被覆盖。
且ENTRYPOINT只能用 exec form写法CMD用于在容器中执行特殊的命令- 当docker run容器指定了额外的参数时,
CMD定义的命令将会被覆盖。
docker run <image> param1 param2相当于,新建CMD [param1, param2],覆盖原来的CMD
- 其中
CMD和ENTRYPOINT指令不区分先后顺序,哪个在前都一样
| # | No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT ["exec_entry", "p1_entry"] |
|---|---|---|---|
| No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
| CMD ["exec_cmd", "p1_cmd"] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd 不建议,一般运行不通 |
| CMD ["p1_cmd", "p2_cmd"] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
| CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd 不建议,一般运行不通 |
-
ENTRYPOINT 看上去与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。
-
ENTRYPOINT exec_entry p1_entryshell格式写法,直接忽略CMD参数和docker run <image> 参数 -
如果在基础镜像(BASIC IMAGE)中定义了
CMD,ENTRYPOINT将重置CMD为空值,那么必须在当前的镜像中定义CMD的值
- 使用
RUN指令安装应用和软件,构建镜像 - 如果镜像的用途是运行应用程序或服务,如运行一个MySQL,
应该优先使用ENTRYPOINT指令的exec格式写法。
CMD可为ENTRYPOINT提供额外的默认参数, 同时可利用docker run <image> 参数1 ...命令行替换默认参数。 - 如果想为容器设置默认的启动命令,可使用
CMD指令。
用户可在docker run <image> 参数1 ...命令行中替换此默认命令