Docker从零单排之一刀牛

0 阅读8分钟

image.png

Docker 完整技术栈

graph TB
    subgraph "基础层"
        A1[Linux Kernel]
        A2[Namespace]
        A3[Cgroups]
        A4[UnionFS]
    end
    
    subgraph "运行时层"
        B1[containerd]
        B2[runc]
        B3[shim]
    end
    
    subgraph "引擎层"
        C1[dockerd]
        C2[REST API]
    end
    
    subgraph "客户端层"
        D1[docker CLI]
        D2[Docker Compose]
    end
    
    subgraph "应用层"
        E1[Dockerfile]
        E2[镜像]
        E3[容器]
        E4[网络]
        E5[数据卷]
    end
    
    subgraph "平台层"
        F1[Docker Hub]
        F2[Harbor]
        F3[Kubernetes]
    end
    
    A1 --> B1
    A2 --> B2
    A3 --> B2
    A4 --> B2
    
    B1 --> C1
    B2 --> C1
    
    C1 --> D1
    C1 --> D2
    
    D1 --> E1
    D1 --> E2
    D1 --> E3
    D2 --> E4
    D2 --> E5
    
    E2 --> F1
    E2 --> F2
    E3 --> F3
    
    style A1 fill:#E1F5FE
    style B1 fill:#E8F5E9
    style C1 fill:#FFF3E0
    style D1 fill:#F3E5F5
    style E3 fill:#90EE90
    style F3 fill:#FFE4B5

Docker从入门到精通

序章:Docker 是什么?—— 集装箱革命

Docker 的本质是一次构建,到处运行,它通过 Namespace 实现隔离、Cgroups 实现限制、UnionFS 实现高效存储,最终让应用交付变得像运送集装箱一样标准化、高效、可靠!

想象你是一个货运公司老板:

  • 传统方式:每艘船装载各种形状、大小的货物,装卸混乱,容易损坏 → 虚拟机时代
  • 集装箱方式:所有货物装进标准集装箱,吊车快速装卸,整齐高效 → Docker 时代
graph LR
    A[应用部署演进] --> B["物理机时代<br/>一台服务器一个应用"]
    A --> C["虚拟机时代<br/>一台服务器多个OS<br/>资源浪费、启动慢"]
    A --> D["容器时代<br/>一台服务器多个容器<br/>共享内核、秒级启动"]
    
    B -->|利用率10%| C
    C -->|利用率30%| D
    D -->|利用率80%+| E[Docker]
    
    style D fill:#90EE90
    style E fill:#90EE90

第一章:Docker 简介 —— 开源的容器化平台

1.1 什么是 Docker?

Docker 是一个开源项目,基于 Google 公司推出的 Go 语言实现,遵从 Apache 2.0 协议。

graph TD
    A["Docker 是什么"] --> B[开源项目]
    A --> C["Go 语言编写"]
    A --> D[Apache 2.0 协议]
    A --> E[容器化平台]
    
    B --> B1["GitHub 开源社区驱动"]
    C --> C1["高性能、跨平台"]
    D --> D1[商业友好]
    E --> E1["打包、分发、运行应用"]
    
    style A fill:#E1F5FE

1.2 背景与目标

背景说明
云计算兴起服务器硬件扩展非常便利
软件服务瓶颈部署成为瓶颈,环境不一致问题严重
DevOps 需求开发、测试、运维需要一致的环境

目标:实现轻量级的操作系统虚拟化解决方案,Docker 的基础是 Linux 容器技术。

graph LR
    A[传统部署痛点] --> B["开发环境<br/>'在我机器上能跑'"]
    A --> C["测试环境<br/>配置不同导致 Bug"]
    A --> D["生产环境<br/>部署复杂、回滚困难"]
    
    B --> E["Docker 解决方案"]
    C --> E
    D --> E
    
    E --> F["一次构建<br/>到处运行"]
    E --> G["环境一致性<br/>消除'works on my machine'"]
    E --> H["快速部署<br/>秒级启动"]
    
    style E fill:#90EE90

第二章:虚拟化技术 —— Docker 的基石

2.1 什么是虚拟化?

定义:在计算机中,虚拟化是一种资源管理技术,是将计算机的各种实体资源(服务器、网络、内存和存储等)予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。

graph TD
    A[虚拟化技术] --> B[软件虚拟化]
    A --> C[硬件虚拟化]
    A --> D[内存虚拟化]
    A --> E[网络虚拟化]
    A --> F[桌面虚拟化]
    A --> G[服务虚拟化]
    A --> H[虚拟机]
    
    B --> B1[VMware Workstation]
    C --> C1[Intel VT-x<br/>AMD-V]
    D --> D1[内存 ballooning]
    E --> E1[SDN<br/>软件定义网络]
    H --> H1[KVM<br/>Xen<br/>Hyper-V]
    
    style H fill:#FFE4B5

