前言
ARG
功能
○ ARG指令类似ENV,定义了一个变量;区别于ENV:用户可以在构建时
docker build --build-arg = 进行对变量的修改;ENV不可以;
○ 如果用户指定了未在Dockerfile中定义的构建参数,那么构建输出警告。
• 语法
Shell
ARG [=]
• 注意事项
○ Dockerfile可以包含一个或多个ARG指令
○ ARG支持指定默认值
○ 使用范围:定义之后才能使用,定义之前为空,如下面的案例,执行命令
docker build --build-arg username=what_user .第二行计算结果为
some_user ,不是我们指定的build-arg中的参数值what_user
Shell
FROM busybox
USER ${username:-some_user}
ARG username
USER $username
○ ENV和ARG同时存在,ENV会覆盖ARG
Shell
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo C O N T I M G V E R 执行下面指令输出 v 1.0.0 P l a i n t e x t d o c k e r b u i l d − − b u i l d − a r g C O N T I M G V E R = v 2.0.1. 我们可以优化写法为 S h e l l F R O M u b u n t u A R G C O N T I M G V E R E N V C O N T I M G V E R = CONT_IMG_VER 执行下面指令输出v1.0.0 Plaintext docker build --build-arg CONT_IMG_VER=v2.0.1 . 我们可以优化写法为 Shell FROM ubuntu ARG CONT_IMG_VER ENV CONT_IMG_VER= CONTIMGVER执行下面指令输出v1.0.0Plaintextdockerbuild−−build−argCONTIMGVER=v2.0.1.我们可以优化写法为ShellFROMubuntuARGCONTIMGVERENVCONTIMGVER={CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
○ 系统内置了一些ARG变量
▪ HTTP_PROXY ▪ http_proxy
▪ HTTPS_PROXY ▪ https_proxy
▪ FTP_PROXY ▪ ftp_proxy
▪ NO_PROXY ▪ no_proxy
▪ ALL_PROXY ▪ all_proxy
• 样例
Shell
FROM busybox
ARG user1=someuser
ARG buildno=1
ARG就是在dockerfile中生效,ENV在镜像启动容器之后仍然生效
bash
ARG SYSVERSION=22.04
FROM ubuntu:${SYSVERSION} as buildstage1
MAINTAINRR bit [email protected]
LABEL version="1.0" desc="create by bit"
ENV MYROOTDIR=/data/web/html/
COPY --chown=news:news ./index.html ${MYROOTDIR}
ENV MYTEST=1
WROKDIR /data/src
ADD https://nginx.org/download/nginx-1.24.0.tar.gz .
WORKDIR /data/src2
ADD ./nginx-1.24.0.tar.gz .
RUN cd /data/src && tar zxvf nginx-1.24.0.tar.gz
RUN ["sh","-c","mkdir -p /data/src3 && cd /data/src3/ && touch new.txt"]
RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev
RUN cd /data/src/nginx-1.24.0 \
&& ./configure \
&& make && make install
COPY ./nginx.conf /usr/local/nginx/conf/
EXPOSE 80/tcp
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

docker build -t myweb:v2.1 .
docker images myweb
docker inspect myweb:v2.1
docker build -t myweb:v2.2 --build-arg SYSVERSION=22.10 .
docker run -d --name myweb2.2test myweb:v2.2
我们看出,ARG是可以在build的时候修改的
docker exec -it myweb2.2test bash
cat /etc/release
VOLUME
• 功能
○ 用于在image中创建一个挂载点目录 ,生成的是匿名的存储卷
○ 通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成
的。
• 语法
Shell
VOLUME
VOLUME [""]
• 参数
○ mountpoint:挂载点目录
○ 注意事项
▪ 如果挂载点目录路径下此前有文件存在,docker run命令会在卷挂载完
成后将此前的所有文件复制到新挂载的卷中
其实VOLUME指令只是起到了声明了容器中的目录作为匿名卷,但是并
没有将匿名卷绑定到宿主机指定目录的功能。
volume 只是指定了一个目录,用以在用户忘记启动时指定**-v参数**也可以
保证容器的正常运行。比如mysql,你不能说用户启动时没有指定-v,然后
删了容器,就把mysql的数据文件都删了,那样生产上是会出大事故的,所
以mysql的dockerfile里面就需要配置volume,这样即使用户没有指定-v,
容器被删后也不会导致数据文件都不在了。还是可以恢复的。
volume 与-v 指令一样,容器被删除以后映射在主机上的文件不会被删除。
如果-v和volume指定了同一个位置,会以-v设定的目录为准,其实
volume 指令的设定的目的就是为了避免用户忘记指定-v的时候导致的数据丢
失,那么如果用户指定了-v,自然而然就不需要volume指定的位置了。
• 样例
Shell
VOLUME ["/data1","/data2"]
bash
ARG SYSVERSION=22.04
FROM ubuntu:${SYSVERSION} as buildstage1
MAINTAINRR bit [email protected]
LABEL version="1.0" desc="create by bit"
ENV MYROOTDIR=/data/web/html/
COPY --chown=news:news ./index.html ${MYROOTDIR}
ENV MYTEST=1
WROKDIR /data/src
ADD https://nginx.org/download/nginx-1.24.0.tar.gz .
WORKDIR /data/src2
ADD ./nginx-1.24.0.tar.gz .
RUN cd /data/src && tar zxvf nginx-1.24.0.tar.gz
RUN ["sh","-c","mkdir -p /data/src3 && cd /data/src3/ && touch new.txt"]
RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev
RUN cd /data/src/nginx-1.24.0 \
&& ./configure \
&& make && make install
COPY ./nginx.conf /usr/local/nginx/conf/
EXPOSE 80/tcp
VOLUME ["/data/"]
#CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
VOLUME ["/data/"]
docker build -t myweb:v2.3 .
docker images myweb
docker run -d --name myweb2.3test myweb:v2.3
docker inspect myweb2.3test
可以看到这里有一个volume的存储卷
名称是随机生成的
source是宿主机的位置
destinaton是容器的位置
我们没有指定存储卷的话,这个就是默认生成的一个匿名存储卷,宿主机位置都是固定的,容器位置由dockerfile指定
docker run -d -v ./:/data1 --name myweb2.3test2 myweb:v2.3
这个就是把当前宿主机目录映射到容器/data1目录
我们来看一下我们的存储卷会不会发生覆盖
docker inspect myweb2.3test2
可以发现存储卷的类型就变为了绑定卷了
源目录就是刚刚run的宿主机目录
目标目录就是我们指定的目录
SHELL
• 功能
• 语法
SHELL指令允许覆盖用于shell命令形式的默认shell。
Linux 上的默认shell是["/bin/sh","-c"],在 Windows上是["cmd","/S",
SHELL指令必须以JSON格式写入Dockerfile。
Shell
SHELL ["executable", "parameters"]
• 参数
○ executable:shell可执行文件的位置
○ parameters:shell执行的参数
○ 注意事项
▪ SHELL指令可以多次出现。
▪ 每个SHELL指令都会覆盖所有先前的SHELL指令,并影响所有后续指
令。
▪ 该SHELL指令在 Windows 上特别有用,因为windows行有两种不同的
shell:cmd 和 powershell
就是把我们docker默认运行的shell覆盖,比如windows的cmd和powershell就是不同的shell,就是更改这个东西,linux上就是bash和sh
mkdir shell
cd shell
vi dockerfile
bash
FROM ubuntu:20.04
RUN ls -l / > /test1.txt
SHELL ["/bin/bash","-cvx"]
RUN ls -l / > /test2.txt
SHELL ["/bin/bash","-cvx"]这个是打印bash执行命令时的详细信息
docker build -t shell:v0.1 --no-cache --progress=plain .
--no-cache就是不要使用原来镜像制作的缓存,从第一层开始做镜像,因为缓存中有原来镜像的层
--progress=plain这个是打印构建的动作
我们发现两个RUN ls -l / > /test2.txt显示的内容不一样
说明我们就发生了shell的切换,因为-cvx会把命令的执行过程打印出来,SHELL ["/bin/bash","-cvx"]就是这样的,所以打印的东西要多一点
Linux的两个shell没有什么区别
USER
• 功能
○ 用于指定运行image时的或运行Dockerfile中任何RUN、CMD或
ENTRYPOINT指令定的程序时的用户名或UID
默认情况下,container的运行身份为root用户
• 语法
Shell
USER [:]
USER [:]
• 参数
○ user:用户
○ group:用户组
○ uid:用户id
○ gid:组id
○ 注意事项
▪ 可以为任意数字,但实践中其必须为/etc/passwd中某用户的有效
UID,否则将运行失败
• 样例
Shell
USER docker:docker
mkdir user
cd user
vi dockerfile
bash
FROM ubuntu:22.04 as buildbase
RUN groupadd nginx
RUN useradd nginx -g nginx
USER nginx:nginx
RUN whoami > /tmp/user1.txt
USER root:root
RUN groupadd mysql
RUN useradd mysql -g mysql
USER mysql:mysql
RUN whoami > /tmp/user2.txt
RUN groupadd nginx 这个是·添加了一个nginx的用户组
RUN useradd nginx -g nginx 是添加了一个nginx的用户,然后指定这个用户的组为nginx组
USER nginx:nginx 就是切换到nginx的用户,和nginxx的组上
RUN whoami > /tmp/user1.txt 这个就是打印你是谁
USER root:root 切换到root账号
因为nginx并没有添加用户的权限,所以我们要切到root ,再来添加一个mysql的用户,和mysql的用户组
然后又切换到mysql的用户与用户组
docker build -t user:v0.1 .
docker run -it --rm user:v0.1 bash
cat /tmp/user1.txt
cat /tmp/user2.txt
我们来可以看出文件的属性也是属于对应的用户与用户组了
HEALTHCHECK
功能
○ HEALTHCHECK指令告诉Docker如何测试容器以检查它是否仍在工作。
○ 即使服务器进程仍在运行,这也可以检测出陷入无限循环且无法处理新连接
的Web服务器等情况。 所以健康与运行不是一个东西
• 语法
Shell
HEALTHCHECK [OPTIONS] CMD command (check container health by
running a command inside the container)
HEALTHCHECK NONE (disable any healthcheck inherited from the base
image)
• 参数
○ OPTIONS 选项有:
▪ --interval=DURATION (default: 30s):每隔多长时间探测一次,默认30-- timeout= DURATION (default: 30s):服务响应超时时长,默认30秒 --start-period= DURATION (default: 0s):服务启动多久后开始探测,默
认0秒
▪ --retries=N (default: 3):认为检测失败几次为宕机,默认3次
○ 返回值
▪ 0:容器成功是健康的,随时可以使用
▪ 1:不健康的容器无法正常工作
▪ 2:保留不使用此退出代码
mkdir healthcheck
cd healthcheck
vi Dockerfile
bash
FROM nginx:1.24.0
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
CMD curl -f http://localhost/ || exit 1 就是curl一下我们的本机,看健不健康,不健康的话,就退出,检测周期是五秒钟,
docker build -t hc:v1.0 .
docker run -d --name hc1 hc:v1.0
docker ps
docker ps
可以看出一开始在检查,然后就检查OK了
bash
FROM nginx:1.24.0
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost:8989/ || exit 1
docker build -t hc:v2.0 .
docker run -d --name hc2 hc:v2.0
docker ps
docker ps
我们发现它会进行多次检查,如果多次检查都不健康,就结束了
ONBUILD
• 功能
○ 用于在Dockerfile中定义一个触发器
○ 以该Dockerfile中的作为基础镜像由FROM指令在build过程中被执行时,
将会"触发"创建其base image的Dockerfile文件中的ONBUILD指令定义的触
发器
• 语法
Shell
ONBUILD
• 参数:
○ INSTRUCTION:dockerfile的一条指令
• 样例
Shell
ONBUILD ADD . /app/src
就是以当前镜像作为基础镜像,然后去构造其他镜像时触发dockerfile中的一些指令
vi dockerfile
bash
ARG SYSVERSION=22.04
FROM ubuntu:${SYSVERSION} as buildstage1
MAINTAINRR bit [email protected]
LABEL version="1.0" desc="create by bit"
ENV MYROOTDIR=/data/web/html/
COPY --chown=news:news ./index.html ${MYROOTDIR}
ENV MYTEST=1
WROKDIR /data/src
ADD https://nginx.org/download/nginx-1.24.0.tar.gz .
WORKDIR /data/src2
ADD ./nginx-1.24.0.tar.gz .
RUN cd /data/src && tar zxvf nginx-1.24.0.tar.gz
RUN ["sh","-c","mkdir -p /data/src3 && cd /data/src3/ && touch new.txt"]
RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev
RUN cd /data/src/nginx-1.24.0 \
&& ./configure \
&& make && make install
COPY ./nginx.conf /usr/local/nginx/conf/
EXPOSE 80/tcp
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
ONBUILD RUN echo "create by bit version" > /tmp/version/txt
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

docker build -t myweb:v2.4 .
mkdir ob
cd ob/
vi Dockerfile
bash
FROM myweb:v2.4
RUN echo "hello" > /test.txt
docker build -t ob:v0.1 .
本来我们的dockerfile只有两步,但是出现了多的一步,就是ONBUILD,就是触发了ONBUILD
docker run -d --name ob1 ob:v0.1
docker exec -it ob1 bash
cd /tmp
cat version.txt
STOPSIGNAL
• 功能
○ STOPSIGNAL指令设置将发送到容器的系统调用信号。
○
此信号可以是与内核的系统调用表中的位置匹配的有效无符号数,例如9,
或者SIGNAME格式的信号名,例如SIGKILL。
• 语法
Shell
STOPSIGNAL signal
• 参数
○ STOPSIGNAL指令设置将发送到容器出口的系统调用信号。 此信号可以是与
内核的系统调用表中的位置匹配的有效无符号数,例如9,或者SIGNAME格式
的信号名,例如SIGKILL。常见的信号如下:
代号
名称
1
SIGHUP
内容
启动被终止的程序,可让该进程重新读取自己的配置文
件,类似重新启动。
2
SIGINT
相当于用键盘输入 [ctrl]-c 来中断一个程序的进行。
9
SIGKILL
代表强制中断一个程序的进行,如果该程序进行到一半,
那么尚未完成的部分可能会有"半产品"产生,类似 vim会
有 .filename.swp 保留下来。
15
SIGTERM
以正常的方式来终止该程序。由于是正常的终止,所以后
续的动作会将他完成。不过,如果该程序已经发生问题,
就是无法使用正常的方法终止时,输入这个 signal 也是没
有用的。
19 SIGSTOP 相当于用键盘输入 [ctrl]-z 来暂停一个程序的进行。
mkdir stopsig
cd stopsig
vi Dockerfile
bash
FROM nginx:1.24.0
STOPSIGNAL 9
docker build -t stopsig:v1.0
docker images stopsig
docker run -d --name stopsig1 stopsig:v1.0
docker logs -f stopsig1
这个设置了STOPSIGNAL的
用来监控日志信息
然后就是再来一个shell
docker run -d --name nostopsig nginx:1.24.0
docker logs -f nostopsig
这个是正常的,没有设置STOPSIGNAL
再开一个shell
docker stop nostopsig
这个就是优雅的退出了
docker stop stopsig1
这个就是在运行的时候,什么都不打印就退出了,因为它的信号变成了9,9就是这样的
docker build
功能
docker build 命令用于使用 Dockerfile 创建镜像。
• 语法
Shell
docker build [OPTIONS] PATH | URL | -
-指的是标准流,url指的是远端的地址, path指的是本地的路径,就是本地的上下文,就是构建镜像的dockerfile地址
• 关键参数
○ --build-arg=[] :设置镜像创建时的变量;
○ -f :指定要使用的Dockerfile路径; ---》默认找dockerfile,这个可以找非dockerfile名字的文件了
○ --label=[] :设置镜像使用的元数据;
○ --no-cache :创建镜像的过程不使用缓存; 、、发现前几层一样,就可以使用缓存了
○ --pull :尝试去更新镜像的新版本;、、就算本地有,也会去拉取
○ --quiet, -q :安静模式,成功后只输出镜像 ID; 不会打印日志了
○ --tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构
建中为一个镜像设置多个标签。
○ --network: 默认 default。在构建期间设置RUN指令的网络模式 ,可以指定走host网络
bash
FROM ubuntu 22.04
ARG AGINX_VERSION=1.24.0
ADD http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz .
RUN echo "v1.0" > ./version.txt
docker build -t build:v0.5 .
这个我们下的nginx是1.24.0
docker build -t build:v0.6 --build-arg NGINX_VERSION=1.23.4 .

--build-arg就是用来替换dockerfile中ARG的
这个就是动态的制作镜像
mv Dockerfile Dockerfile2
docker build -t build:v0.7 .
不指定文件的话,这个默认找的就是本目录下的名字叫做Dockerfile的文件
所以改名就找不到了
docker build -t build:v0.7 -f ./Dockerfile2 .
-f 就是指定文件了
docker build -t build:v0.8 --label MYTEST=1 -f ./Dockerfile2 .
这个就是添加镜像要使用的源数据
docker inspect build:v0.8
之前运行的时候我们可以ADD的时候看到左边有CACHED,就是使用了缓存的
docker build -t build:v0.9 -f ./Dockerfile2 --no-cache .
这次ADD的时候就没有使用cache了
docker build -t build:v1.0 -f ./Dockerfile2 --no-cache --pull .
这个--pull就是每次都要去镜像源新下载 ubuntu这个镜像
所有很慢,--no-cache --pull 这两个就很慢
docker build -t build:v1.0 -f ./Dockerfile2 -q .
-q就是不打印那些日志了
-t就是给镜像取名和tag
docker build -t build:v1.3 -f ./Dockerfile2 --network host .
这个就是构建网络使用本机的网络,只要本机能上网,那么就没事
为了防止使用缓存我们这样
docker build -t build:v1.3 -f ./Dockerfile2 --no-cache --network host .
因为我们主机能上网,所以我们构建的过程中也能上网,而且不是使用的缓存
Dockerfile 编写优秀实践
善用.dockerignore 文件
使用它可以标记在执行docker build时忽略的路径和文件,避免发送不必要的数据内
容,从而加快整个镜像创建过程。 相当于.gitignore
镜像的多阶段构建
通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应
用所需要的最小化环境。当然,用户也可以通过分别构造编译镜像和运行镜像来达到
类似的结果,但这种方式需要维护多个Dockerfile。
合理使用缓存
如合理使用cache,减少内容目录下的文件,内容不变的指令尽量放在前面,这样可
以尽量复用;
基础镜像尽量使用官方镜像,并选择体积较小镜像
容器的核心是应用,大的平台微服务可能几十上百个。选择过大的父镜像(如Ubuntu
系统镜像)会造成最终生成应用镜像的臃肿,推荐选用瘦身过的应用镜像(如
node:slim),或者较为小巧的系统镜像(如alpine、busybox或debian);
减少镜像层数
如果希望所生成镜像的层数尽量少,则要尽量合并RUN、ADD和COPY指令。通常
情况下,多个RUN指令可以合并为一条RUN指令;如apt get update&&apt install尽
量写到一行
精简镜像用途
尽量让每个镜像的用途都比较集中单一,避免构造大而复杂、多功能的镜像;
减少外部源的干扰
如果确实要从外部引入数据,需要指定持久的地址,并带版本信息等,让他人可以复
用而不出错。
减少不必要的包安装
只安装需要的包,不要安装无用的包,减少镜像体积。