首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

前端工程师的 DevOps 学习笔记(七)Swarm 基础功能与应用

Overlay Network

Swarm 引入了一个新的网络驱动: ,可以通过 创建,本质上是创建了一个Swarm 范围内的跨主机桥接网络,使得不同节点的容器间位于同一虚拟网络,同一 Swarm 间的容器能够相互访问。创建网络时可以指定开启 加密,由于性能原因此项是默认关闭的。新建服务时可以将它们加入到多个网络,具体取决于应用设计。

在 node1 节点新建一个 :

可以看到一个名为 ,范围为 的网络被创建成功了。

然后新建一个 PostgreSQL 服务:

然后再建一个 drupal 的服务:

服务建好了,相互间怎么通信呢?使用 。

拷贝任意节点的 IP,访问我们刚才创建的服务。连接数据库的选项里,填入我们刚才创建时的信息,并且数据库域名我们填写数据库的服务名称: ,一路填写,就能创建成功了。

这里最酷的事情就是我们不管使用哪个 IP,都能访问 Drupal,然而实际上只有 node2 节点运行着 drupal 服务。执行 ,可以看到 Overlay Network 上只有一个 IP,那为什么这三个节点都能响应请求呢?

Routing Mesh

上面的魔法正是 Routing Mesh(路由网格),它能够将传入的对某个服务的请求发送至正确的任务,覆盖 Swarm 中的所有节点,原理是它使用了 Linux 内核的 IPVS(IP 虚拟服务器),监听流量的所有节点,并且在所有任务之上提供 Swarm 服务的负载均衡功能。

一般情况下有两种使用方式:

Overlay Network 中的容器间通信(使用 Virtual IP,Swarm 将其置于所有服务之前,并且这是一个位于虚拟网络中的私有 IP,确保负载分布在服务的所有任务中。想象我们需要使位于虚拟网络中的两个不同服务相互之间通信的情况,假设应用中有一个 worker 节点,它运行着10个不同的容器,无需在它们之前再部署一个负载均衡,VIP 都为你做了)。

外部流量请求(所有节点监听的)公开端口,实际上可能命中 Swarm 中的任意节点。Worker 节点中的每一个都会打开那个公开端口并监听容器的流量,然后根据负载均衡将其转入正确的容器。

这意味着在 Swarm 中部署容器时,我们并不需要关心它运行在哪台服务器上,因为这随时都有可能改变。当容器运行失败时,Swarm 的任务将会重新创建容器并可能会把它放到另一个节点上,你肯定不想更改防火墙或 DNS 设置来使容器再次运行。

Routing Mesh 在背后为我们解决了一大堆类似的问题,使得我们位于80端口的 Drupal 网站能被 Swarm 内的任意节点访问,并且在后台将那些来自于服务器的请求包转给相应的容器。如果该容器位于不同的节点,它将会通过虚拟网络来发送。如果位于同一个节点,它将会转发请求到相应容器的端口。整个过程并不需要我们为此做什么,所有的一切都是开箱即用的。

假设我创建了一个网络服务,然后让它自我复制三份,接着让它在三个节点使用三个容器创建了三个服务,Overlay Network 将会创建一个虚拟 IP 并映射至服务的 DNS 名称,而服务的默认 DNS 名称即是服务名称。

上图的例子中,创建了一个名为 的服务,所有位于 Overlay Network 中的容器需要与 Swarm 中的此服务通信,而它们只需正确使用 的 DNS ,而虚拟 IP 将会在所有服务的任务中被负载均衡正确地使用。这并不是 DNS Round Robin (DNS 轮询调度),但配置与之相似,并且实际上真有一个选项能够开启 DNS Round Robin。但是在 DNS Round Robin 中,VIP 的优势将导致产生 DNS 缓存,从而无法正确的分配负载。所以与其不停地配置 DNS,不如直接依赖 VIP,它用起来就好像我们购置了一个专用的硬件负载均衡。

