Dockerfile 书写格式与命令详解
一、Dockerfile 基本概念
Dockerfile 是一个文本文件,包含一系列指令,用于自动化构建 Docker 镜像。每条指令都会在镜像中创建一个新的层。
基本格式规范
# 注释使用 # 号开头
# 指令不区分大小写(但惯例使用大写)
# 指令 参数
指令 参数1 参数2 ...
二、Dockerfile 核心命令详解
1. FROM - 指定基础镜像(必须)
作用:设置构建所基于的镜像,必须是第一条非注释指令。
# 语法
FROM [--platform=<平台>] <镜像名>[:<标签>] [AS <名称>]
# 示例
FROM ubuntu:22.04
FROM alpine:latest
FROM nginx:1.25-alpine
FROM python:3.11-slim
FROM --platform=linux/arm64 ubuntu:22.04 # 指定平台
# 多阶段构建中的别名
FROM golang:1.21 AS builder
FROM alpine:latest
最佳实践:
-
使用官方镜像作为基础
-
选择 Alpine 版本减小体积
-
指定具体标签而不是
latest
2. RUN - 执行命令
作用:在构建镜像时执行命令,每个 RUN 创建一个新层。
# shell 形式(默认使用 /bin/sh -c)
RUN <命令>
# exec 形式(推荐)
RUN ["可执行文件", "参数1", "参数2"]
# 示例
RUN apt-get update && apt-get install -y curl
RUN ["apt-get", "update"]
RUN pip install requests flask
# 多行命令(使用 \ 换行)
RUN apt-get update && \
apt-get install -y \
curl \
wget \
vim && \
rm -rf /var/lib/apt/lists/*
最佳实践:
-
将多个命令合并到一个 RUN(减少层数)
-
使用
&&连接命令 -
清理临时文件减小镜像体积
3. CMD - 容器启动默认命令
作用:提供容器启动时的默认命令,可被 docker run 覆盖。每个 Dockerfile 只能有一个 CMD。
# exec 形式(推荐)
CMD ["可执行文件", "参数1", "参数2"]
# 作为 ENTRYPOINT 的默认参数
CMD ["参数1", "参数2"]
# shell 形式
CMD 命令 参数1 参数2
# 示例
CMD ["nginx", "-g", "daemon off;"]
CMD ["python", "app.py"]
CMD echo "Hello World" # shell形式
# 作为 ENTRYPOINT 的参数
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
区别:
-
CMD会被docker run后的命令覆盖 -
如果 Dockerfile 有多个 CMD,只有最后一个生效
4. ENTRYPOINT - 容器启动入口点
作用:配置容器启动时执行的命令,不会被 docker run 覆盖(除非使用 --entrypoint)。
# exec 形式(推荐)
ENTRYPOINT ["可执行文件", "参数1", "参数2"]
# shell 形式
ENTRYPOINT 命令 参数1 参数2
# 示例
ENTRYPOINT ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["python", "app.py"]
# 配合 CMD 使用
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx"]
CMD vs ENTRYPOINT:
| 场景 | 用法 |
|---|---|
| 固定命令 | ENTRYPOINT ["nginx"] |
| 固定命令+可变参数 | ENTRYPOINT ["nginx"] + CMD ["-g", "daemon off;"] |
| 完全可覆盖 | CMD ["python", "app.py"] |
使用示例:
# 示例1:可执行工具
FROM alpine
ENTRYPOINT ["ping"]
CMD ["localhost"]
# 运行:docker run myping google.com → ping google.com
# 示例2:数据库启动脚本
FROM mysql
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["mysqld"]
5. COPY - 复制文件
作用:从构建上下文复制文件/目录到镜像中。
# 语法
COPY [--chown=<用户>:<组>] <源路径>... <目标路径>
# exec 形式(路径包含空格时使用)
COPY ["源路径", "目标路径"]
# 示例
COPY . /app
COPY package.json /app/
COPY --chown=www-data:www-data index.html /var/www/
COPY --chmod=755 script.sh /usr/local/bin/
COPY --from=builder /app/dist /app/dist # 多阶段构建
# 通配符
COPY hom* /mydir/
COPY abc?.txt /mydir/
注意事项:
-
源路径必须在构建上下文中(不能使用
../xxx) -
目标路径不存在时会自动创建
-
复制目录时,会复制目录内容(不是目录本身)
6. ADD - 增强版 COPY
作用:类似 COPY,但支持更多功能(URL、自动解压)。
# 语法
ADD [--chown=<用户>:<组>] <源路径>... <目标路径>
# 示例
ADD https://example.com/file.tar.gz /tmp/ # 下载文件
ADD app.tar.gz /app/ # 自动解压
ADD --chown=www-data:www-data . /app/
# 特殊功能
ADD *.txt /mydir/ # 通配符
ADD dir /app # 复制目录内容
ADD vs COPY:
| 功能 | COPY | ADD |
|---|---|---|
| 复制本地文件 | ✓ | ✓ |
| 自动解压 tar | ✗ | ✓ |
| 下载 URL | ✗ | ✓ |
| 推荐程度 | 推荐 | 仅在需要自动解压时使用 |
最佳实践:优先使用 COPY,除非需要自动解压功能。
7. WORKDIR - 设置工作目录
作用:设置 RUN, CMD, ENTRYPOINT, COPY, ADD 的工作目录。
# 语法
WORKDIR <目录路径>
# 示例
WORKDIR /app
WORKDIR /usr/local/src
# 相对路径
WORKDIR /app
WORKDIR src # 实际变为 /app/src
# 使用环境变量
WORKDIR /home/${USERNAME}
# 多级目录会自动创建
WORKDIR /a/b/c
注意事项:
-
如果目录不存在会自动创建
-
可以多次使用,支持相对路径
-
建议使用绝对路径避免混淆
8. ENV - 设置环境变量
作用:设置环境变量,在构建和运行时都有效。
# 语法
ENV <键> <值> # 单个变量(空格分隔)
ENV <键>=<值> ... # 多个变量(等号分隔)
# 示例
ENV NODE_VERSION 18.17.0
ENV APP_HOME=/app PATH=/app/bin:$PATH
ENV USER=admin PASSWORD=123456
# 使用变量
ENV APP_DIR /app
WORKDIR ${APP_DIR}
RUN echo $APP_DIR
# 持久化到容器
ENV DATABASE_URL=postgres://user:pass@db:5432/db
变量替换:
-
$variable或${variable} -
${variable:-default}- 如果变量未设置,使用默认值 -
${variable:+override}- 如果变量已设置,使用覆盖值
9. ARG - 构建参数
作用:定义构建时的变量,仅在构建过程中有效。
# 语法
ARG <变量名>[=<默认值>]
# 示例
ARG VERSION=1.0
ARG DEBIAN_FRONTEND=noninteractive
ARG BUILD_DATE
# 使用
FROM ubuntu:${VERSION}
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}
RUN echo "Building version ${APP_VERSION}"
# 构建时传递参数
# docker build --build-arg VERSION=22.04 --build-arg APP_VERSION=2.0 .
ARG vs ENV:
| 特性 | ARG | ENV |
|---|---|---|
| 作用范围 | 仅构建过程 | 构建+运行时 |
| 持久化 | 不保留在镜像 | 保留在镜像 |
| 覆盖方式 | --build-arg | 构建时固定 |
10. EXPOSE - 声明端口
作用:声明容器运行时监听的端口(仅为文档说明,不实际映射)。
# 语法
EXPOSE <端口>[/<协议>] ...
# 示例
EXPOSE 80
EXPOSE 443/tcp
EXPOSE 53/udp
EXPOSE 8080 3000 5000
# 示例:Web应用
FROM nginx
EXPOSE 80 443
注意事项:
-
只是文档性说明,实际端口映射需要在
docker run -p或-P时指定 -
-P会暴露所有 EXPOSE 的端口
11. VOLUME - 创建挂载点
作用:声明容器中的目录为数据卷,用于数据持久化。
# 语法
VOLUME ["<路径1>", "<路径2>", ...]
VOLUME <路径>
# 示例
VOLUME /data
VOLUME ["/var/log", "/var/db"]
VOLUME /var/lib/mysql
# 完整示例
FROM mysql:8.0
VOLUME /var/lib/mysql
EXPOSE 3306
12. USER - 切换用户
作用:指定运行容器时使用的用户名或 UID。
# 语法
USER <用户名|UID>[:<组名|GID>]
# 示例
USER www-data
USER 1000
USER node:node
USER 1000:1000
# 完整示例
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
WORKDIR /app
COPY --chown=nodejs:nodejs . .
最佳实践:
-
不要使用 root 运行应用
-
创建非特权用户运行服务
-
使用
--chown配合 COPY
13. LABEL - 添加元数据
作用:为镜像添加元数据(版本、描述、维护者等)。
# 语法
LABEL <键>=<值> <键>=<值> ...
# 示例
LABEL version="1.0"
LABEL maintainer="john@example.com"
LABEL description="This is my application"
# 多标签
LABEL version="1.0" \
com.example.vendor="ACME" \
com.example.release-date="2024-01-01"
# 查看标签
# docker inspect --format='{{json .Config.Labels}}' 镜像名
常用标签规范(OpenContainer Initiative):
LABEL org.opencontainers.image.title="MyApp"
LABEL org.opencontainers.image.version="1.0.0"
LABEL org.opencontainers.image.description="My awesome app"
LABEL org.opencontainers.image.created="2024-01-01T00:00:00Z"
LABEL org.opencontainers.image.authors="support@example.com"
14. HEALTHCHECK - 健康检查
作用:告诉 Docker 如何检查容器是否健康。
# 语法
HEALTHCHECK [选项] CMD <命令>
HEALTHCHECK NONE # 禁用健康检查
# 选项
--interval=<时间> # 检查间隔(默认30s)
--timeout=<时间> # 超时时间(默认30s)
--start-period=<时间> # 启动等待时间(默认0s)
--retries=<次数> # 失败重试次数(默认3次)
# 示例
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost/ || exit 1
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/health || exit 1
# 检查数据库
HEALTHCHECK --interval=10s --timeout=5s \
CMD mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD || exit 1
健康状态:
-
starting- 初始状态 -
healthy- 健康 -
unhealthy- 不健康
15. SHELL - 更改默认 Shell
作用:更改 RUN、CMD、ENTRYPOINT 使用的默认 shell。
# 语法
SHELL ["可执行文件", "参数"]
# 示例
# Linux 默认使用 ["/bin/sh", "-c"]
SHELL ["/bin/bash", "-c"]
RUN echo "Hello from bash"
# Windows 示例
SHELL ["powershell", "-Command"]
RUN Write-Host "Hello from PowerShell"
# 使用不同的 shell
SHELL ["/bin/sh", "-c"]
RUN apk add --no-cache curl
SHELL ["/bin/bash", "-c"]
RUN source venv/bin/activate && python app.py
16. STOPSIGNAL - 设置停止信号
作用:设置容器停止时发送的系统调用信号。
# 语法
STOPSIGNAL <信号>
# 示例
STOPSIGNAL SIGTERM # 默认
STOPSIGNAL SIGQUIT
STOPSIGNAL SIGKILL
STOPSIGNAL SIGUSR1
17. ONBUILD - 触发指令
作用:设置当镜像被用作其他镜像的基础时触发的指令。
# 语法
ONBUILD <其他指令>
# 示例:构建基础镜像
FROM node:18
ONBUILD COPY package.json /app/
ONBUILD RUN npm install
ONBUILD COPY . /app/
WORKDIR /app
CMD ["npm", "start"]
# 使用时的效果(自动执行 ONBUILD 指令)
FROM my-node-base:latest # 会自动执行 COPY + RUN
适用场景:
-
构建基础框架镜像
-
需要在子镜像中自动执行的配置
三、完整示例:不同场景的 Dockerfile
示例 1:Node.js 应用
# 多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-alpine
WORKDIR /app
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 复制依赖和代码
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
CMD ["node", "server.js"]
示例 2:Python 应用
FROM python:3.11-slim
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
APP_HOME=/app
WORKDIR $APP_HOME
# 安装系统依赖
RUN apt-get update && \
apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制并安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 创建非 root 用户
RUN addgroup --system app && \
adduser --system --group app && \
chown -R app:app $APP_HOME
USER app
EXPOSE 8000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"]
示例 3:Nginx + 静态文件
FROM nginx:alpine
# 设置维护者信息
LABEL maintainer="admin@example.com"
LABEL version="1.0"
# 删除默认配置
RUN rm /etc/nginx/conf.d/default.conf
# 复制自定义配置
COPY nginx.conf /etc/nginx/conf.d/
# 复制静态文件
COPY --chown=nginx:nginx ./dist /usr/share/nginx/html
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/ || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
示例 4:Java/Spring Boot 应用
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 添加构建参数
ARG JAR_FILE=target/*.jar
# 复制 JAR 文件
COPY ${JAR_FILE} app.jar
# 创建非 root 用户
RUN addgroup -S spring && \
adduser -S spring -G spring && \
chown spring:spring app.jar
USER spring
# JVM 优化
ENV JAVA_OPTS="-Xms256m -Xmx512m"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
示例 5:数据库初始化
FROM postgres:15-alpine
# 设置环境变量
ENV POSTGRES_DB=myapp \
POSTGRES_USER=myuser \
POSTGRES_PASSWORD=mypassword
# 复制初始化脚本
COPY init.sql /docker-entrypoint-initdb.d/
COPY scripts/*.sh /docker-entrypoint-initdb.d/
# 设置数据卷
VOLUME /var/lib/postgresql/data
EXPOSE 5432
HEALTHCHECK --interval=30s --timeout=5s \
CMD pg_isready -U myuser -d myapp || exit 1
四、Dockerfile 最佳实践
1. 层级优化
# ❌ 不好:多个 RUN 创建多个层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*
# ✅ 好:合并到单个 RUN
RUN apt-get update && \
apt-get install -y curl nginx && \
rm -rf /var/lib/apt/lists/*
2. 利用构建缓存
# ✅ 将不常变化的指令放在前面
FROM node:18
WORKDIR /app
COPY package*.json ./ # 依赖文件先复制
RUN npm install # 只有 package.json 变化才重新安装
COPY . . # 源代码后复制
3. 使用 .dockerignore
# .dockerignore 文件内容
node_modules
.git
*.log
.env
.DS_Store
dist
coverage
4. 最小化镜像体积
# 使用 Alpine 版本
FROM node:18-alpine
FROM python:3.11-alpine
FROM golang:1.21-alpine
# 清理临时文件
RUN apk add --no-cache curl && \
rm -rf /var/cache/apk/*
# 多阶段构建
FROM golang:1.21 AS builder
# ... 构建二进制文件
FROM alpine:latest
COPY --from=builder /app/binary /usr/local/bin/
5. 安全性实践
# 不要以 root 运行
RUN adduser -D myuser
USER myuser
# 使用特定版本标签,避免 latest
FROM python:3.11.4-slim
# 限制资源
# docker run --memory=512m --cpus=1
# 使用 COPY 而不是 ADD(除非需要自动解压)
COPY . /app
五、常用组合模式
模式 1:开发环境
FROM node:18
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
模式 2:生产环境
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json .
RUN npm ci --only=production
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
模式 3:调试友好
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt debugpy
COPY . .
ENV PYTHONBREAKPOINT=ipdb.set_trace
CMD ["python", "-m", "debugpy", "--listen", "0.0.0.0:5678", "app.py"]
六、Dockerfile 命令优先级
构建阶段:
ARG (构建参数)
↓
FROM (基础镜像)
↓
LABEL / MAINTAINER (元数据)
↓
ARG / ENV (变量)
↓
RUN (构建命令)
↓
COPY / ADD (复制文件)
↓
WORKDIR (工作目录)
↓
EXPOSE / VOLUME (声明)
↓
USER (用户切换)
↓
HEALTHCHECK (健康检查)
↓
ENTRYPOINT / CMD (入口点)
七、调试技巧
# 1. 查看构建过程
docker build --progress=plain -t myapp .
# 2. 查看镜像层
docker history myapp
# 3. 临时运行中间层
docker run -it --rm <中间层镜像ID> /bin/sh
# 4. 查看构建缓存
docker builder du
# 5. 禁用缓存
docker build --no-cache -t myapp .
更多推荐

所有评论(0)