Docker 第五天学习指南:初识 Dockerfile
2026/6/17 22:56:39 网站建设 项目流程

一、学习目标

  1. 理解 Dockerfile 是什么以及它的作用

  2. 掌握 Dockerfile 核心指令:FROMWORKDIRCOPYRUNCMD

  3. 编写第一个 Dockerfile(基于 Python Flask 应用)

  4. 使用docker build构建镜像

二、什么是 Dockerfile?

Dockerfile是一个纯文本文件,里面包含了一系列构建镜像的指令。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—— 设置工作目录

作用:为后续的RUNCMDCOPY等指令设置当前工作目录。如果目录不存在,会自动创建。

# 语法 WORKDIR <路径> # 示例 WORKDIR /app # 绝对路径 WORKDIR /usr/local/src WORKDIR app # 相对路径(相对于上一个 WORKDIR) # 可以多次使用,支持相对路径 WORKDIR /home WORKDIR myapp # 最终路径 = /home/myapp

最佳实践:使用绝对路径,避免混淆。先WORKDIRCOPY,可以简化复制路径。

3.COPY—— 复制文件

作用:将构建上下文(通常是 Dockerfile 所在目录)中的文件/目录复制到镜像中。

# 语法 COPY <源路径>... <目标路径> # 示例 COPY . /app # 将当前目录所有文件复制到 /app COPY requirements.txt /app/requirements.txt COPY --chown=www-data:www-data index.html /var/www/ COPY --from=builder /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", "--host=0.0.0.0"]

RUN的区别

  • RUN构建时执行,结果保存到镜像中

  • CMD运行时执行,只是默认命令,可以被覆盖

补充指令(了解即可,后面会深入学习)

指令作用
ADD类似 COPY,但支持 URL 下载和自动解压 tar(不推荐优先使用)
ENV设置环境变量,构建和运行时都存在
ARG构建参数,只在构建时有效
EXPOSE声明容器监听端口(只是文档,不会真正暴露)
ENTRYPOINT与 CMD 类似,但不会被覆盖,常与 CMD 配合使用

四、实战:为一个 Flask 应用编写 Dockerfile

1. 准备 Flask 应用

首先创建一个新目录,并在其中创建以下文件:

目录结构

my-flask-app/ ├── app.py ├── requirements.txt └── Dockerfile

app.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(host='0.0.0.0', port=5000)

requirements.txt

flask==2.3.2

2. 编写 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-app

4. 运行镜像并测试

# 运行容器,映射宿主机 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.txt

2. 合并 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__.gitvenv),加快构建速度并减小镜像体积。

# .dockerignore 内容 __pycache__ *.pyc .git .venv .env README.md

4. 选择合适的基础镜像

  • 生产环境优先选择-alpine-slim版本,体积小

  • 开发环境可以使用完整镜像(如python:3.9

5. 固定版本号

避免使用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 VERSION=1.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 --from=builder /app/myapp . CMD ["./myapp"]

八、常见问题排查

Q1:构建时提示COPY failed: file not found

原因:源文件路径错误或文件不存在。
解决:检查源文件是否在构建上下文内,路径是否正确。

Q2:容器启动后立即退出

原因CMD指定的命令不是持续运行的(比如直接运行bash)。
解决:对于前台应用(如 Flask),确保CMD是阻塞命令;对于调试,可以先运行sleep infinity

Q3:镜像体积过大

解决

  • 使用 Alpine 或 slim 基础镜像

  • 合并 RUN 指令并清理临时文件

  • 使用多阶段构建

  • 检查是否有不必要的文件(使用.dockerignore

Q4:pip 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 应用

  1. 修改app.py,增加一个新路由/health,返回OK

  2. 重新构建镜像(无需修改 Dockerfile)

  3. 重启容器,测试/health端点

练习 3:添加环境变量

修改 Dockerfile,使用ENV指令设置FLASK_ENV=production,并在app.py中打印该环境变量。

十、小结

今天你学会了:

  • Dockerfile 的核心指令及其含义

  • 如何为一个 Flask 应用编写 Dockerfile

  • 使用docker build构建镜像

  • 运行自己的第一个定制镜像

今日检查清单

  • 理解 Dockerfile 的作用

  • 能独立写出一个包含FROMWORKDIRCOPYRUNCMD的 Dockerfile

  • 成功构建镜像并运行

  • 能通过浏览器或 curl 访问到 Flask 应用的输出

恭喜你完成了第五天的学习!明天我们将深入数据持久化(Volume),解决容器删除后数据丢失的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询