Dockerfile 完全指南:从入门到精通
一、什么是 Dockerfile?
Dockerfile 是一个文本文件,包含了一系列构建 Docker 镜像的指令。通过 Dockerfile,开发者可以将应用程序的构建过程标准化、自动化,确保在任何环境中都能生成一致的镜像。
使用 Dockerfile 构建镜像的核心优势:
可重复性:相同的 Dockerfile 在任何环境下都能生成相同的镜像
可维护性:以代码形式管理镜像构建过程,便于版本控制
可扩展性:便于团队协作和持续集成 / 持续部署 (CI/CD) 流程集成
二、Dockerfile 基本结构
一个完整的 Dockerfile 通常包含以下几个部分(按常规顺序排列):
基础镜像指令:指定构建的基础镜像
维护者信息:可选,说明镜像的维护者
环境配置:设置环境变量、工作目录等
文件操作:复制文件到镜像中
命令执行:运行命令安装依赖或配置应用
暴露端口:声明容器运行时监听的端口
启动命令:指定容器启动时执行的命令
三、常用 Dockerfile 指令详解
\# 格式:FROM <镜像名>:<标签>
FROM ubuntu:22.04
FROM python:3.11-slim
FROM alpine:latest
注意:
每个 Dockerfile 必须以FROM
指令开始(除了 ARG 指令)
推荐使用官方镜像作为基础镜像,确保安全性和稳定性
优先选择轻量级基础镜像(如 alpine 版本)减小镜像体积
\# 格式:WORKDIR <目录路径>
WORKDIR /app
WORKDIR /usr/local/src
特点:
如果目录不存在,Docker 会自动创建
后续指令(如 RUN、CMD 等)都会在该目录下执行
可以多次使用 WORKDIR 切换目录
\# COPY:复制本地文件到镜像
COPY package.json /app/
COPY . /app
\# ADD:功能更丰富,支持URL和自动解压
ADD https://example.com/file.tar.gz /tmp/
ADD local.tar.gz /app/
最佳实践:
优先使用COPY
,因为它更直观、功能明确
需要自动解压或下载远程文件时才使用ADD
\# shell格式
RUN apt-get update && apt-get install -y \\
package1 \\
package2 \\
&& rm -rf /var/lib/apt/lists/\*
\# exec格式
RUN \["npm", "install"]
注意:
每条 RUN 指令都会创建一个新的镜像层
合并多个命令为一条 RUN 指令,减少镜像层数
清理缓存和临时文件,减小镜像体积
\# 设置单个环境变量
ENV NODE\_ENV production
\# 设置多个环境变量
ENV APP\_PORT=3000 \\
DB\_HOST=localhost
优势:
容器运行时可以直接使用这些环境变量
后续指令(如 RUN、CMD 等)也可以使用
可以通过docker run -e
覆盖环境变量
EXPOSE 80
EXPOSE 443 8080
说明:
只是声明容器打算使用的端口,不会自动映射到主机
帮助使用者了解容器运行时需要映射哪些端口
实际端口映射需要在docker run
时使用-p
参数
\# 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 指定默认参数
\# 定义构建参数
ARG VERSION=1.0.0
\# 使用构建参数
RUN wget https://example.com/app-\${VERSION}.tar.gz
特点:
仅在构建过程中有效,不会保留到容器中
可以通过docker build --build-arg
覆盖默认值
可以在 FROM 指令前使用 ARG,用于指定基础镜像标签
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 优化技巧
使用多阶段构建分离构建环境和运行环境
清理包管理器缓存(如apt-get clean
、yum clean all
)
合并 RUN 指令,减少镜像层数
使用.dockerignore
文件排除不需要的文件
合理排序指令,将频繁变动的指令放在后面
利用 Docker 的构建缓存机制
使用更小的基础镜像(如 alpine 版本)
使用非 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 示例
FROM node:18-alpine
WORKDIR /app
\# 先复制依赖文件,利用缓存
COPY package\*.json ./
RUN npm install --production
\# 复制应用代码
COPY . .
ENV NODE\_ENV=production
EXPOSE 3000
CMD \["node", "server.js"]
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"]
\# 构建阶段
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 镜像,从而提高容器化应用的效率和可靠性。