源本科技 | 码上会

什么是 Dockerfile

2025/12/24
37
0

学习目标

通过本课件,你将掌握:

  1. Dockerfile 的本质与作用:理解其作为“镜像构建脚本”的核心价值;

  2. 关键指令详解FROMCOPYRUNCMDENTRYPOINT 等的正确用法与区别;

  3. 最佳实践原则:如何编写高效、安全、可维护的 Dockerfile;

  4. 完整实战案例:从零构建 Jenkins 镜像并运行容器;

  5. 常见问题排查:识别构建失败原因并优化构建性能;

  6. Dockerfile vs Docker Compose:明确两者定位差异,避免混淆。


Dockerfile 是什么?

Dockerfile 是一个纯文本文件,包含一系列指令(instructions),用于自动化构建 Docker 镜像。

你可以把它想象成一份烹饪食谱

  • 食材 = 基础操作系统、代码、依赖库;

  • 步骤 = 安装软件、复制文件、设置环境;

  • 成品 = 可在任何支持 Docker 的环境中运行的标准化镜像

核心概念回顾

概念

说明

类比

Docker Image(镜像)

只读模板,包含运行应用所需的一切(代码、运行时、库、配置)

做好的“蛋糕”

Docker Container(容器)

镜像的运行实例,可启动、停止、删除

从蛋糕上切下的一块,正在被食用

关键价值“一次构建,处处运行” —— 开发、测试、生产环境行为完全一致。


核心指令详解

每条指令都会创建一个新的镜像层(Layer),Docker 利用层缓存机制加速后续构建。

1. FROM 设定基础镜像(必须是第一条)

FROM ubuntu:22.04
FROM openjdk:11-jdk      # 推荐使用官方语言镜像

🔍 建议:优先选择官方镜像(如 python:3.11-slimnode:18-alpine),体积小、安全性高。


2. COPY 复制本地文件到镜像中(推荐使用)

COPY app.py /app/
COPY . /app/             # 复制当前目录所有内容

优点:语义清晰、仅支持本地路径,安全且可预测


3. ADD 功能更强但慎用

ADD https://example.com/app.tar.gz /app/   # 支持远程 URL
ADD archive.tar.gz /app/                   # 自动解压 .tar.gz

⚠️ 最佳实践除非需要自动解压或下载远程文件,否则一律用 COPYADD 行为隐式,易引发意外。


4. RUN 执行构建时命令(安装依赖等)

RUN apt-get update && apt-get install -y curl
RUN pip install -r requirements.txt

💡 技巧:将多个命令合并为一行(用 && 连接),减少镜像层数,减小体积。


5. CMD 容器启动时默认执行的命令(可被覆盖)

CMD ["python", "app.py"]
CMD ["nginx", "-g", "daemon off;"]

🔄 特点:可在 docker run 时被替换:

docker run my-app echo "Hello"  # 覆盖 CMD

6. ENTRYPOINT 容器主进程入口(不可被覆盖)

ENTRYPOINT ["java", "-jar", "app.jar"]

🔒 特点docker run 后的参数会追加到 ENTRYPOINT 后:

docker run my-java-app --debug   # 实际执行: java -jar app.jar --debug

CMD vs ENTRYPOINT 对比

特性

CMD

ENTRYPOINT

可被 docker run 覆盖

✅ 是

❌ 否

适合场景

提供默认参数

定义容器主程序

组合使用

ENTRYPOINT + CMD(CMD 作为默认参数)

📌 推荐模式

ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8080"]   # 默认参数,可被覆盖

7. 其他常用指令

指令

作用

示例

WORKDIR

设置工作目录(后续命令在此目录执行)

WORKDIR /app

ENV

设置环境变量

ENV NODE_ENV=production

EXPOSE

声明容器监听端口(文档作用,不实际发布)

EXPOSE 8080

LABEL

添加元数据标签(替代已弃用的 MAINTAINER

LABEL maintainer="team@example.com"

VOLUME

声明挂载点(用于持久化数据)

VOLUME ["/data"]

🚫 注意MAINTAINER废弃,请改用 LABEL maintainer=...


实战:构建 Jenkins 镜像

步骤 1:编写 Dockerfile

# 使用官方 OpenJDK 11 作为基础镜像
FROM openjdk:11-jdk

# 添加元数据(替代 MAINTAINER)
LABEL maintainer="Lusifer" \
      env="production"

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

# 创建应用目录
RUN mkdir -p $APP_HOME

# 下载 Jenkins WAR 文件(仅在此演示,生产建议 COPY 本地文件)
ADD https://ftp.yz.yamagata-u.ac.jp/pub/misc/jenkins/war/2.397/jenkins.war $APP_HOME/

# 设置工作目录
WORKDIR $APP_HOME

# 声明容器暴露端口
EXPOSE 8080

# 启动 Jenkins
CMD ["java", "-jar", "jenkins.war"]

步骤 2:构建镜像

# 在 Dockerfile 所在目录执行
docker build -t jenkins-custom:2.397 .

🔍 . 表示构建上下文为当前目录。

步骤 3:运行容器

docker run -d -p 8080:8080 --name jenkins jenkins-custom:2.397

步骤 4:访问应用

浏览器打开:http://localhost:8080 → 进入 Jenkins 初始化页面。


Dockerfile 常见问题与优化

故障排查

问题

解决方法

构建失败

查看 docker build 输出日志,定位具体指令错误

文件未找到

检查 COPY/ADD 源路径是否在构建上下文

网络超时

确保能访问远程 URL(如 ADD 下载地址)

权限错误

使用 RUN chmod 或以非 root 用户运行(安全最佳实践)

性能优化

  1. 利用层缓存:将不常变的指令(如 apt-get install)放在前面;

  2. 合并 RUN 指令:减少层数;

  3. 使用 .dockerignore:排除无关文件(如 node_modules/, .git/);

  4. 选择小体积基础镜像:如 alpine-slim 版本。


Dockerfile vs Docker Compose

特性

Dockerfile

Docker Compose

目的

构建单个镜像

编排多容器应用

文件名

Dockerfile(无扩展名)

docker-compose.yml

核心关注点

镜像内容(如何构建)

服务关系(如何运行)

典型指令

FROM, RUN, COPY

services, volumes, networks

使用命令

docker build

docker compose up

适用场景

打包 Python/Java 应用

启动 Web + DB + Redis 服务栈

协作关系

  • 先用 Dockerfile 构建 web-appdb 镜像,

  • 再用 Docker Compose 定义它们如何互联、暴露端口、挂载卷。


重点总结

  • Dockerfile 是声明式脚本,用于自动化构建可移植镜像;

  • 指令顺序影响缓存效率,合理排序可加速 CI/CD;

  • 优先使用 COPY 而非 ADDENTRYPOINT + CMD 组合更灵活;

  • 避免在 Dockerfile 中硬编码敏感信息(密码、密钥);

  • Dockerfile 负责“造车”,Docker Compose 负责“开车上路”。


思考题

  1. 为什么在 Dockerfile 中不建议使用 latest 标签作为基础镜像?

  2. 如何让容器以非 root 用户身份运行,提升安全性?

  3. 如果 COPY . /app 复制了大量无关文件,会导致什么问题?如何解决?


📘 延伸阅读