Dockerfile 常用命令及典型案例整理

核心命令解析

1. FROM:指定基础镜像(必选指令)

  • 语法FROM <镜像名>[:<标签>] [AS <构建阶段名>]
  • 主要功能:定义构建新镜像的基础,所有指令均基于基础镜像执行。
  • 使用场景:必须作为Dockerfile的第一条非注释指令(ARG除外),用于选择官方、版本明确且轻量级的镜像作为基础。
  • 示例FROM python:3.11-alpine AS builder

2. WORKDIR:设置工作目录

  • 语法WORKDIR <路径>
  • 主要功能:为后续的RUN、CMD、ENTRYPOINT、COPY、ADD指令设置默认工作目录,类似Linux的cd命令。
  • 使用场景:避免在RUN指令中使用cd切换目录,提高Dockerfile的可读性和可维护性。
  • 示例WORKDIR /app

3. COPY:本地文件复制到镜像

  • 语法COPY <源路径> <目标路径>
  • 主要功能:将Dockerfile所在目录(或子目录)的文件/目录复制到镜像中,不支持自动解压。
  • 使用场景:绝大多数文件复制场景,操作明确、可预测。
  • 示例COPY ./app /app

4. ADD:增强版复制

  • 语法ADD <源路径> <目标路径>
  • 主要功能:功能与COPY类似,但额外支持自动解压本地tar压缩文件和远程URL下载(不推荐用于URL下载,建议使用RUN + curl/wget)。
  • 使用场景:需要自动解压本地tar压缩文件时使用。
  • 示例ADD https://example.com/file.tar.gz /tmp

5. RUN:构建时执行命令

  • 语法
    • Shell格式:RUN <命令>
    • Exec格式:RUN ["可执行文件", "参数1", "参数2"]
  • 主要功能:在镜像构建过程中执行命令(如安装软件、配置环境),执行结果会被固化到镜像层中。
  • 使用场景:安装系统依赖、配置环境变量等构建阶段操作。
  • 最佳实践:合并多个RUN命令减少镜像层数,清理构建过程中的临时文件。
  • 示例RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*

6. CMD:容器启动时执行命令

  • 语法
    • Exec格式(推荐):CMD ["可执行文件", "参数1", "参数2"]
    • Shell格式:CMD command param1 param2
  • 主要功能:定义容器启动后默认执行的命令,若docker run后指定了命令,会覆盖CMD指令。
  • 使用场景:定义容器的默认启动命令,允许用户在运行时灵活修改。
  • 示例CMD ["python", "app.py"]

7. ENTRYPOINT:容器入口点

  • 语法
    • Exec格式(推荐):ENTRYPOINT ["可执行文件", "参数1", "参数2"]
    • Shell格式:ENTRYPOINT command param1 param2
  • 主要功能:与CMD类似,但docker run后指定的参数会作为ENTRYPOINT的参数,而非覆盖它。
  • 使用场景:适合固定启动流程的工具类镜像,如Nginx、MySQL等。
  • 最佳实践:与CMD结合使用,ENTRYPOINT指定主程序,CMD传递默认参数。
  • 示例ENTRYPOINT ["python", "app.py"]

8. ENV:设置环境变量

  • 语法
    • ENV <key> <value>
    • ENV <key>=<value> ...
  • 主要功能:定义环境变量,该变量在“构建阶段”和“容器运行阶段”均有效。
  • 使用场景:配置应用运行时所需的环境变量,如数据库连接信息、端口号等。
  • 示例ENV NODE_ENV=production

9. ARG:构建时参数

  • 语法ARG <参数名>=<默认值>
  • 主要功能:定义构建过程中使用的临时参数,容器运行时不可见,可通过docker build --build-arg动态传递。
  • 使用场景:在构建过程中动态配置参数,如版本号、构建环境等。
  • 示例ARG VERSION=1.0

10. EXPOSE:声明端口

  • 语法EXPOSE <端口1> <端口2>...
  • 主要功能:声明容器运行时“打算监听”的端口,仅为文档说明,不实际映射到宿主机。
  • 使用场景:告知使用者容器需要哪些端口,与docker run -p配合使用。
  • 示例EXPOSE 8080

11. VOLUME:定义匿名卷

  • 语法VOLUME <路径>["路径2"]
  • 主要功能:在镜像中创建匿名数据卷,用于持久化容器内数据,避免容器删除后数据丢失。
  • 使用场景:持久化数据库数据、日志文件等动态数据。
  • 示例VOLUME /data

12. USER:指定运行用户

  • 语法USER <用户名>["UID"]
  • 主要功能:指定后续RUN、CMD、ENTRYPOINT指令的执行用户,以及容器启动时的默认用户。
  • 使用场景:提升安全性,避免以root用户运行容器。
  • 示例USER appuser

13. LABEL:添加元数据

  • 语法LABEL <key>=<value> <key>=<value> ...
  • 主要功能:为镜像添加描述性元数据(如作者、版本、描述),便于镜像管理与识别。
  • 使用场景:标注镜像的基本信息,方便团队协作和镜像维护。
  • 示例LABEL maintainer="your@email.com"

14. HEALTHCHECK:健康检查

  • 语法
    • HEALTHCHECK --interval=<时间> --timeout=<时间> --retries=<次数> CMD <健康检查命令>
  • 主要功能:定期检查容器内应用的健康状态,若连续失败,Docker会将容器状态标记为unhealthy。
  • 使用场景:确保容器内应用正常运行,及时发现并处理故障。
  • 示例HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/ || exit 1

