小白学 docker「docker-swarm 服务管理 (一)」

小白学 docker「docker-swarm 服务管理 (一)」_dev

Swarm服务使用声明性模型,这意味着我们定义服务的所需状态,并依靠Docker来保持这种状态。包括以下信息(但不限于):

  • 服务容器应该运行的镜像名称和标签
  • 有多少docker参与服务
  • 是否有任何端口暴露在集群之外的客户端
  • 服务是否应该在Docker启动时自动启动
  • 重新启动服务时发生的具体行为(例如是否使用滚动重新启动)
  • 服务可以运行的节点的特征(例如资源约束和放置首选项)

创建服务

要创建无需额外配置的单副本服务,只需提供镜像名称。此命令以随机生成的名称启动Nginx服务,并且没有已发布的端口。但是这个服务无法与Nginx服务交互。

docker service create nginx

小白学 docker「docker-swarm 服务管理 (一)」_Docker_02

我们看到 nginx 的服务已经被部署到了集群中的一个节点上。

创建的服务并不总是立即运行。如果服务的镜像不可用,如果没有节点满足您为服务配置的要求,或者出于其他原因,服务可以处于挂起状态。

要为您的服务提供名称,请使用--name标志:

docker service create --name my_web nginx

就像独立容器一样,您可以通过在图像名称后添加来指定服务容器应运行的命令。此示例启动了一项名为helloworld的服务,该服务使用alpine图像并运行命令ping docker.com

docker service create --name helloworld alpine ping docker.com

还可以指定要使用的服务的镜像标签。此示例修改了前一个,以使用alpine:3.6标签:

docker service create --name helloworld alpine:3.6 ping docker.com

使用私有化镜像仓库创建服务

如果您的镜像在需要登录的私有化镜像仓库上可用,在登录后使用带有docker service create--with-registry-auth标志。如果您的镜像存储在registry.example.com,这是一个私有化镜像仓库,请使用以下命令:

docker login registry.example.com docker service  create \  --with-registry-auth \  --name my_service \  registry.example.com/acme/my_image:latest

这使用加密的WAL日志将登录令牌从本地客户端传递到部署服务的群节点。有了这些信息,节点可以登录注册表并拉取镜像。

更新服务

我们可以使用docker service update命令更改现有服务的几乎所有内容。当更新服务时,Docker会停止其容器,并使用新配置重新启动它们。

由于Nginx是一项网络服务,如果将端口80发布到群外客户端,它的效果会好得多。我们可以在创建服务时使用-p--publish标志来指定此信息。更新现有服务时,标志是--publish-add。还有一个--publish-rm标志来删除之前发布的端口。

假设上一节的web服务仍然存在,请使用以下命令将其更新为发布端口80。

docker service update --publish-add 80 web

小白学 docker「docker-swarm 服务管理 (一)」_devops_03

删除一项服务

要删除服务,请使用docker service remove命令。可以按其ID或名称删除服务,如docker service ls命令的输出所示。以下命令删除了web服务。

docker service remove web

配置运行时环境

我们可以为容器中的运行时环境配置以下选项:

  • 使用--env标志的环境变量
  • 使用--workdir标志的容器内的工作目录
  • 使用--user标志的用户名或UID

以下服务的容器有一个环境变量$MYVAR设置为myvalue,从/tmp/目录运行,并以my_user用户身份运行。

docker service create --name helloworld \  --env MYVAR=myvalue \  --workdir /tmp \  --user my_user \  alpine ping docker.com

更新现有服务运行的命令

要更新现有服务运行的命令,您可以使用--args标志。以下示例更新了名为helloworld的现有服务,以便它运行ping docker.com命令,而不是之前运行的任何命令:

docker service update --args "ping docker.com" helloworld

指定服务使用的镜像版本

当我们在没有指定要使用的图像版本的任何详细信息的情况下创建服务时,该服务将使用带有latest标签的版本。可以根据您想要的结果,以几种不同的方式强制服务使用特定版本的图像。

镜像版本可以用几种不同的方式表达:

  • 如果指定标签,管理器(或Docker客户端)会将该标签解析为摘要。当工作节点上收到创建容器任务的请求时,工作节点只看到摘要,看不到标签。
docker service create --name="myservice" redis:6.0.20-alpine

一些标签表示离散版本,例如 redis:6.0.20-alpine。随着时间的推移,像这样的标签几乎总是会稳定地分解为摘要。建议您在可能的情况下使用这种标签。

其他类型的标签,如latestnightly,可能会经常解析为新的摘要,这取决于镜像作者更新标签的频率。不建议使用经常更新的标签运行服务,以防止不同的服务副本任务使用不同的图像版本。

  • 如果根本没有指定版本,按照惯例,图像latest标签将解析为摘要。docker 在创建服务任务时使用此摘要中的图像。
    因此,以下两个命令是等价的:
docker service create --name="myservice" redis docker service create --name="myservice" redis:latest
  • 如果直接指定摘要,则在创建服务任务时始终使用该图像的明确版本号。
docker service create \    --name="myservice" \    redis:latest@sha256:35bc48a1ca97c3971611dc4662d08d131869daa692acb281c7e9e052924e38b1

当创建服务时,图像的标签将解析为服务创建时标签指向的特定摘要。该服务的工作节点永远使用该特定摘要,除非服务被明确更新。如果您确实使用经常更改的标签(如latest),此功能尤为重要,因为它确保所有服务任务使用相同版本的图像。

创建服务后,除非您明确使用--image标志执行docker service update其映像永远不会更新,如下所述。其他更新操作,如缩放服务、添加或删除网络或卷、重命名服务或任何其他类型的更新操作,都不会更新服务的图像。

