第二部分:Docker 进阶篇(功能拓展)

📝 前言

容器技术已经逐渐成为现代应用部署的主流方式,尤其在微服务、DevOps 和云原生架构中扮演核心角色。

回顾上一篇《第二部分:Docker 基础篇(构建你的第一个容器)》内容,我们在掌握了 Docker 的基本概念和操作后,虽然可以完成日常任务,但实际生产环境往往涉及更多高级功能。

例如:如何管理容器产生的数据,如何让容器之间通信,如何限制资源防止滥用等。

因此我们需要进一步探索其高级功能,以充分发挥 Docker 的潜力。

本篇文章将深入介绍 Docker 的进阶功能,包括存储、网络、监控、日志管理、资源限制以及远程 API 的使用,帮助读者在实际项目中更加灵活高效地使用 Docker。

📋 前提条件

在阅读本文之前,建议读者已经具备以下知识和环境:

  • 熟悉 Docker 的基本概念和架构
  • 了解 Docker 镜像和容器的基本操作
  • 掌握 Dockerfile 的基本编写方法
  • 熟悉 Linux 基本命令和操作系统概念
  • 已安装 Docker(可参看文章:《Docker 与 Docker Compose 安装部署》

🧠 本章知识卡片

🚀 文章小节

1️⃣ Docker 存储

Docker 容器默认是无状态的,无状态又可以理解为无长期记忆(类似金鱼的7秒钟记忆),容器的记忆只在它的生命周期范围内有效(从创建到销毁),一旦容器销毁后,其中产生的数据也会一并跟着被清理。

这在微服务架构中非常有用,可以快速批量的进行横向扩容。

但是如果容器跑的是有状态应用,比如 MySQL 数据库这种呢,应该没有人期望数据库会被随时清空,因此就诞生 Docker 存储。

通过 Docker 存储,我们可以将容器内的数据进行持久化,不会随着其生命周期的结束而被清理。

Docker 提供了三种主要的存储方式:

  • Volumes(数据卷):数据卷是 Docker 推荐的持久化数据存储方式,生命周期独立于容器,适合生产环境。
  • Bind Mounts(绑定挂载):可以将宿主机目录映射到容器中,适合开发调试。
  • tmpfs:只存在于内存中,容器退出即消失,适合存储临时文件。

Volumes(数据卷)

创建和使用数据卷的基本命令如下:

1
2
3
4
5
6
7
8
# 创建数据卷
docker volume create my_volume

# 查看所有数据卷
docker volume ls

# 使用数据卷
docker run -d -v my_volume:/container/path my_image

数据卷默认的存储位置是在本机的该目录下面:

1
/var/lib/docker/volumes/

通过 tree 命令可以查看其存储目录结构:

/var/lib/docker/volumes/
├── _metadata # 卷元数据数据库
├── my_volume # 数据卷名称
│ └── _data # 实际数据目录
│ ├── file1
│ └── subdir
└── a1b2c3… # 匿名卷(自动生成的哈希ID)
└── _data

Bind Mounts(绑定挂载)

绑定挂载通常用于开发、测试环境或需要直接访问主机文件系统的场景,其使用方式如下:

1
docker run -d -v /host/path:/container/path my_image

这其实也是一种比较常用的方式,但是可能会涉及到文件权限控制问题。

tmpfs 挂载

对于只需要临时存储在内存中的数据,可以使用 tmpfs 挂载,这个一般使用的不多:

1
docker run -d --tmpfs /container/path my_image

2️⃣ Docker 网络

Docker 容器跑起来,当有多个容器在跑时,容器与容器之间是如何通信的呢?我们在外部又是与容器如何进行通信呢?

这里就涉及到 Docker 网络知识。

Docker 常用的网络模式为以下几种:

  • bridge:默认网络模式,为每个容器分配独立的网络命名空间
  • host:容器直接使用主机的网络栈
  • none:禁用网络功能,无法在外部访问到容器

为了解决上面提到的问题,我们可以通过 docker network create 创建自定义网络,实现容器与容器之间的互相通信。

示例命令:

1
2
3
4
5
6
7
8
9
# 创建自定义网络
docker network create my-net
# 不同容器使用相同的网络
docker run -d --network my-net --name service1 my_image
docker run -d --network my-net --name service2 my_image

# 此时我们可以在容器内通过容器名称,互相访问另一个容器
# 进入 service1 容器测试连接 service2 容器
docker exec -it service1 curl http://service2

当我们需要在容器外部访问容器时,有两种策略:

  • 使用 host 网络模式(–network=host):容器内直接复用主机的网络,包括端口占用。

示例命令:

1
docker run --network=host nginx
  • 使用 端口映射参数(-p/–publish):网络模式还是使用默认的 bridge,但是可以将某个指定端口映射到容器内。

示例命令:

1
2
# 将主机的 8080 端口映射到容器内的 80 端口
docker run -p 8080:80 nginx

3️⃣ Docker 监控

监控 Docker 容器的资源使用对于维护系统稳定性至关重要,我们需要了解哪些容器占用了多少资源,进而有针对性的进行优化。

Docker 提供了内置的 stats 命令可以用于查看容器的资源使用情况,示例如下:

1
2
3
4
5
6
7
8
9
10
# 查看所有容器资源使用情况
docker stats
# 打印示例如下
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
8db61e0305b6 notion-api 0.27% 39.35MiB / 15.52GiB 0.25% 2.71kB / 126B 8.03MB / 844kB 1
9aaa8b2229be friends-frontend 0.00% 7.395MiB / 15.52GiB 0.05% 4.81kB / 126B 0B / 4.1kB 9
0d2b2dde9549 moments 0.00% 7.395MiB / 15.52GiB 0.05% 4.3kB / 126B 0B / 4.1kB 9

# 查看指定容器资源使用情况
docker stats [容器ID或容器名称]

通常一般在生产环境需要结合 cAdvisor + Prometheus + Grafana 使用,这个在后续章节会介绍到。

4️⃣ Docker 日志

有效的日志管理是排查问题的重要依据,Docker 默认的日志驱动是 json-file,可通过 docker logs 查看容器输出:

1
2
3
4
5
# 查看容器当前所有日志
docker logs [容器ID或容器名称]

# 实时跟踪容器日志
docker logs -f [容器ID或容器名称]

默认 Docker 日志配置如下:

配置项 默认值 说明
日志文件最大大小 -1(无限制) 但实际受宿主机磁盘空间限制
单个日志文件上限 20MB 超过此大小会自动滚动创建新文件
保留的日志文件数 5 即最多保留 5个 日志文件(如 container-id-json.log.1 ~ .5
日志格式 JSON 每行日志记录为 JSON 格式

我们也可以通过修改配置,达到自定义日志限制的目的,用于节省磁盘空间。

修改 vim /etc/docker/daemon.json(若不存在则创建):

1
2
3
4
5
6
7
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m", # 单个日志文件最大10MB
"max-file": "3" # 最多保留3个日志文件
}
}