2.2 虚拟化的目的

graph LR
    A[虚拟化目的] --> B[资源复用]
    A --> C[隔离性]
    A --> D[可移植性]
    A --> E[灵活性]
    
    B --> B1[一台物理机运行多个虚拟机<br/>提高资源利用率]
    C --> C1[每个虚拟机独立运行<br/>互不干扰]
    D --> D1[虚拟机可以迁移<br/>从一台服务器到另一台]
    E --> E1[动态调整资源<br/>按需分配]
    
    style A fill:#E1F5FE

2.3 Docker 与虚拟机的本质区别

graph TB
    subgraph "虚拟机架构"
        A1[应用 A] --> B1[Guest OS<br/>CentOS]
        A2[应用 B] --> B2[Guest OS<br/>Ubuntu]
        A3[应用 C] --> B3[Guest OS<br/>Windows]
        
        B1 --> C1[Hypervisor<br/>VMware/KVM]
        B2 --> C1
        B3 --> C1
        
        C1 --> D1[Host OS<br/>Linux/Windows]
        D1 --> E1[物理硬件]
    end
    
    subgraph "Docker 容器架构"
        F1[应用 A] --> G1[容器 A<br/>bin/libs]
        F2[应用 B] --> G2[容器 B<br/>bin/libs]
        F3[应用 C] --> G3[容器 C<br/>bin/libs]
        
        G1 --> H1[Docker Engine]
        G2 --> H1
        G3 --> H1
        
        H1 --> I1[Host OS<br/>Linux Kernel]
        I1 --> J1[物理硬件]
    end
    
    style B1 fill:#FFB6C1
    style B2 fill:#FFB6C1
    style B3 fill:#FFB6C1
    style C1 fill:#FFB6C1
    
    style G1 fill:#90EE90
    style G2 fill:#90EE90
    style G3 fill:#90EE90
    style H1 fill:#90EE90
对比维度虚拟机Docker 容器
启动速度分钟级秒级
硬盘使用GB 级MB 级
性能接近原生,有 Hypervisor 开销接近原生,共享内核
系统支持量单机几十个单机上千个
隔离级别操作系统级进程级
资源利用率低(每个 VM 需完整 OS)(共享宿主机内核)

第三章:为什么用 Docker?—— 七大优势

mindmap
  root((为什么用<br/>Docker?))
    1. 秒级启动
      容器启动在秒级
      比虚拟机快得多
    2. 资源利用率高
      一台主机运行数千个容器
      单机支持量远超虚拟机
    3. 开发测试上线一条龙
      一次创建配置,到处运行
      环境一致性
    4. 快速交付和部署
      标准化镜像
      快速创建容器
    5. 更高效的虚拟化
      不需要额外 Hypervisor
      内核级虚拟化
    6. 轻松迁移和扩展
      任意平台运行
      物理机/虚拟机/公有云/私有云
    7. 简单管理
      增量修改
      替代大量更新工作

3.1 详细解读

优势说明场景
秒级启动容器共享宿主机内核,无需启动完整 OS弹性伸缩、快速扩容
资源利用率高一台物理机可运行数千容器微服务架构、高密度部署
开发测试上线一条龙开发容器 → 测试容器 → 生产容器DevOps、CI/CD
快速交付部署镜像标准化,一键部署持续集成、自动化部署
更高效的虚拟化无需 Hypervisor,接近原生性能高性能计算、边缘计算
轻松迁移扩展镜像可跨平台运行混合云、多云部署
简单管理增量更新,版本控制灰度发布、快速回滚

第四章:Docker 特性对比 —— 容器 vs 虚拟机

graph LR
    A[特性对比] --> B[启动速度]
    A --> C[硬盘使用]
    A --> D[性能]
    A --> E[系统支持量]
    
    B --> B1["容器:秒级<br/>虚拟机:分钟级"]
    C --> C1["容器:MB<br/>虚拟机:GB"]
    D --> D1["容器:接近原生<br/>虚拟机:略逊"]
    E --> E1["容器:上千个<br/>虚拟机:几十个"]
    
    style B1 fill:#90EE90
    style C1 fill:#90EE90
    style D1 fill:#90EE90
    style E1 fill:#90EE90

第五章:Docker 安装部署

5.1 安装步骤

# 1. 卸载旧版本
sudo yum remove docker docker-client docker-client-latest \
    docker-common docker-latest docker-latest-logrotate \
    docker-logrotate docker-engine

# 2. 安装依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# 3. 添加阿里云镜像源
sudo yum-config-manager --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 4. 安装 Docker CE
sudo yum install -y docker-ce docker-ce-cli containerd.io