创建后更新服务的图像

每个标签代表一个摘要,类似于Git散列。一些标签,如latest,经常更新以指向新的摘要。其他版本,如redis:6.0.20-alpine,代表已发布的软件版本,预计不会经常更新以指向新的摘要。当创建服务时,它必须使用特定的图像摘要创建任务,直到您使用--image标志使用service update来更新服务。

当使用--image标志运行service update时,群组管理器会查询Docker Hub或您的私有Docker仓库,以获取标签当前指向的摘要,并更新服务任务以使用该摘要。

通常,可以将标签解析为新的摘要和服务更新,重新部署每个任务以使用新镜像。如果管理服务无法解决标签或出现其他问题。

如果Manager解决了标签

如果集群管理器可以将镜像标签解析为摘要,它会指示工作节点重新部署任务并在该摘要中使用镜像。

  • 如果worker在该摘要中缓存了镜像,它将使用它。
  • 如果没有,它会尝试从Docker Hub或私有 docker hub 中提取镜像。
  • 如果成功,则使用新镜像部署任务。
  • 如果worker无法提取镜像,则服务无法在该worker节点上部署。Docker再次尝试部署任务,可能是在另一个工作节点上。

如果Manager无法解决标签

如果集群管理器无法将镜像解析为摘要,则不会丢失所有内容:

  • 管理器指示工作节点使用该标签上的镜像重新部署任务。
  • 如果worker有一个本地缓存的图像解析到该标签,它将使用该镜像。
  • 如果worker没有解析为标签的本地缓存图像,则工人尝试连接到Docker Hub或私人注册表,以在该标签处拉取镜像。
  • 如果成功,工人将使用该镜像。
  • 如果失败,任务部署失败,manager 再次尝试部署任务,可能是在另一个工作节点上。

发布端口

当创建集群服务时,可以通过两种方式将该服务的端口发布到群之外的主机:

  • 路由网格。当发布服务端口时,群会使服务在每个节点上的目标端口上都可以访问,无论服务是否在该节点上运行的任务。这样很简单,是许多类型服务的正确选择。
  • 可以直接在服务运行. 这绕过了路由网格,并提供了最大的灵活性,包括开发自己的路由框架的能力。但是,我们有责任跟踪每个任务的运行位置,并将请求路由到任务,并跨节点进行负载平衡。

使用路由网格发布服务的端口

要在集群外部发布服务的端口,请使用--publish <PUBLISHED-PORT>:<SERVICE-PORT>标志。集群使服务可以在每个群节点上的已发布端口访问。如果外部主机连接到任何群节点上的该端口,路由网格会将其路由到任务。外部主机不需要知道服务任务的IP地址或内部使用的端口即可与服务交互。当用户或进程连接到服务时,运行服务任务的任何工作节点都可以响应。

demo:在10节点群上运行三任务Nginx服务

比如我们你有一个10节点群,部署了一个Nginx服务,在10个节点群上运行三个任务:

docker service create --name web \                        --replicas 10 \                        --publish published=8080,target=80 \                        nginx

三个任务在最多三个节点上运行。不需要知道哪些节点正在运行任务;连接到10个节点中的任何一个节点上的端口8080将您连接到三个nginx任务之一。可以使用curl测试此。以下示例假设localhost是群节点之一。如果不是这样,或者localhost没有解析为主机上的IP地址,请替换主机的IP地址或可解析的主机名。

直接在集群节点上发布服务的端口

如果您需要根据应用程序状态做出路由决策,或者您需要完全控制将请求路由到服务任务的过程,那么使用路由网格可能不是应用程序的正确选择。要将服务的端口直接发布在运行的节点上,请使用mode=host选项到--publish标志。

注意:如果使用mode=host直接在群节点上发布服务的端口,并设置published=<PORT>,这会创建一个隐式限制,即只能在给定的群节点上为该服务运行一个任务。我们可以通过在没有端口定义的情况下指定published来解决这个问题,这导致Docker为每个任务分配一个随机端口。

此外,如果您使用mode=host,并且在docker service create不使用--mode=global标志,则很难知道哪些节点正在运行该服务来将工作路由到它们。

是一个开源反向代理、负载均衡器、HTTP缓存和Web服务器。如果您使用路由网格将nginx作为服务运行,则连接到任何群节点上的nginx端口都会向您显示(有效地)运行服务的随机群节点的网页。

以下示例在群中的每个节点上运行nginx作为服务,并在每个群节点上本地公开nginx端口。

docker service create \  --mode global \  --publish mode=host,target=80,published=8080 \  --name=nginx \  nginx:latest

您可以在每个群节点的端口8080上访问nginx服务器。如果您将一个节点添加到群中,则会在上面启动一个nginx任务。您无法在任何绑定到端口8080的群节点上启动另一个服务或容器。

将服务连接到网络

可以使用overlay网络在集群中连接一个或多个服务。

首先,使用带有--driver overlay标志的docker network create命令在管理器节点上创建覆盖网络。

docker network create --driver overlay my-network

在集群模式下创建覆盖网络后,所有管理器节点都可以访问网络。

您可以创建新服务并传递--network标志,以将服务附加到覆盖网络:

docker service create \  --replicas 3 \  --network my-network \  --name my-web \  nginx

群将my-network扩展到运行服务的每个节点。

您还可以使用--network-add标志将现有服务连接到覆盖网络。

docker service update --network-add my-network my-web

要断开正在运行的服务与网络的连接,请使用--network-rm标志。

docker service update --network-rm my-network my-web