保存后,重启 Docker 生效:

1
sudo systemctl restart docker

5️⃣ Docker 资源限制

Docker 容器默认是共享主机的所有资源,但是我们的业务通常是有区分核心和边缘服务,合理限制容器资源可以防止边缘容器占用过多资源影响其他核心服务。

CPU 限制

1
2
3
4
5
6
# 限制容器最多使用 0.5 核的 CPU 资源
# 官方镜像无法下载可使用该镜像:registry.cn-hangzhou.aliyuncs.com/lusyoe/ubuntu
docker run -it --cpus="0.5" ubuntu

# 绑定到特定 CPU 核心
docker run -it --cpuset-cpus="0,1" ubuntu

内存限制

1
2
3
4
5
# 限制内存使用为 100MB
docker run -it -m 100m ubuntu

# 设置内存和交换分区限制
docker run -it -m 100m --memory-swap=200m ubuntu

也可以两者结合使用:

1
docker run --memory="512m" --cpus="1.5" ubuntu

6️⃣ Docker 远程 API

Docker 提供 REST 风格的 Remote API,可以远程控制容器生命周期。

这在管理多台主机容器的场景中非常有用。

默认 Docker 只在本地开启了 UNIX Socket,如果需要远程访问,可以修改配置绑定 TCP。

修改 docker 服务:vim /usr/lib/systemd/system/docker.service

1
2
3
# 找到 ExecStart,最后面加上 -H tcp://0.0.0.0:2375 --tls=false
# Docker 官方建议是使用 tls 暴露该端口,这里为了演示方便禁用了 tls
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 --tls=false

重启服务生效:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

调用 API 接口:

1
2
3
4
5
6
7
# 列出所有容器
curl http://[主机IP]:2375/containers/json

# 创建容器
curl -X POST -H "Content-Type: application/json" \
-d '{"Image":"ubuntu","Cmd":["echo","hello"]}' \
http://[主机IP]:2375/containers/create

✅ 总结

最后总结一下,本文深入探讨了 Docker 的进阶功能,包括:

  • 多种存储方案的选择和配置
  • 网络模式的理解和自定义网络创建
  • 容器监控方法和命令
  • 容器日志查看和配置
  • 资源限制的重要性和实现方法
  • 远程 API 的配置和使用

掌握这些进阶功能将帮助您在生产环境中更高效、更安全地使用 Docker,构建稳定可靠的容器化应用。