在这篇文章中,大家将会了解如何在不停止服务的情况下进行配置更改和镜像更新。我们可以通过动态更新来控制服务的各种设置和选项。对于一些命令,Docker会停止、删除、重建容器,因此,必须考虑到如果同时停止所有的容器所带来的服务连接中断和正常运行时长问题。
1. 限制资源
通过docker service update --help
查看服务更新帮助:
Usage: docker service update [OPTIONS] SERVICE
Update a service
Options:
--args command Service command args
--config-add config Add or update a config file on a service
--config-rm list Remove a configuration file
--constraint-add list Add or update a placement constraint
--constraint-rm list Remove a constraint
--container-label-add list Add or update a container label
--container-label-rm list Remove a container label by its key
--credential-spec credential-spec Credential spec for managed service account (Windows only)
-d, --detach Exit immediately instead of waiting for the service to converge
...
我们将服务中容器的可用资源进行一下限制,cpu=2, memory=512m
,防止单个容器使用太多宿主机资源,docker service update --limit-cpu 2 --limit-memory 512m http
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
85837a185c45 nandy/show-host-info:v1 "/app" 14 seconds ago Up 9 seconds 80/tcp http.2.kzxixawhsrnqzyyg3hkn9x11l
972337b445fb nandy/show-host-info:v1 "/app" 18 seconds ago Up 14 seconds 80/tcp http.1.0ry6cxmpfozkvko3wb2fuzbmp
b31a2e1f1a47 nandy/show-host-info:v1 "/app" 30 seconds ago Exited (2) 15 seconds ago http.1.l8e2qtwln1ratctrs9zq8l5as
20c7d4a6c0ae nandy/show-host-info:v1 "/app" 31 seconds ago Exited (2) 11 seconds ago http.2.mzwi4vdrzvauq0xijap3cax75
运行docker service inspect --pretty http
查看结果:
Resources:
Limits:
CPU: 2
Memory: 512MiB
...
注意,更新服务的资源限制会导致容器重建。
2. 更新副本
不是所有的更新都需要重建容器,更新副本就不会影响已经在运行的容器。
在第一篇文章中,我们通过docker service scale http=6
扩展服务。在这里,我们通过docker service update --replicas=6 http
将服务更新为6个副本。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a5fcff23f737 nandy/show-host-info:v1 "/app" 12 seconds ago Up 8 seconds 80/tcp http.5.fpvkua6wmlotkicluolzjzki8
b7427b1cf664 nandy/show-host-info:v1 "/app" 12 seconds ago Up 8 seconds 80/tcp http.4.llftjraazc2k7l9g9rxnbzeg5
638436cd0bb2 nandy/show-host-info:v1 "/app" 12 seconds ago Up 8 seconds 80/tcp http.6.j2peb7hu7etmzr362mtyxomyz
32498f45da7a nandy/show-host-info:v1 "/app" 12 seconds ago Up 9 seconds 80/tcp http.3.3058xfvf8gsvj9wq68dykayw4
3217d333835c nandy/show-host-info:v1 "/app" 4 minutes ago Up 4 minutes 80/tcp http.2.yjjje6vhzaiaxtbo953e0flnr
07a5dbd7693b nandy/show-host-info:v1 "/app" 4 minutes ago Up 4 minutes 80/tcp http.1.jhq7etce5dkdbgyxeu59qr1rc
fe0360d436ae nandy/show-host-info:v1 "/app" 4 minutes ago Exited (2) 4 minutes ago http.2.qt0w7lco6dpe2hoxvwgoyltyk
8c71bf61823b nandy/show-host-info:v1 "/app" 4 minutes ago Exited (2) 4 minutes ago http.1.kwc4zbadbpau0cslwt8jp1kh9
通过STATUS
的启动时间可以看出,服务额外创建了4个新的容器,已有的容器不变。我们运行docker container inspect http.6.j2peb7hu7etmzr362mtyxomyz
:
...
"Memory": 536870912,
"CpuQuota": 200000,
...
可以看到,新创建的容器,它的资源已被限制,因为服务的设置已经被更新。
3. 更新镜像
一般来说,服务在被创建的时候,各种设置、参数、副本等都是考虑好的,不怎么会再更新。因此,最常更新的便是镜像了。每当发布一个新版本的时候,我们只需要更新一下服务的镜像便可。
运行docker service update --image nandy/show-host-info:v2 http
:
overall progress: 3 out of 6 tasks
1/6: ready [======================================> ]
2/6: running [==================================================>]
3/6:
4/6:
5/6: running [==================================================>]
6/6: running [==================================================>]
更新镜像也会导致所有容器停止并重建。但仔细观察stdout
,容器是一个一个停止并重建的。也就是说,只要服务的副本数大于1个(高可用),那么服务在更新的同时依然会对外提供服务,从而达到永久可用。
4. 滚动升级
滚动升级的目的是为了在发布新版本的时候服务不停止,通过设置服务中容器的并行和延迟创建,Docker可以批量更新,从而实现零停机时间。
--update-parallelism
定义了可以并行更新的数量,这取决于服务的并发量和副本数。
--update-delay
定义了每次更新之间的等待时间。如果应用有预热时间,比如启动JVM或预加载数据,等待时间将很有用,这能确保服务在所有容器重建完成之前,已经有容器可以处理请求。
这两个参数既可以在服务创建的时候指定,也可以通过服务更新实现。接下来,我们设置“1次更新1个容器,间隔10秒”,并进行版本升级:
docker service update --update-delay=10s --update-parallelism=1 --image nandy/show-host-info:v2 http
通过stdout, docker ps, curl
,我们可以看到容器新的v2版本启动并替换旧的的v1。
$ curl 172.17.0.15:80
{"version":"v2","hostname":"400a6b335676","address":"10.0.0.18"}
$ curl 172.17.0.15:80
{"version":"v2","hostname":"e5854c99c632","address":"10.0.0.17"}
$ curl 172.17.0.15:80
{"version":"v2","hostname":"2152a6f28cab","address":"10.0.0.16"}
$ curl 172.17.0.15:80
{"version":"v2","hostname":"c758241498ad","address":"10.0.0.15"}
$ curl 172.17.0.15:80
{"version":"v2","hostname":"102a0494ccc5","address":"10.0.0.14"}
$ curl 172.17.0.15:80
{"version":"v2","hostname":"23c886ed18aa","address":"10.0.0.13"}
可以看到,新的版本"version":"v2"
已经对外服务了。
最后思考一个问题:版本更新时,一个服务将会同时存在两个版本的容器,这是否符合自己公司的业务逻辑?