Move more dockerfile content to chapter 7

This commit is contained in:
Baohua Yang
2026-02-09 13:13:33 -08:00
parent b44c9acd6c
commit bae82e993a
15 changed files with 105 additions and 60 deletions

View File

@@ -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` 等指令进行详细讲解
### 构建镜像