R / Richie全部文章 ↑

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
  • 构建失败的中间层会被丢弃并出新的,没事

参考