使用DaemonSet定制AKS工作节点

很多时候我们需要在AKS (Azure Kubernetes Service)工作节点上运行支持软件,例如,恶意软件扫描程序,Policy Enforcer等。目前AKS的工作节点只能使用云平台指定的操作系统镜像,不能直接用自定义的镜像来定制工作节点。常规操作pod被隔离在容器以内,不能影响其所在的宿主机。这篇文章受到Kured 项目的启发,引导您完成使用守护程序集引导AKS集群的过程,以使其成为可能。

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

原理

Kubernetes中的DaemonSets允许您在每个节点上运行pod; 如果要引导新节点并安装软件,这是一个很好的选择。 您可以配置与DaemonSet一起运行的特权,并根据需要执行的任务来调整DaemonSet所需的访问级别。

如果需要在运行容器的主机上运行命令,则可以使用nsenter命令。 作为能够执行此操作的先决条件,您需要确保DaemonSet容器具有提升的特权。可以通过在Daemonset YAML中将hostPID = true和privileged = true设置为高权限,如下所示。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: installer
  namespace: node-installer
spec:
  selector:
matchLabels:
  job: installer
  template:
metadata:
  labels:
job: installer
spec:
  hostPID: true
  restartPolicy: Always
  containers:
  - image: snowpeak.azurecr.cn/node-installer:centos
name: installer
securityContext:
  privileged: true
volumeMounts:
- name: install-script
  mountPath: /tmp
- name: host-mount
  mountPath: /host
  volumes:
  - name: install-script
configMap:
  name: sample-installer-config
  - name: host-mount
hostPath:
  path: /tmp/install

这样就可以获得在宿主机执行命令的权限,再通过configMap来设置要执行的命令。比如这里有2个配置文件。一个configmap_cowsay.yaml用于执行安装 cowsay 这个纯用作输出文本的小程序。

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-installer-config
  namespace: node-installer
data:
  install.sh: |
#!/bin/bash

# Update and install packages
apt-get update
apt-get install cowsay -y

另一个configmap_nginx.yaml用于执行安装 nginx,这2个配置文件安装的程序都只用作演示。咱们可以根据实际工作需要,以及实际宿主机的操作系统,扩展成其它需要初始化AKS工作节点的命令。

基本部署

应用

演示用的源码在这里: https://github.com/xfsnow/container/tree/master/AKSNodeInstaller

Azure资源

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

定义环境变量

REGION_NAME=ChinaEast2
RESOURCE_GROUP=aksNodeInstaller
AKS_CLUSTER_NAME=NodeInstaller
ACR_NAME=NodeInstaller$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
--------  ----------------  -----------  -----  -------------------  --------------------  ---------------
NodeInstaller1044  aksNodeInstaller  chinaeast2  Standard  nodeinstaller.azurecr.cn  2021-03-28T14:37:32Z  False

创建 AKS 集群

az aks create \
    --resource-group $RESOURCE_GROUP \
    --name $AKS_CLUSTER_NAME \
    --location $REGION_NAME \
    --node-count 1 \
    --attach-acr $ACR_NAME

参数解释一下:

--resource-group 指定资源组。

--name AKS集群的名称。

--location AKS集群所在的区域。

--node-count AKS集群中工作节点台数,作为演示用的集群,我们这个集群只指定1台。实际生产环境中可以指定多台,使用DaemonSet可以同时更新多台工作节点。

--attach-acr 绑定的容器镜像注册表ACR的资源,以便后续可以由这个 AKS集群直接从这个ACR拉取镜像。

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

看一下创建好的 AKS集群

az aks show --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER_NAME -o table
Name           Location     ResourceGroup     KubernetesVersion    ProvisioningState    Fqdn
-------------  -----------  ----------------  -------------------  -------------------  ------
NodeInstaller  chinaeast2  aksNodeInstaller  1.18.14              Succeeded       xxxx.azk8s.cn

再看一下AKS集群背后的虚机扩展组,及里面的虚机。

CLUSTER_RESOURCE_GROUP=$(az aks show --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER_NAME --query nodeResourceGroup -o tsv)
SCALE_SET_NAME=$(az vmss list --resource-group $CLUSTER_RESOURCE_GROUP --query '[0].name' -o tsv)

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

az vmss list-instances -g $CLUSTER_RESOURCE_GROUP -n $SCALE_SET_NAME -o table \
--query "[].{instanceId:instanceId, Name:name, State:provisioningState}"
InstanceId    Name                           State
------------  -----------------------------  ---------
0   aks-nodepool1-29740120-vmss_0  Succeeded

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

部署Kubernetes

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

git clone https://github.com/xfsnow/AKSNodeInstaller

