镜像构建实践

Docker 镜像构建的实践技巧和最佳实践


📋 目录


构建上下文

什么是构建上下文

构建上下文是 Docker 构建时的工作目录,Dockerfile 中的 COPYADD 指令只能访问构建上下文中的文件。

# 当前目录作为构建上下文
docker build -t myapp:latest .
 
# 指定目录作为构建上下文
docker build -t myapp:latest /path/to/build/context
 
# 使用远程 URL 作为构建上下文
docker build -t myapp:latest https://github.com/user/repo.git

构建上下文大小

构建上下文会被发送到 Docker 守护进程,过大的上下文会影响构建速度。

# 查看构建上下文大小
du -sh .
 
# 优化构建上下文
# 1. 使用 .dockerignore 排除不必要的文件
# 2. 只复制必要的文件

.dockerignore 文件

作用

.dockerignore 文件用于排除构建上下文中不需要的文件,类似于 .gitignore

语法规则

# 注释
# 排除文件
*.log
*.tmp
 
# 排除目录
node_modules/
.git/
.vscode/
 
# 排除特定文件
README.md
Dockerfile
 
# 使用通配符
*.md
test/*.js
 
# 排除但包含特定文件
*.md
!README.md

示例

# Git 相关
.git
.gitignore
.gitattributes
 
# 开发工具
.vscode/
.idea/
*.swp
*.swo
 
# 依赖和构建产物
node_modules/
dist/
build/
*.log
 
# 测试文件
test/
*.test.js
coverage/
 
# 文档
*.md
docs/
 
# 环境配置
.env
.env.local

构建缓存机制

缓存原理

Docker 使用层缓存机制,如果 Dockerfile 中的指令没有变化,会复用之前的层。

缓存失效

以下情况会导致缓存失效:

  1. Dockerfile 指令发生变化
  2. 构建上下文中的文件发生变化(COPY/ADD)
  3. 使用 --no-cache 参数
# 不使用缓存构建
docker build --no-cache -t myapp:latest .
 
# 使用特定缓存源
docker build --cache-from myapp:previous -t myapp:latest .

优化缓存使用

# 好的做法:先复制依赖文件,利用缓存
COPY package.json package-lock.json ./
RUN npm install
COPY . .
 
# 不好的做法:先复制所有文件
COPY . .
RUN npm install

构建参数传递

ARG 构建参数

# 定义构建参数
ARG VERSION=latest
ARG BUILD_DATE
ARG NODE_ENV=production
 
# 使用构建参数
LABEL version=$VERSION
ENV NODE_ENV=$NODE_ENV

传递构建参数

# 传递单个参数
docker build --build-arg VERSION=1.0.0 -t myapp:latest .
 
# 传递多个参数
docker build \
  --build-arg VERSION=1.0.0 \
  --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
  -t myapp:latest .
 
# 从环境变量传递
export VERSION=1.0.0
docker build --build-arg VERSION=$VERSION -t myapp:latest .

ARG 与 ENV 的区别

  • ARG:构建时变量,构建后不存在
  • ENV:运行时变量,容器运行时存在
ARG BUILD_VERSION
ENV APP_VERSION=$BUILD_VERSION

多架构构建

Buildx 多架构构建

# 安装 buildx
docker buildx create --use
 
# 构建多架构镜像
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t username/myapp:latest \
  --push .
 
# 查看支持的平台
docker buildx inspect --bootstrap

平台特定构建

# 为特定平台构建
docker buildx build \
  --platform linux/arm64 \
  -t myapp:arm64 \
  --load .

构建优化技巧

1. 减少镜像层数

# 好的做法:合并 RUN 指令
RUN apt-get update && \
    apt-get install -y nginx && \
    rm -rf /var/lib/apt/lists/*
 
# 不好的做法:多个 RUN 指令
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*

2. 使用多阶段构建

# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
 
# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html

3. 合理使用缓存

# 先复制依赖文件(变化频率低)
COPY package.json package-lock.json ./
RUN npm install
 
# 再复制源代码(变化频率高)
COPY . .

4. 清理不必要的文件

RUN apt-get update && \
    apt-get install -y nginx && \
    rm -rf /var/lib/apt/lists/* && \
    apt-get clean

5. 使用 .dockerignore

确保 .dockerignore 文件排除不必要的文件,减小构建上下文。


构建输出控制

输出格式

# 静默构建(只显示错误)
docker build -q -t myapp:latest .
 
# 详细输出
docker build --progress=plain -t myapp:latest .
 
# JSON 输出
docker build --progress=json -t myapp:latest .

构建日志

# 保存构建日志
docker build -t myapp:latest . 2>&1 | tee build.log

常见问题

1. 构建上下文过大

问题:构建上下文包含大量文件,导致构建缓慢。

解决

  • 使用 .dockerignore 排除不必要的文件
  • 只复制必要的文件到构建上下文

2. 缓存失效

问题:每次构建都重新下载依赖。

解决

  • 合理组织 Dockerfile 指令顺序
  • 先复制依赖文件,再复制源代码

3. 构建失败

问题:构建过程中出现错误。

解决

# 查看详细错误信息
docker build --progress=plain -t myapp:latest .
 
# 使用中间镜像调试
docker build --target builder -t myapp:builder .
docker run -it myapp:builder /bin/bash

4. 权限问题

问题:构建时文件权限不正确。

解决

# 设置正确的文件权限
COPY --chown=user:user app /app
RUN chmod +x /app/entrypoint.sh

实用脚本

构建脚本示例

#!/bin/bash
# build.sh
 
IMAGE_NAME="myapp"
VERSION=${1:-latest}
REGISTRY="registry.example.com"
 
echo "Building ${IMAGE_NAME}:${VERSION}"
 
docker build \
  --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
  --build-arg VERSION=${VERSION} \
  -t ${IMAGE_NAME}:${VERSION} \
  -t ${IMAGE_NAME}:latest \
  -t ${REGISTRY}/${IMAGE_NAME}:${VERSION} \
  .
 
echo "Build complete: ${IMAGE_NAME}:${VERSION}"

📚 参考资源


相关笔记