mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-10 11:54:37 +00:00
Move more dockerfile content to chapter 7
This commit is contained in:
@@ -66,53 +66,15 @@ RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
|
||||
|
||||
* *exec* 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。
|
||||
|
||||
既然 `RUN` 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:
|
||||
Dockerfile 中每一个指令都会建立一层,`RUN` 也不例外。每一个 `RUN` 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,`commit` 这一层的修改,构成新的镜像。
|
||||
|
||||
```docker
|
||||
FROM debian:bookworm
|
||||
> **注意**
|
||||
>
|
||||
> 每一个 `RUN` 指令都会产生一个新的镜像层。为了减少镜像体积和层数,我们通常会将多个命令合并到一个 `RUN` 指令中执行。
|
||||
>
|
||||
> 更多关于 `RUN` 指令的详细用法、最佳实践(如清理缓存、使用 pipefail 等)及 `Union FS` 的层数限制等内容,请参阅 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 中的 **[RUN 指令](../07_dockerfile/7.1_run.md)** 小节。
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y gcc libc6-dev make wget
|
||||
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-7.2.4.tar.gz"
|
||||
RUN mkdir -p /usr/src/redis
|
||||
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
|
||||
RUN make -C /usr/src/redis
|
||||
RUN make -C /usr/src/redis install
|
||||
```
|
||||
|
||||
之前说过,Dockerfile 中每一个指令都会建立一层,`RUN` 也不例外。每一个 `RUN` 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,`commit` 这一层的修改,构成新的镜像。
|
||||
|
||||
而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。
|
||||
这是很多初学 Docker 的人常犯的一个错误。
|
||||
|
||||
*Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。*
|
||||
|
||||
上面的 `Dockerfile` 正确的写法应该是这样:
|
||||
|
||||
```docker
|
||||
FROM debian:bookworm
|
||||
|
||||
RUN set -x; buildDeps='gcc libc6-dev make wget' \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y $buildDeps \
|
||||
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-7.2.4.tar.gz" \
|
||||
&& mkdir -p /usr/src/redis \
|
||||
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
|
||||
&& make -C /usr/src/redis \
|
||||
&& make -C /usr/src/redis install \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm redis.tar.gz \
|
||||
&& rm -r /usr/src/redis \
|
||||
&& apt-get purge -y --auto-remove $buildDeps
|
||||
```
|
||||
|
||||
首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 `RUN` 一一对应不同的命令,而是仅仅使用一个 `RUN` 指令,并使用 `&&` 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。
|
||||
|
||||
并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 `\` 的命令换行方式,以及行首 `#` 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。
|
||||
|
||||
此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 `apt` 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。
|
||||
|
||||
很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。
|
||||
要想编写优秀的 `Dockerfile`,必须了解每一条指令的作用和副作用。在 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 中,我们将对 `COPY`, `ADD`, `CMD`, `ENTRYPOINT` 等指令进行详细讲解。
|
||||
|
||||
### 构建镜像
|
||||
|
||||
|
||||
Reference in New Issue
Block a user