# 5. 启动 Docker
sudo systemctl start docker
sudo systemctl enable docker

# 6. 验证安装
docker version
docker run hello-world

安装输出示例:

$ docker version
Client: Docker Engine - Community
 Version:           20.10.7
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        f0df350
 Built:             Wed Jun  2 11:58:10 2021
 OS/Arch:           linux/amd64

Server: Docker Engine - Community
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       b0f5bc3
  Built:            Wed Jun  2 11:56:35 2021
  OS/Arch:          linux/amd64
  Experimental:     false

5.2 配置镜像加速

# 创建/编辑 daemon.json
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker

第六章:Docker 基本操作 —— 核心命令详解

graph TD
    A[Docker 操作] --> B[容器操作]
    A --> C[镜像操作]
    
    B --> B1[docker run/create]
    B --> B2[docker start/stop/restart]
    B --> B3[docker kill]
    B --> B4[docker rm]
    B --> B5[docker pause/unpause]
    B --> B6[docker ps]
    B --> B7[docker exec]
    B --> B8[docker logs]
    B --> B9[docker cp]
    B --> B10[docker export/import]
    
    C --> C1[docker images]
    C --> C2[docker rmi]
    C --> C3[docker tag]
    C --> C4[docker build]
    C --> C5[docker history]
    C --> C6[docker save/load]
    
    style A fill:#E1F5FE

6.1 容器生命周期管理

# ========== 创建并启动容器 ==========
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

# 常用选项:
# -d: 后台运行(detached)
# -p: 端口映射 hostPort:containerPort
# -v: 挂载卷 hostPath:containerPath
# --name: 指定容器名
# -e: 环境变量
# --rm: 停止后自动删除

# 示例:运行 Nginx
docker run -d \
  --name my-nginx \
  -p 80:80 \
  -v /host/html:/usr/share/nginx/html \
  nginx:latest

# 输出:
# 3f8a2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b

# ========== 容器状态管理 ==========
docker ps                    # 查看运行中的容器
docker ps -a                 # 查看所有容器(包括停止的)
docker ps -q                 # 只显示容器 ID

docker start my-nginx        # 启动已停止的容器
docker stop my-nginx         # 优雅停止(发送 SIGTERM,10秒后 SIGKILL)
docker restart my-nginx      # 重启容器
docker kill my-nginx         # 强制停止(发送 SIGKILL)

docker rm my-nginx           # 删除已停止的容器
docker rm -f my-nginx        # 强制删除运行中的容器

docker pause my-nginx        # 暂停容器(冻结进程)
docker unpause my-nginx      # 恢复容器

# ========== 进入容器 ==========
docker exec -it my-nginx /bin/bash    # 交互式进入(推荐)
docker attach my-nginx                # 附加到主进程(不推荐)

# ========== 查看日志 ==========
docker logs my-nginx                  # 查看日志
docker logs -f my-nginx               # 实时跟踪日志(tail -f)
docker logs --since="2024-01-01" my-nginx   # 查看指定时间后的日志
docker logs --tail 100 my-nginx       # 查看最后 100 行

6.2 容器生命周期流程图

image.png

6.3 镜像操作

# ========== 查看镜像 ==========
docker images                    # 列出本地镜像
docker images -q                 # 只显示镜像 ID
docker images --filter "dangling=true"   # 显示悬空镜像

# REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
# nginx         latest    605c77e624dd   2 weeks ago    141MB
# ubuntu        20.04     ba6acccedd29   2 months ago   72.8MB

# ========== 删除镜像 ==========
docker rmi nginx:latest           # 删除指定镜像
docker rmi $(docker images -q)    # 删除所有镜像

# ========== 标记镜像 ==========
docker tag nginx:latest myregistry.com/nginx:v1.0

# ========== 构建镜像 ==========
docker build -t myapp:v1.0 .      # 根据 Dockerfile 构建

# ========== 查看镜像历史 ==========
docker history nginx:latest
# IMAGE          CREATED       CREATED BY                                      SIZE
# 605c77e624dd   2 weeks ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
# ...

# ========== 导出/导入镜像 ==========
docker save -o nginx.tar nginx:latest       # 导出为 tar
docker load -i nginx.tar                    # 从 tar 导入

docker export -o mycontainer.tar my-nginx   # 导出容器文件系统
docker import mycontainer.tar myimage:v1.0   # 导入为镜像

6.4 数据拷贝与备份

# ========== 容器与主机间拷贝 ==========
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf    # 容器→主机
docker cp ./nginx.conf my-nginx:/etc/nginx/nginx.conf    # 主机→容器

