hello云胜

技术与生活

0%

Dockerfile

Dockerfile

Dockerfile可以分成4部分:基础镜像,维护者信息,镜像操作指令,启动命令

指令 作用
FROM 指定基础镜像
LABEL 打标签,k=v形式。多个kv之间空格隔开。v如果有空格等特殊字符,用“”引起。
MAINTAINER 指定维护者信息,已经过时,可以使用LABEL maintainer=xxx 来替代
ENV 环境变量,给RUN和CMD命令使用。会固化到镜像的ContainerConfig里
ARG 指定构建参数。声明后的RUN命令才能使用。可以在build时,通过设置–build-arg来更改
USER 切换用户,他以后的命令都是以该用户运行。USER 1000:1000
RUN build镜像期运行的命令。多个RUN指令之间没有关系。有关系要在同一个RUN使用&&
ADD 复制上下文中的文件进容器
COPY 复制上下文中的文件进容器
CMD
ENTRYPOINT
WORKDIR 指定以后命令的运行目录,不存在会创建
VOLUME 挂载,相当于run的-v参数。
EXPOSE 暴露端口,这只是一个声明。给用户看的而已。docker run -P的时候自动绑定端口,也会使用这个配置。

ENV vs ARG

  • ENV在构建期和运行期都能生效。ARG只在构建期生效
  • ENV的值不能在build时修改,ARG可以通过–build-arg来更改
  • ENV的值可以在run时修改,通过-e参数

ADD vs COPY

ADD复制的如果是压缩包自动解压,如果是远程包,自动下载。

VOLUME

注意:一旦容器内的目录被挂载出去,VOLUME命令之后的对目录的所有修改操作,不会再影响挂载出去的目录。

所以,一般把VOLUME指令放在所有变更命令的后面

RUN

指令的写法又两种:shell写法和exec写法。

shell写法:

RUN

exec写法

RUN [“命令”,“参数1”, “参数2”]

区别:

如果命令中有环境变量,exec写法取不到ENV环境变量

CMD && ENTRYPOINT

ENTRYPOINT和CMD的作用都是容器的启动命令。看起来有一些重复。但是他们还是有一些微妙的区别。

ENTRYPOINT是真正的门,真正的入口。

覆盖

ENTRYPOINT或者CMD命令会自动覆盖之前的ENTRYPOINT或者CMD命令。

所以,在docker镜像运行时, 我们可以在命令指定具体命令, 覆盖在Dockerfile里的命令.

比如,这样的一个Dockerfile

1
2
FROM ubuntu:trusty
CMD ["/bin/ping", "localhost"]

默认运行这个镜像是ping 本机。

我们可以在执行时覆盖这个默认命令。比如用hostname命令替换ping命令。

1
# docker run demo:1.0 hostname

image-20210820163403312

ENTRYPOINT也是一样的,只不过覆盖时要用–entrypoint

1
2
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping", "localhost"]
1
2
# docker run --entrypoint hostname demo:1.0
e2540a13d1f8

所以,CMD命令更容易被docker run命令的方式覆盖。相反, 如果你希望你的docker镜像只执行一个具体程序, 不希望用户在执行docker run的时候随意覆盖默认程序. 建议用ENTRYPOINT.

两种写法

CMD和ENTRYPOINT指令的也是有两种格式写法。shell写法和exec写法。

上面例子是Exec写法。

这两种写法是有一个本质区别的。

同样是ping localhost。用exec写法启动的容器

1
2
3
4
# docker exec caed34225b48 ps -f
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:43 ? 00:00:00 /bin/ping localhost
root 6 0 0 08:44 ? 00:00:00 ps -f

可以看到PID 1启动的就是ping命令。

再看下shell写法的结果

1
2
3
4
5
# docker exec 545269dc6d49 ps -f
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:46 ? 00:00:00 /bin/sh -c /bin/ping localhost
root 6 1 0 08:46 ? 00:00:00 /bin/ping localhost
root 7 0 0 08:46 ? 00:00:00 ps -f

可以看到PID 1启动的其实是sh程序,我们的ping命令作为sh的子程序运行了。

这样会导致的一个问题是,你从外部发给容器的POSIX信号,是被sh接收的,而sh命令并不会将信号转发给ping命令,会导致不能安全的关闭容器。比如用shell方法启动的容器一直在运行,我按Ctrl +C是无法停止的。

总结:exec写法不是bin/sh -c执行的,取不出环境变量的值。

所以最好时使用exec写法。

最终的用法:组合使用

从上面所知,ENTRYPOINT 用于指明程序必须运行的命令。CMD又比较方便覆盖。所以,我们可以将其联合使用,指定必须运行的命令。同时运行用户覆盖一些参数。ENTRYPOINT和CMD同时存在时, docker把CMD的命令拼接到ENTRYPOINT命令之后。

比如

1
2
3
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping","localhost","-c"]
CMD ["3"]

这样就运行用户自己指定运行的次数。

1
2
3
4
5
6
7
8
9
10
docker run demo:1.0 5
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.039 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.042 ms
64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=5 ttl=64 time=0.035 ms

--- localhost ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3999ms

多阶段构建