这张图展示了当外部流量传入时的整个过程,这与我们刚刚使用 Drupal 的情况类似。当创建一个 服务时,它创建了两个任务并将其应用到两个不同的节点上,每一个节点都会对公开的 IP 地址配置一个内置的负载均衡器,对于我们购买的服务器来说,就是 DigitalOcean 分配给我们的 IP。我们使用 来暴露端口,本例中,使用了 8080 端口。这三个节点中任意一个传入的流量将会在 8080 端口命中负载均衡,然后负载均衡会决定分配这个流量给哪个容器,首先查看这个流量请求命中的是不是本地节点,不是的话会通过 Overlay Network 将其发送到另一个节点。重申一遍,所有这些都是发生在背后,并不需要我们额外为此配置什么。

为了演示这个效果,我们在 node1 新建一个 Elastic Search 的服务:

如果我们重复多执行几次 ,可以看到会返回不同的信息。事实上 Virtual IP 会表现得像负载均衡一样分配我的负载至这三个任务。

Routing Mesh 是无状态负载均衡,意味着如果你在应用中使用 Session、Cookie,或者你期望某个固定的容器能够与某个固定的客户端通信,那你得需要一些额外的配置才行。默认情况下,你请求同一个服务,有可能会返回不同的信息。

Routing Mesh 的负载均衡位于 OSI 的第三层(TCP),操作 IP 和端口,而不是第四层(DNS),所以无法在同一台服务器的同一个端口上运行多个不同的 Web 服务。

幸运的是这些需求都很常见,我们可以很容易解决上述两个限制:* 使用 Nginx 或者 HAProxy,置于 Routing Mesh 之前,然后成为一个有状态的负载均衡或第四层负载均衡,而且还能根据你的需要提供缓存等其他各种各样的功能。* 使用 Docker 企业版,内置了 L4 Web 代理,能够直接使用 Swarm 中服务的 DNS 名称

Swarm Stacks ——生产环境

在1.13版本的 Docker 中,加入了一层新的 Swarm 抽象,名为 。

Stacks 接收 Compose 文件作为服务、网络和数据卷的声明定义。

我们使用 来替代逐个的 。

Stacks 为我们管理好了所有配置,包括每一个 stack 的 overlay network。

YAML 文件中新增 键,不能使用 ,因为镜像的构建过程不应该发生在生产环境的 Swarm 里,应该让 CI 系统来做。

Compose 会忽略 ,Swarm 会忽略 。

CLI 对于 Swarm 服务器来说不是必须的。

在新的 Stacks 里,单个 YAML 文件里可能会配置有数十个服务。它们与我们之前创建的服务没什么不同,只是现在我们需要在 Compose 文件中指定数据卷和 Overlay Network,所以这就组成了 Stacks。

注意 Stack 只能用于一个 Swarm。

接下来我们创建一个拥有5个服务的投票应用:

编写 stack YAML 文件(只支持 Version 3):

注意这里与 Compose YAML 相比,多出来了 字段。

指定了需要多少个副本, 可以指定副本创建时的配置, 指定服务重启的触发策略,还可以指定约束节点的角色是 Manager 还是 Worker。

在 node1 服务器执行:

依次打开:http://139.59.98.19:5000/、http://139.59.98.19:5001/、http://139.59.98.19:8080/,可以看到我们的应用就跑起来了。

注意,如果我们需要更改 stack 的配置,确保每一次修改都是通过 YAML 配置文件来的,尽量不要使用 ,因为重新创建会覆盖之前的配置,我们需要将它作为唯一数据来源。

更新后再执行一遍: ,即可更新 stack。

Swarm 中的密钥存储:保护环境变量

Docker 1.13中还有一个新功能,内置对密钥的完全支持,算是 Swarm 中存储密钥最容易的“安全”方案了。

密钥一般是如下几种:

Swarm 支持大小在500 Kb 以内的通用字符串或二进制内容,而且不需要重写应用。