# ========== 查看容器信息 ==========
docker inspect my-nginx          # 查看详细配置(JSON)
docker inspect -f '{{.NetworkSettings.IPAddress}}' my-nginx   # 提取特定字段
docker top my-nginx              # 查看容器内进程
docker stats my-nginx            # 实时查看资源使用

第七章:Docker 架构 —— 客户端-守护进程模式

graph TB
    subgraph "Docker 客户端"
        A1[docker CLI]
        A2[Docker API 调用]
    end
    
    subgraph "Docker 守护进程 (dockerd)"
        B1[REST API Server]
        B2[容器管理]
        B3[镜像管理]
        B4[网络管理]
        B5[卷管理]
        B6[containerd<br/>容器运行时]
    end
    
    subgraph "底层技术"
        C1[Linux Namespace<br/>隔离]
        C2[Linux Cgroups<br/>资源限制]
        C3[UnionFS<br/>联合文件系统]
        C4[容器运行时 runc]
    end
    
    A1 -->|HTTP/Unix Socket| B1
    B1 --> B2
    B1 --> B3
    B1 --> B4
    B1 --> B5
    B2 --> B6
    B6 --> C4
    C4 --> C1
    C4 --> C2
    C4 --> C3
    
    style B6 fill:#90EE90
    style C1 fill:#E8F5E9
    style C2 fill:#E8F5E9
    style C3 fill:#E8F5E9

7.1 Docker 组件详解

组件作用类比
docker CLI用户命令行工具遥控器
dockerd守护进程,接收 API 请求电视机主板
containerd容器生命周期管理频道管理器
runc低级别容器运行时信号解码器
shim保持容器运行(dockerd 可退出)备用电源
sequenceDiagram
    participant User as 用户
    participant CLI as docker CLI
    participant Dockerd as dockerd
    participant Containerd as containerd
    participant Runc as runc
    participant Kernel as Linux Kernel
    
    User->>CLI: docker run nginx
    CLI->>Dockerd: POST /containers/create
    Dockerd->>Dockerd: 解析参数,准备配置
    
    Dockerd->>Containerd: 创建容器
    Containerd->>Containerd: 准备 rootfs(联合文件系统)
    
    Containerd->>Runc: runc create
    Runc->>Kernel: clone() 创建 Namespace
    Runc->>Kernel: 设置 Cgroups
    Runc->>Kernel: pivot_root() 切换根文件系统
    Runc->>Kernel: exec() 执行容器进程
    
    Kernel-->>Runc: 容器进程启动
    Runc-->>Containerd: 容器创建完成
    Containerd-->>Dockerd: 返回容器 ID
    Dockerd-->>CLI: 返回成功
    CLI-->>User: 容器运行中
    
    Note over Kernel: 容器进程隔离运行<br/>有自己的 PID/NET/IPC/MNT/UTS/User Namespace

第八章:Docker 实现原理 —— 三大核心技术

graph TD
    A[Docker 核心技术] --> B[Namespace<br/>隔离]
    A --> C[Cgroups<br/>限制]
    A --> D[UnionFS<br/>联合文件系统]
    
    B --> B1[PID Namespace<br/>进程隔离]
    B --> B2[NET Namespace<br/>网络隔离]
    B --> C1[CPU 限制]
    C --> C2[内存限制]
    C --> C3[IO 限制]
    D --> D1[分层存储]
    D --> D2[写时复制]
    
    style A fill:#E1F5FE
    style B fill:#E8F5E9
    style C fill:#FFF3E0
    style D fill:#F3E5F5

8.1 Linux Namespace —— 隔离

Namespace 让容器以为自己在一个独立的环境中:

graph TB
    subgraph "宿主机视角"
        A1[宿主机 PID 1: systemd]
        A2[容器 A PID 1: nginx<br/>实际 PID 2345]
        A3[容器 B PID 1: mysql<br/>实际 PID 5678]
    end
    
    subgraph "容器 A 视角"
        B1[PID 1: nginx<br/>以为自己是 1]
        B2[PID 2: php-fpm]
        B3[看不到宿主机进程]
    end
    
    subgraph "容器 B 视角"
        C1[PID 1: mysql<br/>以为自己是 1]
        C2[PID 2: mysqld_safe]
        C3[看不到宿主机进程]
    end
    
    style B1 fill:#90EE90
    style C1 fill:#90EE90

六种 Namespace:

Namespace隔离资源说明
PID进程 ID容器内 PID 从 1 开始
NET网络设备、端口每个容器有自己的 IP
IPC信号量、消息队列、共享内存进程间通信隔离
MNT挂载点文件系统视图隔离
UTS主机名、域名可以有自己的 hostname
USER用户和用户组容器内可以是 root,宿主机不是
# 查看进程的 Namespace
ls -l /proc/self/ns/
# lrwxrwxrwx 1 root root 0 Jan  1 00:00 ipc -> ipc:[4026531839]
# lrwxrwxrwx 1 root root 0 Jan  1 00:00 mnt -> mnt:[4026531840]
# lrwxrwxrwx 1 root root 0 Jan  1 00:00 net -> net:[4026531992]
# lrwxrwxrwx 1 root root 0 Jan  1 00:00 pid -> pid:[4026531836]
# lrwxrwxrwx 1 root root 0 Jan  1 00:00 user -> user:[4026531837]
# lrwxrwxrwx 1 root root 0 Jan  1 00:00 uts -> uts:[4026531838]

# 查看容器的 Namespace
docker inspect -f '{{.State.Pid}}' my-nginx
# 2345
ls -l /proc/2345/ns/

8.2 Linux Cgroups —— 限制

Cgroups(Control Groups)限制容器能使用的资源:

graph LR
    A[Cgroups 子系统] --> B[cpu]
    A --> C[cpuacct]
    A --> D[memory]
    A --> E[blkio]
    A --> F[pids]
    A --> G[devices]
    A --> H[net_cls]
    
    B --> B1[cpu.cfs_quota_us<br/>限制 CPU 使用率]
    D --> D1[memory.limit_in_bytes<br/>限制内存]
    E --> E1[blkio.throttle.read_bps_device<br/>限制磁盘 IO]
    F --> F1[pids.max<br/>限制进程数]
    
    style B fill:#E1F5FE
    style D fill:#E8F5E9
    style E fill:#FFF3E0
# 查看容器的 Cgroups 配置
docker run -d \
  --name limited-nginx \
  --cpus="1.5" \           # 限制使用 1.5 个 CPU
  --memory="512m" \        # 限制内存 512MB
  --memory-swap="1g" \     # 限制交换空间 1GB
  --pids-limit=100 \       # 限制进程数 100
  nginx

# 查看 Cgroups 实际配置
cat /sys/fs/cgroup/cpu/docker/<container-id>/cpu.cfs_quota_us
# 150000  (1.5 * 100000)

cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes
# 536870912  (512 * 1024 * 1024)

8.3 UnionFS —— 联合文件系统

核心思想:分层存储,写时复制(Copy-on-Write)

graph TB
    subgraph "镜像分层结构"
        A1["可写层<br/>容器层<br/>Container Layer"] --> B1["修改的数据"]
        
        A2[镜像层 3<br/>nginx:v1.0] --> B2["/usr/share/nginx/html"]
        
        A3[镜像层 2<br/>nginx:base] --> B3["/etc/nginx/nginx.conf"]
        
        A4[镜像层 1<br/>debian:bullseye] --> B4["/bin/bash<br/>基础系统"]
    end
    
    subgraph "联合视图"
        C1["/bin/bash"] --> C2["/etc/nginx/nginx.conf"]
        C2 --> C3["/usr/share/nginx/html"]
        C3 --> C4["容器看到的完整文件系统"]
    end
    
    A1 -.->|叠加| C4
    A2 -.->|叠加| C4
    A3 -.->|叠加| C4
    A4 -.->|叠加| C4
    
    style A1 fill:#90EE90
    style C4 fill:#E1F5FE

写时复制(Copy-on-Write)原理:

sequenceDiagram
    participant Container as 容器
    participant UnionFS as UnionFS
    participant Image as 镜像层(只读)
    participant WriteLayer as 可写层
    
    Note over Container,WriteLayer: 读取文件
    
    Container->>UnionFS: read /etc/nginx/nginx.conf
    UnionFS->>Image: 查找文件
    Image-->>UnionFS: 返回文件内容
    UnionFS-->>Container: 返回
    
    Note over Container,WriteLayer: 修改文件(写时复制!)
    
    Container->>UnionFS: write /etc/nginx/nginx.conf
    UnionFS->>Image: 查找文件(只读,不能修改)
    UnionFS->>WriteLayer: 复制文件到可写层
    UnionFS->>WriteLayer: 在可写层修改
    WriteLayer-->>UnionFS: 修改完成
    UnionFS-->>Container: 返回成功
    
    Note over Container,WriteLayer: 再次读取
    
    Container->>UnionFS: read /etc/nginx/nginx.conf
    UnionFS->>WriteLayer: 先查可写层
    WriteLayer-->>UnionFS: 找到修改后的版本
    UnionFS-->>Container: 返回新版本
    
    Note over Image,WriteLayer: 镜像层保持不变<br/>可被其他容器共享

查看镜像分层:

