网络是云原生的灵魂,这篇文章将带你深入理解 Docker 在多机中是如何通信的
目录
引言
一、Docker overlay 网络实现原理
(一) Docker overlay 网络内部架构
(二) Docker overlay 网络传输过程
二、Docker Swarm 介绍
(一) 架构图
(二) 核心概念
Node
Service
Task
Node、Service 及 Task 关系图
三、Docker Swarm 实战
(一) 环境准备
(二) 搭建 Swarm 集群
(三) 实战案例
不使用 Docker Stack
使用 Docker Stack
参考文档
上一篇介绍了 Docker 单机通信原理:链接
为了保证高可用,更常用的是将不同容器部署在多台机器上。这篇文章继续介绍 Docker 的多机通信原理,以及进行 Docker Swarm 实战
Docker 多机通信有多种实现方式,如直接路由方式、桥接方式、Overlay 隧道方式等。当前版本 Docker(20.10.21) 提供了多种解决方案,如 MacVlan 网络、overlay 网络。overlay 网络实际上是目前最主流的容器跨节点数据传输和路由方案
从 1.12 版本开始,Docker 官方提供了 Docker Swarm 容器编排工具,并内置在 Docker Engine 中。Docker Swarm 底层使用了Docker overlay 网络,因此这篇文章主要介绍 overlay 网络的实现原理
Docker overlay 网络是基于 VXLAN 实现的分布式网络,可用于 Docker 的多机通信
VXLAN 是在底层物理网络(Underlay)之上使用隧道技术,借助 UDP 层构建的 Overlay 逻辑网络,它对原有的网络架构几乎没有影响,不需要对原网络做任何改动,就可以架设一层新的网络
Linux 内核在 3.7.0 版本时,就已经加入了对 VXLAN 的支持,Docker 正是利用了 Linux 内核中的原生 VXLAN 特性实现 overlay 网络的
两台机器之间通过物理网络可以互相通信,如下图所示
在此基础上,使用 VXLAN 隧道技术创建虚拟二层覆盖网络,VXLAN 隧道两端都是 VXLAN 隧道终端(VXLAN Tunnel Endpoint, VTEP)。VTEP 的主要功能是 VXLAN 报文的封装和解封装
通常情况下,需要在宿主机中为 Overlay 网络创建一个默认的网桥,用于和物理网络通信。在 Docker Swarm 初始化后,引擎会自动创建 bridge 类型的 docker_gwbridge,且每台宿主机最多只能有一个 docker_gwbridge,如下图所示
创建 Overlay 网络时,Docker 引擎为每台主机创建所需的网络基础资源。每创建一个 Overlay,就会有一个 Linux 网桥关联到一个 VXLAN 接口上,VTEP 的位置和状态会被保存在分布式 Overlay 控制层中。但只有当容器被连接到 Overlay 网络时,Docker 引擎才会在主机上实例化 Overlay 网络,这可以预防连接一些不存的容器时,创建无效的网络实例
关联容器并实例化网络后,Docker overlay 驱动为集群内的每个容器分配唯一 IP 地址,如 eth1: 10.0.0.3 和 eth1: 10.0.0.6
通信过程中,Docker overlay 驱动将容器间通信所需的信息封装到 VXLAN Header 中,包括源容器 IP 及目标容器 IP
如图所示,假设 2 台 CentOS 主机上的 2 个容器都已经连接到 ov-net 网络中,CentOS-A 上的 web-app 容器想要发送一条 Redis 命令到 CentOS-B 上的 redis-demo 中的 Redis 服务,查询网站当前的访问次数
传输过程如下
Docker Swarm 是 Docker 官方提供的容器编排工具,可以很方便地在多个服务器或主机上搭建集群服务。Docker Swarm 底层使用了上文提到的 Docker overlay 网络,集群管理和容器编排特性来自于 Docker 的另一个独立项目 swarmkit
一个 swarm 集群包含一个或多个管理节点(Manager),以及若干个工作节点(Worker),组成的集群架构图如下
Node 是 Swarm 集群中 Docker 引擎的实例,在一台物理机或云服务器上可以部署一个或多个 Node。Node 按角色可以分为管理节点和工作节点
管理节点的作用如下
工作节点上运行着一个代理,接收并执行管理节点分派的任务,并向管理节点报告任务执行情况
Service 是对任务(Task)的定义,是 Swarm 系统的核心结构,创建服务时需要指定容器镜像,以及需要在容器中执行的命令
服务可以分为副本服务和全局服务。对于副本服务,管理节点根据设置的副本数调度对应的任务到工作节点;对于全局服务,每个节点有且只有一个任务
Task 是 Swarm 的最小调度单元,包含一个容器,以及需要在容器中执行的命令
需要注意的是,一个 Task 的生命周期是单向的。比如,状态可以从 NEW 到 RUNNING,再到 COMPLETE,但不能反向。也就是说,一旦任务结束了,需要再次执行时,会创建新的任务并执行
Service 相当于绘制一张施工蓝图,Swarm 则根据蓝图的细节,分配 Task 到不同的 Node 上干活
可以参考下图进行理解
基于以上的介绍,接下来进行 Docker Swarm 实战
首先准备 3 台云服务器,IP 地址及计划节点角色如下,准备搭建 1 主 2 从的 Swarm 集群
121.37.169.103 # Manager节点,设置hostname为manager
1.116.156.102 # Worker节点,设置hostname为worker-01
139.196.219.92 # Worker节点,设置hostname为worker-02
设置 3 台云服务器的安全组,入方向规则添加 2377, 6379, 8080, 80 端口
在管理节点主机执行如下命令
# 语法 docker swarm init --advertise-addr
docker swarm init --advertise-addr 121.37.169.103
在工作节点对应的 2 台主机执行如下命令,这里的 Token 是在初始化管理节点时生成的,需要保持一致
docker swarm join --token SWMTKN-1-3w41cnhrqfu8tddsmfwzr05wgt7yupyfmwkavck6cdftfv47r9-19tgpf75tb3hqtaob5wz8dmtu 121.37.169.103:2377
# 打印的日志如下
This node joined a swarm as a worker.
进入管理节点,查看集群状态(该命令只能在管理节点执行)
docker node ls
输出信息如下
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
622h0w3knqdrvifyvb2thkkr6 * manager Ready Active Leader 20.10.21
gztfkjkrzrqljrhqtnpgq4b31 worker-01 Ready Active 20.10.21
k3r6xodo2cb5tdzks7h48zabm worker-02 Ready Active 20.10.21
此时查看 Docker 网络,发现 3 台主机都有 docker_gwbridge 及 ingress,其中 3 台主机的 ingress 网络 ID 均为 pupcl8y1k6kk,这也验证了 Docker Swarm 底层使用了 Docker overlay 网络进行多机通信
docker network ls
如图所示,准备一个 SpringBoot 项目,使用 Redis 缓存应用的访问次数,计划为应用创建 3 个实例,并用 nginx 做负载均衡
SpringBoot 项目 docker-demo,对外提供了 2 个 Rest API,分别是
host:8080/hello - 增加访问次数
host:8080/visitCount - 返回当前总访问次数
项目地址:[email protected]:jason315/docker-demo.git
Step 1 - 创建自定义 overlay 网络
进入管理节点,执行以下命令
docker network create -d overlay ov-net
Step 2 - 创建 Redis 服务
进入管理节点,执行以下命令
docker service create --network ov-net --name redis-demo -p 6379:6379 redis
# 上一条命令执行成功后,查看 Redis 运行在哪个节点上
docker service ps redis-demo
发现 Redis 运行在了 worker-02 节点上,对应的 IP 地址为 1.116.156.102
修改 docker-demo 项目的 application.yml 文件
server:
port: 8080
spring:
application:
name: docker-demo
redis:
host: 139.196.219.92
port: 6379
Step 3 - 将 docker-demo 项目打成镜像
# 进入 docker-demo 项目根目录
mvn clean package
在 target 目录下会生成 jar 包:docker-demo-0.0.1-SNAPSHOT.jar
将 jar 包上传到 3 台主机的一个目录下,假设为 /var/local/dockerdemo
分别进入 /var/local/dockerdemo 目录,创建 Dockerfile
FROM openjdk:8
MAINTAINER jason315
LABEL name="docker-demo" version="1.0" author="jason315"
COPY docker-demo-0.0.1-SNAPSHOT.jar docker-demo-image.jar
CMD ["java", "-jar", "docker-demo-image.jar"]
在 3 台主机上都将 jar 包构建成 Docker 镜像
docker build -t docker-demo:1.0 .
Step 4 - 创建 docker-demo 服务
docker service create --name docker-demo \
--network ov-net \
--replicas 3 \
docker-demo:1.0
Step 5 - 创建 nginx 服务
在 3 台主机的 /var/local/dockerdemo 目录下都创建 nginx.conf 文件
user nginx;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
location / {
proxy_pass http://balance;
}
}
upstream balance {
server docker-demo:8080;
}
include /etc/nginx/conf.d/*.conf;
}
这里 upstream 中的服务名设置的是 docker-demo,这是在第 5 步创建的,由于是在同一个 overlay 网络中,可以进行自动域名解析
Step 6 - 创建 nginx 服务
docker service create --name nginx-demo \
--mount type=bind,src=/var/local/dockerdemo/nginx.conf,dst=/etc/nginx/nginx.conf \
--network ov-net \
-p 80:80 \
nginx
查看 nginx 服务在哪个节点上,发现是在管理节点上,对应的 IP 地址为 121.37.169.103
Step 7 - 测试
通过 /hello 接口访问一定次数后,调用 /visitCount 接口查看总访问次数,测试通过,然后可以停止服务了,Redis 服务先保留,后面还会再用到
# 在管理节点执行如下命令
docker service rm nginx-demo
docker service rm docker-demo
Docker 提供了类似于 Docker Compose 的机制,可以通过一个配置文件来描述集群需要创建哪些服务,这就需要用到 Docker Stack 了
同样地,在管理节点的 /var/local/dockerdemo 目录下创建 service.yml 文件
version: '3'
services:
docker-demo:
image: docker-demo:1.0
networks:
- ov-net2
deploy:
mode: replicated
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
nginx-demo:
image: nginx
ports:
- 80:80
networks:
- ov-net2
volumes:
- /var/local/dockerdemo/nginx.conf:/etc/nginx/nginx.conf
deploy:
mode: replicated
replicas: 1
networks:
ov-net2:
driver: overlay
用 Stack 部署服务时,需要指定名称(假设为 stack-demo)
最终创建出来的服务会加上 Stack 名称作为前缀,如 stack-demo_docker-demo,因此需要适当修改 nginx.conf 的 upstram 部分
upstream balance {
server stack-demo_docker-demo:8080;
}
接下来,根据 service.yml 创建服务
docker stack deploy -c service.yml stack-demo
创建完成后仍然可以用 docker service 相关命令查看
# 查看服务列表
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
rgk39ijvtvsq redis-demo replicated 1/1 redis:latest *:6379->6379/tcp
lw737pf0uuv5 stack-demo_docker-demo replicated 3/3 docker-demo:1.0
em7okvpd7e7a stack-demo_nginx-demo replicated 1/1 nginx:latest *:80->80/tcp
# 查看 nginx 服务部署在哪里
docker service ps stack-demo_nginx-demo
当前 nginx 服务部署在管理节点上,访问结果如下
此时在管理节点上查看 overlay 网络,可以看到默认的 ingress, 以及自定义的 ov-net,还有 Stack 根据配置文件创建的 stack-demo_ov-net2
[root@manager dockerdemo]# docker network ls -f SCOPE=swarm
NETWORK ID NAME DRIVER SCOPE
pupcl8y1k6kk ingress overlay swarm
lu3k1fu7yjw0 ov-net overlay swarm
wg81aqso2ovv stack-demo_ov-net2 overlay swarm
到这里 Docker 多机通信底层原理及实战就结束了,但 Docker Swarm 只是匆匆过客,最终还是要回到 Kubernetes 上
Use overlay networks | Docker Documentation
Github 关于 overlay 网络的介绍
VXLAN 基础教程:VXLAN 协议原理介绍