Dockerfile 最佳实践及案例
核心最佳实践
1. 基础镜像选择
# 推荐:使用具体版本标签
FROM node:20-alpine AS builder
# 避免:使用latest
FROM node:latest
2. 分层缓存优化
# 推荐:先复制依赖文件,后复制源码
COPY package*.json ./
RUN npm install
COPY . .
# 避免:一次性复制所有文件
COPY . .
RUN npm install
3. 多阶段构建
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o myapp
# 运行阶段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
4. 减小镜像体积
# 清理缓存和临时文件
RUN apt-get update && apt-get install -y \
package1 \
package2 \
&& rm -rf /var/lib/apt/lists/*
# 合并RUN命令减少层数
RUN apt-get update \
&& apt-get install -y curl \
&& curl -sL https://example.com/script.sh | bash \
&& apt-get remove -y curl \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
实际案例
案例1:Node.js 应用
# 多阶段构建Node.js应用
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
# 创建非root用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --chown=nodejs:nodejs package*.json ./
USER nodejs
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "dist/main.js"]
案例2:Python 应用
FROM python:3.11-slim AS builder
WORKDIR /app
# 安装构建依赖
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
# 只复制运行时依赖
COPY --from=builder /root/.local /root/.local
COPY . .
# 确保PATH包含用户安装的包
ENV PATH=/root/.local/bin:$PATH
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:8000/health')"
EXPOSE 8000
# 使用非root用户
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
CMD ["python", "app.py"]
案例3:Java/Spring Boot 应用
# 多阶段构建Spring Boot应用
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
# 利用Docker层缓存
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
RUN ./mvnw dependency:go-offline -B
COPY src src
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# 使用jlink创建自定义JRE(可选)
RUN $JAVA_HOME/bin/jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base,java.logging,java.sql \
--output /custom-jre
# 复制jar文件
COPY --from=builder /app/target/*.jar app.jar
# JVM优化参数
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s \
CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
案例4:Nginx 静态网站
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
# 复制自定义nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 设置正确的权限
RUN chown -R nginx:nginx /usr/share/nginx/html && \
chmod -R 755 /usr/share/nginx/html
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
.dockerignore 示例
# Git
.git/
.gitignore
# Node
node_modules/
npm-debug.log
.env
.env.local
# Build
dist/
build/
*.log
# IDE
.vscode/
.idea/
*.swp
# OS
.DS_Store
Thumbs.db
# Docker
Dockerfile
.dockerignore
docker-compose*.yml
# CI/CD
.github/
.gitlab-ci.yml
.travis.yml
安全最佳实践
# 1. 使用具体版本标签
FROM alpine:3.19
# 2. 避免以root运行
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
# 3. 使用COPY而非ADD(除非需要解压)
COPY --chown=appuser:appuser . .
# 4. 设置不可变文件系统
RUN chmod -R 755 /app && \
chown -R appuser:appgroup /app
USER appuser
# 5. 使用secrets,避免在镜像中存储敏感信息
# docker build --secret id=mysecret,src=secret.txt .
RUN --mount=type=secret,id=mysecret \
cat /run/secrets/mysecret > /app/config.json
通用推荐设置
# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 限制日志大小(部分应用)
ENV NODE_OPTIONS="--max-old-space-size=512"
模版参考
# 多阶段构建:生产环境镜像
# ============================================
# ---------- 阶段 1:依赖安装 ----------
FROM node:20-alpine AS deps
# 如果有私有 npm 源或需要系统依赖
RUN apk add --no-cache python3 make g++
WORKDIR /app
# 只复制依赖文件(利用 Docker 缓存)
COPY package.json package-lock.json ./
# 如果需要 yarn,使用 yarn.lock
# COPY yarn.lock ./
# 安装仅生产依赖(+ 可选:lock 文件校验)
RUN npm ci --only=production --no-audit --no-fund && \
npm cache clean --force
# ---------- 阶段 2:构建/编译(如果需要)----------
FROM node:20-alpine AS builder
WORKDIR /app
# 复制依赖文件并安装所有依赖(含 dev)
COPY package.json package-lock.json ./
RUN npm ci --no-audit --no-fund
# 复制源码并构建(如 TypeScript、React 等)
COPY . .
RUN npm run build # 假设有构建脚本
# ---------- 阶段 3:生产运行 ----------
FROM node:20-alpine
# 创建非 root 用户(安全最佳实践)
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 安装必要的系统工具(例如 curl 用于健康检查,可选)
RUN apk add --no-cache curl
WORKDIR /app
# 从 deps 阶段复制生产 node_modules
COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules
# 如果需要构建产物(如 dist, build 目录)
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
# 或者直接复制整个应用(但建议 .dockerignore 忽略不必要文件)
COPY --chown=nodejs:nodejs package.json ./
# 切换用户
USER nodejs
# 暴露应用端口
EXPOSE 3000
# 健康检查(根据应用调整间隔和命令)
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 启动命令
CMD ["node", "dist/main.js"]