docker history nginx:latest
# IMAGE          CREATED       CREATED BY                                      SIZE
# 605c77e624dd   2 weeks ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  COPY file:... in /            4.62kB
# <missing>      2 weeks ago   /bin/sh -c #(nop)  COPY multi:... in /          16.1MB
# <missing>      2 weeks ago   /bin/sh -c set -x ...                            54.1MB
# <missing>      2 weeks ago   /bin/sh -c #(nop)  ENV PKG_RELEASE=1~bullseye   0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  ENV NJS_VERSION=0.7.1       0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  ENV NGINX_VERSION=1.21.5    0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
# <missing>      2 weeks ago   /bin/sh -c #(nop)  CMD ["bash"]                0B
# <missing>      2 weeks ago   /bin/sh -c #(nop) ADD file:... in /              80.4MB

第九章:Dockerfile —— 镜像构建脚本

9.1 Dockerfile 指令详解

# ========== 基础镜像 ==========
FROM centos:7
# 指定基础镜像,必须作为第一个指令

# ========== 维护者信息 ==========
LABEL maintainer="yourname@example.com"
LABEL version="1.0"
LABEL description="This is a sample Dockerfile"

# ========== 环境变量 ==========
ENV NODE_VERSION 14.17.0
ENV APP_HOME /usr/src/app
ENV PATH $APP_HOME/node_modules/.bin:$PATH

# ========== 工作目录 ==========
WORKDIR $APP_HOME
# 后续指令的工作目录,如果不存在会自动创建

# ========== 复制文件 ==========
COPY package*.json ./
# 从构建上下文复制文件到容器

COPY . .
# 复制当前目录所有文件

# ========== 运行命令 ==========
RUN yum install -y gcc-c++ make && \
    curl -sL https://rpm.nodesource.com/setup_14.x | bash - && \
    yum install -y nodejs && \
    yum clean all
# RUN 在构建时执行,每行 RUN 创建一个新层

# ========== 暴露端口 ==========
EXPOSE 3000
# 声明容器运行时监听的端口(只是文档说明,实际映射用 -p)

# ========== 数据卷 ==========
VOLUME ["/usr/src/app/data"]
# 创建挂载点,用于持久化数据

# ========== 健康检查 ==========
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# ========== 启动命令 ==========
CMD ["node", "server.js"]
# 容器启动时执行的命令(只能有一个,可被覆盖)

# 或
ENTRYPOINT ["node"]
CMD ["server.js"]
# ENTRYPOINT 固定执行程序,CMD 提供默认参数

9.2 Dockerfile 构建流程

graph TD
    A[Dockerfile] --> B[FROM<br/>基础镜像]
    B --> C[RUN yum install...<br/>创建新层]
    C --> D[COPY package.json<br/>创建新层]
    D --> E[RUN npm install<br/>创建新层]
    E --> F[COPY . .<br/>创建新层]
    F --> G[EXPOSE 3000<br/>元数据]
    G --> H["CMD [node, server.js]<br/>容器启动配置"]
    
    H --> I[最终镜像<br/>多层叠加]
    
    style B fill:#E1F5FE
    style C fill:#E8F5E9
    style D fill:#E8F5E9
    style E fill:#E8F5E9
    style F fill:#E8F5E9
    style I fill:#90EE90

9.3 构建示例

# 构建镜像
docker build -t mynodeapp:v1.0 .

# 输出:
# Sending build context to Docker daemon  15.36kB
# Step 1/10 : FROM centos:7
#  ---> eeb6ee3f44bd
# Step 2/10 : LABEL maintainer="yourname@example.com"
#  ---> Running in 3a4b5c6d7e8f
#  ---> 1a2b3c4d5e6f
# Step 3/10 : ENV NODE_VERSION 14.17.0
#  ---> Running in 4b5c6d7e8f9a
#  ---> 2b3c4d5e6f7a
# ...
# Successfully built 9f8e7d6c5b4a
# Successfully tagged mynodeapp:v1.0

# 查看构建缓存
docker build --no-cache -t mynodeapp:v1.0 .   # 不使用缓存

# 多阶段构建(减小镜像体积)
docker build -t mynodeapp:production -f Dockerfile.prod .

9.4 多阶段构建示例

# ========== 构建阶段 ==========
FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# ========== 生产阶段 ==========
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
graph TB
    subgraph "第一阶段:builder"
        A1[node:14] --> B1[npm install]
        B1 --> C1[COPY 源码]
        C1 --> D1[npm run build]
        D1 --> E1[产出 dist 目录]
    end
    
    subgraph "第二阶段:production"
        F1["nginx:alpine<br/>仅 ~20MB"] --> G1[COPY --from=builder /app/dist]
        G1 --> H1[最终镜像<br/>极小体积]
    end
    
    E1 -.->|仅复制产物| G1
    
    style E1 fill:#FFE4B5
    style H1 fill:#90EE90

第十章:Docker 数据管理 —— 卷与持久化

10.1 数据卷类型

