2017-12-06 19:29:09 +08:00
# Dockerfile 最 佳 实 践
2019-01-06 09:40:31 +08:00
本 附 录 是 笔 者 对 Docker 官 方 文 档 中 [ Best practices for writing Dockerfiles ] ( https : //docs.docker.com/develop/develop-images/dockerfile_best-practices/) 的理解与翻译。
2017-12-06 19:29:09 +08:00
# # 一 般 性 的 指 南 和 建 议
# # # 容 器 应 该 是 短 暂 的
通 过 ` Dockerfile ` 构 建 的 镜 像 所 启 动 的 容 器 应 该 尽 可 能 短 暂 ( 生 命 周 期 短 ) 。 「 短 暂 」 意 味 着 可 以 停 止 和 销 毁 容 器 , 并 且 创 建 一 个 新 容 器 并 部 署 好 所 需 的 设 置 和 配 置 工 作 量 应 该 是 极 小 的 。
# # # 使 用 ` .dockerignore ` 文 件
使 用 ` Dockerfile ` 构 建 镜 像 时 最 好 是 将 ` Dockerfile ` 放 置 在 一 个 新 建 的 空 目 录 下 。 然 后 将 构 建 镜 像 所 需 要 的 文 件 添 加 到 该 目 录 中 。 为 了 提 高 构 建 镜 像 的 效 率 , 你 可 以 在 目 录 下 新 建 一 个 ` .dockerignore ` 文 件 来 指 定 要 忽 略 的 文 件 和 目 录 。 ` .dockerignore ` 文 件 的 排 除 模 式 语 法 和 Git 的 ` .gitignore ` 文 件 相 似 。
# # # 使 用 多 阶 段 构 建
在 ` Docker 17.05 ` 以 上 版 本 中 , 你 可 以 使 用 [ 多 阶 段 构 建 ] ( . . / image / multistage - builds . md ) 来 减 少 所 构 建 镜 像 的 大 小 。
# # # 避 免 安 装 不 必 要 的 包
为 了 降 低 复 杂 性 、 减 少 依 赖 、 减 小 文 件 大 小 、 节 约 构 建 时 间 , 你 应 该 避 免 安 装 任 何 不 必 要 的 包 。 例 如 , 不 要 在 数 据 库 镜 像 中 包 含 一 个 文 本 编 辑 器 。
# # # 一 个 容 器 只 运 行 一 个 进 程
应 该 保 证 在 一 个 容 器 中 只 运 行 一 个 进 程 。 将 多 个 应 用 解 耦 到 不 同 容 器 中 , 保 证 了 容 器 的 横 向 扩 展 和 复 用 。 例 如 web 应 用 应 该 包 含 三 个 容 器 : web 应 用 、 数 据 库 、 缓 存 。
如 果 容 器 互 相 依 赖 , 你 可 以 使 用 [ Docker 自 定 义 网 络 ] ( . . / network / linking . md ) 来 把 这 些 容 器 连 接 起 来 。
# # # 镜 像 层 数 尽 可 能 少
你 需 要 在 ` Dockerfile ` 可 读 性 ( 也 包 括 长 期 的 可 维 护 性 ) 和 减 少 层 数 之 间 做 一 个 平 衡 。
# # # 将 多 行 参 数 排 序
将 多 行 参 数 按 字 母 顺 序 排 序 ( 比 如 要 安 装 多 个 包 时 ) 。 这 可 以 帮 助 你 避 免 重 复 包 含 同 一 个 包 , 更 新 包 列 表 时 也 更 容 易 。 也 便 于 ` PRs ` 阅 读 和 审 查 。 建 议 在 反 斜 杠 符 号 ` \ ` 之 前 添 加 一 个 空 格 , 以 增 加 可 读 性 。
下 面 是 来 自 ` buildpack-deps ` 镜 像 的 例 子 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
RUN apt - get update && apt - get install - y \
bzr \
cvs \
git \
mercurial \
subversion
` ` `
# # # 构 建 缓 存
在 镜 像 的 构 建 过 程 中 , Docker 会 遍 历 ` Dockerfile ` 文 件 中 的 指 令 , 然 后 按 顺 序 执 行 。 在 执 行 每 条 指 令 之 前 , Docker 都 会 在 缓 存 中 查 找 是 否 已 经 存 在 可 重 用 的 镜 像 , 如 果 有 就 使 用 现 存 的 镜 像 , 不 再 重 复 创 建 。 如 果 你 不 想 在 构 建 过 程 中 使 用 缓 存 , 你 可 以 在 ` docker build ` 命 令 中 使 用 ` --no-cache=true ` 选 项 。
但 是 , 如 果 你 想 在 构 建 的 过 程 中 使 用 缓 存 , 你 得 明 白 什 么 时 候 会 , 什 么 时 候 不 会 找 到 匹 配 的 镜 像 , 遵 循 的 基 本 规 则 如 下 :
* 从 一 个 基 础 镜 像 开 始 ( ` FROM ` 指 令 指 定 ) , 下 一 条 指 令 将 和 该 基 础 镜 像 的 所 有 子 镜 像 进 行 匹 配 , 检 查 这 些 子 镜 像 被 创 建 时 使 用 的 指 令 是 否 和 被 检 查 的 指 令 完 全 一 样 。 如 果 不 是 , 则 缓 存 失 效 。
* 在 大 多 数 情 况 下 , 只 需 要 简 单 地 对 比 ` Dockerfile ` 中 的 指 令 和 子 镜 像 。 然 而 , 有 些 指 令 需 要 更 多 的 检 查 和 解 释 。
* 对 于 ` ADD ` 和 ` COPY ` 指 令 , 镜 像 中 对 应 文 件 的 内 容 也 会 被 检 查 , 每 个 文 件 都 会 计 算 出 一 个 校 验 和 。 文 件 的 最 后 修 改 时 间 和 最 后 访 问 时 间 不 会 纳 入 校 验 。 在 缓 存 的 查 找 过 程 中 , 会 将 这 些 校 验 和 和 已 存 在 镜 像 中 的 文 件 校 验 和 进 行 对 比 。 如 果 文 件 有 任 何 改 变 , 比 如 内 容 和 元 数 据 , 则 缓 存 失 效 。
* 除 了 ` ADD ` 和 ` COPY ` 指 令 , 缓 存 匹 配 过 程 不 会 查 看 临 时 容 器 中 的 文 件 来 决 定 缓 存 是 否 匹 配 。 例 如 , 当 执 行 完 ` RUN apt-get -y update ` 指 令 后 , 容 器 中 一 些 文 件 被 更 新 , 但 Docker 不 会 检 查 这 些 文 件 。 这 种 情 况 下 , 只 有 指 令 字 符 串 本 身 被 用 来 匹 配 缓 存 。
一 旦 缓 存 失 效 , 所 有 后 续 的 ` Dockerfile ` 指 令 都 将 产 生 新 的 镜 像 , 缓 存 不 会 被 使 用 。
# # Dockerfile 指 令
下 面 针 对 ` Dockerfile ` 中 各 种 指 令 的 最 佳 编 写 方 式 给 出 建 议 。
# # # FROM
2017-12-31 14:20:04 +08:00
尽 可 能 使 用 当 前 官 方 仓 库 作 为 你 构 建 镜 像 的 基 础 。 推 荐 使 用 [ Alpine ] ( https : //hub.docker.com/_/alpine/) 镜像,因为它被严格控制并保持最小尺寸(目前小于 5 MB) , 但它仍然是一个完整的发行版。
2017-12-06 19:29:09 +08:00
# # # LABEL
你 可 以 给 镜 像 添 加 标 签 来 帮 助 组 织 镜 像 、 记 录 许 可 信 息 、 辅 助 自 动 化 构 建 等 。 每 个 标 签 一 行 , 由 ` LABEL ` 开 头 加 上 一 个 或 多 个 标 签 对 。 下 面 的 示 例 展 示 了 各 种 不 同 的 可 能 格 式 。 ` # ` 开 头 的 行 是 注 释 内 容 。
> 注 意 : 如 果 你 的 字 符 串 中 包 含 空 格 , 必 须 将 字 符 串 放 入 引 号 中 或 者 对 空 格 使 用 转 义 。 如 果 字 符 串 内 容 本 身 就 包 含 引 号 , 必 须 对 引 号 使 用 转 义 。
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
# Set one or more individual labels
LABEL com . example . version = "0.0.1-beta"
LABEL vendor = "ACME Incorporated"
LABEL com . example . release - date = "2015-02-12"
LABEL com . example . version . is - production = ""
` ` `
一 个 镜 像 可 以 包 含 多 个 标 签 , 但 建 议 将 多 个 标 签 放 入 到 一 个 ` LABEL ` 指 令 中 。
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
# Set multiple labels at once , using line - continuation characters to break long lines
LABEL vendor = ACME \ Incorporated \
com . example . is - beta = \
com . example . is - production = "" \
com . example . version = "0.0.1-beta" \
com . example . release - date = "2015-02-12"
` ` `
2019-01-06 09:40:31 +08:00
关 于 标 签 可 以 接 受 的 键 值 对 , 参 考 [ Understanding object labels ] ( https : //docs.docker.com/config/labels-custom-metadata/)。关于查询标签信息,参考 [Managing labels on objects](https://docs.docker.com/config/labels-custom-metadata/)。
2017-12-06 19:29:09 +08:00
# # # RUN
为 了 保 持 ` Dockerfile ` 文 件 的 可 读 性 , 可 理 解 性 , 以 及 可 维 护 性 , 建 议 将 长 的 或 复 杂 的 ` RUN ` 指 令 用 反 斜 杠 ` \ ` 分 割 成 多 行 。
# # # # apt - get
` RUN ` 指 令 最 常 见 的 用 法 是 安 装 包 用 的 ` apt-get ` 。 因 为 ` RUN apt-get ` 指 令 会 安 装 包 , 所 以 有 几 个 问 题 需 要 注 意 。
不 要 使 用 ` RUN apt-get upgrade ` 或 ` dist-upgrade ` , 因 为 许 多 基 础 镜 像 中 的 「 必 须 」 包 不 会 在 一 个 非 特 权 容 器 中 升 级 。 如 果 基 础 镜 像 中 的 某 个 包 过 时 了 , 你 应 该 联 系 它 的 维 护 者 。 如 果 你 确 定 某 个 特 定 的 包 , 比 如 ` foo ` , 需 要 升 级 , 使 用 ` apt-get install -y foo ` 就 行 , 该 指 令 会 自 动 升 级 ` foo ` 包 。
永 远 将 ` RUN apt-get update ` 和 ` apt-get install ` 组 合 成 一 条 ` RUN ` 声 明 , 例 如 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
RUN apt - get update && apt - get install - y \
package - bar \
package - baz \
package - foo
` ` `
将 ` apt-get update ` 放 在 一 条 单 独 的 ` RUN ` 声 明 中 会 导 致 缓 存 问 题 以 及 后 续 的 ` apt-get install ` 失 败 。 比 如 , 假 设 你 有 一 个 ` Dockerfile ` 文 件 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2018-12-19 17:24:52 +08:00
FROM ubuntu : 18.04
2017-12-06 19:29:09 +08:00
RUN apt - get update
RUN apt - get install - y curl
` ` `
构 建 镜 像 后 , 所 有 的 层 都 在 Docker 的 缓 存 中 。 假 设 你 后 来 又 修 改 了 其 中 的 ` apt-get install ` 添 加 了 一 个 包 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2018-12-19 17:24:52 +08:00
FROM ubuntu : 18.04
2017-12-06 19:29:09 +08:00
RUN apt - get update
RUN apt - get install - y curl nginx
` ` `
Docker 发 现 修 改 后 的 ` RUN apt-get update ` 指 令 和 之 前 的 完 全 一 样 。 所 以 , ` apt-get update ` 不 会 执 行 , 而 是 使 用 之 前 的 缓 存 镜 像 。 因 为 ` apt-get update ` 没 有 运 行 , 后 面 的 ` apt-get install ` 可 能 安 装 的 是 过 时 的 ` curl ` 和 ` nginx ` 版 本 。
使 用 ` RUN apt-get update && apt-get install -y ` 可 以 确 保 你 的 Dockerfiles 每 次 安 装 的 都 是 包 的 最 新 的 版 本 , 而 且 这 个 过 程 不 需 要 进 一 步 的 编 码 或 额 外 干 预 。 这 项 技 术 叫 作 ` cache busting ` 。 你 也 可 以 显 示 指 定 一 个 包 的 版 本 号 来 达 到 ` cache-busting ` , 这 就 是 所 谓 的 固 定 版 本 , 例 如 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
RUN apt - get update && apt - get install - y \
package - bar \
package - baz \
package - foo = 1.3 . *
` ` `
固 定 版 本 会 迫 使 构 建 过 程 检 索 特 定 的 版 本 , 而 不 管 缓 存 中 有 什 么 。 这 项 技 术 也 可 以 减 少 因 所 需 包 中 未 预 料 到 的 变 化 而 导 致 的 失 败 。
下 面 是 一 个 ` RUN ` 指 令 的 示 例 模 板 , 展 示 了 所 有 关 于 ` apt-get ` 的 建 议 。
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
RUN apt - get update && apt - get install - y \
aufs - tools \
automake \
build - essential \
curl \
dpkg - sig \
libcap - dev \
libsqlite3 - dev \
mercurial \
reprepro \
ruby1 .9 .1 \
ruby1 .9 .1 - dev \
s3cmd = 1.1 . * \
&& rm - rf / var / lib / apt / lists / *
` ` `
2023-01-07 23:45:41 +08:00
其 中 ` s3cmd ` 指 令 指 定 了 一 个 版 本 号 ` 1.1.* ` 。 如 果 之 前 的 镜 像 使 用 的 是 更 旧 的 版 本 , 指 定 新 的 版 本 会 导 致 ` apt-get update ` 缓 存 失 效 并 确 保 安 装 的 是 新 版 本 。
2017-12-06 19:29:09 +08:00
2023-01-07 23:45:41 +08:00
另 外 , 清 理 掉 apt 缓 存 ` var/lib/apt/lists ` 可 以 减 小 镜 像 大 小 。 因 为 ` RUN ` 指 令 的 开 头 为 ` apt-get update ` , 包 缓 存 总 是 会 在 ` apt-get install ` 之 前 刷 新 。
2017-12-06 19:29:09 +08:00
> 注 意 : 官 方 的 Debian 和 Ubuntu 镜 像 会 自 动 运 行 apt - get clean , 所 以 不 需 要 显 式 的 调 用 apt - get clean 。
# # # CMD
` CMD ` 指 令 用 于 执 行 目 标 镜 像 中 包 含 的 软 件 , 可 以 包 含 参 数 。 ` CMD ` 大 多 数 情 况 下 都 应 该 以 ` CMD ["executable", "param1", "param2"...] ` 的 形 式 使 用 。 因 此 , 如 果 创 建 镜 像 的 目 的 是 为 了 部 署 某 个 服 务 ( 比 如 ` Apache ` ) , 你 可 能 会 执 行 类 似 于 ` CMD ["apache2", "-DFOREGROUND"] ` 形 式 的 命 令 。 我 们 建 议 任 何 服 务 镜 像 都 使 用 这 种 形 式 的 命 令 。
多 数 情 况 下 , ` CMD ` 都 需 要 一 个 交 互 式 的 ` shell ` ( bash , Python , perl 等 ) , 例 如 ` CMD ["perl", "-de0"] ` , 或 者 ` CMD ["PHP", "-a"] ` 。 使 用 这 种 形 式 意 味 着 , 当 你 执 行 类 似 ` docker run -it python ` 时 , 你 会 进 入 一 个 准 备 好 的 ` shell ` 中 。 ` CMD ` 应 该 在 极 少 的 情 况 下 才 能 以 ` CMD ["param", "param"] ` 的 形 式 与 ` ENTRYPOINT ` 协 同 使 用 , 除 非 你 和 你 的 镜 像 使 用 者 都 对 ` ENTRYPOINT ` 的 工 作 方 式 十 分 熟 悉 。
# # # EXPOSE
` EXPOSE ` 指 令 用 于 指 定 容 器 将 要 监 听 的 端 口 。 因 此 , 你 应 该 为 你 的 应 用 程 序 使 用 常 见 的 端 口 。 例 如 , 提 供 ` Apache ` web 服 务 的 镜 像 应 该 使 用 ` EXPOSE 80 ` , 而 提 供 ` MongoDB ` 服 务 的 镜 像 使 用 ` EXPOSE 27017 ` 。
对 于 外 部 访 问 , 用 户 可 以 在 执 行 ` docker run ` 时 使 用 一 个 标 志 来 指 示 如 何 将 指 定 的 端 口 映 射 到 所 选 择 的 端 口 。
# # # ENV
为 了 方 便 新 程 序 运 行 , 你 可 以 使 用 ` ENV ` 来 为 容 器 中 安 装 的 程 序 更 新 ` PATH ` 环 境 变 量 。 例 如 使 用 ` ENV PATH /usr/local/nginx/bin:$PATH ` 来 确 保 ` CMD ["nginx"] ` 能 正 确 运 行 。
` ENV ` 指 令 也 可 用 于 为 你 想 要 容 器 化 的 服 务 提 供 必 要 的 环 境 变 量 , 比 如 Postgres 需 要 的 ` PGDATA ` 。
最 后 , ` ENV ` 也 能 用 于 设 置 常 见 的 版 本 号 , 比 如 下 面 的 示 例 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3 .4
RUN curl - SL http : //example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH / usr / local / postgres - $ PG_MAJOR / bin : $ PATH
` ` `
类 似 于 程 序 中 的 常 量 , 这 种 方 法 可 以 让 你 只 需 改 变 ` ENV ` 指 令 来 自 动 的 改 变 容 器 中 的 软 件 版 本 。
# # # ADD 和 COPY
虽 然 ` ADD ` 和 ` COPY ` 功 能 类 似 , 但 一 般 优 先 使 用 ` COPY ` 。 因 为 它 比 ` ADD ` 更 透 明 。 ` COPY ` 只 支 持 简 单 将 本 地 文 件 拷 贝 到 容 器 中 , 而 ` ADD ` 有 一 些 并 不 明 显 的 功 能 ( 比 如 本 地 tar 提 取 和 远 程 URL 支 持 ) 。 因 此 , ` ADD ` 的 最 佳 用 例 是 将 本 地 tar 文 件 自 动 提 取 到 镜 像 中 , 例 如 ` ADD rootfs.tar.xz ` 。
如 果 你 的 ` Dockerfile ` 有 多 个 步 骤 需 要 使 用 上 下 文 中 不 同 的 文 件 。 单 独 ` COPY ` 每 个 文 件 , 而 不 是 一 次 性 的 ` COPY ` 所 有 文 件 , 这 将 保 证 每 个 步 骤 的 构 建 缓 存 只 在 特 定 的 文 件 变 化 时 失 效 。 例 如 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
COPY requirements . txt / tmp /
RUN pip install -- requirement / tmp / requirements . txt
COPY . / tmp /
` ` `
如 果 将 ` COPY . /tmp/ ` 放 置 在 ` RUN ` 指 令 之 前 , 只 要 ` . ` 目 录 中 任 何 一 个 文 件 变 化 , 都 会 导 致 后 续 指 令 的 缓 存 失 效 。
为 了 让 镜 像 尽 量 小 , 最 好 不 要 使 用 ` ADD ` 指 令 从 远 程 URL 获 取 包 , 而 是 使 用 ` curl ` 和 ` wget ` 。 这 样 你 可 以 在 文 件 提 取 完 之 后 删 掉 不 再 需 要 的 文 件 来 避 免 在 镜 像 中 额 外 添 加 一 层 。 比 如 尽 量 避 免 下 面 的 用 法 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
ADD http : //example.com/big.tar.xz /usr/src/things/
RUN tar - xJf / usr / src / things / big . tar . xz - C / usr / src / things
RUN make - C / usr / src / things all
` ` `
而 是 应 该 使 用 下 面 这 种 方 法 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
RUN mkdir - p / usr / src / things \
&& curl - SL http : //example.com/big.tar.xz \
| tar - xJC / usr / src / things \
&& make - C / usr / src / things all
` ` `
上 面 使 用 的 管 道 操 作 , 所 以 没 有 中 间 文 件 需 要 删 除 。
对 于 其 他 不 需 要 ` ADD ` 的 自 动 提 取 功 能 的 文 件 或 目 录 , 你 应 该 使 用 ` COPY ` 。
# # # ENTRYPOINT
` ENTRYPOINT ` 的 最 佳 用 处 是 设 置 镜 像 的 主 命 令 , 允 许 将 镜 像 当 成 命 令 本 身 来 运 行 ( 用 ` CMD ` 提 供 默 认 选 项 ) 。
例 如 , 下 面 的 示 例 镜 像 提 供 了 命 令 行 工 具 ` s3cmd ` :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
ENTRYPOINT [ "s3cmd" ]
CMD [ "--help" ]
` ` `
现 在 直 接 运 行 该 镜 像 创 建 的 容 器 会 显 示 命 令 帮 助 :
` ` ` bash
$ docker run s3cmd
` ` `
或 者 提 供 正 确 的 参 数 来 执 行 某 个 命 令 :
` ` ` bash
$ docker run s3cmd ls s3 : //mybucket
` ` `
这 样 镜 像 名 可 以 当 成 命 令 行 的 参 考 。
` ENTRYPOINT ` 指 令 也 可 以 结 合 一 个 辅 助 脚 本 使 用 , 和 前 面 命 令 行 风 格 类 似 , 即 使 启 动 工 具 需 要 不 止 一 个 步 骤 。
例 如 , ` Postgres ` 官 方 镜 像 使 用 下 面 的 脚 本 作 为 ` ENTRYPOINT ` :
` ` ` bash
# ! / bin / bash
set - e
if [ "$1" = ' postgres ' ] ; then
chown - R postgres "$PGDATA"
if [ - z "$(ls -A " $ PGDATA ")" ] ; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
` ` `
> 注 意 : 该 脚 本 使 用 了 Bash 的 内 置 命 令 exec , 所 以 最 后 运 行 的 进 程 就 是 容 器 的 PID 为 1 的 进 程 。 这 样 , 进 程 就 可 以 接 收 到 任 何 发 送 给 容 器 的 Unix 信 号 了 。
该 辅 助 脚 本 被 拷 贝 到 容 器 , 并 在 容 器 启 动 时 通 过 ` ENTRYPOINT ` 执 行 :
2019-10-29 14:31:45 +08:00
` ` ` docker
2017-12-06 19:29:09 +08:00
COPY . / docker - entrypoint . sh /
ENTRYPOINT [ "/docker-entrypoint.sh" ]
` ` `
该 脚 本 可 以 让 用 户 用 几 种 不 同 的 方 式 和 ` Postgres ` 交 互 。
你 可 以 很 简 单 地 启 动 ` Postgres ` :
` ` ` bash
$ docker run postgres
` ` `
也 可 以 执 行 ` Postgres ` 并 传 递 参 数 :
` ` ` bash
$ docker run postgres postgres -- help
` ` `
最 后 , 你 还 可 以 启 动 另 外 一 个 完 全 不 同 的 工 具 , 比 如 ` Bash ` :
` ` ` bash
$ docker run -- rm - it postgres bash
` ` `
# # # VOLUME
` VOLUME ` 指 令 用 于 暴 露 任 何 数 据 库 存 储 文 件 , 配 置 文 件 , 或 容 器 创 建 的 文 件 和 目 录 。 强 烈 建 议 使 用 ` VOLUME ` 来 管 理 镜 像 中 的 可 变 部 分 和 用 户 可 以 改 变 的 部 分 。
# # # USER
如 果 某 个 服 务 不 需 要 特 权 执 行 , 建 议 使 用 ` USER ` 指 令 切 换 到 非 root 用 户 。 先 在 ` Dockerfile ` 中 使 用 类 似 ` RUN groupadd -r postgres && useradd -r -g postgres postgres ` 的 指 令 创 建 用 户 和 用 户 组 。
2022-01-04 10:41:51 +08:00
> 注 意 : 在 镜 像 中 , 用 户 和 用 户 组 每 次 被 分 配 的 UID / GID 都 是 不 确 定 的 , 下 次 重 新 构 建 镜 像 时 被 分 配 到 的 UID / GID 可 能 会 不 一 样 。 如 果 要 依 赖 确 定 的 UID / GID , 你 应 该 显 式 的 指 定 一 个 UID / GID 。
2017-12-06 19:29:09 +08:00
你 应 该 避 免 使 用 ` sudo ` , 因 为 它 不 可 预 期 的 TTY 和 信 号 转 发 行 为 可 能 造 成 的 问 题 比 它 能 解 决 的 问 题 还 多 。 如 果 你 真 的 需 要 和 ` sudo ` 类 似 的 功 能 ( 例 如 , 以 root 权 限 初 始 化 某 个 守 护 进 程 , 以 非 root 权 限 执 行 它 ) , 你 可 以 使 用 [ gosu ] ( https : //github.com/tianon/gosu)。
最 后 , 为 了 减 少 层 数 和 复 杂 度 , 避 免 频 繁 地 使 用 ` USER ` 来 回 切 换 用 户 。
# # # WORKDIR
为 了 清 晰 性 和 可 靠 性 , 你 应 该 总 是 在 ` WORKDIR ` 中 使 用 绝 对 路 径 。 另 外 , 你 应 该 使 用 ` WORKDIR ` 来 替 代 类 似 于 ` RUN cd ... && do-something ` 的 指 令 , 后 者 难 以 阅 读 、 排 错 和 维 护 。
2019-01-06 09:40:31 +08:00
# # 官 方 镜 像 示 例
2017-12-06 19:29:09 +08:00
2019-01-06 09:40:31 +08:00
这 些 官 方 镜 像 的 Dockerfile 都 是 参 考 典 范 : https : //github.com/docker-library/docs