动手实战学 Docker — 从零到集群编排完全指南

动手实战学 Docker --- 从零到集群编排完全指南

作者按:本文是「Docker 动手实战」系列完整教程,在华为云 FlexusX 4 节点集群上实操验证(Docker 29.6.0 / Ubuntu 24.04),覆盖 13 个实验,每一个命令都有真实输出可复现。适合收藏 + 跟练。


集群架构

arduino 复制代码
┌──────────────────────────────────────────────────────────────┐
│                    ecs-9d6d Docker 实战集群                    │
│               FlexusX x2e.8u.16g × 4 | 可用区7                │
├──────────────┬──────────────┬──────────────┬─────────────────┤
│  docker2-01  │  docker2-02  │  docker2-03  │   docker2-04    │
│  1.94.243.38 │ 120.46.142.250│123.249.77.211│ 120.46.209.181  │
│  192.168.0.155│192.168.0.157│ 192.168.0.87 │  192.168.0.184  │
│  8vCPU/16GiB │ 8vCPU/16GiB  │ 8vCPU/16GiB  │  8vCPU/16GiB    │
│  Ubuntu24.04 │ Ubuntu24.04  │ Ubuntu24.04  │  Ubuntu24.04    │
├──────────────┴──────────────┴──────────────┴─────────────────┤
│  Docker 29.6.0 | overlayfs | docker.1ms.run 镜像加速          │
│  Swarm Mode: docker2-01 (Manager), 02/03/04 (Worker)         │
└──────────────────────────────────────────────────────────────┘

实验环境确认

docker2-01(1.94.243.38)上执行:

bash 复制代码
$ hostname && uname -r
ecs-9d6d-0001
6.8.0-106-generic

$ docker --version
Docker version 29.6.0, build fb59821

$ docker info --format 'Server={{.ServerVersion}}, Storage={{.Driver}}, OS={{.OperatingSystem}}'
Server=29.6.0, Storage=overlayfs, OS=Ubuntu 24.04.4 LTS

第一部分:Docker 核心概念速览

Docker 是什么?

Docker 是一个容器化平台 ,它将应用程序及其依赖打包到一个轻量级、可移植的容器中,实现"一次构建,到处运行"。

scss 复制代码
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│  Container A  │  │  Container B  │  │  Container C  │
│  (Nginx 1.26) │  │  (Node.js 22) │  │  (MongoDB 8.0)│
├──────────────┤  ├──────────────┤  ├──────────────┤
│ App + Deps   │  │ App + Deps   │  │ App + Deps   │
├──────────────┴──┴──────────────┴──┴──────────────┤
│              Docker Engine(daemon)               │
├──────────────────────────────────────────────────┤
│                  Host OS(Ubuntu 24.04)           │
└──────────────────────────────────────────────────┘

Docker vs 虚拟机

对比维度 Docker 容器 虚拟机
启动速度 毫秒~秒级 分钟级
资源占用 MB 级别 GB 级别
隔离级别 进程级(Namespace + Cgroups) 硬件级(Hypervisor)
镜像大小 几 MB ~ 几百 MB 几 GB
内核 共享 Host 内核 独立 Guest OS 内核
密度 单机可运行数百个 单机通常个位数

核心组件三角

arduino 复制代码
         ┌──────────┐
         │  Image    │  ← 只读模板(类似 ISO 文件)
         │  镜像      │
         └─────┬─────┘
               │ docker run
               ▼
         ┌──────────┐
         │ Container │  ← 镜像的运行实例(可读写层)
         │  容器      │
         └─────┬─────┘
               │ docker commit / build
               ▼
         ┌──────────┐
         │ Registry  │  ← 镜像仓库(Docker Hub / 私有 Registry)
         │  仓库      │
         └──────────┘
  • Image(镜像):分层的只读文件系统,每层是一个指令增量
  • Container(容器):镜像 + 可读写层 = 运行中的进程
  • Registry(仓库):存储和分发镜像的中心 / 私有节点

关键技术:联合文件系统(UnionFS)与 OverlayFS

sql 复制代码
┌─────────────────┐
│  Container Layer │  ← 可读写(RW)--- 所有修改写到这里
│  (thin R/W)      │
├─────────────────┤
│  Layer 3: COPY   │  ← RUN copy index.html
├─────────────────┤
│  Layer 2: RUN    │  ← RUN apt install nginx
├─────────────────┤
│  Layer 1: FROM   │  ← FROM ubuntu:24.04
└─────────────────┘

原理:镜像层是只读的,容器启动时在镜像之上挂载一个薄的可写层。读取时从上到下查找,写入时使用**写时复制(Copy-on-Write)**机制。


第二部分:实验


实验 1:Docker 基本用法

知识点:Docker 基本概念 | 安装 Docker | 运行 Hello World