15. ONBUILD:触发器

  • 语法ONBUILD <指令>
  • 主要功能:定义镜像作为其他镜像基础时执行的指令,不会在当前构建中执行,而是在基于此镜像的子镜像构建中执行。
  • 使用场景:构建将作为基础使用的镜像,如框架模板镜像。
  • 示例ONBUILD COPY . /app/src

典型案例展示

案例1:基础Node.js应用部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 使用官方Node.js镜像作为基础
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制package.json并安装依赖
COPY package*.json ./
RUN npm install --production

# 复制应用代码
COPY . .

# 暴露应用端口
EXPOSE 3000

# 定义启动命令
CMD ["npm", "start"]

关键步骤解释

  1. 使用node:18-alpine轻量级基础镜像,减小镜像体积。
  2. 先复制package*.json文件并安装依赖,利用Docker缓存机制,仅在依赖变更时重新安装。
  3. 复制应用代码,暴露3000端口,定义容器启动命令。

案例2:Go应用多阶段构建

1
2
3
4
5
6
7
8
9
10
11
12
13
# 阶段1:构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/myapp

# 阶段2:运行阶段
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/myapp /app/
EXPOSE 8080
CMD ["/app/myapp"]

关键步骤解释

  1. 第一阶段使用Go官方镜像进行编译,生成二进制文件。
  2. 第二阶段使用轻量级的Alpine镜像,仅复制编译好的二进制文件,大幅减小最终镜像体积。
  3. 暴露8080端口,定义容器启动命令。

案例3:前端项目部署(Vue/React)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基础镜像(轻量,约20MB)
FROM nginx:alpine

# 删除默认Nginx配置
RUN rm /etc/nginx/conf.d/default.conf

# 复制自定义Nginx配置
COPY nginx.conf /etc/nginx/conf.d/

# 复制前端打包后的dist目录到容器
COPY dist/ /usr/share/nginx/html/

# 暴露80端口
EXPOSE 80

# 启动Nginx(前台运行,避免容器退出)
CMD ["nginx", "-g", "daemon off;"]

关键步骤解释

  1. 使用nginx:alpine轻量级基础镜像,减少镜像体积和攻击面。
  2. 删除默认Nginx配置,复制自定义配置文件,解决前端路由刷新404问题。
  3. 复制前端打包后的静态文件到Nginx默认目录,暴露80端口,启动Nginx服务。

案例4:Spring Boot应用镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 第一阶段:编译(用带Maven的JDK镜像,仅用于打包)
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
# 复制pom.xml和源代码
COPY pom.xml .
COPY src ./src
# 打包生成jar包(输出到target目录)
RUN mvn package -DskipTests

# 第二阶段:运行(用仅含JRE的轻量镜像)
FROM openjdk:17-jre-slim
WORKDIR /app
# 从第一阶段复制jar包到当前镜像
COPY --from=builder /app/target/*.jar app.jar
# 暴露应用端口(与Spring Boot配置一致)
EXPOSE 8080
# 启动命令(指定JVM参数,优化性能)
CMD ["java", "-jar", "app.jar", "-Xms512m", "-Xmx512m"]

关键步骤解释

  1. 第一阶段使用Maven镜像编译Spring Boot项目,生成jar包。
  2. 第二阶段使用仅含JRE的轻量镜像,复制jar包并启动应用。
  3. 指定JVM参数优化性能,暴露8080端口。

最佳实践总结

一、镜像体积优化

  1. 使用轻量级基础镜像:优先选择Alpine、distroless、scratch等轻量级基础镜像,替代Ubuntu/CentOS全量系统镜像。

  2. 多阶段构建分离环境:构建阶段使用完整工具链(如含编译器的镜像),运行阶段仅保留运行必需组件(如二进制文件、精简运行时)。

  3. 清理构建缓存:安装系统依赖后立即清理缓存,避免冗余文件。

    1
    2
    RUN apt-get update && apt-get install -y gcc \
    && apt-get clean && rm -rf /var/lib/apt/lists/*
  4. 语言专属精简

    • Java:使用分层JAR,仅复制运行必需层。
    • Python:安装依赖时加--no-cache-dir,避免缓存。
    • Node.js:分离生产依赖(npm ci --only=production),清理npm缓存。

二、分层与缓存优化

  1. 按“变更频率”分层:高频变更文件(如应用代码)放顶层,低频变更文件(如依赖、系统配置)放底层,利用Docker分层缓存减少重建耗时。
  2. 精准控制文件复制:使用.dockerignore排除无关文件(如.gittests/node_modules),按需复制文件而非全量复制。
  3. 启用BuildKit:支持并行构建、远程缓存,加速多环境/团队构建。

三、安全加固

  1. 固定基础镜像版本:使用明确的版本号而非latest标签,确保构建可复现、漏洞可追溯。

  2. 非root用户运行:创建低权限用户,禁止容器以root启动。

    1
    2
    RUN addgroup -S appgroup && adduser -S appuser -G appgroup
    USER appuser
  3. 移除无用工具:删除sh/bash/curl/wget等非必需二进制,缩小攻击面。

  4. 扫描镜像漏洞:使用Trivy、Clair等工具扫描镜像中的已知漏洞。

四、其他最佳实践

  1. 优先使用COPY:除非需要自动解压本地tar压缩文件,否则一律使用COPY替代ADD。
  2. 合并RUN指令:减少镜像层数,在最后清理包管理器缓存。
  3. 设置健康检查:定期检查容器内应用的健康状态,确保服务可用性。
  4. 添加元数据:使用LABEL添加镜像元数据(如维护者、版本、描述信息),便于镜像管理与识别。