graph TD
    A[数据持久化] --> B[Bind Mount<br/>绑定挂载]
    A --> C[Volume<br/>数据卷]
    A --> D[tmpfs Mount<br/>内存挂载]
    
    B --> B1[主机路径:容器路径<br/>-v /host:/container]
    C --> C1["Docker 管理<br/>-v myvolume:/container"]
    D --> D1["仅存储在内存<br/>--tmpfs /container"]
    
    B --> B2[适合开发<br/>直接编辑主机文件]
    C --> C2["适合生产<br/>备份、迁移方便"]
    D --> D2[适合敏感数据<br/>不写入磁盘]
    
    style B fill:#E1F5FE
    style C fill:#90EE90
    style D fill:#FFF3E0

10.2 数据卷操作

# ========== 创建卷 ==========
docker volume create my-data
# my-data

docker volume ls
# DRIVER    VOLUME NAME
# local     my-data

docker volume inspect my-data
# [
#     {
#         "CreatedAt": "2024-01-01T00:00:00Z",
#         "Driver": "local",
#         "Labels": null,
#         "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
#         "Name": "my-data",
#         "Options": null,
#         "Scope": "local"
#     }
# ]

# ========== 使用卷 ==========
docker run -d \
  --name mysql \
  -v my-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

# ========== 数据卷容器 ==========
docker create -v /dbdata --name dbstore busybox
docker run -d --volumes-from dbstore --name db1 mysql
docker run -d --volumes-from dbstore --name db2 mysql
# db1 和 db2 共享 dbstore 的数据卷

# ========== 备份数据卷 ==========
docker run --rm \
  --volumes-from mysql \
  -v $(pwd):/backup \
  busybox \
  tar cvf /backup/mysql-backup.tar /var/lib/mysql

# ========== 恢复数据卷 ==========
docker run --rm \
  --volumes-from mysql \
  -v $(pwd):/backup \
  busybox \
  tar xvf /backup/mysql-backup.tar -C /

第十一章:Docker 网络 —— 容器通信

11.1 网络模式

graph TB
    A[Docker 网络模式] --> B[bridge<br/>桥接模式]
    A --> C[host<br/>主机模式]
    A --> D[none<br/>无网络]
    A --> E[container<br/>容器模式]
    A --> F[自定义网络]
    
    B --> B1[默认模式<br/>容器通过 docker0 桥通信<br/>NAT 访问外网]
    C --> C1[共享宿主机网络<br/>无隔离<br/>性能最好]
    D --> D1[完全隔离<br/>需手动配置网络]
    E --> E1[共享另一个容器网络<br/>localhost 互通]
    F --> F1[用户定义桥接<br/>DNS 自动解析<br/>推荐用于多容器应用]
    
    style B fill:#90EE90
    style F fill:#90EE90

11.2 网络操作

# ========== 查看网络 ==========
docker network ls
# NETWORK ID     NAME      DRIVER    SCOPE
# 3a4b5c6d7e8f   bridge    bridge    local
# 4b5c6d7e8f9a   host      host      local
# 5c6d7e8f9a0b   none      null      local

# ========== 创建自定义网络 ==========
docker network create my-network
docker network create --driver bridge --subnet 172.20.0.0/16 my-subnet

# ========== 容器加入网络 ==========
docker run -d --name web --network my-network nginx
docker run -d --name api --network my-network myapi

# 同一网络内,容器可通过名称互访
docker exec web ping api
# PING api (172.20.0.3): 56 data bytes
# 64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.072 ms

# ========== 端口映射 ==========
docker run -d -p 8080:80 nginx          # 主机 8080 → 容器 80
docker run -d -p 127.0.0.1:8080:80 nginx # 仅本机可访问
docker run -d -p 8080:80/udp nginx       # UDP 端口
docker run -d -P nginx                   # 随机映射 EXPOSE 的端口

# ========== 查看端口映射 ==========
docker port my-nginx
# 80/tcp -> 0.0.0.0:8080

11.3 网络架构图

graph TB
    subgraph "宿主机"
        A1[eth0<br/>192.168.1.100]
        A2[docker0<br/>172.17.0.1/16<br/>虚拟网桥]
    end
    
    subgraph "容器 A"
        B1[eth0<br/>172.17.0.2]
        B2[nginx<br/>:80]
    end
    
    subgraph "容器 B"
        C1[eth0<br/>172.17.0.3]
        C2[myapp<br/>:3000]
    end
    
    A1 -->|NAT| A2
    A2 -->|veth-pair| B1
    A2 -->|veth-pair| C1
    
    B1 -.->|内部通信<br/>172.17.0.0/16| C1
    
    B2 -->|暴露端口| D1[主机 8080]
    D1 -->|访问| E1[外部用户]
    
    style A2 fill:#E1F5FE
    style B1 fill:#E8F5E9
    style C1 fill:#E8F5E9

