一、问题引入
在我们日常学习或开发过程中,如果我们的服务均采用docker容器的方式运行,比如提供后端接口服务的容器containerA和提供数据存取服务的容器containerB,如下图所示,不同的docker 容器拥有各自的ip地址和端口号。

1. 多容器位于同一主机
本文假定一台主机只有一个docker进程。在这种情况下,容器AcontainerA和容器BcontainerB被同一个docker进程管理,容器之间的通信就可以通过目标容器ip+port进行直接访问,如下图所示

当容器A containerA需要和容器B containerB进行通信时,只需要在容器AcontainerA中通过172.18.0.2:3306即可完成通信。这种通信方式为同主机容器到容器的通信。
这种方式有个较明显的缺点就是将容器限制在一台主机了。如果要求将容器分布在不同的主机,那这个方法就行不通了。
2. 多容器位于不同主机
但是在企业级项目部署中往往存在多个主机协同提供服务的情况。例如,我们的后端接口服务容器containerA位于主机AhostA中,数据存储 容器containerB位于主机BhostB中,在这种情况下,我们为了方便省事,往往会将数据存储容器containerB的端口号(比如mysql的3306)映射到主机BhostB的3306端口,当位于主机AhostA的后端接口服务容器containerA需要对数据进行操作时,通过主机A的ip:port对其数据存储容器containerB进行访问。如下图所示