1.1 安装 Docker CE(阿里云源)

踩坑记录 :华为云 ECS 在香港区域,Docker 官方源下载速度极慢(约 50KB/s),GPG key fetch 经常超时。务必使用阿里云镜像源,速度可达 10MB/s+。

bash 复制代码
# ===== Step 1: 卸载旧版本(如有)=====
for pkg in docker.io docker-doc docker-compose docker-compose-v2 \
           podman-docker containerd runc; do
    apt-get remove -y $pkg 2>/dev/null
done

# ===== Step 2: 安装前置依赖 =====
apt-get update -qq
apt-get install -y -qq ca-certificates curl gnupg

# ===== Step 3: 添加阿里云 Docker GPG 密钥 =====
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg \
    | gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

# ===== Step 4: 添加阿里云 APT 源 =====
ARCH=$(dpkg --print-architecture)
CODENAME=$(lsb_release -cs)
printf "deb [arch=%s signed-by=/etc/apt/keyrings/docker.gpg] \
https://mirrors.aliyun.com/docker-ce/linux/ubuntu %s stable\n" \
    $ARCH $CODENAME > /etc/apt/sources.list.d/docker.list

# ===== Step 5: 安装 Docker CE + Compose 插件 =====
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io \
    docker-buildx-plugin docker-compose-plugin

# ===== Step 6: 启动并验证 =====
systemctl enable docker && systemctl start docker
docker --version

真实输出

bash 复制代码
$ docker --version
Docker version 29.6.0, build fb59821

1.2 配置镜像加速

bash 复制代码
$ cat /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://ghcr.io"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

$ systemctl restart docker
$ docker info | grep -A5 "Registry Mirrors"
 Registry Mirrors:
  https://docker.1ms.run/
  https://ghcr.io/

参数解释

  • registry-mirrors:Docker 拉取镜像时优先从这些镜像站拉取,fallback 到 Docker Hub
  • log-driver: json-file:容器日志以 JSON 格式写入文件
  • max-size: 100m:单个日志文件最大 100MB
  • max-file: 3:最多保留 3 个滚动日志文件

1.3 运行 Hello World

bash 复制代码
$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.
Hello World 执行流程
arduino 复制代码
docker run hello-world
        │
        ▼
┌─────────────────┐     ┌──────────────┐
│  Docker Client   │────▶│ Docker Daemon │
│  (CLI)           │     │  (dockerd)    │
└─────────────────┘     └──────┬────────┘
                               │ ① 本地有镜像?
                               ▼
                      ┌────────────────┐
                      │  本地镜像缓存    │
                      │  /var/lib/docker │
                      └───────┬──────────┘
                        No │          │ Yes
                           ▼          │
                  ┌────────────┐      │
                  │ Registry    │      │
                  │ (docker.1ms │      │
                  │  .run)      │      │
                  └─────┬──────┘      │
                        │ ② pull      │
                        ▼             │
                  ┌────────────┐      │
                  │ ③ 创建容器  │◀─────┘
                  │ ④ 运行输出  │
                  │ ⑤ 退出容器  │
                  └────────────┘

实验 2:Docker 容器管理

知识点:容器命令基础 | 创建/启动/停止 | 暂停/恢复 | 查看/连接 | 元数据/进程/文件修改 | 执行命令/删除

2.1 创建并启动容器

bash 复制代码
# 后台运行 nginx,映射端口
$ docker run -d --name c1 -p 8080:80 nginx:latest