第十二章:Docker Compose —— 多容器编排

12.1 Compose 文件示例

# docker-compose.yml
version: '3.8'

services:
  web:
    image: nginx:latest
    container_name: my-web
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    networks:
      - frontend
    depends_on:
      - api
    restart: unless-stopped

  api:
    build: ./api                    # 从 Dockerfile 构建
    container_name: my-api
    environment:
      - NODE_ENV=production
      - DB_HOST=db
      - DB_PORT=3306
    volumes:
      - api-logs:/app/logs
    networks:
      - frontend
      - backend
    restart: unless-stopped

  db:
    image: mysql:8.0
    container_name: my-db
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: myapp
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - backend
    restart: unless-stopped

  redis:
    image: redis:alpine
    container_name: my-redis
    volumes:
      - redis-data:/data
    networks:
      - backend

volumes:
  api-logs:
  db-data:
  redis-data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

12.2 Compose 架构图

graph TB
    subgraph "Docker Compose"
        A1[docker-compose.yml]
    end
    
    subgraph "Services"
        B1[web<br/>nginx<br/>端口 80]
        B2[api<br/>Node.js<br/>构建自 ./api]
        B3[db<br/>mysql:8.0]
        B4[redis<br/>redis:alpine]
    end
    
    subgraph "Networks"
        C1["frontend<br/>web ↔ api"]
        C2["backend<br/>api ↔ db ↔ redis"]
    end
    
    subgraph "Volumes"
        D1[html<br/>主机目录]
        D2[db-data<br/>Docker 卷]
        D3[redis-data<br/>Docker 卷]
    end
    
    A1 --> B1
    A1 --> B2
    A1 --> B3
    A1 --> B4
    
    B1 -.->|访问| C1
    B2 -.->|访问| C1
    B2 -.->|访问| C2
    B3 -.->|访问| C2
    B4 -.->|访问| C2
    
    B1 --> D1
    B3 --> D2
    B4 --> D3
    
    style A1 fill:#E1F5FE
    style C1 fill:#E8F5E9
    style C2 fill:#E8F5E9

12.3 Compose 命令

# 启动所有服务
docker-compose up
docker-compose up -d              # 后台运行
docker-compose up --build         # 重新构建

# 停止服务
docker-compose down
docker-compose down -v            # 同时删除卷

# 查看状态
docker-compose ps
docker-compose logs -f            # 实时查看日志
docker-compose logs web           # 查看特定服务日志

# 扩展服务
docker-compose up -d --scale web=3   # 启动 3 个 web 实例

# 执行命令
docker-compose exec db mysql -u root -p
docker-compose run --rm api npm test  # 运行一次性命令

第十三章:Docker 私有仓库 —— Harbor

13.1 为什么需要私有仓库?

graph LR
    A[镜像管理需求] --> B[安全性<br/>敏感镜像不公开]
    A --> C[速度<br/>内网传输更快]
    A --> D["控制<br/>版本管理、权限控制"]
    A --> E[合规<br/>企业审计要求]
    
    B --> F[Harbor<br/>企业级私有仓库]
    C --> F
    D --> F
    E --> F
    
    style F fill:#90EE90

13.2 Harbor 架构

graph TB
    subgraph "Harbor"
        A1[Proxy<br/>Nginx]
        A2[Core Services<br/>API/UI]
        A3[Registry<br/>镜像存储]
        A4[Database<br/>PostgreSQL]
        A5[Redis<br/>缓存]
        A6[Clair<br/>漏洞扫描]
        A7[Notary<br/>镜像签名]
        A8[Job Service<br/>异步任务]
    end
    
    subgraph "存储后端"
        B1[文件系统<br/>/data]
        B2[S3/MinIO<br/>对象存储]
        B3[Swift<br/>OpenStack]
    end
    
    A1 --> A2
    A2 --> A3
    A2 --> A4
    A2 --> A5
    A2 --> A6
    A2 --> A7
    A2 --> A8
    
    A3 --> B1
    A3 --> B2
    A3 --> B3
    
    style A1 fill:#E1F5FE
    style A3 fill:#E8F5E9

13.3 使用私有仓库

# 登录私有仓库
docker login harbor.example.com
# Username: admin
# Password:
# Login Succeeded

# 标记镜像
docker tag myapp:v1.0 harbor.example.com/project/myapp:v1.0

# 推送镜像
docker push harbor.example.com/project/myapp:v1.0
# The push refers to repository [harbor.example.com/project/myapp]
# v1.0: digest: sha256:3f8a2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b size: 1234

# 拉取镜像
docker pull harbor.example.com/project/myapp:v1.0