📌 引言:从零开始掌握Dockerfile
想象一下,你刚刚写好了一个Node.js应用,想要分享给同事。传统方式:写一份冗长的安装文档,让他们一步步配置环境。现代方式:一个Dockerfile,一条命令,在任何地方都能完美运行。
Dockerfile就是Docker世界的"食谱"——它精确描述了如何构建一个可重复、可移植的应用环境。
🏗️ 第一章:Dockerfile基础概念
1.1 什么是Dockerfile?
Dockerfile是一个文本文件,包含了一系列的指令,这些指令告诉Docker如何构建一个Docker镜像。
简单比喻:
- Dockerfile = 菜谱(如何制作一道菜)
- 镜像 = 预制菜(按照菜谱做好的成品)
- 容器 = 热好的预制菜(正在享用的状态)
1.2 Dockerfile工作流程
#mermaid-svg-kBU4ujC6bnbQlBgh{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kBU4ujC6bnbQlBgh .error-icon{fill:#552222;}#mermaid-svg-kBU4ujC6bnbQlBgh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kBU4ujC6bnbQlBgh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kBU4ujC6bnbQlBgh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kBU4ujC6bnbQlBgh .marker.cross{stroke:#333333;}#mermaid-svg-kBU4ujC6bnbQlBgh svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kBU4ujC6bnbQlBgh p{margin:0;}#mermaid-svg-kBU4ujC6bnbQlBgh .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh .cluster-label text{fill:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh .cluster-label span{color:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh .cluster-label span p{background-color:transparent;}#mermaid-svg-kBU4ujC6bnbQlBgh .label text,#mermaid-svg-kBU4ujC6bnbQlBgh span{fill:#333;color:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh .node rect,#mermaid-svg-kBU4ujC6bnbQlBgh .node circle,#mermaid-svg-kBU4ujC6bnbQlBgh .node ellipse,#mermaid-svg-kBU4ujC6bnbQlBgh .node polygon,#mermaid-svg-kBU4ujC6bnbQlBgh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kBU4ujC6bnbQlBgh .rough-node .label text,#mermaid-svg-kBU4ujC6bnbQlBgh .node .label text,#mermaid-svg-kBU4ujC6bnbQlBgh .image-shape .label,#mermaid-svg-kBU4ujC6bnbQlBgh .icon-shape .label{text-anchor:middle;}#mermaid-svg-kBU4ujC6bnbQlBgh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kBU4ujC6bnbQlBgh .rough-node .label,#mermaid-svg-kBU4ujC6bnbQlBgh .node .label,#mermaid-svg-kBU4ujC6bnbQlBgh .image-shape .label,#mermaid-svg-kBU4ujC6bnbQlBgh .icon-shape .label{text-align:center;}#mermaid-svg-kBU4ujC6bnbQlBgh .node.clickable{cursor:pointer;}#mermaid-svg-kBU4ujC6bnbQlBgh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kBU4ujC6bnbQlBgh .arrowheadPath{fill:#333333;}#mermaid-svg-kBU4ujC6bnbQlBgh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kBU4ujC6bnbQlBgh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kBU4ujC6bnbQlBgh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kBU4ujC6bnbQlBgh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kBU4ujC6bnbQlBgh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kBU4ujC6bnbQlBgh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kBU4ujC6bnbQlBgh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kBU4ujC6bnbQlBgh .cluster text{fill:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh .cluster span{color:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kBU4ujC6bnbQlBgh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kBU4ujC6bnbQlBgh rect.text{fill:none;stroke-width:0;}#mermaid-svg-kBU4ujC6bnbQlBgh .icon-shape,#mermaid-svg-kBU4ujC6bnbQlBgh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kBU4ujC6bnbQlBgh .icon-shape p,#mermaid-svg-kBU4ujC6bnbQlBgh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kBU4ujC6bnbQlBgh .icon-shape rect,#mermaid-svg-kBU4ujC6bnbQlBgh .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kBU4ujC6bnbQlBgh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kBU4ujC6bnbQlBgh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kBU4ujC6bnbQlBgh :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
镜像分层结构
指令1 → 层1
指令2 → 层2
指令3 → 层3
… → …
最终层 → 镜像
Dockerfile构建流程
编写Dockerfile
docker build
Docker Daemon读取指令
按顺序执行每条指令
每条指令创建新镜像层
最终生成镜像
推送到仓库
其他人拉取运行
📝 第二章:Dockerfile语法全解析
2.1 Dockerfile指令速查表
| FROM | 指定基础镜像 | FROM ubuntu:20.04 |
| RUN | 执行命令 | RUN apt-get update |
| COPY | 复制文件 | COPY app.py /app/ |
| ADD | 复制文件(支持URL) | ADD https://… /tmp/ |
| CMD | 容器启动命令 | CMD ["python", "app.py"] |
| ENTRYPOINT | 入口点 | ENTRYPOINT ["nginx"] |
| ENV | 设置环境变量 | ENV NODE_ENV=production |
| ARG | 构建参数 | ARG VERSION=latest |
| WORKDIR | 工作目录 | WORKDIR /app |
| EXPOSE | 声明端口 | EXPOSE 8080 |
| VOLUME | 数据卷挂载点 | VOLUME /data |
| USER | 指定用户 | USER node |
| HEALTHCHECK | 健康检查 | HEALTHCHECK CMD curl… |
| LABEL | 元数据标签 | LABEL version="1.0" |
2.2 指令详解与最佳实践
1. FROM – 基础镜像选择
# 选择官方、轻量级的基础镜像
FROM node:14-alpine # ✅ 推荐:Alpine版本,体积小
# 指定版本标签,避免使用latest
FROM python:3.9-slim # ✅ 推荐:明确版本
# 多阶段构建中可以使用多个FROM
FROM golang:1.16 AS builder # ✅ 构建阶段
FROM alpine:latest # ✅ 运行阶段
常见基础镜像对比:
| ubuntu:20.04 | 72MB | 完整功能 | 需要完整系统工具 |
| debian:buster-slim | 69MB | 精简版 | 平衡功能和大小 |
| alpine:latest | 5MB | 极简 | 最小化镜像 |
| scratch | 0MB | 空镜像 | 静态编译应用 |
2. RUN – 执行命令
# 合并多个RUN命令,减少层数
RUN apt-get update && \\
apt-get install -y \\
python3 \\
python3-pip \\
nginx \\
&& apt-get clean \\
&& rm -rf /var/lib/apt/lists/* # ✅ 清理缓存
# 错误的做法 – 产生多个不必要的层
RUN apt-get update # ❌
RUN apt-get install -y python3 # ❌
RUN apt-get install -y python3-pip # ❌
3. COPY vs ADD
# COPY – 简单的文件复制(推荐)
COPY requirements.txt /app/
COPY . /app/
# ADD – 额外的功能(自动解压、支持URL)
ADD app.tar.gz /app/ # 自动解压tar包
ADD https://example.com/file /tmp/ # 从URL下载
# 最佳实践:优先使用COPY,除非需要ADD的特殊功能
4. CMD vs ENTRYPOINT
# CMD – 提供默认命令,可被覆盖
CMD ["nginx", "-g", "daemon off;"]
# ENTRYPOINT – 固定入口,CMD作为参数
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx"]
# 组合使用示例
ENTRYPOINT ["java", "-jar"]
CMD ["app.jar"] # 运行时可覆盖:docker run myapp app2.jar
2.3 Dockerfile语法图解
#mermaid-svg-TUAZgebRuk5hQMIq{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TUAZgebRuk5hQMIq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TUAZgebRuk5hQMIq .error-icon{fill:#552222;}#mermaid-svg-TUAZgebRuk5hQMIq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TUAZgebRuk5hQMIq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TUAZgebRuk5hQMIq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TUAZgebRuk5hQMIq .marker.cross{stroke:#333333;}#mermaid-svg-TUAZgebRuk5hQMIq svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TUAZgebRuk5hQMIq p{margin:0;}#mermaid-svg-TUAZgebRuk5hQMIq .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TUAZgebRuk5hQMIq .cluster-label text{fill:#333;}#mermaid-svg-TUAZgebRuk5hQMIq .cluster-label span{color:#333;}#mermaid-svg-TUAZgebRuk5hQMIq .cluster-label span p{background-color:transparent;}#mermaid-svg-TUAZgebRuk5hQMIq .label text,#mermaid-svg-TUAZgebRuk5hQMIq span{fill:#333;color:#333;}#mermaid-svg-TUAZgebRuk5hQMIq .node rect,#mermaid-svg-TUAZgebRuk5hQMIq .node circle,#mermaid-svg-TUAZgebRuk5hQMIq .node ellipse,#mermaid-svg-TUAZgebRuk5hQMIq .node polygon,#mermaid-svg-TUAZgebRuk5hQMIq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TUAZgebRuk5hQMIq .rough-node .label text,#mermaid-svg-TUAZgebRuk5hQMIq .node .label text,#mermaid-svg-TUAZgebRuk5hQMIq .image-shape .label,#mermaid-svg-TUAZgebRuk5hQMIq .icon-shape .label{text-anchor:middle;}#mermaid-svg-TUAZgebRuk5hQMIq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TUAZgebRuk5hQMIq .rough-node .label,#mermaid-svg-TUAZgebRuk5hQMIq .node .label,#mermaid-svg-TUAZgebRuk5hQMIq .image-shape .label,#mermaid-svg-TUAZgebRuk5hQMIq .icon-shape .label{text-align:center;}#mermaid-svg-TUAZgebRuk5hQMIq .node.clickable{cursor:pointer;}#mermaid-svg-TUAZgebRuk5hQMIq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TUAZgebRuk5hQMIq .arrowheadPath{fill:#333333;}#mermaid-svg-TUAZgebRuk5hQMIq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TUAZgebRuk5hQMIq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TUAZgebRuk5hQMIq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TUAZgebRuk5hQMIq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TUAZgebRuk5hQMIq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TUAZgebRuk5hQMIq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TUAZgebRuk5hQMIq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TUAZgebRuk5hQMIq .cluster text{fill:#333;}#mermaid-svg-TUAZgebRuk5hQMIq .cluster span{color:#333;}#mermaid-svg-TUAZgebRuk5hQMIq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TUAZgebRuk5hQMIq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TUAZgebRuk5hQMIq rect.text{fill:none;stroke-width:0;}#mermaid-svg-TUAZgebRuk5hQMIq .icon-shape,#mermaid-svg-TUAZgebRuk5hQMIq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TUAZgebRuk5hQMIq .icon-shape p,#mermaid-svg-TUAZgebRuk5hQMIq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TUAZgebRuk5hQMIq .icon-shape rect,#mermaid-svg-TUAZgebRuk5hQMIq .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TUAZgebRuk5hQMIq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TUAZgebRuk5hQMIq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TUAZgebRuk5hQMIq :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Dockerfile
构建镜像
指令执行
FROM: 基础层
RUN: 执行命令
COPY: 添加文件
ENV: 环境变量
WORKDIR: 工作目录
EXPOSE: 暴露端口
CMD/ENTRYPOINT: 启动命令
镜像层 L1
镜像层 L2
镜像层 L3
镜像层 L4
镜像层 L5
镜像层 L6
镜像层 L7
…
最终镜像
🚀 第三章:第一个完整的Dockerfile实战
3.1 项目准备:简单的Python Flask应用
项目结构:
my-flask-app/
├── app.py # Flask应用
├── requirements.txt # Python依赖
├── templates/ # HTML模板
│ └── index.html
├── static/ # 静态文件
│ └── style.css
└── Dockerfile # 我们将编写的Dockerfile
app.py:
from flask import Flask, render_template
import os
app = Flask(__name__)
@app.route('/')
def hello():
return render_template('index.html',
message='Hello from Docker!',
hostname=os.getenv('HOSTNAME', 'unknown'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
requirements.txt:
Flask==2.0.1
templates/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Docker Flask App</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container">
<h1>{{ message }}</h1>
<p>Container ID: {{ hostname }}</p>
<p>This app is running inside a Docker container!</p>
</div>
</body>
</html>
3.2 逐步编写Dockerfile
版本1:基础版Dockerfile
# Dockerfile
# 第1步:选择基础镜像
FROM python:3.9-slim
# 第2步:设置工作目录
WORKDIR /app
# 第3步:复制依赖文件
COPY requirements.txt .
# 第4步:安装依赖
RUN pip install –no-cache-dir -r requirements.txt
# 第5步:复制应用代码
COPY . .
# 第6步:暴露端口
EXPOSE 5000
# 第7步:启动命令
CMD ["python", "app.py"]
版本2:优化版Dockerfile
# Dockerfile.optimized
# 1. 使用更小的基础镜像
FROM python:3.9-alpine
# 2. 设置环境变量(构建时和运行时)
ENV PYTHONUNBUFFERED=1 \\
PYTHONDONTWRITEBYTECODE=1 \\
FLASK_ENV=production
# 3. 安装系统依赖
RUN apk add –no-cache –virtual .build-deps gcc musl-dev \\
&& pip install –no-cache-dir -r requirements.txt \\
&& apk del .build-deps gcc musl-dev \\
&& rm -rf /var/cache/apk/*
# 4. 创建非root用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 5. 工作目录
WORKDIR /app
# 6. 复制依赖文件(利用Docker缓存)
COPY requirements.txt .
# 7. 安装Python依赖
RUN pip install –no-cache-dir -r requirements.txt
# 8. 复制应用代码
COPY . .
# 9. 更改文件权限
RUN chown -R appuser:appgroup /app
# 10. 切换到非root用户
USER appuser
# 11. 健康检查
HEALTHCHECK –interval=30s –timeout=3s –start-period=5s –retries=3 \\
CMD curl -f http://localhost:5000/ || exit 1
# 12. 暴露端口
EXPOSE 5000
# 13. 启动命令
CMD ["python", "app.py"]
版本3:多阶段构建版
# Dockerfile.multistage
# 第一阶段:构建阶段
FROM python:3.9 AS builder
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装依赖到虚拟环境
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install –no-cache-dir -r requirements.txt
# 第二阶段:运行阶段
FROM python:3.9-alpine
# 创建非root用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# 从构建阶段复制虚拟环境
COPY –from=builder /opt/venv /opt/venv
# 复制应用代码
COPY . .
# 设置环境变量
ENV PATH="/opt/venv/bin:$PATH" \\
PYTHONUNBUFFERED=1
# 更改权限
RUN chown -R appuser:appgroup /app
# 切换到非root用户
USER appuser
# 健康检查
HEALTHCHECK –interval=30s –timeout=3s CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000')"
# 暴露端口
EXPOSE 5000
# 启动命令
CMD ["python", "app.py"]
3.3 Dockerfile构建过程详解
构建缓存
Docker Daemon
Docker CLI
用户
构建缓存
Docker Daemon
Docker CLI
用户
#mermaid-svg-rG0UQnuPRhcnvquj{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rG0UQnuPRhcnvquj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rG0UQnuPRhcnvquj .error-icon{fill:#552222;}#mermaid-svg-rG0UQnuPRhcnvquj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rG0UQnuPRhcnvquj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rG0UQnuPRhcnvquj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rG0UQnuPRhcnvquj .marker.cross{stroke:#333333;}#mermaid-svg-rG0UQnuPRhcnvquj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rG0UQnuPRhcnvquj p{margin:0;}#mermaid-svg-rG0UQnuPRhcnvquj .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rG0UQnuPRhcnvquj text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-rG0UQnuPRhcnvquj .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rG0UQnuPRhcnvquj .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-rG0UQnuPRhcnvquj .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-rG0UQnuPRhcnvquj .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-rG0UQnuPRhcnvquj #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-rG0UQnuPRhcnvquj .sequenceNumber{fill:white;}#mermaid-svg-rG0UQnuPRhcnvquj #sequencenumber{fill:#333;}#mermaid-svg-rG0UQnuPRhcnvquj #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-rG0UQnuPRhcnvquj .messageText{fill:#333;stroke:none;}#mermaid-svg-rG0UQnuPRhcnvquj .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rG0UQnuPRhcnvquj .labelText,#mermaid-svg-rG0UQnuPRhcnvquj .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-rG0UQnuPRhcnvquj .loopText,#mermaid-svg-rG0UQnuPRhcnvquj .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-rG0UQnuPRhcnvquj .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rG0UQnuPRhcnvquj .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-rG0UQnuPRhcnvquj .noteText,#mermaid-svg-rG0UQnuPRhcnvquj .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-rG0UQnuPRhcnvquj .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rG0UQnuPRhcnvquj .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rG0UQnuPRhcnvquj .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rG0UQnuPRhcnvquj .actorPopupMenu{position:absolute;}#mermaid-svg-rG0UQnuPRhcnvquj .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-rG0UQnuPRhcnvquj .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rG0UQnuPRhcnvquj .actor-man circle,#mermaid-svg-rG0UQnuPRhcnvquj line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-rG0UQnuPRhcnvquj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
第1步: 解析Dockerfile
第2步: FROM指令
alt
[缓存命中]
[缓存未命中]
第3步: RUN指令
alt
[命令缓存命中]
第4步: COPY指令
alt
[文件未更改]
[文件已更改]
第5步: 后续指令
第6步: 生成镜像
docker build -t myapp:latest .
发送构建上下文
读取Dockerfile指令
检查基础镜像缓存
使用缓存层
拉取基础镜像
检查命令缓存
使用缓存结果
执行命令创建新层
存储缓存结果
检查文件哈希
使用缓存层
复制文件创建新层
存储新哈希
依次执行所有指令
组合所有层
生成最终镜像
返回镜像ID
构建成功
🔧 第四章:Dockerfile构建实战
4.1 构建镜像的多种方式
# 1. 基础构建命令
docker build -t my-flask-app:latest .
# 2. 指定Dockerfile路径
docker build -f Dockerfile.prod -t myapp:prod .
# 3. 构建时传递参数
docker build \\
–build-arg VERSION=1.0 \\
–build-arg NODE_ENV=production \\
-t myapp:v1.0 .
# 4. 不使用缓存构建
docker build –no-cache -t myapp:clean .
# 5. 仅使用特定阶段的缓存
docker build –target builder -t myapp:builder .
# 6. 输出详细构建信息
docker build –progress=plain -t myapp:debug .
4.2 构建过程监控与调试
查看构建详情
# 查看构建过程
docker build -t myapp:debug .
# 实时输出构建日志
docker build –progress=plain -t myapp:debug .
# 查看中间层
docker history myapp:debug
# 查看镜像分层
docker image inspect myapp:debug –format='{{.RootFS.Layers}}'
使用BuildKit(新一代构建引擎)
# 启用BuildKit(Docker 18.09+)
export DOCKER_BUILDKIT=1
# 或者修改Docker配置
# /etc/docker/daemon.json
{
"features": {
"buildkit": true
}
}
# BuildKit特性:更快的构建、更好的缓存、更安全的构建
DOCKER_BUILDKIT=1 docker build -t myapp:buildkit .
4.3 完整的构建示例
#!/bin/bash
# build.sh – 完整的构建脚本
set -e # 遇到错误退出
echo "=== 开始构建Docker镜像 ==="
# 1. 清理旧的构建
echo "步骤1: 清理旧镜像…"
docker rmi my-flask-app:latest 2>/dev/null || true
# 2. 构建新镜像
echo "步骤2: 构建新镜像…"
docker build \\
–tag my-flask-app:latest \\
–tag my-flask-app:$(date +%Y%m%d-%H%M%S) \\
–build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \\
–build-arg VERSION=1.0.0 \\
.
# 3. 验证构建结果
echo "步骤3: 验证镜像…"
docker images | grep my-flask-app
# 4. 运行测试容器
echo "步骤4: 运行测试容器…"
docker run -d –name test-app -p 5000:5000 my-flask-app:latest
sleep 5 # 等待应用启动
# 5. 测试应用
echo "步骤5: 测试应用…"
curl -f http://localhost:5000/ || (echo "测试失败"; exit 1)
echo "测试成功!"
# 6. 清理测试容器
echo "步骤6: 清理…"
docker stop test-app && docker rm test-app
echo "=== 构建完成 ==="
echo "镜像: my-flask-app:latest"
echo "运行: docker run -p 5000:5000 my-flask-app:latest"
🎯 第五章:Dockerfile高级技巧
5.1 使用.dockerignore文件
为什么要用.dockerignore?
- 减少构建上下文大小
- 避免敏感文件泄漏
- 提高构建速度
# .dockerignore 文件示例
# 排除版本控制目录
.git/
.gitignore
.svn/
# 排除IDE配置文件
.vscode/
.idea/
*.swp
*.swo
# 排除日志和缓存
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# 排除依赖目录(在Dockerfile中会重新安装)
node_modules/
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# 排除环境配置文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# 排除构建产物
dist/
build/
out/
*.tgz
*.zip
# 排除Docker相关文件(除了Dockerfile)
Dockerfile.*
docker-compose*.yml
.dockerignore
# 排除特定文件
README.md
LICENSE
5.2 多阶段构建(Multi-stage Build)
# 多阶段构建示例:Go应用
# 阶段1:构建阶段
FROM golang:1.16 AS builder
WORKDIR /app
# 复制go模块文件
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码
COPY . .
# 构建静态二进制文件
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 阶段2:运行阶段
FROM alpine:latest
RUN apk –no-cache add ca-certificates
WORKDIR /root/
# 从构建阶段复制二进制文件
COPY –from=builder /app/main .
# 创建非root用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# 暴露端口
EXPOSE 8080
# 运行应用
CMD ["./main"]
多阶段构建的优势:
5.3 使用ARG和ENV
# ARG – 构建时的参数(构建后不存在)
ARG VERSION=latest
ARG BUILD_DATE
ARG COMMIT_SHA
# ENV – 运行时环境变量(会保留在镜像中)
ENV APP_VERSION=${VERSION}
ENV APP_BUILD_DATE=${BUILD_DATE}
ENV NODE_ENV=production
ENV PORT=3000
# 使用构建参数
LABEL org.label-schema.version=${VERSION}
LABEL org.label-schema.build-date=${BUILD_DATE}
LABEL org.label-schema.vcs-ref=${COMMIT_SHA}
构建时传递参数:
docker build \\
–build-arg VERSION=1.0.0 \\
–build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \\
-t myapp:1.0.0 .
5.4 健康检查配置
# 健康检查示例
HEALTHCHECK –interval=30s –timeout=3s –start-period=5s –retries=3 \\
CMD curl -f http://localhost:${PORT}/health || exit 1
# 或使用自定义脚本
HEALTHCHECK –interval=30s –timeout=10s –start-period=40s –retries=3 \\
CMD ["/usr/bin/health-check.sh"]
# 或禁用健康检查
HEALTHCHECK NONE
🐛 第六章:Dockerfile调试技巧
6.1 构建失败调试
# 1. 查看详细的构建输出
docker build –progress=plain -t myapp:debug .
# 2. 在失败前停止,进入调试
# 修改Dockerfile,在失败命令前添加调试
FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install -y curl # 假设这里失败
# 改为:
RUN apt-get update && \\
apt-get install -y curl || \\
(apt-get update && apt-get install -y curl)
# 3. 使用–target在特定阶段停止
docker build –target builder -t myapp:builder .
# 4. 运行中间层进行调试
docker run -it –rm <intermediate-image-id> /bin/bash
6.2 使用Dockerfile最佳实践检查工具
# 1. 使用hadolint(Dockerfile linter)
# 安装
docker pull hadolint/hadolint
# 检查Dockerfile
docker run –rm -i hadolint/hadolint < Dockerfile
# 或直接使用
hadolint Dockerfile
# 2. 使用dockle(容器镜像安全检查)
# 安装
docker pull goodwithtech/dockle
# 检查镜像
docker run –rm -v /var/run/docker.sock:/var/run/docker.sock \\
goodwithtech/dockle myapp:latest
# 3. 使用divine(Dockerfile验证)
docker pull lukechannings/divine
docker run –rm -v $(pwd):/app lukechannings/divine
6.3 交互式调试容器
# 1. 构建时保留中间层
docker build –target builder -t myapp:builder .
# 2. 运行中间层容器进行调试
docker run -it –rm myapp:builder /bin/bash
# 3. 在容器中手动执行失败的命令
cd /app
# 手动执行构建步骤,查看具体错误
# 4. 修改Dockerfile后重新构建
📊 第七章:实际项目示例
7.1 Node.js应用Dockerfile
# Node.js应用完整示例
FROM node:16-alpine AS development
# 设置环境变量
ENV NODE_ENV=development
# 安装开发工具
RUN apk add –no-cache python3 make g++
# 设置工作目录
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖(开发环境包含devDependencies)
RUN npm ci
# 复制源代码
COPY . .
# 暴露端口
EXPOSE 3000
# 开发模式命令
CMD ["npm", "run", "dev"]
# ============================================
FROM node:16-alpine AS builder
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装生产依赖
RUN npm ci –only=production
# 复制源代码
COPY . .
# 构建应用
RUN npm run build
# ============================================
FROM node:16-alpine AS production
# 创建非root用户
RUN addgroup -g 1001 -S nodejs && \\
adduser -S nodejs -u 1001
WORKDIR /app
# 从builder阶段复制node_modules和构建产物
COPY –from=builder /app/node_modules ./node_modules
COPY –from=builder /app/dist ./dist
# 复制package.json
COPY package*.json ./
# 更改文件权限
RUN chown -R nodejs:nodejs /app
# 切换到非root用户
USER nodejs
# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000
# 健康检查
HEALTHCHECK –interval=30s –timeout=3s –start-period=5s –retries=3 \\
CMD node -e "require('http').get('http://localhost:${PORT}/health', (r) => {if (r.statusCode !== 200) throw new Error()})"
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "dist/server.js"]
7.2 Java Spring Boot应用Dockerfile
# 多阶段构建:Spring Boot应用
# 阶段1:Maven构建
FROM maven:3.8.4-openjdk-11 AS builder
WORKDIR /app
# 复制pom文件(利用缓存)
COPY pom.xml .
# 下载依赖(如果pom未改变则使用缓存)
RUN mvn dependency:go-offline
# 复制源代码
COPY src ./src
# 构建应用
RUN mvn clean package -DskipTests
# 阶段2:运行环境
FROM openjdk:11-jre-slim
# 创建非root用户
RUN groupadd -r spring && useradd -r -g spring spring
USER spring
WORKDIR /app
# 从构建阶段复制jar文件
COPY –from=builder /app/target/*.jar app.jar
# JVM参数
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
# 健康检查
HEALTHCHECK –interval=30s –timeout=3s –start-period=60s –retries=3 \\
CMD java -jar app.jar health-check || exit 1
# 暴露端口
EXPOSE 8080
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
7.3 前端React应用Dockerfile
# React应用 – 多阶段构建
# 阶段1:构建阶段
FROM node:16-alpine AS builder
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm ci
# 复制源代码
COPY . .
# 构建应用
RUN npm run build
# 阶段2:Nginx服务阶段
FROM nginx:alpine
# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
# 从构建阶段复制构建产物
COPY –from=builder /app/build /usr/share/nginx/html
# 暴露端口
EXPOSE 80
# 启动nginx
CMD ["nginx", "-g", "daemon off;"]
nginx.conf:
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \\.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
📈 第八章:性能优化与最佳实践
8.1 Dockerfile优化技巧
优化前 vs 优化后对比
# ❌ 未优化的Dockerfile
FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y python3-pip
RUN pip3 install flask
RUN apt-get install -y curl
RUN apt-get install -y wget
COPY . /app
WORKDIR /app
CMD ["python3", "app.py"]
# 镜像大小:~350MB,构建时间:~60s
# ✅ 优化后的Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install –no-cache-dir -r requirements.txt && \\
apt-get update && \\
apt-get install -y –no-install-recommends curl wget && \\
apt-get clean && \\
rm -rf /var/lib/apt/lists/*
COPY . .
CMD ["python", "app.py"]
# 镜像大小:~120MB,构建时间:~20s
优化策略总结
8.2 安全最佳实践
# 安全加固的Dockerfile
FROM node:16-alpine
# 1. 定期更新基础镜像
# 使用特定版本,而不是latest
# 定期检查安全更新
# 2. 使用非root用户
RUN addgroup -g 1001 -S nodejs && \\
adduser -S nodejs -u 1001
# 3. 最小化安装
RUN apk add –no-cache –virtual .build-deps gcc musl-dev && \\
npm install && \\
apk del .build-deps
# 4. 设置只读文件系统(可选)
RUN chmod -R a-w /app && \\
chown -R nodejs:nodejs /app
USER nodejs
# 5. 设置健康检查
HEALTHCHECK –interval=30s –timeout=3s –start-period=5s –retries=3 \\
CMD node healthcheck.js
# 6. 使用安全扫描工具
# 构建后运行:docker scan myapp:latest
WORKDIR /app
COPY –chown=nodejs:nodejs . .
EXPOSE 3000
CMD ["node", "server.js"]
8.3 性能监控
# 查看镜像大小
docker images –format "table {{.Repository}}\\t{{.Tag}}\\t{{.Size}}"
# 查看镜像分层
docker history myapp:latest
# 分析镜像内容
docker run –rm -v /var/run/docker.sock:/var/run/docker.sock \\
wagoodman/dive myapp:latest
# 查看构建时间
time docker build -t myapp:test .
🚀 第九章:实战练习与挑战
9.1 练习1:将现有应用Docker化
任务:选择一个你熟悉的项目,为其编写Dockerfile
步骤:
9.2 练习2:优化现有Dockerfile
现有Dockerfile:
FROM ubuntu:latest
RUN apt update
RUN apt install -y python3
RUN apt install -y python3-pip
RUN pip3 install flask requests pandas numpy
RUN apt install -y curl
RUN apt install -y vim
RUN apt install -y git
COPY . .
CMD ["python3", "app.py"]
优化目标:
9.3 挑战:多服务Dockerfile
项目要求:创建一个包含以下服务的应用:
- 前端:React应用
- 后端:Node.js API
- 数据库:PostgreSQL
- 缓存:Redis
任务:
📚 总结与下一步
关键知识点回顾
Dockerfile编写检查清单
- 使用了合适的基础镜像(Alpine/Slim)
- 合并了多个RUN命令
- 清理了包管理器缓存
- 使用了非root用户
- 设置了健康检查
- 添加了.dockerignore文件
- 使用了特定版本标签
- 优化了构建顺序(不常变的在前)
- 考虑了多阶段构建
- 添加了必要的元数据标签
下一步学习路径
#mermaid-svg-8VVcMBvK8VxPLVty{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8VVcMBvK8VxPLVty .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8VVcMBvK8VxPLVty .error-icon{fill:#552222;}#mermaid-svg-8VVcMBvK8VxPLVty .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8VVcMBvK8VxPLVty .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8VVcMBvK8VxPLVty .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8VVcMBvK8VxPLVty .marker.cross{stroke:#333333;}#mermaid-svg-8VVcMBvK8VxPLVty svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8VVcMBvK8VxPLVty p{margin:0;}#mermaid-svg-8VVcMBvK8VxPLVty .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8VVcMBvK8VxPLVty .cluster-label text{fill:#333;}#mermaid-svg-8VVcMBvK8VxPLVty .cluster-label span{color:#333;}#mermaid-svg-8VVcMBvK8VxPLVty .cluster-label span p{background-color:transparent;}#mermaid-svg-8VVcMBvK8VxPLVty .label text,#mermaid-svg-8VVcMBvK8VxPLVty span{fill:#333;color:#333;}#mermaid-svg-8VVcMBvK8VxPLVty .node rect,#mermaid-svg-8VVcMBvK8VxPLVty .node circle,#mermaid-svg-8VVcMBvK8VxPLVty .node ellipse,#mermaid-svg-8VVcMBvK8VxPLVty .node polygon,#mermaid-svg-8VVcMBvK8VxPLVty .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8VVcMBvK8VxPLVty .rough-node .label text,#mermaid-svg-8VVcMBvK8VxPLVty .node .label text,#mermaid-svg-8VVcMBvK8VxPLVty .image-shape .label,#mermaid-svg-8VVcMBvK8VxPLVty .icon-shape .label{text-anchor:middle;}#mermaid-svg-8VVcMBvK8VxPLVty .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8VVcMBvK8VxPLVty .rough-node .label,#mermaid-svg-8VVcMBvK8VxPLVty .node .label,#mermaid-svg-8VVcMBvK8VxPLVty .image-shape .label,#mermaid-svg-8VVcMBvK8VxPLVty .icon-shape .label{text-align:center;}#mermaid-svg-8VVcMBvK8VxPLVty .node.clickable{cursor:pointer;}#mermaid-svg-8VVcMBvK8VxPLVty .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8VVcMBvK8VxPLVty .arrowheadPath{fill:#333333;}#mermaid-svg-8VVcMBvK8VxPLVty .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8VVcMBvK8VxPLVty .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8VVcMBvK8VxPLVty .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8VVcMBvK8VxPLVty .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8VVcMBvK8VxPLVty .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8VVcMBvK8VxPLVty .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8VVcMBvK8VxPLVty .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8VVcMBvK8VxPLVty .cluster text{fill:#333;}#mermaid-svg-8VVcMBvK8VxPLVty .cluster span{color:#333;}#mermaid-svg-8VVcMBvK8VxPLVty div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8VVcMBvK8VxPLVty .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8VVcMBvK8VxPLVty rect.text{fill:none;stroke-width:0;}#mermaid-svg-8VVcMBvK8VxPLVty .icon-shape,#mermaid-svg-8VVcMBvK8VxPLVty .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8VVcMBvK8VxPLVty .icon-shape p,#mermaid-svg-8VVcMBvK8VxPLVty .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8VVcMBvK8VxPLVty .icon-shape rect,#mermaid-svg-8VVcMBvK8VxPLVty .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8VVcMBvK8VxPLVty .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8VVcMBvK8VxPLVty .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8VVcMBvK8VxPLVty :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
本篇: Dockerfile编写
Docker Compose多容器编排
Docker网络深度解析
Docker存储与数据卷
容器安全加固
CI/CD流水线集成
💡 动手实践: 现在尝试构建并运行我们的Flask应用:
# 1. 创建项目目录
mkdir my-first-dockerfile && cd my-first-dockerfile
# 2. 创建上述文件(app.py, requirements.txt, Dockerfile等)
# 3. 构建镜像
docker build -t my-flask-app .
# 4. 运行容器
docker run -d -p 5000:5000 –name flask-app my-flask-app
# 5. 访问应用
curl http://localhost:5000
# 或打开浏览器访问 http://localhost:5000
# 6. 查看容器日志
docker logs flask-app
# 7. 进入容器(可选)
docker exec -it flask-app /bin/sh
📢 下篇预告: 下一篇我们将学习《Docker镜像管理全攻略》,内容包括:
- 镜像的拉取、推送、删除操作
- 镜像标签管理和版本控制
- 私有镜像仓库搭建
- 镜像安全扫描和漏洞修复
🤔 思考题:
欢迎在评论区分享你编写的第一个Dockerfile,或提出遇到的问题!




