Skip to content

Dockerfile 详解

Dockerfile 是用于构建 Docker 镜像的文本文件,包含了一系列的指令和参数。本文将详细介绍 Dockerfile 的编写方法、最佳实践和优化技巧。

Dockerfile 基础

什么是 Dockerfile?

Dockerfile 是一个文本文件,包含了构建 Docker 镜像所需的一系列命令。它允许用户自动化地创建镜像,确保了镜像构建过程的可重复性和一致性。

Dockerfile 的基本结构

dockerfile
# 注释
FROM base-image

# 设置元数据
LABEL maintainer="your-email@example.com"
LABEL version="1.0"

# 设置环境变量
ENV APP_HOME=/app

# 工作目录
WORKDIR $APP_HOME

# 复制文件
COPY . .

# 运行命令
RUN apt-get update && apt-get install -y package-name

# 暴露端口
EXPOSE 8080

# 启动命令
CMD ["executable", "param1", "param2"]

Dockerfile 指令详解

FROM

指定基础镜像:

dockerfile
# 使用特定版本
FROM ubuntu:20.04

# 使用最新版本(不推荐)
FROM ubuntu:latest

# 使用精简版本
FROM alpine:3.14

# 多阶段构建中的命名
FROM golang:1.17 AS builder

LABEL

添加镜像元数据:

dockerfile
# 添加单个标签
LABEL version="1.0"

# 添加多个标签
LABEL maintainer="Name <email@example.com>" \
      description="This is my application" \
      version="1.0"

ENV

设置环境变量:

dockerfile
# 设置单个环境变量
ENV MY_VAR="my value"

# 设置多个环境变量
ENV APP_HOME="/app" \
    APP_PORT=8080 \
    APP_USER="appuser"

WORKDIR

设置工作目录:

dockerfile
# 设置绝对路径
WORKDIR /app

# 使用环境变量
WORKDIR $APP_HOME

# 创建并切换到多级目录
WORKDIR /app/src/config

COPY 和 ADD

复制文件到镜像:

dockerfile
# 复制单个文件
COPY file.txt /app/

# 复制多个文件
COPY file1.txt file2.txt /app/

# 使用通配符
COPY *.txt /app/

# 保留文件权限
COPY --chown=user:group files* /app/

# ADD 支持 URL 和自动解压
ADD https://example.com/file.tar.gz /app/

RUN

执行命令:

dockerfile
# Shell 格式
RUN apt-get update && \
    apt-get install -y \
        package1 \
        package2 \
    && rm -rf /var/lib/apt/lists/*

# Exec 格式
RUN ["executable", "param1", "param2"]

# 使用变量
RUN sh -c "echo $HOME"

CMD 和 ENTRYPOINT

设置容器启动命令:

dockerfile
# CMD 示例
CMD ["nginx", "-g", "daemon off;"]
CMD ["python", "app.py"]
CMD ["sh", "-c", "echo $HOME"]

# ENTRYPOINT 示例
ENTRYPOINT ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["python"]
CMD ["app.py"]  # 作为 ENTRYPOINT 的参数

EXPOSE

声明容器端口:

dockerfile
# 声明单个端口
EXPOSE 80

# 声明多个端口
EXPOSE 80 443

# 声明 UDP 端口
EXPOSE 53/udp

# 声明 TCP 和 UDP 端口
EXPOSE 53/tcp 53/udp

VOLUME

声明数据卷:

dockerfile
# 声明单个数据卷
VOLUME /data

# 声明多个数据卷
VOLUME ["/data", "/var/log"]

# 声明带环境变量的数据卷
VOLUME ["$DATA_DIR"]

USER

设置用户:

dockerfile
# 使用用户名
USER nginx

# 使用 UID
USER 1000

# 使用用户和组
USER user:group

ARG

定义构建参数:

dockerfile
# 定义参数
ARG VERSION=latest

# 使用参数
FROM ubuntu:${VERSION}

# 设置默认值
ARG USER=default_user

# 限制作用域
FROM ubuntu
ARG SETTINGS
# 这里可以使用 SETTINGS
FROM alpine
# 这里不能使用 SETTINGS,需要重新定义

SHELL

更改默认 Shell:

dockerfile
# 更改为 PowerShell(Windows)
SHELL ["powershell", "-Command"]

# 更改为 bash
SHELL ["/bin/bash", "-c"]

多阶段构建

基本多阶段构建

dockerfile
# 构建阶段
FROM golang:1.17 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# 最终阶段
FROM alpine:3.14
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]

高级多阶段构建

dockerfile
# 开发阶段
FROM node:14 AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

# 构建阶段
FROM development AS builder
RUN npm run build

# 生产阶段
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html

构建优化

1. 减小镜像大小

dockerfile
# 使用精简基础镜像
FROM alpine:3.14

# 合并 RUN 指令
RUN apk add --no-cache \
    python3 \
    py3-pip \
    && pip install --no-cache-dir \
    requests \
    flask

# 清理不必要的文件
RUN apt-get update && apt-get install -y \
    package1 \
    package2 \
    && rm -rf /var/lib/apt/lists/*

2. 利用构建缓存

dockerfile
# 先复制依赖文件
COPY package*.json ./
RUN npm install

# 再复制源代码
COPY . .

3. 使用 .dockerignore

text
# .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md

最佳实践

1. 基础镜像选择

dockerfile
# 使用官方镜像
FROM node:14-alpine

# 使用精简版本
FROM debian:slim

# 使用特定版本
FROM python:3.9.7-slim

2. 安全性考虑

dockerfile
# 使用非 root 用户
RUN useradd -r -s /bin/false appuser
USER appuser

# 使用固定版本
FROM ubuntu:20.04

# 最小化安装包
RUN apt-get update && apt-get install -y --no-install-recommends \
    package1 \
    package2

3. 多行参数排序

dockerfile
# 按字母顺序排序依赖
RUN apt-get update && apt-get install -y \
    curl \
    nginx \
    python3 \
    vim \
    && rm -rf /var/lib/apt/lists/*

4. 使用环境变量

dockerfile
# 定义版本变量
ARG NODE_VERSION=14
FROM node:${NODE_VERSION}

# 使用环境变量
ENV APP_HOME=/app \
    APP_PORT=3000 \
    NODE_ENV=production

实际应用案例

1. Node.js 应用

dockerfile
# 构建阶段
FROM node:14-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 生产阶段
FROM node:14-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["npm", "start"]

2. Python Web 应用

dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV FLASK_APP=app.py \
    FLASK_ENV=production

EXPOSE 5000

CMD ["flask", "run", "--host=0.0.0.0"]

3. Java Spring Boot 应用

dockerfile
# 构建阶段
FROM maven:3.8-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests

# 运行阶段
FROM openjdk:11-jre-slim
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

调试和故障排查

1. 构建时调试

bash
# 显示详细构建信息
docker build --progress=plain .

# 查看构建历史
docker history image-name

# 检查中间容器
docker build --target stage-name .

2. 常见问题解决

dockerfile
# 解决权限问题
RUN chown -R user:user /app

# 解决时区问题
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 解决编码问题
ENV LANG=C.UTF-8

高级技巧

1. 条件构建

dockerfile
ARG ENV=prod

# 开发环境
FROM base AS development
RUN if [ "$ENV" = "dev" ]; then \
        apt-get update && apt-get install -y debug-tools; \
    fi

# 生产环境
FROM base AS production
RUN if [ "$ENV" = "prod" ]; then \
        apt-get update && apt-get install -y optimization-tools; \
    fi

2. 构建钩子

dockerfile
# 使用 ONBUILD 指令
FROM base
ONBUILD COPY . /app
ONBUILD RUN npm install

3. 健康检查

dockerfile
# 添加健康检查
HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost/ || exit 1

性能优化

1. 缓存优化

dockerfile
# 优化依赖安装
COPY package*.json ./
RUN npm ci
COPY . .

# 使用缓存挂载
RUN --mount=type=cache,target=/var/cache/apt \
    apt-get update && apt-get install -y package

2. 多阶段构建优化

dockerfile
# 使用缓存阶段
FROM base AS deps
COPY package*.json ./
RUN npm ci

FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html

3. 并行构建

dockerfile
# 使用 BuildKit 并行构建
FROM base
RUN --parallel wget https://example.com/file1 \
                  https://example.com/file2

总结

编写高效的 Dockerfile 需要考虑多个方面:

  1. 基础知识

    • 理解每个指令的作用
    • 掌握基本语法规则
    • 了解构建上下文
  2. 最佳实践

    • 使用多阶段构建
    • 优化镜像大小
    • 合理利用缓存
    • 注意安全性
  3. 性能优化

    • 减少层数
    • 优化构建速度
    • 减小最终镜像大小
  4. 实际应用

    • 根据具体需求选择基础镜像
    • 合理组织构建步骤
    • 做好错误处理和日志记录

在下一章中,我们将探讨 Docker 的最佳实践,包括安全性、性能优化和开发流程等方面的内容。