欢迎光临
我们一直在努力

《手把手教你编写第一个Dockerfile》

📌 引言:从零开始掌握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

    优化策略总结
  • 使用合适的基础镜像:选择Alpine或Slim版本
  • 合并RUN命令:减少镜像层数
  • 清理缓存:apt-get clean, rm -rf /var/lib/apt/lists/*
  • 使用.no-cache-dir:pip install –no-cache-dir
  • 使用多阶段构建:分离构建环境和运行环境
  • 合理使用.dockerignore:减少构建上下文
  • 利用构建缓存:将不经常变动的指令放在前面
  • 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

    步骤:

  • 分析应用依赖
  • 选择合适的基础镜像
  • 编写Dockerfile
  • 创建.dockerignore文件
  • 构建并测试镜像
  • 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"]

    优化目标:

  • 减小镜像大小(目标:从500MB降到150MB以内)
  • 减少构建时间
  • 提高安全性
  • 9.3 挑战:多服务Dockerfile

    项目要求:创建一个包含以下服务的应用:

    • 前端:React应用
    • 后端:Node.js API
    • 数据库:PostgreSQL
    • 缓存:Redis

    任务:

  • 为每个服务编写独立的Dockerfile
  • 使用docker-compose编排所有服务
  • 配置服务间网络通信
  • 添加健康检查
  • 📚 总结与下一步

    关键知识点回顾

  • Dockerfile是指令集:告诉Docker如何构建镜像
  • 分层构建:每条指令创建一个只读层
  • 构建缓存:未改变的层会被缓存重用
  • 多阶段构建:减少最终镜像大小
  • 安全最佳实践:使用非root用户、最小化镜像
  • 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中COPY指令的位置会影响构建缓存?
  • 如何在不暴露敏感信息的情况下在Dockerfile中使用密码?
  • 多阶段构建真的能显著减小镜像大小吗?在什么场景下效果最明显?
  • 欢迎在评论区分享你编写的第一个Dockerfile,或提出遇到的问题!

    赞(0)
    未经允许不得转载:171主机测评 » 《手把手教你编写第一个Dockerfile》
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址