把精简换到安全时踩过的坑
CI 流水线里最拖时间的一步是拉镜像:一个基于 Ubuntu 的全量 Python 镜像 1.2GB,冷启动要八分钟。把镜像瘦到 200MB 之后,安全扫描又报出一堆本不该进生产环境的包——优化和安全在这件事上是绑在一起的。
最初的问题
镜像太大
# 最初的 Dockerfile
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
curl \
vim \
git
COPY . /app
WORKDIR /app
RUN pip3 install -r requirements.txt
CMD ["python3", "app.py"]
问题:
- 镜像太大(500MB+)
- 包含不需要的软件
- 安全隐患多
基础优化
选择合适的基础镜像
# 使用官方镜像
FROM python:3.9-slim
# 或者使用 Alpine
FROM python:3.9-alpine
| 基础镜像 | 大小 | 优势 | 劣势 |
|---|---|---|---|
| ubuntu:20.04 | ~70MB | 软件丰富 | 镜像大 |
| debian:bullseye-slim | ~80MB | 稳定性好 | 镜像中等 |
| alpine:3.14 | ~5MB | 镜像小 | 兼容性问题 |
| scratch | 0B | 最小 | 需要静态编译 |
多阶段构建
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 运行阶段
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
CMD ["node", "dist/index.js"]
结果:镜像从 500MB 降到 100MB
深度优化
合并 RUN 指令
# 错误:多个 RUN
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
# 正确:合并 RUN
RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/*
删除不需要的文件
# 使用 .dockerignore
node_modules
npm-debug.log
.git
.github
.vscode
*.md
test
coverage
使用缓存
# 优化 Dockerfile 利用缓存
FROM python:3.9-slim
# 先复制依赖文件
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再复制代码
COPY . .
CMD ["python", "app.py"]
安全优化
使用非 root 用户
FROM python:3.9-slim
# 创建非 root 用户
RUN useradd -m appuser
# 切换到非 root 用户
USER appuser
WORKDIR /home/appuser
COPY --chown=appuser:appuser . .
CMD ["python", "app.py"]
扫描漏洞
# 使用 Trivy 扫描
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:latest
# 使用 Clair 扫描
clairctl analyze myapp:latest
定期更新基础镜像
# 使用明确的版本号
FROM python:3.9.13-slim
# 不要使用 latest
# FROM python:latest # 不要这样做
最小化攻击面
FROM python:3.9-slim
# 只安装必要的依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 不安装不必要的软件
# RUN apt-get install -y vim git curl # 不要这样做
最佳实践
层级顺序
FROM python:3.9-slim
# 1. 系统依赖(变化少,放前面)
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 2. 应用依赖(变化中等,放中间)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 3. 应用代码(变化多,放后面)
COPY . .
# 4. 配置(可能变化,放最后)
ENV FLASK_ENV=production
健康检查
FROM python:3.9-slim
COPY . .
RUN pip install -r requirements.txt
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CMD ["python", "app.py"]
信号处理
# app.py
import signal
import sys
def handle_shutdown(signum, frame):
print('Received shutdown signal, cleaning up...')
# 清理资源
sys.exit(0)
signal.signal(signal.SIGTERM, handle_shutdown)
signal.signal(signal.SIGINT, handle_shutdown)
# 主循环
while True:
# 应用逻辑
pass
镜像构建工具
BuildKit
# 启用 BuildKit
export DOCKER_BUILDKIT=1
# 使用 BuildKit 构建
docker build -t myapp:latest .
Buildx 多架构构建
# 构建多架构镜像
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push .
Kaniko(在 Kubernetes 中构建)
apiVersion: v1
kind: Pod
metadata:
name: kaniko
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args:
- "--dockerfile=Dockerfile"
- "--context=s3://myapp-bucket/"
- "--destination=myapp:latest"
踩过的坑
坑一:Alpine 兼容性问题
用了 Alpine,结果有些依赖安装失败。
解决:
- 使用 Debian-slim 替代
- 或者使用官方维护的 Alpine 变体
# Debian-slim 更稳定
FROM python:3.9-slim # 推荐
# 或者使用 Node.js 官方的 Alpine 镜像
FROM node:16-alpine # 官方维护,兼容性好
坑二:时区问题
容器时区不对,导致日志时间混乱。
解决:
FROM python:3.9-slim
# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
ENV TZ=Asia/Shanghai
坑三:文件权限问题
容器内的文件权限不正确。
解决:
FROM python:3.9-slim
RUN useradd -m appuser
COPY --chown=appuser:appuser . /app
USER appuser
WORKDIR /app
镜像检查清单
构建前
- 选择合适的基础镜像
- 使用多阶段构建
- 配置 .dockerignore
- 合并 RUN 指令
- 利用缓存机制
构建后
- 扫描安全漏洞
- 检查镜像大小
- 验证镜像功能
- 测试镜像启动
部署前
- 测试非 root 用户
- 测试健康检查
- 测试信号处理
- 设置资源限制
写在最后
Docker 镜像优化这东西,不只是为了小,更是为了安全和稳定。
优化了:
- 镜像大小
- 构建速度
- 启动速度
- 安全性
带来了:
- 维护成本
- 测试成本
- 复杂度增加
优化之前先评估:
- 镜像使用场景
- 安全要求
- 构建频率
- 团队能力
不是所有场景都需要极致优化,有时候一个合适的镜像就够了。
这次 Docker 镜像优化花了两周,从基础优化到深度优化。优化完成后,镜像从 500MB 降到 100MB,构建时间从 5 分钟降到 2 分钟,部署速度提升明显。
版权声明: 本文首发于 指尖魔法屋-把精简换到安全时踩过的坑(https://blog.thinkmoon.cn/post/69-docker-image-optimization-security-practice/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。