如果网络端点的健康检查失败,自动重新启动实例

這篇文章是 Google Cloud 平台 Advent Calendar 2020 的第四天的文章。
由於第四天的負責人一直沒有發文,所以我隨便來填一下。
原本似乎會有一篇關於 Cloud SQL 的文章被發表,但是筆者對關聯式數據庫管理系統不感興趣,所以我決定寫一些自己喜歡的事情。

您是否正在使用 Network Endpoint Group(NEG)?

筆者在能夠使用之後,選擇使用 NEG 而非 Unmanaged Instance Group,通過 BackendService 來使用 VM 實例。

雖然在 Unmanaged Instance Group 也可以實現相同的功能,但筆者之所以選擇使用 NEG,是因為 NEG 不僅適用於 VM 實例,還可以應用於其他無服務器服務,具有廣泛應用性。

尽管在Google Cloud Load Balancing(GCLB)上使用NEG可以使用VM实例,但与Managed Instance Group(MIG)不同的是,它不能自动重新启动实例,当健康检查失败时。
在本文中,我们将介绍如何更好地使用NEG使其达到生产可用性的目标。

如果你已经理解了”Uptime checks, Notification channels, PubSub, Cloud Functions”这些词的含义,那么你就没有必要阅读下面的内容了。

所有的事件都应该通往PubSub。

如果能够将事件发布到PubSub,无论要做什么,基本上都可以确定胜利。这可能有三成是虚假的。在这里,我们利用Cloud Monitoring的Uptime checks来监控实例,并在健康检查失败时将事件通知到PubSub,然后使用Cloud Functions来接收并重置相应的实例。

准备好

准备应用程序在VM实例上。

由于只在容器优化操作系统(COS)上部署单个容器,无需特殊处理,它会自动重启。因此,在这里我们将使用Docker Compose启动两个容器,一个是WordPress,另一个是MySQL。
我们将尝试使用以下Compose定义。
为简单起见,忽略了数据持久化。
如果觉得麻烦,可以在任何适当的操作系统上安装一个Web服务器,并运行systemctl enable命令。

version: "3"

services:
  wordpress:
    hostname: wordpress
    image: wordpress:5-php7.2-apache
    ports:
      - 80:80
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: password
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_TABLE_PREFIX: wp_
  mysql:
    hostname: mysql
    image: mysql:5
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: password
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"

将此文件保存在COS主机操作系统上的可写区域。
在这里保存为 /var/wordpress.yaml。

接下来,为了在COS上使用Docker Compose,禁用”live restore”并重新启动Docker。

$ sudo sed -i 's/true/false/g' /etc/docker/daemon.json

$ sudo systemctl restart docker 

之后就像往常一样。

$ docker swarm init
...

$ docker stack deploy -c /var/wordpress.yaml wordpress
Creating network wordpress_default
Creating service wordpress_wordpress
Creating service wordpress_mysql

$ docker stack services wordpress
ID                  NAME                  MODE                REPLICAS            IMAGE                       PORTS
h4tnwbuvamru        wordpress_mysql       replicated          1/1                 mysql:5
ugg0mpp6sqs5        wordpress_wordpress   replicated          1/1                 wordpress:5-php7.2-apache   *:80->80/tcp

$ curl -I localhost/readme.html
HTTP/1.1 200 OK
Date: Wed, 09 Dec 2020 14:26:06 GMT
Server: Apache/2.4.38 (Debian)
Last-Modified: Fri, 26 Jun 2020 13:58:02 GMT
ETag: "1c6e-5a8fd18ee0e80"
Accept-Ranges: bytes
Content-Length: 7278
Vary: Accept-Encoding
Content-Type: text/html

顺便说一句,由于/etc/docker/daemon.json文件在实例重启时会恢复到原始状态,因此我们应该在实例的启动脚本中进行修改。具体来说,请将以下内容写入实例的元数据startup-script中。


sed -i 's/true/false/g' /etc/docker/daemon.json &&\
docker stack deploy -c /var/wordpress.yaml wordpress

這樣一來,實例已經準備好了。

创建网络端点组并将实例关联

从这里开始,我们将使用命令行进行操作。
请根据需要替换网络等内容。
请指定与实例的区域相同的区域。这里我们将使用asia-northeast1-b。

创建NEG

$ gcloud compute network-endpoint-groups create wordpress \
  --zone=asia-northeast1-b \
  --subnet=default \
  --network-endpoint-type=GCE_VM_IP_PORT \
  --default-port=80

创建网络端点。

$ gcloud compute network-endpoint-groups update wordpress \
  --zone=asia-northeast1-b \
  --add-endpoint=instance=<インスタンス名>,ip=<インスタンス内部IP>,port=80

启动GCLB

因为在命令行中进行的话会非常冗长,所以我决定放弃。
重点在于以下几点:
* 使用 Zonal Network Endpoint Group 作为 Backend Service 来使用 wordpress。
* 将健康检查协议设置为HTTP,路径设置为 /readme.html。

image.png

请在GCLB的IP地址上成功进行健康检查,并确认能够在WordPress的初始设置页面上显示。

创建PubSub主题、运行时间检查、通知渠道。

为执行实例的健康检查,创建 Uptime checks
如果 Uptime checks 失败,则发送通知到 Notification channel
创建要接收通知的 PubSub Topic

发布-订阅主题

$ gcloud pubsub topics create notification

通知渠道

image.png

运行时间检查

image.png

设定必要的IAM

根据文件要求,将Monitoring的Service Account配置为PubSub Topic通知的消息发布角色。

当创建第一个 Pub/Sub 渠道时,Cloud Monitoring 会为创建渠道的项目创建一个用于监控通知服务代理的服务账号。

有一件事,但一直完不成。
不过,我曾经见过完成的情况,所以这可能还是个β版本。
何谓production ready呢,呜哇哇哇。

通过 Cloud Functions 实现自动重启。

我虽然不太擅长,但是我用Node.js写了代码。
我会从PubSub的消息中获取相关实例的区域等信息,并进行重置。
被重置的实例将通过启动脚本启动所需的容器。

const Compute = require('@google-cloud/compute');
const compute = new Compute();

exports.resetInstance = async (event, context, callback) => {
  const data = JSON.parse(Buffer.from(event.data, 'base64').toString());
  const vm   = await getVm(data);

  try {
    await vm.reset();
  } catch (err) {
    console.error(err);
    callback(err);
  }
}

async function getVm(data) {
  const zone       = compute.zone(data.incident.resource.labels.zone);
  const vmname     = data.incident.resource_display_name;
  const vminstance = zone.vm(vmname);

  return vminstance;
}
{
  "name": "instance-auto-restarter",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "@google-cloud/compute": "^2.4.0"
  }
}

我本想部署这个东西,停止容器以进行Uptime检查,但应该自动生成的服务帐户没有被创建,无法测试其功能。

一旦创建了服务账号,如果我记得的话,我会追加写上的。

bannerAds