要在Docker Engine处于群集模式时部署应用程序镜像,创建服务。通常,服务是某个大型应用程序上下文中微服务的镜像。service示例可能包括HTTP服务器,数据库或希望在分布式环境中运行的任何其他类型的可执行程序。
service的作用
创建service时,可以指定要使用的容器镜像以及要在运行中的容器中执行的命令。还可以定义service的选项,包括:
- 集群对外提供服务的端口
- 用于服务连接到集群中其他服务的overlay网络
- CPU和内存限制与保留
- 滚动更新策略
- 集群中要运行的镜像的副本数
Services, tasks, and containers
当将服务部署到swarm时,swarm manager接受你的服务定义作为服务的期望状态。然后,它将集群中的节点上的服务作为一个或多个副本任务进行调度。这些任务在集群中的节点上独立运行。
例如,假设希望在HTTP侦听器的三个实例之间实现负载平衡。下图显示了一个带有三个副本的HTTP侦听器服务。侦听器的三个实例中的每个实例都是集群中的一个任务。
在manager中,发出了一个期望状态,3 nginx replicas
,理解为三个nginx容器。manager发布一个任务nginx,将此任务调度给swarm集群中的主机中,并以service.1、service.2...
来命名集群主机中任务,每个任务里面都包含一个container,所有集群的任务加起来就是manager所期望的状态。
容器是一个独立的进程。在集群模式模型中,每个任务只调用一个容器。任务类似于调度程序放置容器的“槽”。一旦容器处于活跃状态,调度程序就会识别任务处于运行状态。如果容器的健康检查失败或终止,则任务终止。
运行service
实验环境
ip | 服务 | 备注 |
---|---|---|
192.168.1.11 | Docker(已安装) | swarm-manage |
192.168.1.12 | Docker(已安装) | swarm node1 |
192.168.1.13 | Docker(已安装) | swarm node2 |
实验步骤
swarm基础环境已经部署好,根据上两个文档部署
创建一个service
swarm-manage
名为web_server,指定该service使用的镜像是httpd:latest,表示当service将任务调度到各个集群主机后,都使用该镜像进行运行容器
[root@swarm-manage ~]# docker service create --name web_server httpd:latest
99jo23xo3z172c8f80s7gsan5
overall progress: 1 out of 1 tasks
1/1: running
verify: Service converged
查看service
swarm-manage
在service中比较受关注的一个字段是REPLICAS 1/1
,表示当前副本的总量为1,正在运行1。
[root@swarm-manage ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
99jo23xo3z17 web_server replicated 1/1 httpd:latest
查看副本部署的节点
swarm-manage
部署的NODE为node2主机,如果在部署swarm过程中,运行了图形化的swarm集群显示工具,在web页面中也可以直观的看到
查看到当前service中任务的为we_server.1,状态为Running,下面会详细介绍任务状态
[root@swarm-manage ~]# docker service ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
fz9ftakdhvzi web_server.1 httpd:latest node2 Running Running 4 minutes a
切换到node2主机进行查看是否运行了容器
node2
确实正在运行,且任务名为web_server.1.fz9ftakdhvziku67rc
[root@node2 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
37756e6d3b92 httpd:latest "httpd-foreground" 8 minutes ago Up 8 minutes 80/tcp web_server.1.fz9ftakdhvziku67rc
进入容器查看
[root@node2 ~]# docker exec -it web_server.1.fz9ftakdhvziku67rcv9rongi /bin/sh
# cat /usr/local/apache2/htdocs/index.html
<html><body><h1>It works!</h1></body></html>
swarm的任务状态
Docker创建可以启动任务的服务。服务是对所需状态的描述,而任务则在工作。按以下顺序在群体节点上安排工作:
- 使用
docker service create
创建service - 该请求将转到Docker管理器节点.
- Docker管理器节点计划服务在特定节点上运行.
- 每个服务可以启动多个任务.
- 每个任务都有一个生命周期,状态为
NEW
,PENDING
和COMPLETE
.
任务是运行一次才能完成的执行单元. 任务停止时,不会再次执行,但是新任务可能会代替它。
任务会经过许多状态,直到它们完成或失败. 任务在NEW
状态下初始化。任务通过多个状态前进,并且其状态不后退。例如,任务永远不会从COMPLETE
转到RUNNING
。
任务按以下顺序遍历状态:
任务状态 | Description |
---|---|
NEW |
任务已初始化 |
PENDING |
已分配任务资源 |
ASSIGNED |
Docker将任务分配给节点 |
ACCEPTED |
任务已被工作节点接受. 如果工作程序节点拒绝任务,则状态更改为REJECTED |
PREPARING |
Docker正在准备任务 |
STARTING |
Docker正在启动任务 |
RUNNING |
任务正在执行 |
COMPLETE |
任务退出,没有错误代码 |
FAILED |
任务退出并显示错误代码 |
SHUTDOWN |
Docker请求关闭任务 |
REJECTED |
工作节点拒绝了任务 |
ORPHANED |
节点关闭时间过长 |
REMOVE |
该任务不是终端,但关联的service已删除或按比例缩小 |
service的弹性伸缩
上面只有一个副本的service,不过对于web服务,我们通常会运行多个实例。这样可以负载均衡,同时也能提供高可用。
swarm实现这个目标非常简单,增加service的副本就可以了
service的伸展
如下
swarm-manage
[root@swarm-manage ~]# docker service scale web_server=5
web_server scaled to 5
overall progress: 5 out of 5 tasks
1/5: running # 运行时会看到这个部分来回变化,也即是任务的状态在发生变化
2/5: running
3/5: running
4/5: running
5/5: running
verify: Service converged
查看副本数量
[root@swarm-manage ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
99jo23xo3z17 web_server replicated 5/5 httpd:latest
查看每个容器运行在哪个节点
任务名分别为web_server.1/2/3/4/5
,node2主机由两个,node1主机两个,swarm-manage主机一个
[root@swarm-manage ~]# docker service ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
fz9ftakdhvzi web_server.1 httpd:latest node2 Running Running 33 minutes ago
kdyfmk6gergs web_server.2 httpd:latest swarm-manage Running Running about a minute ago
yx57yk7m5mtu web_server.3 httpd:latest node1 Running Running about a minute ago
ku04jr9torad web_server.4 httpd:latest node1 Running Running about a minute ago
3enxfmuio43j web_server.5 httpd:latest node2 Running Running 2 minutes ago
同样web页面也会实时更新
192.168.1.11:8888
service的缩减
直接改变scale数量比伸展时少即可
[root@swarm-manage ~]# docker service scale web_server=3
web_server scaled to 3
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
此时就剩下了三个容器在运行
[root@swarm-manage ~]# docker service ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
fz9ftakdhvzi web_server.1 httpd:latest node2 Running Running 51 minutes ago
nslao093vd7n web_server.2 httpd:latest swarm-manage Running Running 8 minutes ago
yx57yk7m5mtu web_server.3 httpd:latest node1 Running Running 19 minutes ago
swarm-manage是否参与调度
这个状态表示,作为管理节点的swarm主机,不仅参与任务的调度,也就是这台主机不会被允许容器,修改节点的availability
的值为drain
[root@swarm-manage ~]# docker node update --availability drain swarm-manage
查看节点的availability
[root@swarm-manage ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
r0patdk7bc423h0hg0aa1ycid node1 Ready Active 19.03.8
ptu10j3s76ivn52uksrssm4t3 node2 Ready Active 19.03.8
p2mydxnx99cezu80e4b3z9xnw * swarm-manage Ready Drain Leader 19.03.8
这时这个节点中的容器会被调度到其他节点去运行
看到swarm-manage主机的任务名变为了\_web_server.2
,“\”表示关闭,然后去其他节点重新运行一个容器
[root@swarm-manage ~]# docker service ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
fz9ftakdhvzi web_server.1 httpd:latest node2 Running Running 45 minutes ago
kdyfmk6gergs web_server.2 httpd:latest node2 Running Running about a minute ago
nslao093vd7n \_ web_server.2 httpd:latest swarm-manage Shutdown Shutdown about a minute ago
yx57yk7m5mtu web_server.3 httpd:latest node1 Running Running 13 minutes ago
重新将swarm-manage主机改为可以进行运行容器
[root@swarm-manage ~]# docker node update --availability active swarm-manage
这时进行副本伸展时,就会将该主机加入可以运行的容器行列
如何访问service
这也涉及到了前面说到过的负载均衡的功能,通过访问一个ip及端口,做到访问整个容器集群。
创建一个service
创建service时,也可以直接创建指定副本数量的任务
[root@swarm-manage ~]# docker service create --name web_server1 \
--replicas 2 httpd:latest
uyjjb0fgl0adz54n3z6h0prgz
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
service已经创建好,容器也已经在运行了
获取容器ip进行访问
要访问 http 服务,最起码网络得通,服务的 IP 得知道,但这些信息目前都不清楚。不过至少知道每个副本都是一个运行的容器,要不先看看容器的网络配置吧。
查看容器在哪两个节点运行,分别是node1和node2
[root@swarm-manage ~]# docker service ps web_server1
ID NAME IMAGE NODE DESIRED STATE
lnp96998mcdv web_server1.1 httpd:latest node1 Running
6fyhtx22x25i web_server1.2 httpd:latest node2 Running
在node1上运行了一个容器,是 web_server1 的一个副本,容器监听了 80 端口,但并没有映射到 Docker Host,所以只能通过容器的 IP 访问。查看一下容器的 IP。
node1
查看到ip为172.17.0.3
[root@node1 ~]# docker run -it --rm --network \
container:web_server1.1.lnp96998mcdvgv6pbdawtpaif busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
访问该容器
[root@node1 ~]# curl 172.17.0.3
<html><body><h1>It works!</h1></body></html>
知道了容器的ip也只能在本地主机访问,但凡换一台主机都无法访问。换句话说,当前配置下,我们无法访问 service web_server1
要在集群当中使用入口网络,需要在集群节点之间打开两个端口
7946/tcp
:容器网络发现协议
4789/udp
:overlay端口
每台集群节点都进行放行端口,如果关闭防火墙则不需要
firewall-cmd --add-port=7946/tcp
firewall-cmd --add-port=4789/udp
暴露service
swarm-manage
将web_server的容器集群的所有端口,都映射到各自所在主机的8080端口
[root@swarm-manage ~]# docker service update --publish-add 8080:80 web_server1
web_server1
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
在创建service时,也可以直接进行映射
docker service create --name web --publish 8080:80 --replicas 3 httpd
访问service
现在访问容器所在节点的ip的8080端口
[root@swarm-manage ~]# curl 192.168.1.12:8080
<html><body><h1>It works!</h1></body></html>
[root@swarm-manage ~]# curl 192.168.1.13:8080
<html><body><h1>It works!</h1></body></html>
[root@swarm-manage ~]# curl 192.168.1.11:8080
<html><body><h1>It works!</h1></body></html>
查看映射端口
[root@swarm-manage ~]# docker service inspect --format "{{json .Endpoint.Spec.Ports}}" web_server1
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080,"PublishMode":"ingress"}]
routing mesh
路由网络
发现刚才只运行了两个容器节点,都是访问三个ip都可以访问到
当访问任何节点的 8080 端口时,swarm 内部的 load balancer 会将请求转发给 web_server 其中的一个副本。这就是routing mesh的作用
所以,无论访问哪个节点,即使该节点上没有运行 service 的副本,最终都能访问到 service。
另外,还可以配置一个外部负载均衡器,将请求路由到 swarm service。比如配置 haproxy,将请求分发到各个节点的 8080 端口。
ingress网络
当我们应用 –publish-add 8080:80 时,swarm 会重新配置 service
现在来看下容器的网络是怎样的
node1
发现除了swarm网络的网卡,还有一个172.18.0.0/16网络的网卡,类似于之前的overlay网络,其实就是overlay网络。
eth0用于容器之间通信,eth1用于与物理机通信
在查看该容器网卡信息时,发现容器名和之前查看网卡时不一样,后面会说到
[root@node1 ~]# docker run -it --rm --network container:web_server1.1.ttv2intr4ob2g0z8v0e8ph8o8 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether 02:42:0a:00:00:06 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.6/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever
17: eth1@if18: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
查看docker的网卡
多出了ingress的overlay网卡,和docker_gwbridge的bridge网卡。
[root@node1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
b5f7f7433bac bridge bridge local
e3e1b1ce5f4d docker_gwbridge bridge local
dc8bfdbda464 host host local
2wua1iwpr90b ingress overlay swarm
ecbab8a758e6 none null local
前面在用overlay的时候讲到过如果不在swarm集群使用overlay,需要开启混杂模式,进行脱离集群的使用
也可以通过查看网卡信息,看到他们的网段
[root@node1 ~]# docker network inspect ingress
[root@node1 ~]# docker network inspect docker_gwbridge
当去查看service web_server1的状态时,发现容器被关闭了一次,然后去重新运行
[root@swarm-manager ~]# docker service ps web_server1
ID NAME IMAGE NODE DESIRED STATE
ttv2intr4ob2 web_server1.1 httpd:latest node1 Running
f05ai3nufbpp \_ web_server1.1 httpd:latest node1 Shutdown
42rx8o3b2p0r web_server1.2 httpd:latest swarm-manager Running
z6ejrbwns7l9 \_ web_server1.2 httpd:latest swarm-manager Shutdown
ingress 网络是 swarm 创建时 Docker 自动创建的,swarm 中的每个 node 都能使用 ingress。
通过 overlay 网络,主机与容器、容器与容器之间可以相互访问;同时,routing mesh 将外部请求路由到不同主机的容器,从而实现了外部网络对 service 的访问
如果使用不带routing mesh
路由的负载均衡器,可以使用--endpoint-mode dnsrr
,默认情况下使用的是VIP的方式,进行负载均衡轮询,不使用路由的负载均衡就是不通过vip进行轮询副本容器
service之间的通信
上述的vip可能有的人不理解,接下来就来说vip
微服务架构的应用由若干 service 组成。比如有运行 nginx 的 web 前端,有提供缓存的 memcached/redis,有存放数据的 mysql,每一层都是 swarm 的一个 service,每个 service 运行了若干容器。在这样的架构中,service 之间是必然要通信的。
如:LNMP之间需要去互相访问,他们之间怎么去通信,就需要通过服务发现
服务发现
一种实现方法是将所有 service 都 publish 出去,然后通过 routing mesh 访问。但明显的缺点是把 memcached 和 mysql 也暴露到外网,增加了安全隐患。所以后端的服务出于安全考虑是不能够将端口映射出去的
如果不 publish,那么 swarm 就要提供一种机制,能够:
- 让 service 通过简单的方法访问到其他 service。
- 当 service 副本的 IP 发生变化时,不会影响访问该 service 的其他 service。
- 当 service 的副本数发生变化时,不会影响访问该 service 的其他 service。
这其实就是服务发现(service discovery)。Docker Swarm 原生就提供了这项功能,通过服务发现,service 的使用者不需要知道 service 运行在哪里,IP 是多少,有多少个副本,就能与 service 通信。
创建overlay网络
要使用服务发现,需要相互通信的 service 必须属于同一个 overlay 网络,所以我们先得创建一个新的 overlay 网络。因为自动生成的ingress网络没有提供服务发现的功能,必须使用自己创建的overlay网络
[root@swarm-manager ~]# docker network create --driver overlay myapp_net
f5zlinotujpao0ucfkwium5lt
部署service到overlay
重新创建一个service,并使用新创建的overlay,为了实验方便将之前的service已经删除
swarm-manager
my_web service
[root@swarm-manager ~]# docker service create --name my_web --replicas 3 \
--network myapp_net httpd:latest
ue4j9cv403kllnx8frhviz8gv
overall progress: 3 out of 3 tasks
1/3: running
2/3: running
3/3: running
verify: Service converged
部署一个 util 服务用于测试,挂载到同一个 overlay 网络。
[root@swarm-manager ~]# docker service create --name util \
--network myapp_net busybox sleep 1000000
xf8u3w8x4aybtx2s4ah2wzdc0
overall progress: 1 out of 1 tasks
1/1: running
verify: Service converged
sleep 10000000 的作用是保持 busybox 容器处于运行的状态,我们才能够进入到容器中访问 service my_web。
验证service之间的访问
先找到util的service所运行在哪个主机
[root@swarm-manager ~]# docker service ps util
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
n09gl1f1xyv3 util.1 busybox:latest swarm-manager Running Running 20 minutes ago
swarm-manager
可以看到使用util与my_web service进行互通时,是由10.0.1.2来回应的
[root@swarm-manager ~]# docker exec util.1.n09gl1f1xyv34soha3ocltm13 ping -c 2 my_web
PING my_web (10.0.1.2): 56 data bytes
64 bytes from 10.0.1.2: seq=0 ttl=64 time=0.253 ms
64 bytes from 10.0.1.2: seq=1 ttl=64 time=0.144 ms
--- my_web ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.144/0.198/0.253 ms
这个地址好像没有见过,是哪里来的,它不是任何swarm集群中的副本ip,就是web_web service的VIP,swarm 会将对 VIP 的访问负载均衡到每一个副本。
各个副本有自己的 IP。不过对于服务的使用者(这里是 util.1),根本不需要知道 my_web副本的 IP,也不需要知道 my_web 的 VIP,只需直接用 service 的名字 my_web 就能访问服务。
如果使用将my_web解析将会得到三个ip,分别是三台my_web容器的ip,也可以去查看这几个容器的ip
[root@swarm-manager ~]# docker exec util.1.n09gl1f1xyv34soha3ocltm13 nslookup tasks.my_web
Server: 127.0.0.11
Address: 127.0.0.11:53
Non-authoritative answer:
Name: tasks.my_web
Address: 10.0.1.5
Name: tasks.my_web
Address: 10.0.1.3
Name: tasks.my_web
Address: 10.0.1.4
查看该service的详细信息会看到这个VIP
[root@swarm-manager ~]# docker service inspect my_web
...
"Spec": {
"Mode": "vip"
},
"VirtualIPs": [
{
"NetworkID": "f5zlinotujpao0ucfkwium5lt",
"Addr": "10.0.1.2/24"
}
]
}
...
加入overlay网络之后,service之间互相访问不需要知道每个容器的ip,也不需要知道service的VIP,直接访问service name即可,VIP会对副本的ip进行轮询,不需要去管副本的增加或者减少
删除映射端口
swarm-manage
[root@swarm-manage ~]# docker service update --publish-rm 8080:80 web_server1
web_server1
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
验证
[root@swarm-manage ~]# curl 192.168.1.11:8080
curl: (7) Failed connect to 192.168.1.11:8080; Connection refused
[root@swarm-manage ~]# curl 192.168.1.12:8080
curl: (7) Failed connect to 192.168.1.12:8080; Connection refused
[root@swarm-manage ~]# curl 192.168.1.13:8080
curl: (7) Failed connect to 192.168.1.13:8080; No route to host