Dockerfile 完全指南:从入门到精通
一、什么是 Dockerfile?
Dockerfile 是一个文本文件,包含了一系列构建 Docker 镜像的指令。通过 Dockerfile,开发者可以将应用程序的构建过程标准化、自动化,确保在任何环境中都能生成一致的镜像。
使用 Dockerfile 构建镜像的核心优势:
可重复性:相同的 Dockerfile 在任何环境下都能生成相同的镜像
可维护性:以代码形式管理镜像构建过程,便于版本控制
可扩展性:便于团队协作和持续集成 / 持续部署 (CI/CD) 流程集成
二、Dockerfile 基本结构
一个完整的 Dockerfile 通常包含以下几个部分(按常规顺序排列):
基础镜像指令:指定构建的基础镜像
维护者信息:可选,说明镜像的维护者
环境配置:设置环境变量、工作目录等
文件操作:复制文件到镜像中
命令执行:运行命令安装依赖或配置应用
暴露端口:声明容器运行时监听的端口
启动命令:指定容器启动时执行的命令
三、常用 Dockerfile 指令详解
1. FROM:指定基础镜像
\# 格式:FROM <镜像名>:<标签>
FROM ubuntu:22.04
FROM python:3.11-slim
FROM alpine:latest
注意:
每个 Dockerfile 必须以
FROM
指令开始(除了 ARG 指令)推荐使用官方镜像作为基础镜像,确保安全性和稳定性
优先选择轻量级基础镜像(如 alpine 版本)减小镜像体积
2. WORKDIR:设置工作目录
\# 格式:WORKDIR <目录路径>
WORKDIR /app
WORKDIR /usr/local/src
特点:
如果目录不存在,Docker 会自动创建
后续指令(如 RUN、CMD 等)都会在该目录下执行
可以多次使用 WORKDIR 切换目录
3. COPY 与 ADD:复制文件
\# COPY:复制本地文件到镜像
COPY package.json /app/
COPY . /app
\# ADD:功能更丰富,支持URL和自动解压
ADD https://example.com/file.tar.gz /tmp/
ADD local.tar.gz /app/
最佳实践:
优先使用
COPY
,因为它更直观、功能明确需要自动解压或下载远程文件时才使用
ADD
4. RUN:执行命令
\# shell格式
RUN apt-get update && apt-get install -y \\
  package1 \\
  package2 \\
  && rm -rf /var/lib/apt/lists/\*
\# exec格式
RUN \["npm", "install"]
注意:
每条 RUN 指令都会创建一个新的镜像层
合并多个命令为一条 RUN 指令,减少镜像层数
清理缓存和临时文件,减小镜像体积
5. ENV:设置环境变量
\# 设置单个环境变量
ENV NODE\_ENV production
\# 设置多个环境变量
ENV APP\_PORT=3000 \\
  DB\_HOST=localhost
优势:
容器运行时可以直接使用这些环境变量
后续指令(如 RUN、CMD 等)也可以使用
可以通过
docker run -e
覆盖环境变量
6. EXPOSE:声明端口
EXPOSE 80
EXPOSE 443 8080
说明:
只是声明容器打算使用的端口,不会自动映射到主机
帮助使用者了解容器运行时需要映射哪些端口
实际端口映射需要在
docker run
时使用-p
参数
7. CMD 与 ENTRYPOINT:容器启动命令
\# CMD:设置容器启动命令,可被docker run后的命令覆盖
CMD \["node", "app.js"]
CMD npm start
\# ENTRYPOINT:设置容器入口点,docker run后的命令作为参数
ENTRYPOINT \["python", "-m", "http.server"]
\# 此时运行docker run -p 8000:8000 image 8000 等价于 python -m http.server 8000
区别与使用场景:
CMD
适合设置默认命令,允许用户在运行时覆盖ENTRYPOINT
适合设置固定的程序入口,用户只能传递参数可以组合使用:ENTRYPOINT 指定固定部分,CMD 指定默认参数
8. ARG:构建参数
\# 定义构建参数
ARG VERSION=1.0.0
\# 使用构建参数
RUN wget https://example.com/app-\${VERSION}.tar.gz
特点:
仅在构建过程中有效,不会保留到容器中
可以通过
docker build --build-arg
覆盖默认值可以在 FROM 指令前使用 ARG,用于指定基础镜像标签
9. VOLUME:定义数据卷
VOLUME \["/data"]
VOLUME /var/log /var/lib/mysql
作用:
声明容器中的持久化存储目录
运行容器时会自动创建这些目录并挂载卷
防止重要数据因容器删除而丢失
四、构建镜像命令
使用docker build
命令从 Dockerfile 构建镜像:
\# 基本用法
docker build -t myapp:1.0 .
\# 指定Dockerfile路径
docker build -t myapp:1.0 -f ./docker/Dockerfile .
\# 使用构建参数
docker build -t myapp:1.0 --build-arg VERSION=2.0.0 .
\# 不使用缓存构建
docker build -t myapp:1.0 --no-cache .
其中.
表示构建上下文(Docker daemon 可以访问的文件目录)。
五、Dockerfile 优化技巧
1. 减小镜像体积
使用多阶段构建分离构建环境和运行环境
清理包管理器缓存(如
apt-get clean
、yum clean all
)合并 RUN 指令,减少镜像层数
使用
.dockerignore
文件排除不需要的文件
2. 优化构建速度
合理排序指令,将频繁变动的指令放在后面
利用 Docker 的构建缓存机制
使用更小的基础镜像(如 alpine 版本)
3. 提高安全性
使用非 root 用户运行容器
避免在 Dockerfile 中包含敏感信息
定期更新基础镜像,修复安全漏洞
六、多阶段构建详解
多阶段构建是减小镜像体积的有效方法,尤其适合编译型语言:
\# 第一阶段:构建阶段
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp main.go
\# 第二阶段:运行阶段
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD \["./myapp"]
优势:
最终镜像只包含运行所需的文件,不包含构建工具和源代码
大幅减小镜像体积(通常可以减小 70% 以上)
提高安全性,减少攻击面
七、常见应用的 Dockerfile 示例
1. Node.js 应用
FROM node:18-alpine
WORKDIR /app
\# 先复制依赖文件,利用缓存
COPY package\*.json ./
RUN npm install --production
\# 复制应用代码
COPY . .
ENV NODE\_ENV=production
EXPOSE 3000
CMD \["node", "server.js"]
2. Python 应用
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD \["python", "app.py"]
3. Java 应用(多阶段构建)
\# 构建阶段
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests
\# 运行阶段
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=builder /app/target/\*.jar app.jar
EXPOSE 8080
CMD \["java", "-jar", "app.jar"]
八、Dockerfile 最佳实践总结
保持镜像精简:只包含运行所需的文件和依赖
使用
.dockerignore
文件:排除不必要的文件和目录合并 RUN 指令:减少镜像层数,每个 RUN 尽量完成一个完整功能
正确排序指令:将频繁变动的文件放在后面,充分利用缓存
不要以 root 用户运行:创建并使用非特权用户
避免在 Dockerfile 中存储敏感信息:使用环境变量或秘密管理工具
为镜像添加标签:使用有意义的标签,避免使用 latest 标签
使用多阶段构建:分离构建和运行环境
测试 Dockerfile:确保构建过程可重复、镜像可正常运行
添加注释:解释复杂指令的用途,提高可维护性
通过遵循这些最佳实践,你可以创建出更小、更安全、更易于维护的 Docker 镜像,从而提高容器化应用的效率和可靠性。