在微软云AKS上部署弹性应用

Azure Kubernetes 服务 (AKS)是微软云Azure上托管的Kubernetes 群集,可以用于快速部署Kubernetes 群集,结合Azure其它服务和功能,简化日常运维,轻松实现业务应用的弹性。本文是一个动手小实验,演示弹性部署的基本步骤。适用的场景是在AKS背后的虚拟机意外停机时,通过Kubernetes 的配置实现自动故障转移。

阅读本文需要掌握Kubernetes的基本知识和操作,以及AKS的基本概念部署

基本部署

应用

演示用的应用源码在这里

https://github.com/xfsnow/pipelines-javascript-docker/

请先fork到自己的GitHub账号下。

这个应用是个非常简单的Node.js 应用,用来输出时间和运行的主机名,用以演示负载均衡后具体运行在哪个后端上。主要文件只有一个

https://github.com/xfsnow/pipelines-javascript-docker/blob/a0aa063b97d6d6819c2adcdcea9b11e47959a86b/app/server.js#L19

输出内容就是下面这行:

res.send('Hello world! Now is '+now+'.\nRunning on '+os.hostname()+'.');

Kubernetes的部署文件主要是manifests/hello-deployment.yml,以 4 个replica部署一个deployment。这个配置文件的声明和作用都写在相关注释里了。核心原理是使用

通过 toleration 来检测节点状态,一旦发现节点不可用,则不再置放 pod

tolerations:

- key: "node.kubernetes.io/unreachable"

operator: "Exists"

effect: "NoExecute"

tolerationSeconds: 1

- key: "node.kubernetes.io/not-ready"

operator: "Exists"

effect: "NoExecute"

tolerationSeconds: 1

以及

健康检查,通过 httpGet 检查应用运行状态

livenessProbe:

httpGet:

port: 80

scheme: HTTP

path: /

初始化延迟 3 秒,用于应用首次启动时等待的时间,避免首次启动慢误判为失败。

initialDelaySeconds: 10

之后检查的间隔时间

periodSeconds: 1

超时多久认为是失败

timeoutSeconds: 3

另有一个hello-service.yml,部署了一个负载均衡器,暴露出公网IP对外提供服务。这个负载均衡器也还有健康检查,在发现后端pod不可用时,会把其撤掉,然后再把新生成的pod挂载上来。

Azure资源

一个ACR,用于保存容器镜像,注意目前只有东2区域可以支持az acr build 命令在本地直接构建并推送到ACR。所以我们创建的ACR资源在东2区,而AKS集群可以在任何一个国内的区域。

定义环境变量

REGION_NAME=ChinaEast2

RESOURCE_GROUP=aksResilience

AKS_CLUSTER_NAME=aksResilience

ACR_NAME=acr$RANDOM

创建资源组

az group create --location $REGION_NAME --name $RESOURCE_GROUP

创建ACR

az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --location $REGION_NAME --sku Standard

列表看一下结果

az acr list -o table

NAME RESOURCE GROUP LOCATION SKU LOGIN SERVER CREATION DATE ADMIN ENABLED

-------- ---------------- ----------- ----- ------------------- -------------------- ---------------

acr11044 aksResilience chinaeast2 Standard acr11044.azurecr.cn 2021-02-05T03:12:36Z False

创建 AKS 集群

az aks create \

--resource-group $RESOURCE_GROUP \

--name $AKS_CLUSTER_NAME \

--location $REGION_NAME \

--node-count 2 \

--enable-addons monitoring \

--generate-ssh-keys

这步比较慢,请耐心等待,直到有返回结果。

列一下 AKS集群

az aks list -o table

Name Location ResourceGroup KubernetesVersion ProvisioningState Fqdn

------------- ----------- --------------- ------------------- -------------------

aksResilience chinanorth2 aksResilience 1.18.14 Succeeded

把 ACR 绑定到AKS集群上,这样就可以自动验证从ACR拉取镜像到AKS集群了

az aks update \

--name $AKS_CLUSTER_NAME \

--resource-group $RESOURCE_GROUP \

--attach-acr $ACR_NAME

这步比较慢,请耐心等待,直到有返回结果。

