要在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所期望的状态。

services diagram

容器是一个独立的进程。在集群模式模型中,每个任务只调用一个容器。任务类似于调度程序放置容器的“槽”。一旦容器处于活跃状态,调度程序就会识别任务处于运行状态。如果容器的健康检查失败或终止,则任务终止。

运行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页面中也可以直观的看到

swarm_service

查看到当前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创建可以启动任务的服务。服务是对所需状态的描述,而任务则在工作。按以下顺序在群体节点上安排工作:

  1. 使用docker service create创建service
  2. 该请求将转到Docker管理器节点.
  3. Docker管理器节点计划服务在特定节点上运行.
  4. 每个服务可以启动多个任务.
  5. 每个任务都有一个生命周期,状态为NEWPENDINGCOMPLETE .

任务是运行一次才能完成的执行单元. 任务停止时,不会再次执行,但是新任务可能会代替它。

任务会经过许多状态,直到它们完成或失败. 任务在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

scale_5web

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

评论




正在载入...
PoweredHexo
HostedAliyun
DNSAliyun
ThemeVolantis
UV
PV
BY-NC-SA 4.0