在该示意图中,容器A要想与容器B通信,需要容器B将端口映射到宿主机hostB中,当连接主机B的3306端口时,根据映射规则,将访问请求转发到容器B中。这种通信方式为跨主机容器到主机再到容器的通信。
这种方式虽然做到了将容器分布在不同的主机,但是仍暴露出一个缺点,目标容器的端口不得不挂载到宿主机对应端口,且目标容器宿主机的端口也不得不对外开放,万一该端口被不法分子扫描到进行攻击,那就GG了。
针对以上两个方式所暴露出的缺点,当我们既希望将容器分布在不同的主机,又希望不暴露宿主机端口而是直接通过容器的ip:port进行通信,docker官方推荐我们考虑一下集群环境。
二、介绍
Swam是Docker引擎内置(原生)的集群管理和编排工具。Docker Swarm是Docker官方三剑客项目之一,提供 Docker容器集群服务,是Docker官方对容器云生态进行支持的核心方案。使用它,用户可以将多个Docker主机封装为单个大型的虚拟Docker主机,快速打造一套容器云平台。Swarm mode内置kv存储功能,提供了众多的新特性,比如:具有容错能力的去中心化设计、内置服务发现、负载均衡、路由网格、动态伸缩、滚动更新、安全传输等。使得Docker原生的 Swam集群具备与Mesos ,Kubernetes竞争的实力。
一个集群由多个Docker主机组成,这些主机以集群模式运行,充当管理者(管理成员资格和委托)和工作者(运行群服务)。一个给定的Docker主机可以是一个管理者manager、一个工作者worker,或者同时扮演这两个角色。创建服务时,您需要定义其最佳状态(副本数量、可用的网络和存储资源、服务对外开放的端口等)。Docker致力于保持这种理想状态。例如,如果一个工作节点变得不可用,Docker会将该节点的任务调度到其他节点上。任务是一个运行的容器,它是群服务的一部分,由群管理器管理,而不是一个独立的容器
与独立容器相比,swarm服务的一个关键优势是,您可以修改服务的配置,包括它所连接的网络和卷,而无需手动重启服务 。Docker将更新配置,停止使用过期配置的服务任务,并创建与所需配置匹配的新任务。当Docker在swarm模式下运行时,您仍然可以在任何参与集群的Docker主机上运行独立的容器和swarm服务。独立容器和集群服务之间的一个关键区别是只有集群管理器可以管理集群,而独立容器可以在任何守护进程上启动。Docker守护进程可以作为管理人员、工作人员或两者参与到一个群体中。
三、特性
docker集群模式swarm mode为我们提供了非常丰富的特性来满足不同的场景需求。
- 与Docker引擎集成的集群管理 使用Docker Engine CLI创建一组Docker引擎,可以在其中部署应用程序服务。不需要额外的编排软件来创建或管理一个群体。这不就是打算和k8s杠一杠呢?
- 去中心化设计 Docker引擎在运行时处理任何专门化,而不是在部署时处理节点角色之间的差异。我们可以使用Docker引擎部署两种类型的节点,管理器和工作器。这意味着我们可以从单个磁盘映像构建整个swarm。
- 声明式服务模型 Docker引擎使用一种声明性的方法,让我们自定义应用程序堆栈中各种服务的期望状态。例如,我们可能会描述一个由带有消息队列服务的web前端服务和数据库后端组成的应用程序。
- 动态伸缩 对于每个服务,我们可以声明想要运行的任务数量。当扩大或缩小规模时,swarm manager会通过添加或删除任务来自动适应,以保持所需的状态。swarm manager节点持续监控集群状态,并协调实际状态和期望状态之间的任何差异。例如,如果我们设置一个服务来运行一个容器的10个副本,并且托管其中两个副本的工作机崩溃,管理器将创建两个新副本来替换崩溃的副本。群组管理器将新的副本分配给正在运行且可用的worker。
- 多主机网络 我们可以为服务指定叠加网络。当初始化或更新应用程序时,群管理器自动分配地址给覆盖网络上的容器。
- 服务发现 集群管理器节点为集群中的每个服务分配一个唯一的DNS名称和负载平衡运行容器。可以通过嵌入在swarm中的DNS服务器查询swarm中运行的每一个容器。
- 负载均衡 可以向外部负载平衡器公开服务端口。在内部,swarm允许我们指定如何在节点之间分发服务容器。
- 安全传输 群中的每个节点强制执行TLS相互认证和加密,以确保自身和所有其他节点之间的通信安全。我们可以选择使用自签名根证书或来自自定义根CA的证书。
- 滚动更新 在部署时,我们可以增量地将服务更新应用到节点。swarm manager允许您控制不同节点集的服务部署之间的延迟。如果出现任何问题,可以回滚到服务的前一个版本。
四、概念
在搭建docker集群之前,我们需要了解几个概念:节点(node) 、服务(service) 、任务(task) 、负载均衡(loadbalance)。
1. 节点nodes
每个节点node 都是docker集群中的每一个docker实例,当然也可以理解为运行docker进程的物理机 。在集群中的所有节点中,又分为管理节点 和工作节点。
- 管理节点 管理节点用于对docker集群的管理,对集群的操作命令都需要在管理节点中执行(例外:工作节点退出集群时需要在工作节点中执行退出命令),一个docker集群可以具有多个管理节点,在众多管理节点中通过raft协议选举出唯一一个leader节点。
- 工作节点 工作节点接收并执行来自管理节点分发的任务(task),默认地,管理节点也可以同时具有工作节点的功能。
2. 服务(service)和任务(task)
服务是对任务的定义,而任务执行于管理节点或工作节点。服务是docker集群的中心结构,也是用户与swarm交互的主要介质
-
服务 docker集群中的服务类似于微服务系统中的服务 ,一个服务是一个或多个实例的集合。而在docker 集群中,一个服务我们可以理解为多个提供相同服务的docker容器的集合。在创建一个服务时,我们可以指定该服务使用的docker镜像,以及该服务能够创建的docker实例的副本数量。如在一个docker集群中,主机A中有两个容器:mysqlA和redisA,主机B中有两个容器:mysqlB和redisB,那么主机A中的mysqlA和主机B中的mysqlB属于同一个服务,主机A中的redisA和主机B中的redisB属于同一个服务,并且由管理节点实现对服务的管理(如同微服务系统中的注册中心),如下图所示

