Linux · 2 分钟阅读
06. Dockerfile 定制镜像
Dockerfile = 把镜像构建过程脚本化。commit 留下的黑盒、臃肿、不可复现,这里一次性解决。
最小示例
mkdir mynginx && cd mynginx
# Dockerfile
FROM nginx:1.27
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
docker build -t nginx:v2 .
FROM
# 官方基础镜像
FROM nginx:1.27
FROM python:3.12-slim
# 多阶段命名
FROM node:22 AS builder
# 完全空白
FROM scratch
scratch 不含任何东西,适合放静态二进制。FROM 必须是 Dockerfile 的第一条指令。
RUN
两种格式:
# shell 形式
RUN apt-get update && apt-get install -y curl
# exec 形式
RUN ["apt-get", "install", "-y", "curl"]
合并 RUN
每条 RUN 是一层。N 个 RUN = N 层——体积膨胀、构建变慢。合并到一条 RUN,用 && 串联。
反面教材:
FROM debian:stretch
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-5.0.3.tar.gz
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
正确写法:
FROM debian:stretch
RUN 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-5.0.3.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
编译依赖、缓存文件、临时源码都应该在同一个 RUN 末尾清掉。每一层一旦构建就不会"减重"。
UnionFS 早期对层数有上限(AUFS 旧版本 42 层),现虽放宽但仍要养成合并习惯。
构建命令
docker build -t nginx:v2 .
. 是构建上下文,不是 Dockerfile 所在目录(这点经常被搞混)。
上下文与 .dockerignore
COPY/ADD的源路径都相对于构建上下文- 不会跨出上下文去读
/opt/xxx - 大上下文会让构建明显变慢,写
.dockerignore排除无关文件:
.git
node_modules
*.log
Dockerfile
README.md
其他构建方式
# 从 Git 仓库
docker build https://github.com/user/repo.git#main
# 从 tar 包
docker build https://example.com/context.tar.gz
# 从标准输入读 Dockerfile
docker build - < Dockerfile
# 从标准输入读上下文压缩包
docker build - < context.tar.gz
用 stdin 读 Dockerfile 时没有上下文,
COPY/ADD用不了。
几条经验
- 每个
RUN末尾清缓存、清理临时文件 - 把稳定的层放前面(依赖装一次就行),变动的层(COPY 源码)放后面——缓存命中率高
apt-get install加--no-install-recommends- 构建失败的中间层会被丢弃并出新的,没事