Dockerfile编写完全指南#
1. 简介#
Dockerfile是一个文本文件,包含了一系列指令和参数,用于自动化构建Docker镜像。通过Dockerfile,我们可以定义应用程序的运行环境、依赖项、配置文件等,实现应用程序的容器化部署。
1.1 什么是Dockerfile#
Dockerfile是Docker用来构建镜像的构建文件,是由一系列命令和参数构成的脚本。这些命令应用于基础镜像并最终创建一个新的镜像。
1.2 Dockerfile的优势#
- 可重复性: 确保在不同环境中构建相同的镜像
- 版本控制: 可以像代码一样进行版本管理
- 自动化: 支持CI/CD流水线自动构建
- 透明性: 清晰地展示镜像的构建过程
- 可移植性: 在任何支持Docker的环境中都能运行
2. Dockerfile基本语法#
2.1 基本结构#
1
2
3
4
5
6
7
8
| # 注释
FROM base_image
MAINTAINER author_info
RUN command
COPY source destination
WORKDIR /path
EXPOSE port
CMD ["executable", "param1", "param2"]
|
2.2 语法规则#
- 每个指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
# 表示注释- 每个指令都会创建一个新的镜像层,并对镜像进行提交
3. 常用指令详解#
3.1 FROM指令#
FROM指令用于指定基础镜像,是Dockerfile的第一个指令。
1
2
3
4
5
6
7
8
9
| # 使用官方Python镜像
FROM python:3.9-slim
# 使用特定版本的Ubuntu
FROM ubuntu:20.04
# 使用多阶段构建
FROM node:16 AS builder
FROM nginx:alpine AS production
|
最佳实践:
- 优先选择官方镜像
- 使用具体的版本标签而不是
latest - 选择最小化的基础镜像(如alpine版本)
3.2 RUN指令#
RUN指令用于在镜像构建过程中执行命令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Shell形式
RUN apt-get update && apt-get install -y \
curl \
vim \
git \
&& rm -rf /var/lib/apt/lists/*
# Exec形式
RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "curl"]
# 多个命令合并
RUN apt-get update \
&& apt-get install -y python3 python3-pip \
&& pip3 install --upgrade pip \
&& apt-get clean
|
最佳实践:
- 合并多个RUN指令减少镜像层数
- 清理包管理器缓存
- 使用
\进行换行提高可读性
3.3 COPY和ADD指令#
COPY指令#
1
2
3
4
5
6
7
8
9
10
11
| # 复制文件
COPY app.py /app/
# 复制目录
COPY ./src /app/src
# 复制多个文件
COPY requirements.txt package.json /app/
# 设置文件权限
COPY --chown=user:group files* /app/
|
ADD指令#
1
2
3
4
5
| # ADD具有额外功能:自动解压缩
ADD archive.tar.gz /app/
# 从URL下载文件
ADD https://example.com/file.txt /app/
|
最佳实践:
- 优先使用COPY而不是ADD
- 只有需要自动解压缩或从URL下载时才使用ADD
- 使用.dockerignore文件排除不需要的文件
3.4 WORKDIR指令#
1
2
3
4
5
6
7
8
| # 设置工作目录
WORKDIR /app
# 相对路径(基于当前WORKDIR)
WORKDIR src
# 绝对路径
WORKDIR /usr/local/app
|
最佳实践:
3.5 EXPOSE指令#
1
2
3
4
5
6
7
8
9
| # 暴露单个端口
EXPOSE 80
# 暴露多个端口
EXPOSE 80 443
# 指定协议
EXPOSE 53/udp
EXPOSE 80/tcp
|
**注意:**EXPOSE指令只是声明端口,实际运行时需要使用-p参数映射端口。
3.6 ENV指令#
1
2
3
4
5
6
7
8
| # 设置环境变量
ENV NODE_ENV=production
ENV PATH=/app/bin:$PATH
# 一次设置多个变量
ENV NODE_ENV=production \
PORT=3000 \
DEBUG=false
|
3.7 ARG指令#
1
2
3
4
5
6
7
| # 定义构建参数
ARG VERSION=latest
ARG BUILD_DATE
# 使用构建参数
FROM node:${VERSION}
LABEL build_date=${BUILD_DATE}
|
构建时传递参数:
1
| docker build --build-arg VERSION=16 --build-arg BUILD_DATE=$(date) .
|
3.8 CMD和ENTRYPOINT指令#
CMD指令#
1
2
3
4
5
6
7
8
| # Exec形式(推荐)
CMD ["python", "app.py"]
# Shell形式
CMD python app.py
# 作为ENTRYPOINT的默认参数
CMD ["--help"]
|
ENTRYPOINT指令#
1
2
3
4
5
6
| # 设置容器启动命令
ENTRYPOINT ["python", "app.py"]
# 结合CMD使用
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8080"]
|
区别:
- CMD可以被docker run命令覆盖
- ENTRYPOINT不会被覆盖,docker run的参数会作为参数传递给ENTRYPOINT
3.9 VOLUME指令#
1
2
3
4
5
6
| # 创建挂载点
VOLUME ["/data"]
VOLUME ["/var/log", "/var/db"]
# 单个目录
VOLUME /app/uploads
|
3.10 USER指令#
1
2
3
4
5
6
7
8
| # 创建用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 切换用户
USER appuser
# 使用UID
USER 1000
|
3.11 HEALTHCHECK指令#
1
2
3
4
5
6
| # 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# 禁用健康检查
HEALTHCHECK NONE
|
3.12 LABEL指令#
1
2
3
4
5
6
7
8
9
| # 添加元数据
LABEL version="1.0"
LABEL description="This is a web application"
LABEL maintainer="developer@example.com"
# 多个标签
LABEL version="1.0" \
description="Web application" \
maintainer="developer@example.com"
|
4. 实际应用示例#
4.1 Python Web应用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| # 使用官方Python运行时作为基础镜像
FROM python:3.9-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# 安装系统依赖
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制requirements文件
COPY requirements.txt .
# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /app
USER appuser
# 暴露端口
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
|
4.2 Node.js应用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # 使用官方Node.js镜像
FROM node:16-alpine
# 设置工作目录
WORKDIR /usr/src/app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production && npm cache clean --force
# 复制应用代码
COPY . .
# 创建用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# 更改文件所有权
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
# 暴露端口
EXPOSE 3000
# 启动应用
CMD ["node", "server.js"]
|
4.3 Java Spring Boot应用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| # 多阶段构建
FROM maven:3.8.4-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:11-jre-slim
WORKDIR /app
# 创建用户
RUN groupadd -r spring && useradd -r -g spring spring
# 复制jar文件
COPY --from=builder /app/target/*.jar app.jar
# 更改所有权
RUN chown spring:spring app.jar
USER spring
# 暴露端口
EXPOSE 8080
# JVM参数优化
ENV JAVA_OPTS="-Xmx512m -Xms256m"
# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
4.4 静态网站(Nginx)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # 构建阶段
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:alpine
# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
# 暴露端口
EXPOSE 80
# 启动nginx
CMD ["nginx", "-g", "daemon off;"]
|
5. 多阶段构建#
5.1 基本概念#
多阶段构建允许在一个Dockerfile中使用多个FROM指令,每个FROM指令可以使用不同的基础镜像。
5.2 优势#
- 减小最终镜像大小
- 分离构建环境和运行环境
- 提高安全性
- 简化构建流程
5.3 实际示例#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 第一阶段:构建
FROM golang:1.19-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# 第二阶段:运行
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# 只复制编译后的二进制文件
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
|
5.4 命名构建阶段#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 命名构建阶段
FROM node:16 AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm install
FROM node:16 AS builder
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
|
6. 最佳实践#
6.1 镜像大小优化#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 使用alpine镜像
FROM node:16-alpine
# 合并RUN指令
RUN apk add --no-cache \
python3 \
make \
g++ \
&& npm install \
&& apk del make g++
# 清理缓存
RUN apt-get update \
&& apt-get install -y package \
&& rm -rf /var/lib/apt/lists/*
# 使用.dockerignore
# .dockerignore文件内容:
# node_modules
# .git
# *.log
# .DS_Store
|
6.2 安全最佳实践#
1
2
3
4
5
6
7
8
9
10
11
12
| # 使用非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# 最小权限原则
COPY --chown=appuser:appuser . /app
# 使用具体版本
FROM node:16.14.2-alpine
# 扫描漏洞
# 使用工具如Trivy、Clair等
|
6.3 构建缓存优化#
1
2
3
4
5
6
7
| # 先复制依赖文件
COPY package*.json ./
RUN npm install
# 后复制源代码
COPY . .
RUN npm run build
|
6.4 环境变量管理#
1
2
3
4
5
6
7
8
9
| # 使用ARG进行构建时配置
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
# 敏感信息使用secrets
# docker build --secret id=mypassword,src=./password.txt .
RUN --mount=type=secret,id=mypassword \
PASSWORD=$(cat /run/secrets/mypassword) && \
echo "Password: $PASSWORD"
|
6.5 健康检查#
1
2
3
4
5
6
7
| # Web应用健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# 数据库健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD mysqladmin ping -h localhost || exit 1
|
7. 常见问题和解决方案#
7.1 镜像层过多#
**问题:**每个RUN指令都会创建新的镜像层
解决方案:
1
2
3
4
5
6
7
8
9
10
| # 错误做法
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN rm -rf /var/lib/apt/lists/*
# 正确做法
RUN apt-get update && \
apt-get install -y curl vim && \
rm -rf /var/lib/apt/lists/*
|
7.2 缓存失效#
**问题:**代码变更导致依赖重新安装
解决方案:
1
2
3
4
5
6
| # 先复制依赖文件
COPY requirements.txt .
RUN pip install -r requirements.txt
# 后复制源代码
COPY . .
|
7.3 时区问题#
1
2
3
| # 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
7.4 权限问题#
1
2
3
| # 确保文件权限正确
COPY --chown=appuser:appuser . /app
RUN chmod +x /app/start.sh
|
8. 调试和测试#
8.1 构建调试#
1
2
3
4
5
6
7
8
| # 查看构建过程
docker build --progress=plain --no-cache .
# 构建到特定阶段
docker build --target builder .
# 查看镜像历史
docker history image_name
|
8.2 运行时调试#
1
2
3
4
5
6
7
8
| # 交互式运行
docker run -it image_name /bin/bash
# 覆盖入口点
docker run -it --entrypoint /bin/bash image_name
# 查看容器日志
docker logs container_name
|
8.3 镜像分析#
1
2
3
4
5
| # 使用dive分析镜像层
dive image_name
# 查看镜像大小
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
|
9. CI/CD集成#
9.1 GitHub Actions#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| name: Build and Push Docker Image
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Push to registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:${{ github.sha }}
|
9.2 GitLab CI#
1
2
3
4
5
6
7
8
9
10
| stages:
- build
- test
- deploy
build:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
10. 高级技巧#
10.1 BuildKit特性#
1
2
3
4
5
6
7
8
9
10
| # syntax=docker/dockerfile:1
FROM alpine
# 使用缓存挂载
RUN --mount=type=cache,target=/var/cache/apk \
apk add --update git
# 使用secrets
RUN --mount=type=secret,id=mypassword \
cat /run/secrets/mypassword
|
10.2 条件构建#
1
2
3
4
5
6
| ARG ENVIRONMENT=production
# 开发环境安装调试工具
RUN if [ "$ENVIRONMENT" = "development" ]; then \
apt-get update && apt-get install -y vim curl; \
fi
|
10.3 动态标签#
1
2
3
| ARG VERSION
LABEL version=$VERSION
LABEL build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
11. 性能优化#
11.1 并行构建#
1
2
3
4
5
6
7
8
9
10
11
12
| # 使用BuildKit并行构建
# DOCKER_BUILDKIT=1 docker build .
FROM alpine AS stage1
RUN sleep 10
FROM alpine AS stage2
RUN sleep 10
FROM alpine
COPY --from=stage1 /etc/os-release /stage1-info
COPY --from=stage2 /etc/os-release /stage2-info
|
11.2 镜像压缩#
1
2
3
4
5
| # 使用squash减少层数
docker build --squash -t myapp .
# 使用multi-stage构建
# 只保留最终运行时需要的文件
|
12. 监控和日志#
12.1 日志配置#
1
2
3
| # 确保日志输出到stdout/stderr
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
|
12.2 监控集成#
1
2
3
| # 添加监控端点
EXPOSE 9090
HEALTHCHECK --interval=30s CMD curl -f http://localhost:9090/metrics
|
Dockerfile是容器化应用的核心,掌握其编写技巧对于现代应用开发至关重要。
关键要点:#
- 基础指令掌握:熟练使用FROM、RUN、COPY、WORKDIR等基本指令
- 多阶段构建:有效减小镜像大小,分离构建和运行环境
- 安全实践:使用非root用户,最小权限原则
- 性能优化:合理利用缓存,减少镜像层数
- 最佳实践:遵循Docker官方推荐的编写规范
实际应用建议:#
- 从简单开始:先编写基本的Dockerfile,逐步优化
- 版本控制:将Dockerfile纳入版本控制系统
- 自动化测试:在CI/CD流水线中集成镜像构建和测试
- 安全扫描:定期扫描镜像漏洞
- 监控优化:持续监控镜像大小和构建时间