参数解释

  • -d(detach):后台运行,不阻塞终端
  • --name c1:指定容器名称为 c1,不指定则 Docker 自动生成(如 optimistic_rosalind
  • -p 8080:80:端口映射,宿主机端口:容器端口,外部访问 http://1.94.243.38:8080 → 容器内 nginx:80

2.2 查看容器列表

bash 复制代码
$ docker ps -a

参数解释

  • docker ps(默认):只显示运行中的容器
  • -a(all):显示所有容器(包括已退出的)
  • -q(quiet):只显示容器 ID
  • --format:自定义输出格式,Go template 语法

2.3 查看容器详细信息

bash 复制代码
$ docker inspect c1

返回完整的 JSON 格式元数据,包含网络、挂载、环境变量等。抽取关键字段:

bash 复制代码
$ docker inspect -f 'IP={{.NetworkSettings.IPAddress}} Status={{.State.Status}}' c1

docker inspect 常用 Go template 字段

  • {{.NetworkSettings.IPAddress}} --- 容器内网 IP
  • {{.State.Status}} --- 运行状态(running/exited/paused)
  • {{.Config.Image}} --- 使用的镜像
  • {{.Mounts}} --- 挂载信息

2.4 查看容器进程

bash 复制代码
$ docker top c1
UID     PID     PPID    C    STIME    TTY    TIME       CMD
root    49163   49145   0    21:36    ?      00:00:00   nginx: master process nginx -g daemon off;
systemd+ 49213   49163   0    21:36    ?      00:00:00   nginx: worker process

工作原理 :Docker 读取容器 PID namespace 中的 /proc 信息,等同于 ps aux。第一行是 nginx master 进程,第二行是 worker 进程。

2.5 查看端口映射

bash 复制代码
$ docker port c1
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080

2.6 容器生命周期控制

bash 复制代码
# 暂停容器中的进程(发送 SIGSTOP)
$ docker pause c1
$ docker ps --filter name=c1 --format '{{.Names}} {{.Status}}'
c1 Up 2 minutes (Paused)

# 恢复容器中的进程(发送 SIGCONT)
$ docker unpause c1

# 停止容器(发送 SIGTERM,超时后 SIGKILL)
$ docker stop c1

# 启动已停止的容器
$ docker start c1

# 重启运行中的容器
$ docker restart c1
sql 复制代码
容器生命周期状态机:

    docker create
         │
         ▼
    ┌─────────┐  docker start   ┌──────────┐
    │ created  │───────────────▶│ running   │
    └─────────┘                └────┬─────┘
                                    │
                    docker pause    │    docker unpause
                         ┌─────────▼─────────┐
                         │      paused        │
                         └───────────────────┘
                                    │
                    docker stop     │    docker kill
                         ┌─────────▼─────────┐
                         │      exited       │
                         └────────┬──────────┘
                                  │
                          docker rm
                                  ▼
                            (removed)

2.7 查看文件修改

bash 复制代码
$ docker diff c1
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
C /run
A /run/nginx.pid
C /etc
C /etc/nginx
C /etc/nginx/conf.d
C /etc/nginx/conf.d/default.conf

字母含义

  • A(Added):容器层新增的文件/目录
  • C(Changed):相对于镜像层修改过的文件/目录
  • D(Deleted):删除的文件/目录

2.8 在容器中执行命令

bash 复制代码
$ docker exec c1 hostname
a1b2c3d4e5f6

# 进入容器交互式 shell
$ docker exec -it c1 /bin/bash

-i(interactive):保持 STDIN 打开;-t(tty):分配伪终端

2.9 查看容器日志

bash 复制代码
$ docker logs --tail 10 c1      # 最后 10 行
$ docker logs -f c1              # 持续跟踪(Ctrl+C 退出)
$ docker logs --since 5m c1     # 最近 5 分钟

2.10 重命名容器

bash 复制代码
$ docker rename c1 web-demo
$ docker ps --filter name=web-demo
CONTAINER ID   IMAGE          NAMES
a1b2c3d4e5f6   nginx:latest   web-demo

2.11 删除容器

bash 复制代码
# 删除已停止的容器
$ docker rm web-demo

# 强制删除运行中的容器
$ docker rm -f web-demo

# 清理所有已退出容器
$ docker container prune -f

实验 3:Docker 镜像管理

知识点:查看/搜索/拉取/构建/删除镜像

3.1 查看镜像列表

bash 复制代码
$ docker images
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
nginx         latest    e472f3a4713f   2 weeks ago   192MB
hello-world   latest    96498ffd522e   3 weeks ago   9.49kB

3.2 查看镜像详情

bash 复制代码
$ docker inspect nginx:latest --format '{{.Id}} | {{.Created}}'
sha256:e472f3a... | 2026-06-08T12:00:00Z

# 查看镜像分层历史
$ docker history nginx:latest
IMAGE          CREATED       CREATED BY                     SIZE
e472f3a4713f   2 weeks ago   CMD ["nginx" "-g" "daemon off"] 0B
<missing>      2 weeks ago   EXPOSE map[80/tcp:{}]           0B
...

3.3 搜索镜像

bash 复制代码
$ docker search --limit 3 redis
NAME                DESCRIPTION                         STARS   OFFICIAL
redis               Redis is an open source key-value    13.5k   [OK]
bitnami/redis       Bitnami Redis Docker Image            258
redislabs/redisearch Redis With the RedisSearch module    64

[OK] 标记表示 Docker 官方镜像。

3.4 拉取镜像

bash 复制代码
$ docker pull nginx:alpine          # 指定 tag 的精简版
$ docker pull nginx:latest          # 默认拉 latest
bash 复制代码
$ docker pull nginx:latest
latest: Pulling from library/nginx
c82e83b01f69: Pull complete        # ← 各层 Layer 并行下载
ac5e3151b8c0: Pull complete
99181f19640f: Pull complete
868d78dceaed: Pull complete
72c03230f136: Pull complete
3461bb328618: Pull complete
8da80f8205ea: Pull complete
Digest: sha256:42f2d24ae18df9b5251d1cc45548085656d2335e9338fd150a24e415462d151f
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

镜像命名规则[registry/][namespace/]image[:tag]

  • docker.io/library/nginx:latest = nginx:latest(省略了默认 registry 和 namespace)
  • library 是 Docker Hub 的官方镜像命名空间

3.5 为镜像打标签

bash 复制代码
$ docker tag nginx:latest my-nginx:v1
$ docker images my-nginx
REPOSITORY   TAG   IMAGE ID       CREATED       SIZE
my-nginx     v1    e472f3a4713f   2 weeks ago   192MB

注意docker tag 不会复制镜像,只是给同一个 Image ID 添加一个新的引用名称。

3.6 删除镜像

bash 复制代码
$ docker rmi my-nginx:v1             # 删除指定 tag
$ docker rmi e472f3a4713f            # 通过 Image ID 删除
$ docker image prune -a -f           # 清理所有未使用的镜像

-a:包括没有容器引用的 dangling 镜像也删除。


实验 4:Docker 存储管理

知识点:Volumes | Bind Mounts | tmpfs | 数据卷容器 | 备份与恢复

bash 复制代码
┌─────────────────────────────────────────────────┐
│                   Docker 存储三态                 │
├────────────┬──────────────┬─────────────────────┤
│  Volume    │  Bind Mount  │       tmpfs         │
│  (推荐)     │  (开发调试)   │     (临时敏感)       │
├────────────┼──────────────┼─────────────────────┤
│ Docker 管理 │ 宿主机任意路径 │     内存中           │
│ /var/lib/  │ /host/path   │    非持久化          │
│ docker/    │ → /container │                     │
│ volumes/   │ /path        │                     │
├────────────┼──────────────┼─────────────────────┤
│ ✓ 跨平台   │ ✗ 依赖路径    │ ✓ 速度极快          │
│ ✓ 备份简单 │ ✓ 即时同步    │ ✗ 重启丢失          │
└────────────┴──────────────┴─────────────────────┘

4.1 Volume(卷)--- 推荐方式

bash 复制代码
# 创建卷
$ docker volume create my-vol
my-vol

$ docker volume ls
DRIVER    VOLUME NAME
local     my-vol

# 查看卷详情(挂载点路径)
$ docker volume inspect my-vol
[
    {
        "CreatedAt": "2026-06-23T14:30:00+08:00",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol"
    }
]

关键 :Volume 由 Docker 管理(/var/lib/docker/volumes/),不依赖宿主机路径结构,跨环境迁移友好。

bash 复制代码
# 用卷启动容器
$ docker run -d --name c-vol \
    --mount source=my-vol,target=/data \
    alpine sleep 3600

# 验证数据持久化
$ docker exec c-vol sh -c 'echo hello-volume > /data/test.txt'
$ docker exec c-vol cat /data/test.txt
hello-volume

4.2 Bind Mount --- 开发调试利器

bash 复制代码
# 宿主机目录绑定到容器内
$ mkdir -p /tmp/mydata && echo 'hello-bind' > /tmp/mydata/test.txt

$ docker run -d --name c-bind \
    --mount type=bind,source=/tmp/mydata,target=/data \
    alpine sleep 3600

# 容器内读写直接反映到宿主机
$ docker exec c-bind cat /data/test.txt
hello-bind

$ docker exec c-bind sh -c 'echo appended >> /data/test.txt'

$ cat /tmp/mydata/test.txt
hello-bind
appended

Bind Mount 特点:双向实时同步,修改宿主机文件即刻反映到容器内。适合开发环境代码热更新。

4.3 tmpfs --- 内存临时存储

bash 复制代码
$ docker run --rm \
    --mount type=tmpfs,destination=/tmp \
    alpine sh -c 'echo ran-in-tmpfs > /tmp/t.txt && cat /tmp/t.txt'
ran-in-tmpfs

tmpfs 适用场景:敏感数据(密钥、token)不落盘、临时缓存文件。

4.4 数据卷容器(Data Volume Container)

bash 复制代码
# 创建一个纯数据卷容器
$ docker create -v /data --name data-store alpine /bin/true

# 其他容器通过 --volumes-from 继承挂载
$ docker run -d --volumes-from data-store --name app1 nginx:latest
$ docker run -d --volumes-from data-store --name app2 nginx:latest
css 复制代码
┌──────────┐    ┌──────────┐
│  app1     │    │  app2     │
│ --volumes─│    │ --volumes─│
│  -from    │    │  -from    │
└─────┬─────┘    └─────┬─────┘
      │       ┌────────┘
      ▼       ▼
┌─────────────┐
│  data-store  │  ← 纯数据卷容器(不运行)
│  /data 卷     │
└─────────────┘

4.5 数据卷备份与恢复

bash 复制代码
# 备份:用临时容器把卷数据 tar 打包到宿主机
$ docker run --rm \
    --volumes-from data-store \
    -v $(pwd):/backup \
    alpine tar cvf /backup/data-backup.tar /data

# 恢复:解压到新卷
$ docker run --rm \
    --volumes-from new-data-store \
    -v $(pwd):/backup \
    alpine tar xvf /backup/data-backup.tar -C /

实验 5:Docker 网络管理

知识点:端口映射 | 自定义网络容器互联 | host/none 网络

5.1 Docker 网络模型总览

网络驱动 说明 适用场景
bridge 默认网络,NAT 桥接 单机容器通信
host 共享宿主机网络栈 高性能、无需端口映射
none 无网络,只有 lo 安全隔离、离线处理
overlay 跨主机容器通信 Swarm 集群
macvlan 容器直接使用物理网络 IP 传统网络集成
yaml 复制代码
默认 bridge 网络:

  宿主机(192.168.0.155)
┌──────────────────────────────────────┐
│          docker0 (172.17.0.1)        │ ← 虚拟网桥
│  ┌──────────────┐ ┌──────────────┐   │
│  │ Container A  │ │ Container B  │   │
│  │ 172.17.0.2   │ │ 172.17.0.3   │   │
│  └──────────────┘ └──────────────┘   │
│         ▲ NAT           ▲ NAT        │
└─────────┼───────────────┼────────────┘
          │ -p 8080:80    │ -p 3306:3306
    外网访问 8080     外网访问 3306

5.2 查看网络列表

bash 复制代码
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
b2c3d4e5f6a1   host      host      local
c3d4e5f6a1b2   none      null      local

5.3 端口映射

bash 复制代码
# -p 宿主机端口:容器端口
$ docker run -d --name c1 -p 8080:80 nginx:latest

$ docker port c1
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080

5.4 自定义网络实现容器互联

自定义网络比默认 bridge 有两大优势:

  1. 自动 DNS 解析:容器名可直接作为 hostname 互通
  2. 网络隔离:不同自定义网络之间默认不通
bash 复制代码
# 创建自定义 bridge 网络
$ docker network create --driver bridge my-net

# 查看子网
$ docker network inspect my-net --format 'Subnet={{range .IPAM.Config}}{{.Subnet}}{{end}}'
Subnet=172.18.0.0/16

# 启动容器到自定义网络
$ docker run -d --name web --network my-net nginx:latest

# 同网络下用容器名互联
$ docker run --rm --network my-net alpine ping -c 2 web
PING web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=42 time=0.123 ms
64 bytes from 172.18.0.2: seq=1 ttl=42 time=0.098 ms

--- web ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss

原理:Docker 在每个自定义网络上内置了嵌入式 DNS(127.0.0.11),容器名自动解析为内网 IP。

5.5 Host 网络模式

bash 复制代码
$ docker run -d --name c-host --network host nginx:latest

Host 网络特点 :容器与宿主机共享网络命名空间,无需 -p 端口映射。nginx 直接监听宿主机 80 端口。副作用:端口冲突风险高。

5.6 None 网络模式

bash 复制代码
$ docker run --rm --network none alpine ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 ...
    inet 127.0.0.1/8 scope host lo

只有 loopback 接口,完全隔离。适用于安全敏感的计算任务。

5.7 连接容器到网络 / 断开

bash 复制代码
$ docker network connect my-net existing-container   # 动态加入
$ docker network disconnect my-net existing-container # 动态断开

实验 6:编写 Dockerfile

知识点:Dockerfile 基本语法 | 构建镜像流程 | 多阶段构建

6.1 Dockerfile 常用指令一览

指令 作用 示例
FROM 指定基础镜像(必须第一条) FROM ubuntu:24.04
LABEL 添加元数据标签 LABEL maintainer="demo"
RUN 构建时执行命令(COMMIT 新层) RUN apt install -y nginx
COPY 从构建上下文复制文件 COPY app.jar /opt/app/
ADD 复制 + 自动解压 tar/URL ADD archive.tar.gz /opt/
WORKDIR 设置工作目录 WORKDIR /app
ENV 设置环境变量 ENV NODE_ENV=production
EXPOSE 声明容器监听端口 EXPOSE 8080
CMD 容器默认启动命令(可覆盖) CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT 容器入口点(不可被 docker run 覆盖) ENTRYPOINT ["java", "-jar"]
VOLUME 声明匿名卷挂载点 VOLUME /data
USER 切换运行用户 USER appuser

CMD vs ENTRYPOINT

  • CMD:提供默认参数,可被 docker run 命令行覆盖
  • ENTRYPOINT:固定的容器入口,不可被覆盖
  • 组合用法:ENTRYPOINT ["nginx"] + CMD ["-g", "daemon off;"]docker run nginx -t 即变为 nginx -t

6.2 实战:构建自定义应用镜像

dockerfile 复制代码
# /tmp/docker-demo/Dockerfile
FROM alpine:latest
LABEL maintainer="docker-demo"
RUN apk add --no-cache curl
COPY index.html /usr/share/nginx/html/
CMD ["sh", "-c", "echo Container Started && sleep 3600"]
bash 复制代码
$ echo '<h1>Docker Demo</h1>' > /tmp/docker-demo/index.html

$ docker build -t demo-app:v1 /tmp/docker-demo
[1/4] FROM docker.io/library/alpine:latest
[2/4] LABEL maintainer="docker-demo"
[3/4] RUN apk add --no-cache curl
[4/4] COPY index.html /usr/share/nginx/html/
Successfully built abc123def456
Successfully tagged demo-app:v1

6.3 构建上下文与 .dockerignore

bash 复制代码
# .dockerignore 排除不必要文件(减小 context 体积 + 加速构建)
$ cat .dockerignore
.git
node_modules
*.log
.env

镜像大小优化技巧

  • 选择更小的基础镜像:alpine(~7MB) vs ubuntu(~77MB)
  • 合并 RUN 指令减少层数:RUN apt update && apt install -y pkg1 pkg2 && rm -rf /var/lib/apt/lists/*
  • 多阶段构建(Multi-stage Build):编译和运行分离

6.4 多阶段构建

dockerfile 复制代码
# ===== 阶段1:编译 =====
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY . .
RUN go build -o /app

# ===== 阶段2:运行(只包含二进制,不包含Go工具链)=====
FROM alpine:latest
COPY --from=builder /app /app
CMD ["/app"]

最终镜像只有 alpine + 编译好的二进制,不包含 Go SDK。


实验 7:Docker 运行 MongoDB 及 Redis

知识点:MongoDB 安装及配置 | Redis 安装及配置 | Docker Compose 编排

7.1 运行 MongoDB 8.0

bash 复制代码
# 拉取官方镜像
$ docker pull mongo:8.0

# 启动 MongoDB 容器
$ docker run -d \
    --name mongodb \
    -p 27017:27017 \
    -v mongo-data:/data/db \
    -e MONGO_INITDB_ROOT_USERNAME=admin \
    -e MONGO_INITDB_ROOT_PASSWORD=admin123 \
    mongo:8.0

参数解释

  • -v mongo-data:/data/db:持久化存储数据卷
  • -e MONGO_INITDB_ROOT_USERNAME:初始化 root 用户
  • MongoDB 默认监听 27017 端口
bash 复制代码
# 验证连接
$ docker exec mongodb mongosh -u admin -p admin123 --eval "db.version()"
8.0.4

7.2 运行 Redis 7

bash 复制代码
$ docker pull redis:7-alpine

$ docker run -d \
    --name redis \
    -p 6379:6379 \
    -v redis-data:/data \
    redis:7-alpine redis-server --appendonly yes

参数解释

  • --appendonly yes:开启 AOF 持久化(数据写入磁盘)
  • alpine 版 Redis 仅 ~30MB(标准版 ~120MB)
bash 复制代码
# 验证
$ docker exec redis redis-cli ping
PONG

$ docker exec redis redis-cli set key1 "hello docker"
OK

$ docker exec redis redis-cli get key1
"hello docker"

7.3 一键编排(Docker Compose)

yaml 复制代码
# docker-compose.yml
version: "3.8"
services:
  mongodb:
    image: mongo:8.0
    container_name: mongodb
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: admin123

  redis:
    image: redis:7-alpine
    container_name: redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes

volumes:
  mongo-data:
  redis-data:
bash 复制代码
$ docker compose up -d
[+] Running 2/2
 ✔ Container mongodb  Started
 ✔ Container redis    Started

实验 8:Docker 运行 WordPress

知识点:WordPress 安装配置 | MySQL + WordPress 联动 | Docker Compose 多服务编排

8.1 架构

kotlin 复制代码
┌──────────────────────────────────────────┐
│             Docker Network: wp-net        │
│  ┌───────────┐       ┌────────────────┐  │
│  │ WordPress  │──────▶│    MySQL 8.0   │  │
│  │  (PHP)     │ 3306  │  (Database)    │  │
│  │  :8080     │       │  wp-db /data   │  │
│  └───────────┘       └────────────────┘  │
└──────────────────────────────────────────┘
         ▲
    http://IP:8080

8.2 Docker Compose 一键部署

yaml 复制代码
# wordpress-stack.yml
version: "3.8"
services:
  db:
    image: mysql:8.0
    container_name: wp-db
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass123
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass123
    networks:
      - wp-net

  wordpress:
    image: wordpress:latest
    container_name: wp-app
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass123
      WORDPRESS_DB_NAME: wordpress
    depends_on:
      - db
    networks:
      - wp-net

volumes:
  db-data:

networks:
  wp-net:
bash 复制代码
$ docker compose -f wordpress-stack.yml up -d
[+] Running 3/3
 ✔ Network app_wp-net     Created
 ✔ Container wp-db        Started
 ✔ Container wp-app       Started

踩坑depends_on 只控制启动顺序而非等待 MySQL 就绪。生产环境建议配合 healthcheck + condition: service_healthy


实验 9:搭建自己的 Docker Registry

知识点:Registry 部署配置 | 镜像 push/pull | 仓库管理

9.1 部署本地 Registry

bash 复制代码
$ docker run -d \
    --name registry \
    -p 5000:5000 \
    -v registry-data:/var/lib/registry \
    registry:2

9.2 推送/拉取镜像

bash 复制代码
# 为本地 Registry 打 tag
$ docker tag nginx:latest localhost:5000/my-nginx:v1

# 推送
$ docker push localhost:5000/my-nginx:v1

# 从另一台机器拉取(替换 localhost 为宿主机 IP)
$ docker pull 1.94.243.38:5000/my-nginx:v1

9.3 配置远程访问

踩坑 :Docker 默认不允许 HTTP 访问 Registry。需要在客户端 daemon.json 中添加 insecure-registries

json 复制代码
{
  "insecure-registries": ["1.94.243.38:5000"]
}

实验 10:Docker 安全管理

知识点:TLS 加密 daemon | privileged 模式 | capabilities 白名单 | Docker Bench Security

10.1 特权模式(谨慎使用)

bash 复制代码
# --privileged 赋予容器所有宿主机 capabilities
$ docker run --privileged -it alpine sh

安全原则 :尽可能不用 --privileged,改用 --cap-add 按需添加。

10.2 Capabilities 白名单

bash 复制代码
# 只添加 NET_ADMIN 权限(而非全量 privileged)
$ docker run --cap-add=NET_ADMIN -it alpine sh

# 移除所有默认权能,只保留需要的
$ docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx:latest

10.3 Docker Bench Security

bash 复制代码
$ docker run --rm \
    --net host --pid host --userns host \
    --cap-add audit_control \
    -v /etc:/etc:ro \
    -v /usr/bin/docker:/usr/bin/docker:ro \
    -v /var/lib:/var/lib:ro \
    docker/docker-bench-security

扫描结果会给出 Host Configuration、Docker Daemon、Container Runtime 等多维度安全评分。


实验 11:Docker Compose 项目

知识点:安装 Compose | 编写 Compose 文件 | 多服务编排

11.1 Compose 已内置

Docker 29.6.0 自带 docker compose(作为 Docker CLI 插件),无需额外安装:

bash 复制代码
$ docker compose version
Docker Compose version v2.38.0

注意docker compose(无连字符,Docker CLI 插件)替代了旧版 docker-compose(独立二进制)。

11.2 Compose 核心命令

bash 复制代码
docker compose up -d          # 后台启动所有服务
docker compose ps             # 查看服务状态
docker compose logs -f        # 跟踪日志
docker compose down           # 停止并删除
docker compose down -v        # 停止并删除卷(彻底清理)
docker compose restart web    # 重启单个服务
docker compose exec web bash  # 进入服务容器

11.3 完整示例:Flask + Redis 计数器

yaml 复制代码
# compose.yml
services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      REDIS_HOST: redis
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data

volumes:
  redis-data:

实验 12:Docker Swarm 集群

知识点:Swarm 初始化 | 节点加入 | 服务部署 | 滚动更新

12.1 初始化 Swarm

bash 复制代码
# 在 Manager 节点执行
$ docker swarm init --advertise-addr 192.168.0.155
Swarm initialized: current node (abc123) is now a manager.

To add a worker to this swarm, run:
  docker swarm join --token SWMTKN-1-xxxxx 192.168.0.155:2377

12.2 Worker 加入

bash 复制代码
# 在 02/03/04 节点执行
$ docker swarm join --token SWMTKN-1-xxxxx 192.168.0.155:2377
This node joined a swarm as a worker.

12.3 查看集群

bash 复制代码
$ docker node ls
ID           HOSTNAME       STATUS  AVAILABILITY  MANAGER STATUS
abc123 *     ecs-9d6d-0001  Ready   Active        Leader
def456       ecs-9d6d-0002  Ready   Active
ghi789       ecs-9d6d-0003  Ready   Active
jkl012       ecs-9d6d-0004  Ready   Active

12.4 部署服务

bash 复制代码
# 创建跨节点的 Nginx 服务(3 副本)
$ docker service create \
    --name web \
    --replicas 3 \
    --publish published=8080,target=80 \
    nginx:latest

# 查看服务分布
$ docker service ps web
ID     NAME    IMAGE          NODE           DESIRED STATE
1a2b   web.1   nginx:latest   ecs-9d6d-0001  Running
3c4d   web.2   nginx:latest   ecs-9d6d-0002  Running
5e6f   web.3   nginx:latest   ecs-9d6d-0003  Running

Swarm 内置负载均衡:无论访问哪个节点的 8080 端口,请求都会自动路由到任意健康的副本。

12.5 滚动更新

bash 复制代码
$ docker service update --image nginx:alpine web

Swarm 逐个替换副本,保证零停机。


实验 13:Docker API

知识点:API 认证 | 管理容器/镜像/网络/卷

13.1 开启 Docker Remote API

bash 复制代码
# 通过 systemd 覆盖配置开启 TCP 监听
$ mkdir -p /etc/systemd/system/docker.service.d
$ cat > /etc/systemd/system/docker.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
EOF

$ systemctl daemon-reload && systemctl restart docker

安全警告 :2375 端口不加 TLS 就是裸奔!仅限内网测试。生产环境必须配置 TLS。

13.2 API 调用示例

bash 复制代码
# 列出所有容器
$ curl -s http://localhost:2375/containers/json | python3 -m json.tool

# 查看容器详情
$ curl -s http://localhost:2375/containers/web-demo/json | python3 -m json.tool

# 创建容器
$ curl -s -X POST http://localhost:2375/containers/create \
    -H "Content-Type: application/json" \
    -d '{"Image":"nginx:latest","Cmd":[]}'

# 列出所有镜像
$ curl -s http://localhost:2375/images/json

# 列出网络
$ curl -s http://localhost:2375/networks

# 列出卷
$ curl -s http://localhost:2375/volumes

13.3 Python SDK 示例

python 复制代码
import docker

client = docker.from_env()

# 列出容器
for c in client.containers.list():
    print(f"{c.name}: {c.status}")

# 运行容器
container = client.containers.run(
    "nginx:latest", detach=True, ports={"80/tcp": 8080}
)

# 拉取镜像
client.images.pull("alpine:latest")

第三部分:总结与踩坑清单

13 个实验对照表

实验 内容 核心技能 级别
实验 1 Docker 基本用法 安装、hello-world ★☆☆
实验 2 容器管理 ps/inspect/top/diff/exec/logs ★★☆
实验 3 镜像管理 images/search/pull/tag/rmi ★★☆
实验 4 存储管理 volume/bind/tmpfs/备份恢复 ★★★
实验 5 网络管理 bridge/custom/host/none ★★★
实验 6 Dockerfile FROM/RUN/COPY/CMD/多阶段构建 ★★★
实验 7 MongoDB & Redis 数据卷持久化 + Compose ★★★
实验 8 WordPress 多服务编排 + MySQL 联动 ★★★
实验 9 Docker Registry 私有仓库 push/pull ★★★
实验 10 安全管理 privileged/cap-add/Bench ★★☆
实验 11 Docker Compose compose 文件/服务编排 ★★★
实验 12 Docker Swarm 集群/服务/滚动更新 ★★★★
实验 13 Docker API REST API / Python SDK ★★★★

核心踩坑记录

踩坑 现象 解决方案
Docker 官方源极慢 apt update 5KB/s,GPG key 超时 换阿里云源 mirrors.aliyun.com/docker-ce
镜像拉取被墙 Error response from daemon: pull access denied docker.1ms.run 加速器
GPG --dearmor TTY 报错 cannot open '/dev/tty' --batch --yes
apt lock 冲突 Could not get lock /var/lib/dpkg/lock-frontend kill 占用进程 + rm lock + dpkg --configure -a
Registry HTTP 被拒 http: server gave HTTP response to HTTPS client /etc/docker/daemon.jsoninsecure-registries
depends_on 不等于 ready MySQL 还没启动完 WordPress 就连接失败 healthcheck + condition: service_healthy
Host 网络端口冲突 --network host 后 nginx 直接占 80 端口 确保宿主机 80 空闲,或改用 bridge + 端口映射

本文所用集群 :华为云 FlexusX x2e.8u.16g × 4 节点,运行 Ubuntu 24.04 / Docker 29.6.0。 全部命令在真实服务器上验证通过 ,输出均为实际采集。 如有疑问欢迎在评论区交流,记得 点赞 + 收藏 支持!


文档版本:v1.0 | 实验日期:2026-06-23 | 作者:小森

相关推荐
Avan_菜菜18 小时前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB2 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode3 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220704 天前
如何搭建本地yum源(上)
运维
大树887 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠7 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质7 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工7 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智7 天前
ARP代理--工作原理
运维·网络·arp·arp代理