-
任务 任务就是当我们需要调用集群中某一个服务时,管理节点从其管理的服务集合中找到该服务所对应的docker容器实例,然后向该实例指派一个任务,使其提供相应的服务。一旦任务被分配到某一个节点,则不允许再对其进行修改。
3. 负载均衡
当我们需要调用某一个服务时,管理节点自动对该服务所对应的节点进行负载均衡调用。例如微服务系统中服务注册中心的负载均衡机制。
五、docker网络
介绍完docker集群的概念,我们需要考虑一下在docker集群中,由于是多主机架构,那么什么样的网络驱动能使跨主机的容器相互通信呢?docker给我们提供的网络驱动有:bridge、host、overlay、none、ipvlan、macvlan。
-
bridge默认的网络驱动。适用于同一主机中docker容器之间的通信。
-
host移除docker容器与宿主机之间的网络隔离,直接使用宿主机的网络。
-
overlay 该网络驱动允许多个主机之间的docker容器相互通信。
-
none 脱机独立运行docker容器,无法与其他容器以及主机之间通信。
-
ipvlan 把对ipv4和ipv6的ip地址的控制权完全交给我们。
-
macvlan 允许我们对docker容器分配物理地址,使docker容器具有物理机表现。
在docker集群中我们使用overlay网络实现多主机内容器之间的相互通信。overlay网络驱动在多个Docker主机之间创建分布式网络。这个网络位于(覆盖)特定于主机的网络之上,允许连接到它的容器(包括集群服务容器)在启用加密(TLS)时安全地通信。Docker透明地处理往返于正确的Docker守护进程主机和正确的目的地容器的每个分组的路由。
六、docker集群搭建
1. 环境介绍
- 两台linux虚拟机
|---------|--------------|---------------|----------|
| 操作系统 | 主机名 | 主机ip | docker版本 |
| CentOS7 | host10(管理节点) | 192.168.10.10 | v24.0.7 |
| CentOS7 | host20(工作节点) | 192.168.10.20 | v24.0.7 |
| CentOS7 | host20(工作节点) | 192.168.10.30 | v24.0.7 |
docker版本必须大于v1.12.0,docker的集群模式是从该版本开始的,该版本发行于2016.07.28。我们直接安装最新版,还能避免许多bug。
- 端口开放(或关闭防火墙)
2377/tcp:用于集群管理通信。
7946/tcp、7946/udp:集群中各节点之间的通信。
4789/udp: overlay网络使用。
开放端口的命令如下
bash
# --permanent表示永久生效
[root@host10 ~] firewall-cmd --zone=public --add-port=2377/tcp --permanent
success
[root@host10 ~] firewall-cmd --zone=public --add-port=7946/tcp --permanent
success
[root@host10 ~] firewall-cmd --zone=public --add-port=7946/udp --permanent
success
[root@host10 ~] firewall-cmd --zone=public --add-port=4789/udp --permanent
success
# 重新加载防火墙配置
[root@host10 ~] firewall-cmd --reload
success
查看端口是否开放成功
bash
# 查看已开放的端口列表
[root@host10 ~] firewall-cmd --zone=public --list-ports
2377/tcp 7946/tcp 7946/udp 4789/udp
或者直接关闭防火墙
bash
# 关闭防火墙
[root@host10 ~] systemctl stop firewalld
# 开机禁用防火墙
[root@host10 ~] systemctl disable firewalld
2. 创建集群
在管理节点host10创建集群,使用以下命令
bash
docker swarm init --advertise-addr 192.168.10.10
# 输出
Swarm initialized: current node (0cibl244fqcmh7k6x8a3kwkfp) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
--advertise-addr用于配置一个ip地址,集群中其他节点使用此ip地址与管理节点实现通信。
创建集群的节点默认为管理节点。
另外,在输出中包含以下命令,用于其他工作节点加入该集群使用
bash
docker swarm join --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
如果需要加入管理节点,则使用下面命令
bash
docker swarm join-token manager --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
注意:该token有效期为24小时。如果该token 已过期,则可以使用下面命令重新获取
bash
[root@host10 ~] docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
使用docker info查看当前集群的详细信息
使用docker node ls查看当前集群中的各个节点信息
bash
[root@host10 ~] docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
0cibl244fqcmh7k6x8a3kwkfp * host10 Ready Active Leader 24.0.7
其中,*表示当前位于此节点中。
3. 集群网络
当我们在管理节点使用docker swarm init初始化一个docker集群时,docker默认会创建两个网络:
-
overlay驱动的网络,名称为ingress,该网络用于处理集群中各个服务之间的控制与数据传输。当我们创建服务service时,如果没有指定自定义的overlay网络,则默认使用ingress网络。
-
bridge驱动的网络,名称为docker_gwbridge,该网络用于维持集群中各个节点之间的连接。
bash
[root@host10 docker-scripts]# docker network ls
NETWORK ID NAME DRIVER SCOPE
2d3e81dc418a bridge bridge local
5675307b8b8d docker_gwbridge bridge local
496c4eb3bb51 host host local
j4lsm97dkovz ingress overlay swarm
69df9100d962 myBridge bridge local
e72b30a2ea15 none null local
4. 加入工作节点
bash
docker swarm join --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
我们把host20和host30作为工作节点,加入到该集群。复制上面的命令,分别到到host20和host30两台主机中中执行
添加成功
bash
[root@host20 ~] docker swarm join --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
This node joined a swarm as a worker.
[root@host30 ~] docker swarm join --token SWMTKN-1-0glmg3n472i4bbpxrynjw0z0uoa7lnxagcfcgnwg40dd3mu4ga-eazqx6idqvn954nfpumn2vob0 192.168.10.10:2377
This node joined a swarm as a worker.
4. 查看的节点详情
bash
docker node inspect <HOSTNAME>
5.退出集群
- 工作节点退出集群 在目标服务器(worker1、worker2)中执行下列命令:
bash
docker swarm leave
- 管理节点退出集群(需谨慎)管理节点退出可能影响集群选举(Raft协议),需额外处理
降级为工作节点再退出(推荐)
bash
#先降级为工作节点
[root@manager1 /]# docker node demote <节点ID>
#再执行退出
[root@manager1 /]# docker swarm leave
强制退出(风险较高)
若无法降级(如单管理节点集群),使用强制命令
bash
docker swarm leave --force
在管理节点中对刚才退出节点进行删除
bash
docker node rm 节点名称|节点ID
七、Docker Compose
1.Docker Compose 文件结构
Docker Compose 文件通常包含以下关键部分:
version: 指定使用的 Compose 文件格式版本。
services: 定义一组服务,每个服务都对应一个或多个容器。服务名称: 自定义的服务标识。
image: 指定容器使用的镜像。
container_name: 可选,为容器指定一个名称。
environment: 定义环境变量。
ports: 端口映射,将主机的端口映射到容器的端口。
networks: 指定容器使用的网络。
volumes: 定义卷,用于数据持久化或共享数据。
depends_on: 定义服务之间的依赖关系。其他配置: 如构建参数、日志配置等。
2.Docker Compose的使用
-
安装 Docker Compose: 可以通过官方文档提供的步骤来安装 Docker Compose。
-
编写
docker-compose.yml文件: 根据应用程序的需求,编写 YAML 格式的配置文件 -
启动服务: 使用
docker-compose up命令启动所有服务。该命令会创建网络、卷、服务容器,并根据依赖关系按顺序启动它们。 -
停止服务: 使用
docker-compose down命令停止并移除所有容器、网络和卷(除非在配置文件中指定了volumes的外部存储) -
其他常用命令:
docker-compose start: 启动已停止的服务。
docker-compose stop: 停止正在运行的服务。
docker-compose restart: 重启服务。
docker-compose logs: 查看服务日志。
docker-compose ps: 列出当前正在运行的服务容器。
**3.**Docker Compose部署安装指南
下载 Docker Compose
从 GitHub Docker Compose Releases 页面下载对应版本的 Docker Compose 二进制文件。
bash
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
安装 Docker Compose
给予执行权限并查看版本。
bash
chmod +x /usr/local/bin/docker-compose
docker-compose --version
4.YAML 文件格式 及编写注意事项
- YAML 基础:
大小写敏感。
使用空格缩进表示层级关系,不支持制表符(Tab)。
缩进空格数目相同层级对齐,通常开头缩进2个空格。
使用
#号注释。符号字符后缩进1个空格,如冒号
:、逗号,、横杠-。单引号
''引起来作为普通字符串处理,双引号""内的特殊字符作为本身含
5.Docker Compose 配置常用字段
-
build:指定 Dockerfile 路径或构建上下文。
-
dockerfile:在 build 下指定 Dockerfile 名称。
-
context:构建镜像的上下文路径或 Git 仓库 URL。
-
image:指定镜像名称。
-
command:覆盖容器启动后的默认命令。
-
container_name:指定容器名称(唯一性限制)。
-
environment:添加环境变量。
-
networks:加入网络。
-
network_mode:设置容器的网络模式。
-
ports:暴露容器端口。
-
volumes:挂载宿主机目录或命名卷。
-
volumes_from:从另一个服务或容器挂载卷(版本 '2' 支持)。
-
hostname:容器主机名。
-
sysctls:在容器内设置内核参数。
-
links:连接到另一个容器(不推荐使用)。
-
privileged:给予容器 root 权限(不安全)。
-
restart:设置重启策略。
-
depends_on:解决服务依赖和启动顺序问题。