到此,Azure资源已创建完毕,总共1个AKS集群,包含1个虚拟机扩展集,其中有2台虚拟机。

列一下 AKS集群背后的虚拟机扩展集。注意这里的虚拟机扩展集由Azure自动创建在另一个资源组里。

az vmss list -o table

Name ResourceGroup Location Zones Capacity Overprovision UpgradePolicy

--------------------------- ------------------------------------------ ----------- ------- ---------- --------------- ---------------

aks-nodepool1-40474697-vmss MC_AKSRESILIENCE_AKSRESILIENCE_CHINANORTH2 chinanorth2 2 False Manual

最后列一下虚拟机扩展集中的实例

az vmss list-instances \

-g MC_AKSRESILIENCE_AKSRESILIENCE_CHINANORTH2 \

-n aks-nodepool1-40474697-vmss \

-o table \

--query "[].{instanceId:instanceId, Name:name, State:provisioningState}"

InstanceId Name State

------------ ----------------------------- ---------

0 aks-nodepool1-40474697-vmss_0 Succeeded

1 aks-nodepool1-40474697-vmss_1 Succeeded

部署Kubernetes

构建镜像推送到镜像注册表。

git clone https://github.com/xfsnow/pipelines-javascript-docker/

拉取源码。

先构建并推送镜像

cd app

az acr build \

--resource-group $RESOURCE_GROUP \

--registry $ACR_NAME \

--image helloworld:1.0 .

然后获取镜像注册表具体的URL

az acr repository show -n $ACR_NAME --repository helloworld --query "registry"

"acr11044.azurecr.cn"

记下这个形如acr11044.azurecr.cn的URL,后面接上helloworld:1.0就是完整的镜像拉取地址,形如

acr11044.azurecr.cn/helloworld:1.0

cd manifests,

找到 hello-deployment.yml 中的这行

容器镜像使用 Azure 上的 ACR 服务。

image: snowpeak.azurecr.cn/helloworld:1.3

替换成你自己的 ACR 镜像的URL “acr11044.azurecr.cn/helloworld:1.0”。

连接AKS集群并部署镜像

连接AKS集群

az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER_NAME

先查看一下AKS集群中的节点

kubectl get nodes -o wide

NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUN

TIME

aks-nodepool1-40474697-vmss000000 Ready agent 142m v1.18.14 10.240.0.4 Ubuntu 18.04.5 LTS 5.4.0-1035-azure docker://19.3

.14

aks-nodepool1-40474697-vmss000001 Ready agent 142m v1.18.14 10.240.0.5 Ubuntu 18.04.5 LTS 5.4.0-1035-azure docker://19.3

.14

再看一下pod。

kubectl get pods -o wide

No resources found in default namespace.

此时尚未部署镜像,所以没有资源。

然后用 cd ../manifests/ 来到Kubernetes 配置文件目录下。

kubectl apply -f hello-deployment.yml和 kubectl apply -f hello-service.yml 部署到 AKS 集群中。部署好的集群初始是这样的。

kubectl get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

helloworld-869d58f588-5tmpf 1/1 Running 0 71s 10.244.1.6 aks-nodepool1-40474697-vmss000001 <none> <none>

helloworld-869d58f588-bt54r 1/1 Running 0 71s 10.244.1.7 aks-nodepool1-40474697-vmss000001 <none> <none>

helloworld-869d58f588-qh6hn 1/1 Running 0 71s 10.244.0.6 aks-nodepool1-40474697-vmss000000 <none> <none>

helloworld-869d58f588-w8xbx 1/1 Running 0 71s 10.244.0.7 aks-nodepool1-40474697-vmss000000 <none> <none>

这是运行起来的4个pod,每个节点上2个pod。

kubectl get services

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

helloworld LoadBalancer 10.0.116.133 139.217.112.135 80:30754/TCP 7d17h

这是部署起来的LoadBalancer,外网IP 139.217.112.135 可以直接访问,返回的结果如下:

curl http://139.217.112.135/;

Hello world! Now is 2021-02-04 08:26:52.924.

Running on helloworld-745c979464-q54vd.

这里Running on helloworld-745c979464-q54vd. 正是上述1个pod的名称。

如果连续请求多次,会看到请求大致均匀地分发到4个后端pod上。