Docker 1.13.0 起 Swarm 中的 Raft DB 数据默认加密存储在硬盘上,并且当服务关闭的时候,通过密钥加密的数据则会被安全地存储起来。

密钥只存储在 Manager 节点的硬盘,而且也只有这些节点才能使用 key 来解密数据。

Swarm 中下发密钥到容器的方式本质上是通过 Manager 和 Worker 节点的 或者加密的 TLS 网络之间的数据交换,而这个连接是使用了 TLS + Mutual PKI 认证,所以通过已有的信道来下发密钥给容器是一种非常安全的方式。

密钥首先是存储在 Swarm 的数据库中,使用 命令,然后派发给服务。

告知 Swarm 谁有权访问这些密钥的关键在于,只有被派发密钥的服务中的容器才能访问它们。由于该项功能是 Docker Engine 内置的,所以 Docker Worker 只会在内存中存储加密过的 key。

看上去密钥像是存储在容器中的文件,然而实际上是内存文件系统。

通过 或 访问,键值对存储。

本地 能够使用基于文件的密钥,但是并不安全,生产环境我们决不能这样做,应该使用 。

显而易见,生产环境中密钥的使用依赖于 Swarm,且只有 Swarm 才能访问密钥。此外 使用密钥时本质上是把密钥挂载到了本地容器的某个文本文件中,尽管这不安全,但能够方便本地开发。

在 Swarm Services 中使用密钥

在 node1 节点上编写一个文本文件 ,存储 PostgreSQL 的用户名,写入:

Swarm 中创建密钥有两种方式,一种是指定某个文件,另一种是命令行里传入值。

先使用第一种方式传入用户名的值:

然后从命令行中传入密码的值:

注意末尾的 dash 符号,它能够让命令从 echo 标准输入中读取数据。

然而这两种方式都是有缺陷的。

第一种方式里,我们事实上是把密钥存到了服务器主机的硬盘上,而这并不是我们想要的。

在第二种方式,我们其实是把密钥存到了 root 用户的 bash 历史里,所以技术上讲,如果某人能够进入 root 账号,那他就能够获取这个密码。

查看当前的密钥列表: 。

查看某个密钥的元信息: 。

这里一旦生成密钥,它将会被存储在 Raft 数据库中,并且只有指定的容器和服务才有权限访问解密后的密钥。

手动创建一个服务,并指定密钥: ,这将会把密钥映射给服务,然后所有该服务的容器都将有权限访问这个密钥,但并不会发送给我们通过这个镜像创建的 PostgreSQL 数据库。那我们该怎么使用这个密钥?

通常情况下,需要对镜像做一些额外配置。本例中,DockerHub 官方的 PGSQL 镜像已经设置好了一个标准,可以直接使用环境变量: 。

如果传入密钥错误,那这里就会不断重新创建容器并输出日志。

我们可以使用 来移除服务的密钥,执行后容器将会重新部署,因为密钥也是服务不可变的一部分。如果需要更新服务中容器的信息,事实上服务会停止容器并重新部署一个新的,并不会进入容器去更新信息。很明显这对于数据库来说并不理想,所以如果需要更新数据库密码的话,就得另想办法,这个之后再说。

在 Swarm Stacks 中使用密钥

在 node1 另起一个文件夹,写入如下文件:

psql_user.txt

psql_password.txt

docker-compose.yml

注意 YAML 的版本我们需要指定为 ,因为从这个版本起 Stacks 才支持在 yml 文件中使用 secrets。

文件的末尾加入了 键,可以在这里定义我们的密钥。这里有两种方式可以定义密钥,要么为每个密钥指定一个密钥文本文件,要么使用之前已经创建好的密钥,具体查看文档。这里我们使用了文本文件的方式,统一将密钥定义在 YAML 文件末尾,然后再在服务中分别使用它们。

执行: ,创建服务集群。

执行: ,可以查看已经创建好的密钥,名称格式为 。

执行: ,将会删除 mydb stack 创建的密钥。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180117G0Z1RH00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com