拉取源码。

先构建并推送镜像

cd app
IMAGE_NAME=node-installer
az acr build --file Dockerfile_centos \
    --resource-group $RESOURCE_GROUP \
    --registry $ACR_NAME \
    --image $IMAGE_NAME:1.0 .

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

az acr repository show -n $ACR_NAME --repository $IMAGE_NAME --query "registry"
"nodeinstaller11044.azurecr.cn"

记下这个形如nodeinstaller11044.azurecr.cn的URL,后面接上node-installer:1.0就是完整的镜像拉取地址,形如 “acr11044.azurecr.cn/node-installer:1.0”。

找到 daemonset_centos.yaml 中的这行

# 容器镜像使用 Azure 上的 ACR 服务。
- image: snowpeak.azurecr.cn/node-installer:centos

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

连接AKS集群并部署镜像

连接AKS集群

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

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

kubectl get nodes -o wide –-namespace node-installer
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    <none>        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 ./k8s/ 来到Kubernetes 配置文件目录下。

kubectl apply -f daemonset_centos.yaml 和kubectl apply -f configmap_cowsay.yaml 部署到 AKS 集群中。部署好的集群是这样的。

kubectl get pods -o wide --namespace node-installer
NAME                          READY   STATUS    RESTARTS   AGE   IP           NODE                                NOMINATED NODE   READINESS GATES
installer-c68jv   1/1     Running   0          32s   10.244.0.8   aks-nodepool1-40474697-vmss000000   <none>           <none>

查看一下 pod 日志

kubectl logs installer-c68jv --namespace node-installer
……
The following NEW packages will be installed:
  cowsay
0 upgraded, 1 newly installed, 0 to remove and 14 not upgraded.
Need to get 17.7 kB of archives.
After this operation, 89.1 kB of additional disk space will be used.
Get:1 http://azure.archive.ubuntu.com/ubuntu bionic/universe amd64 cowsay all 3.03+dfsg2-4 [17.7 kB]
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
dpkg-preconfigure: unable to re-open stdin:
Fetched 17.7 kB in 1s (24.6 kB/s)
Selecting previously unselected package cowsay.
(Reading database ... 121200 files and directories currently installed.)
Preparing to unpack .../cowsay_3.03+dfsg2-4_all.deb ...
Unpacking cowsay (3.03+dfsg2-4) ...
Setting up cowsay (3.03+dfsg2-4) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...

可以看到日志最后显示 cowsay 已安装成功。

登录并验证

下面我们通过SSH远程连接上AKS的工作节点,最终验证一下 cowsay已经成功安装。使用 kubectl debug 在节点上运行特权容器。在之前的步骤中我们曾查看过当前的工作节点名称是aks-nodepool1-40474697-vmss000000。

1. 使用 kubectl debug 在节点上运行容器镜像以连接到该容器。

kubectl debug node/aks-nodepool1-40474697-vmss000000
 -it --image=mcr.microsoft.com/dotnet/runtime-deps:6.0
Creating debugging pod node-debugger-aks-nodepool1-40474697-vmss000000-5bw76 with container debugger on node aks-nodepool1-40474697-vmss000000.
If you don't see a command prompt, try pressing enter.
root@aks-nodepool1-40474697-vmss000000:/#

2. 在这个特权容器运行 chroot /host 命令获取隔离的根路径,安全地与工作节点会话进行交互。注意切换根路径后,再执行命令需要使用绝对路径,不能使用命令快捷名字了。可以使用exec bash来从dash切换到bash,就恢复正常的命令了。如:

root@aks-nodepool1-40474697-vmss000000:/# chroot /host
# exec bash
root@aks-nodepool1-40474697-vmss000000:/# cowsay -l
Cow files in /usr/share/cowsay/cows:
apt bud-frogs bunny calvin cheese cock cower daemon default dragon
dragon-and-cow duck elephant elephant-in-snake eyes flaming-sheep
ghostbusters gnu hellokitty kiss koala kosh luke-koala mech-and-cow milk
moofasa moose pony pony-smaller ren sheep skeleton snowman stegosaurus
stimpy suse three-eyes turkey turtle tux unipony unipony-smaller vader
vader-koala www

证明在VM工作节点上cowsay确实安装成功了。

当然这个cowsay只是一个纯演示用的小程序,没有什么实际用处。大家可以根据自己的需求再编辑或创建其它的configmap.yaml文件,比如源码中还有一个configmap_nginx.yaml可以用来安装 nginx。只需要删除现有的DaemonSet再重新执行相关的配置文件就可以了。

资源清理

az group delete --name $RESOURCE_GROUP --yes

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