while(true); do curl --connect-timeout 2 http://139.217.112.135/; echo -e '\n'; sleep 0.5; done

Hello world! Now is 2021-02-04 08:34:19.523.

Running on helloworld-745c979464-q54vd.

Hello world! Now is 2021-02-04 08:34:20.160.

Running on helloworld-745c979464-ljrcv.

Hello world! Now is 2021-02-04 08:34:20.798.

Running on helloworld-745c979464-97v26.

Hello world! Now is 2021-02-04 08:34:21.427.

Running on helloworld-745c979464-q54vd.

Hello world! Now is 2021-02-04 08:34:22.56.

Running on helloworld-745c979464-l2jpm.

Hello world! Now is 2021-02-04 08:34:22.697.

Running on helloworld-745c979464-ljrcv.

Hello world! Now is 2021-02-04 08:34:23.368.

Running on helloworld-745c979464-l2jpm.

Hello world! Now is 2021-02-04 08:34:24.6.

Running on helloworld-745c979464-ljrcv.

Hello world! Now is 2021-02-04 08:34:24.659.

Running on helloworld-745c979464-l2jpm.

模拟故障

我们手工停止一台虚拟机,模拟虚拟机发生故障的情况。

  1. 我们先打开实时观察 pod 运行状况

kubectl get po -o wide -w

  1. 再开一个新窗口运行

while(true); do curl --connect-timeout 2 http://139.217.112.135/; echo -e '\n'; sleep 0.5; done

来看访问应用的情况

  1. 再开一个窗口执行停止一台虚拟机的命令

az vmss stop -g MC_DEMO_HELLOWORLD_CHINANORTH2 -n aks-agentpool-11798053-vmss --instance-id 0

  1. 回到第一个窗口,查看 pod 运行情况,看到类似如下的返回。

kubectl get po -o wide -w

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

helloworld-9bbdbf45b-2hnz4 1/1 Running 0 119s 10.240.0.152 aks-agentpool-11798053-vmss000001

helloworld-9bbdbf45b-64qt4 1/1 Terminating 0 6m41s 10.240.0.106 aks-agentpool-11798053-vmss000000

helloworld-9bbdbf45b-gczpn 1/1 Running 0 6m37s 10.240.0.135 aks-agentpool-11798053-vmss000001

helloworld-9bbdbf45b-l6dxm 1/1 Terminating 0 5m11s 10.240.0.68 aks-agentpool-11798053-vmss000000

helloworld-9bbdbf45b-rhn2m 1/1 Terminating 0 4m45s 10.240.0.42 aks-agentpool-11798053-vmss000000

helloworld-9bbdbf45b-smtft 1/1 Running 0 118s 10.240.0.185 aks-agentpool-11798053-vmss000001

helloworld-9bbdbf45b-tmdkr 1/1 Running 0 119s 10.240.0.149 aks-agentpool-11798053-vmss000001

第1个节点上的pod显示正在停止,而新的pod运行在第2个节点上。

  1. 再来到运行curl的窗口查看

while(true); do curl --connect-timeout 2 http://139.217.112.135/; echo -e '\n'; sleep 0.5; done

curl: (28) Connection timed out after 2013 milliseconds

curl: (28) Connection timed out after 2009 milliseconds

Hello world! Now is 2021-02-04 01:40:37.752.

Running on helloworld-9bbdbf45b-2vjxt.

Hello world! Now is 2021-02-04 01:40:38.409.

Running on helloworld-9bbdbf45b-vpvn6.

Hello world! Now is 2021-02-04 01:40:39.57.

Running on helloworld-9bbdbf45b-2vjxt.

Hello world! Now is 2021-02-04 01:40:39.736.

在出现若干次超时后,返回结果恢复正常了,并且主机名出现了新的pod名称。

仔细查看输出内容的时间,可以看到超时经历的时间不超过1分钟。

  1. 最后我们再恢复节点运行

可以看到原来停止的节点上的pod显示为正常Terminating并最终消失,现有4个pod都运行在第2个节点上。可以通过设置replicas 来重新均衡置放pod。

资源清理

az group delete --name $RESOURCE_GROUP --yes

删除该资源组及其所有资源,实验结束。