一、学习目标理解 Dockerfile 是什么以及它的作用掌握 Dockerfile 核心指令FROM、WORKDIR、COPY、RUN、CMD编写第一个 Dockerfile基于 Python Flask 应用使用docker build构建镜像二、什么是 DockerfileDockerfile是一个纯文本文件里面包含了一系列构建镜像的指令。Docker 可以读取这些指令并自动构建出一个镜像。Dockerfile 的作用自动化构建将应用及其运行环境固化为代码Infrastructure as Code可重复性任何人只要拿到 Dockerfile就能构建出完全相同的镜像版本控制Dockerfile 可以像源代码一样提交到 Git 仓库一个简单的例子# 使用 Python 官方镜像作为基础 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 复制当前目录下的所有文件到容器内的 /app COPY . /app # 安装 Python 依赖 RUN pip install -r requirements.txt # 声明容器启动时运行的命令 CMD [python, app.py]三、核心指令详解1.FROM—— 指定基础镜像作用每个 Dockerfile 的第一条指令必须是FROM它指定了构建所使用的“地基”镜像。# 语法 FROM 镜像名[:标签] # 示例 FROM ubuntu:22.04 # 基于 Ubuntu 22.04 FROM python:3.9-slim # 基于精简版 Python 3.9 FROM alpine:latest # 基于 Alpine Linux极小体积 FROM scratch # 从零开始用于静态二进制文件最佳实践尽量选择官方镜像并指定具体标签如python:3.9-slim不要使用latest不可复现。2.WORKDIR—— 设置工作目录作用为后续的RUN、CMD、COPY等指令设置当前工作目录。如果目录不存在会自动创建。# 语法 WORKDIR 路径 # 示例 WORKDIR /app # 绝对路径 WORKDIR /usr/local/src WORKDIR app # 相对路径相对于上一个 WORKDIR # 可以多次使用支持相对路径 WORKDIR /home WORKDIR myapp # 最终路径 /home/myapp最佳实践使用绝对路径避免混淆。先WORKDIR再COPY可以简化复制路径。3.COPY—— 复制文件作用将构建上下文通常是 Dockerfile 所在目录中的文件/目录复制到镜像中。# 语法 COPY 源路径... 目标路径 # 示例 COPY . /app # 将当前目录所有文件复制到 /app COPY requirements.txt /app/requirements.txt COPY --chownwww-data:www-data index.html /var/www/ COPY --frombuilder /output /app # 多阶段构建中从上一阶段复制 # 通配符 COPY hom* /mydir/ COPY abc?.txt /mydir/注意源路径必须在构建上下文中即不能COPY ../xxx目标路径可以是绝对路径也可以是相对WORKDIR的相对路径如果目标路径不存在Docker 会自动创建4.RUN—— 执行命令作用在构建过程中执行命令通常用于安装软件、配置环境。每个RUN都会在镜像中创建一个新层。# 两种语法形式 # shell 形式默认使用 /bin/sh -c RUN apt-get update apt-get install -y curl # exec 形式推荐不会启动 shell 解析 RUN [apt-get, update] # 示例 RUN pip install flask redis RUN npm install --production RUN mkdir -p /app/data chown 1000:1000 /app/data # 多行命令使用 \ 换行提高可读性 RUN apt-get update \ apt-get install -y --no-install-recommends \ build-essential \ curl \ git \ rm -rf /var/lib/apt/lists/*最佳实践将多个RUN合并为一个减少层数缩小镜像体积安装完成后清理临时文件如apt-get clean使用确保命令失败时构建失败5.CMD—— 容器启动默认命令作用指定容器启动时执行的默认命令。每个 Dockerfile 只能有一个CMD最后一个生效。CMD可以被docker run后面的命令覆盖。# 三种形式 # 1. exec 形式推荐 CMD [python, app.py] # 2. 作为 ENTRYPOINT 的默认参数 CMD [--port, 8080] # 3. shell 形式 CMD python app.py # 示例 CMD [nginx, -g, daemon off;] CMD [flask, run, --host0.0.0.0]与RUN的区别RUN在构建时执行结果保存到镜像中CMD在运行时执行只是默认命令可以被覆盖补充指令了解即可后面会深入学习指令作用ADD类似 COPY但支持 URL 下载和自动解压 tar不推荐优先使用ENV设置环境变量构建和运行时都存在ARG构建参数只在构建时有效EXPOSE声明容器监听端口只是文档不会真正暴露ENTRYPOINT与 CMD 类似但不会被覆盖常与 CMD 配合使用四、实战为一个 Flask 应用编写 Dockerfile1. 准备 Flask 应用首先创建一个新目录并在其中创建以下文件目录结构my-flask-app/ ├── app.py ├── requirements.txt └── Dockerfileapp.py简单的 Flask Web 应用from flask import Flask app Flask(__name__) app.route(/) def hello(): return Hello from Docker! I am running inside a container. if __name__ __main__: app.run(host0.0.0.0, port5000)requirements.txtflask2.3.22. 编写 Dockerfile在同一目录下创建Dockerfile无扩展名# 1. 指定基础镜像 FROM python:3.9-slim # 2. 设置工作目录 WORKDIR /app # 3. 复制依赖文件先复制 requirements.txt利用缓存 COPY requirements.txt . # 4. 安装 Python 依赖 RUN pip install --no-cache-dir -r requirements.txt # 5. 复制应用源代码 COPY app.py . # 6. 声明容器运行时监听的端口仅文档作用 EXPOSE 5000 # 7. 定义容器启动时运行的命令 CMD [python, app.py]逐行解释FROM python:3.9-slim使用官方精简版 Python 镜像体积小适合生产。WORKDIR /app所有后续命令都在/app目录下执行。COPY requirements.txt .只复制依赖文件这样在代码修改时只要requirements.txt没变Docker 就会使用缓存的依赖安装层加速构建。RUN pip install --no-cache-dir -r requirements.txt安装依赖--no-cache-dir可以减小镜像体积。COPY app.py .复制应用代码。EXPOSE 5000告诉使用者这个容器会监听 5000 端口。CMD [python, app.py]启动 Flask 应用。3. 构建镜像在my-flask-app目录下执行docker build -t my-first-flask-app .参数说明-t my-first-flask-app给镜像起一个名字tag.构建上下文路径当前目录Docker 会把这个目录下的所有文件发送给守护进程构建过程输出示例[] Building 15.2s (10/10) FINISHED [1/5] FROM python:3.9-slim [2/5] WORKDIR /app [3/5] COPY requirements.txt . [4/5] RUN pip install --no-cache-dir -r requirements.txt [5/5] COPY app.py . exporting to image naming to docker.io/library/my-first-flask-app4. 运行镜像并测试# 运行容器映射宿主机 5000 端口到容器 5000 端口 docker run -d --name my-flask-container -p 5000:5000 my-first-flask-app # 检查容器是否运行 docker ps # 访问应用使用 curl 或浏览器 curl http://localhost:5000 # 应该输出Hello from Docker! I am running inside a container.5. 查看日志和停止容器# 查看容器日志 docker logs my-flask-container # 停止并删除容器 docker stop my-flask-container docker rm my-flask-container五、Dockerfile 编写最佳实践1. 利用构建缓存将不经常变化的指令放在前面经常变化的代码放在后面。# ✅ 好依赖放前面代码放后面 COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # ❌ 不好复制所有代码在前导致每次代码修改都要重装依赖 COPY . . RUN pip install -r requirements.txt2. 合并 RUN 命令减少层数# ❌ 不好多个 RUN产生多个层 RUN apt-get update RUN apt-get install -y curl RUN apt-get clean # ✅ 好合并为一个 RUN RUN apt-get update \ apt-get install -y curl \ apt-get clean \ rm -rf /var/lib/apt/lists/*3. 使用.dockerignore文件创建.dockerignore文件排除不需要的文件如__pycache__、.git、venv加快构建速度并减小镜像体积。# .dockerignore 内容 __pycache__ *.pyc .git .venv .env README.md4. 选择合适的基础镜像生产环境优先选择-alpine或-slim版本体积小开发环境可以使用完整镜像如python:3.95. 固定版本号避免使用latest指定具体版本如python:3.9-slim确保环境一致性。6. 以非 root 用户运行安全性默认容器内是 root 用户最佳实践是创建普通用户。FROM python:3.9-slim RUN useradd -m -u 1000 appuser WORKDIR /app COPY . . RUN chown -R appuser:appuser /app USER appuser CMD [python, app.py]六、常用构建命令# 基础构建 docker build -t 镜像名:标签 . # 不使用缓存强制重新构建所有层 docker build --no-cache -t my-image . # 指定 Dockerfile 名称默认是 Dockerfile docker build -f Dockerfile.prod -t my-image . # 构建时传递构建参数ARG docker build --build-arg VERSION1.2.3 -t my-image . # 查看镜像构建历史 docker history my-image # 查看镜像详情 docker inspect my-image七、完整示例多阶段构建进阶对于一个需要编译的应用如 Go、Java可以使用多阶段构建来减小最终镜像体积。# 第一阶段构建使用完整编译环境 FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp main.go # 第二阶段运行使用极简基础镜像 FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --frombuilder /app/myapp . CMD [./myapp]八、常见问题排查Q1构建时提示COPY failed: file not found原因源文件路径错误或文件不存在。解决检查源文件是否在构建上下文内路径是否正确。Q2容器启动后立即退出原因CMD指定的命令不是持续运行的比如直接运行bash。解决对于前台应用如 Flask确保CMD是阻塞命令对于调试可以先运行sleep infinity。Q3镜像体积过大解决使用 Alpine 或 slim 基础镜像合并 RUN 指令并清理临时文件使用多阶段构建检查是否有不必要的文件使用.dockerignoreQ4pip install速度慢解决使用国内镜像源在 Dockerfile 中配置RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple九、课后练习练习 1构建一个静态网站镜像# 使用 nginx:alpine 作为基础镜像 # 创建 index.html 文件内容任意 # 复制到 /usr/share/nginx/html # 构建并运行通过浏览器访问解析FROM nginx:alpine COPY index.html /usr/share/nginx/html/index.html EXPOSE 80 CMD [nginx, -g, daemon off;]练习 2修改 Flask 应用修改app.py增加一个新路由/health返回OK重新构建镜像无需修改 Dockerfile重启容器测试/health端点练习 3添加环境变量修改 Dockerfile使用ENV指令设置FLASK_ENVproduction并在app.py中打印该环境变量。十、小结今天你学会了Dockerfile 的核心指令及其含义如何为一个 Flask 应用编写 Dockerfile使用docker build构建镜像运行自己的第一个定制镜像今日检查清单理解 Dockerfile 的作用能独立写出一个包含FROM、WORKDIR、COPY、RUN、CMD的 Dockerfile成功构建镜像并运行能通过浏览器或 curl 访问到 Flask 应用的输出恭喜你完成了第五天的学习明天我们将深入数据持久化Volume解决容器删除后数据丢失的问题。