本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

支持 Azure VMSS、Cluster-Autoscaler 和用户分配的身份

引言

随着 Kubernetes v1.12 的发布,Azure 虚拟机规模集(VMSS)和集群自动扩缩器已达到通用可用性(GA),用户分配的身份作为预览功能提供。

Azure VMSS 允许您创建和管理相同、负载均衡的虚拟机,这些虚拟机根据需求或预定计划自动增加或减少。这使您可以轻松管理和扩缩多个虚拟机,以提供高可用性和应用程序弹性,非常适合容器工作负载等大规模应用程序 [1]

集群自动扩缩器允许您根据负载条件自动调整 Kubernetes 集群的大小。

v1.12 带来的另一个令人兴奋的功能是能够将用户分配的身份与 Kubernetes 集群一起使用 [12]

在本文中,我们将简要概述 Azure 上的 VMSS、集群自动扩缩器和用户分配的身份功能。

VMSS

Azure 的虚拟机规模集(VMSS)功能允许用户从单个中心配置自动创建虚拟机,通过 L4 和 L7 负载均衡提供负载均衡,提供使用可用性区域实现高可用性的途径,提供大规模虚拟机实例等。

VMSS 由一组相同的虚拟机组成,可以在组级别进行管理和配置。有关 Azure 中此功能的更多详细信息,请参见以下链接 [1]

通过 Kubernetes v1.12,客户可以利用 VMSS 实例创建 k8s 集群并使用 VMSS 功能。

Azure 上的集群组件

通常,Azure 中的独立 Kubernetes 集群由以下部分组成

  • 计算 - 虚拟机本身及其属性。
  • 网络 - 包括 IP 和负载均衡器。
  • 存储 - 与虚拟机关联的磁盘。

计算

云 k8s 集群中的计算由虚拟机组成。这些虚拟机由 acs-engine 或 AKS(对于托管服务)等预配工具创建。最终,它们作为进程(在某些版本中)或作为 docker 容器运行各种系统守护程序,例如 kubelet、kube-api server 等。

网络

在 Azure Kubernetes 集群中,各种网络组件协同工作,为用户提供所需功能。它们通常包括网络接口、网络安全组、公共 IP 资源、VNET(虚拟网络)、负载均衡器等。

存储

Kubernetes 集群建立在 Azure 中创建的磁盘之上。在典型配置中,我们使用托管磁盘来保存常规 OS 镜像,并使用单独的磁盘用于 etcd。

云提供商组件

Kubernetes 云提供商接口提供与云的交互,用于管理云特定资源,例如公共 IP 和路由。这些组件的良好概述可在 [2] 中找到。对于 Azure Kubernetes 集群,Kubernetes 交互通过 Azure 云提供商层进行,并联系云中运行的各种服务。

K8s 的云提供商实现可大致分为以下需要实现的组件接口

  1. 负载均衡器
  2. 实例
  3. 区域
  4. 路由

除了上述接口外,云提供商的存储服务通过卷插件层连接。

Azure 云提供商实现和 VMSS

在 Azure 云提供商中,对于我们实现的每种集群类型,都有一个我们指定的 VMType 选项。对于 VMSS,VM 类型为“vmss”。预配软件(acs-engine,未来 AKS 等)将在 /etc/kubernetes/azure.json 文件中设置这些值。根据此类型,将实例化各种实现 [3]

负载均衡器接口提供对底层云提供商负载均衡器服务的访问。Kubernetes 需要有关负载均衡器的信息及其控制操作,以处理托管在 Kubernetes 集群上的服务。对于 VMSS 支持,更改确保 VMSS 实例按需成为负载均衡器池的一部分。

实例接口帮助云控制器从云提供商层获取有关节点的各种详细信息。例如,通过云提供商层向控制器注册的实例接口,控制器可以获取节点的 IP 地址、实例 ID 等详细信息。在 VMSS 支持的情况下,我们与 VMSS 服务通信以收集有关实例的信息。

区域接口帮助云控制器获取每个节点的区域信息。调度器可以使用这些信息将 Pod 分散到不同的可用区。它也是支持拓扑感知动态预配功能(例如 AzureDisk)所必需的。每个 VMSS 实例都将标记其当前区域和区域。

路由接口帮助云控制器为 Pod 网络设置高级路由。例如,将为每个节点设置一个前缀为节点 podCIDR 且下一跳为节点内部 IP 的路由。在 VMSS 支持的情况下,下一跳是 VMSS 虚拟机的内部 IP 地址。

Azure 卷插件接口已修改,以使 VMSS 正常工作。例如,附加/分离到 AzureDisk 的操作已修改为在 VMSS 实例级别执行这些操作。

在 Azure 上设置 VMSS 集群

以下链接 [4] 提供了一个使用 acs-engine 创建 Kubernetes 集群的示例。

acs-engine deploy --subscription-id <subscription id> \
    --dns-prefix <dns> --location <location> \
    --api-model examples/kubernetes.json

API 模型文件提供了 acs-engine 用于创建集群的各种配置。此处 [5] 的 API 模型提供了设置 VMSS 集群的良好初始配置。

VMSS 集群创建完成后,您可以运行一些步骤来了解更多关于集群设置的信息。以下是使用上述命令创建的集群的 kubectl get nodes 输出

$ kubectl get nodes
NAME                                 STATUS    ROLES     AGE       VERSION
k8s-agentpool1-92998111-vmss000000   Ready     agent     1h        v1.12.0-rc.2
k8s-agentpool1-92998111-vmss000001   Ready     agent     1h        v1.12.0-rc.2
k8s-master-92998111-0                Ready     master    1h        v1.12.0-rc.2

该集群由两个工作节点和一个主节点组成。那么我们如何在 Azure 术语中检查哪个节点是哪个呢?在 VMSS 列表中,我们可以看到一个 VMSS

$ az vmss list -o table -g k8sblogkk1
Name                          ResourceGroup    Location    Zones      Capacity  Overprovision    UpgradePolicy
----------------------------  ---------------  ----------  -------  ----------  ---------------  ---------------
k8s-agentpool1-92998111-vmss  k8sblogkk1       westus2                       2  False            Manual

我们看到的作为代理的节点(在 kubectl get nodes 命令中)是此 vmss 的一部分。我们可以使用以下命令列出作为虚拟机规模集一部分的实例

$ az vmss list-instances -g k8sblogkk1 -n k8s-agentpool1-92998111-vmss -o table
  InstanceId  LatestModelApplied    Location    Name                            ProvisioningState    ResourceGroup    VmId
------------  --------------------  ----------  ------------------------------  -------------------  ---------------  ------------------------------------
           0  True                  westus2     k8s-agentpool1-92998111-vmss_0  Succeeded            K8SBLOGKK1       21c57d6c-9c8f-4a62-970f-63ed0fcba53f
           1  True                  westus2     k8s-agentpool1-92998111-vmss_1  Succeeded            K8SBLOGKK1       840743b9-0076-4a2e-920e-5ba9da296665

节点名称与虚拟机规模集中的名称不匹配,但是如果我们运行以下命令列出 providerID,我们可以找到与实例名称相似的匹配节点

$  kubectl describe nodes k8s-agentpool1-92998111-vmss000000| grep ProviderID
ProviderID:                  azure:///subscriptions/<subscription id>/resourceGroups/k8sblogkk1/providers/Microsoft.Compute/virtualMachineScaleSets/k8s-agentpool1-92998111-vmss/virtualMachines/0

现状与未来

目前支持以下功能

  1. VMSS 主节点和工作节点
  2. VMSS 在工作节点上,可用性集在主节点上组合。
  3. 每个虚拟机磁盘连接
  4. Azure 磁盘和 Azure 文件支持
  5. 可用区(Alpha)

将来将支持以下功能

  1. AKS 支持 VMSS
  2. 每个 VM 实例公共 IP

集群自动扩缩器

Kubernetes 集群由节点组成。这些节点可以是虚拟机、裸机服务器,甚至可以是虚拟节点(虚拟 kubelet)。为了避免在 Kubernetes 生态系统的排列组合中迷失方向 ;-), 让我们考虑我们正在讨论的集群由托管在云中(例如:Azure、Google 或 AWS)的虚拟机组成。这实际意味着您可以访问运行 Kubernetes 代理的虚拟机和运行 API 服务器等 k8s 服务的主节点。k8s 架构的详细版本可在此处找到 [11]

集群所需的节点数量取决于集群上的工作负载。当负载增加时,需要增加节点;当负载减小时,需要减少节点并清理不再使用的资源。一种解决方法是手动扩缩 Kubernetes 集群中的节点,并在需求减少时手动缩减。但这不应该自动完成吗?这个问题的答案是集群自动扩缩器 (CA)。

集群自动扩缩器本身作为 Pod 在 Kubernetes 集群中运行。下图说明了与 k8s 集群相关的设置的高级视图

由于集群自动扩缩器是 k8s 集群中的一个 Pod,它可以使用集群内部配置和 Kubernetes go 客户端 [10] 来联系 API 服务器。

内部机制

API 服务器是利用后端存储(etcd 数据库)管理 k8s 集群状态的中心服务,它运行在管理节点上或云中(对于 AKS 等托管服务)。对于 Kubernetes 集群中的任何组件,要了解集群的状态,例如集群中注册的节点,联系 API 服务器是可行的方法。

为了简化我们的讨论,我们将 CA 功能分为以下 3 个部分

CA 的主要部分是一个控制循环,它在每个扫描间隔持续运行。此循环负责更新自动扩缩指标和健康探测。在进入此循环之前,自动扩缩器执行各种操作,例如在执行 Kubernetes 领导选举后声明领导者状态。主循环初始化静态自动扩缩器组件。此组件根据传递给 CA 的参数初始化底层云提供商。

CA 执行的各种管理集群状态的操作会传递给云提供商组件。一些例子,例如 - 增加目标大小,减小目标大小等,会导致云提供商组件在内部与云服务通信,并执行添加节点或删除节点等操作。这些操作在集群中的节点组上执行。静态自动扩缩器还通过查询 API 服务器来跟踪系统状态 - 例如列出 pod 和列出节点等操作用于获取此类信息。

扩容的决定是基于未调度的 Pod 和各种检查与平衡。可以缩减的空闲节点将从集群中删除,并从云本身删除。集群自动扩缩器在扩容和缩容之前会进行检查和平衡——例如,最近添加的节点会得到特殊考虑。在删除过程中,节点会被耗尽,以确保正在运行的 Pod 不受干扰。

在 Azure 上设置 CA

集群自动扩缩器作为 acs-engine 的附加组件提供。以下链接 [15] 包含一个用于使用 acs-engine 部署自动扩缩器的示例配置文件。以下链接 [8] 提供了手动逐步操作的详细信息。

在 acs-engine 的情况下,我们使用常规命令行部署

acs-engine deploy --subscription-id <subscription id> \
    --dns-prefix <dns> --location <location> \
    --api-model examples/kubernetes.json

主要区别在于配置文件中 [15] 的以下几行确保 CA 作为插件部署

"addons": [
          {
            "name": "cluster-autoscaler",
            "enabled": true,
            "config": {
              "minNodes": "1",
              "maxNodes": "5"
            }
          }
        ]

上述 json 中的 config 部分可用于向集群自动扩缩器 Pod 提供配置,例如:如上所示的最小和最大节点数。

设置完成后,我们可以看到 cluster-autoscaler pod 已部署在系统命名空间中

$kubectl get pods -n kube-system  | grep autoscaler
cluster-autoscaler-7bdc74d54c-qvbjs             1/1       Running             1          6m

这是来自示例集群的 CA configmap 和事件的输出

$kubectl -n kube-system describe configmap cluster-autoscaler-status
Name:         cluster-autoscaler-status
Namespace:    kube-system
Labels:       <none>
Annotations:  cluster-autoscaler.kubernetes.io/last-updated=2018-10-02 01:21:17.850010508 +0000 UTC

Data
====
status:
----
Cluster-autoscaler status at 2018-10-02 01:21:17.850010508 +0000 UTC:
Cluster-wide:
  Health:      Healthy (ready=3 unready=0 notStarted=0 longNotStarted=0 registered=3 longUnregistered=0)
               LastProbeTime:      2018-10-02 01:21:17.772229859 +0000 UTC m=+3161.412682204
               LastTransitionTime: 2018-10-02 00:28:49.944222739 +0000 UTC m=+13.584675084
  ScaleUp:     NoActivity (ready=3 registered=3)
               LastProbeTime:      2018-10-02 01:21:17.772229859 +0000 UTC m=+3161.412682204
               LastTransitionTime: 2018-10-02 00:28:49.944222739 +0000 UTC m=+13.584675084
  ScaleDown:   NoCandidates (candidates=0)
               LastProbeTime:      2018-10-02 01:21:17.772229859 +0000 UTC m=+3161.412682204
               LastTransitionTime: 2018-10-02 00:39:50.493307405 +0000 UTC m=+674.133759650

NodeGroups:
  Name:        k8s-agentpool1-92998111-vmss
  Health:      Healthy (ready=2 unready=0 notStarted=0 longNotStarted=0 registered=2 longUnregistered=0 cloudProviderTarget=2 (minSize=1, maxSize=5))
               LastProbeTime:      2018-10-02 01:21:17.772229859 +0000 UTC m=+3161.412682204
               LastTransitionTime: 2018-10-02 00:28:49.944222739 +0000 UTC m=+13.584675084
  ScaleUp:     NoActivity (ready=2 cloudProviderTarget=2)
               LastProbeTime:      2018-10-02 01:21:17.772229859 +0000 UTC m=+3161.412682204
               LastTransitionTime: 2018-10-02 00:28:49.944222739 +0000 UTC m=+13.584675084
  ScaleDown:   NoCandidates (candidates=0)
               LastProbeTime:      2018-10-02 01:21:17.772229859 +0000 UTC m=+3161.412682204
               LastTransitionTime: 2018-10-02 00:39:50.493307405 +0000 UTC m=+674.133759650


Events:
  Type    Reason          Age   From                Message
  ----    ------          ----  ----                -------
  Normal  ScaleDownEmpty  42m   cluster-autoscaler  Scale-down: removing empty node k8s-agentpool1-92998111-vmss000002

从事件中可以看出,由于此集群上没有负载,集群自动扩缩器缩减并删除了一个节点。在这种情况下,configmap 的其余部分表示自动扩缩器目前没有采取进一步的行动。

现状与未来

集群自动扩缩器目前支持四种虚拟机类型:标准(VMAS)、VMSS、ACS 和 AKS。未来,集群自动扩缩器将集成到 AKS 产品中,用户可以一键启用。

用户分配的身份

为了使 Kubernetes 集群组件能够安全地与云服务通信,它需要使用云提供商进行身份验证。在 Azure Kubernetes 集群中,到目前为止,这通过两种方式完成——服务主体或托管标识。对于服务主体,凭据存储在集群中,并且存在密码轮换和其他用户需要承担的挑战才能适应此模型。托管服务标识消除了用户的负担,并直接管理服务实例 [12]

有两种类型的托管身份可能存在——一种是系统分配的,另一种是用户分配的。在系统分配身份的情况下,Kubernetes 集群中的每个虚拟机在创建时都会分配一个托管身份。此身份由需要访问 Azure 资源的各种 Kubernetes 组件使用。这些操作的示例包括获取/更新负载均衡器配置、获取/更新虚拟机信息等。对于系统分配的托管身份,用户无法控制分配给底层虚拟机的身份。系统会自动分配它,这降低了用户的灵活性。

通过 v1.12,我们为 Kubernetes 带来了用户分配的托管身份支持。有了此支持,用户无需管理任何密码,同时又可以灵活地管理集群使用的身份。例如,如果用户需要允许集群访问特定的存储帐户或 Azure 密钥保管库,则可以提前创建用户分配的身份并提供密钥保管库访问权限。

内部机制

为了理解内部机制,我们将重点关注使用 acs-engine 创建的集群。这可以通过其他方式进行配置,但基本交互模式相同。

acs-engine 使用所需配置设置集群。/etc/kubernetes/azure.json 文件为集群组件(例如:kube-apiserver)提供了一种获取如何访问云资源的配置方式。在用户管理身份集群中,有一个键为 UserAssignedIdentityID 的值。此值填充了由 acs-engine 创建或由用户提供的用户分配身份的客户端 ID,具体情况视情况而定。用于 Kubernetes 在 Azure 上进行身份验证的代码可在此处找到 [14]。此代码使用 Azure adal 包进行身份验证以访问云中的各种资源。在用户分配身份的情况下,会进行以下 API 调用以获取新令牌

adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint,
env.ServiceManagementEndpoint,
config.UserAssignedIdentityID)

此调用会访问实例元数据服务或虚拟机扩展 [12],以获取用于访问各种资源的令牌。

使用用户分配的身份设置集群

随着 v1.12 中用户分配身份的上游支持,现在 acs-engine 支持创建具有用户分配身份的集群。此处 [13] 提供的 json 配置文件可用于创建具有用户分配身份的集群。创建 vmss 集群的相同步骤可用于创建具有用户分配身份的集群。

acs-engine deploy --subscription-id <subscription id> \
    --dns-prefix <dns> --location <location> \
    --api-model examples/kubernetes-msi-userassigned/kube-vmss.json

这里的主要配置值为以下各项

"useManagedIdentity": true
"userAssignedID": "acsenginetestid"

第一个 useManagedIdentity 指示 acs-engine 我们将使用托管身份扩展。这设置了托管身份工作所需的必要包和扩展。下一个 userAssignedID 提供有关将与集群一起使用的用户身份的信息。

现状与未来

目前我们支持使用 acs-engine 的部署在集群中创建用户分配的身份。未来这将成为 AKS 的一部分。

参与其中

有关 Azure 特定讨论,请查看 [6] 上的 Azure SIG 页面,并加入 #sig-azure Slack 频道了解更多信息。

对于 CA,请查看此处的自动扩缩器项目 [7],并加入 #sig-autoscaling Slack 频道进行更多讨论。

有关 Azure 上的 acs-engine(非托管版本)文档,请参见此处:[9]。有关 Azure Kubernetes 服务(AKS)托管服务的更多详细信息,请参见此处 [5]

参考资料

  1. https://learn.microsoft.com/azure/virtual-machine-scale-sets/overview

  2. /docs/concepts/architecture/cloud-controller/

  3. https://github.com/kubernetes/kubernetes/blob/release-1.17/staging/src/k8s.io/legacy-cloud-providers/azure/azure_vmss.go

  4. https://github.com/Azure/acs-engine/blob/master/docs/kubernetes/deploy.md

  5. https://learn.microsoft.com/azure/aks/

  6. https://github.com/kubernetes/community/tree/master/sig-azure

  7. https://github.com/kubernetes/autoscaler

  8. https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/azure/README.md

  9. https://github.com/Azure/acs-engine

  10. https://github.com/kubernetes/client-go

  11. /docs/concepts/architecture/

  12. https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview

  13. https://github.com/Azure/acs-engine/tree/master/examples/kubernetes-msi-userassigned

14)https://github.com/kubernetes/kubernetes/blob/release-1.17/staging/src/k8s.io/legacy-cloud-providers/azure/auth/azure_auth.go

  1. https://github.com/Azure/acs-engine/tree/master/examples/addons/cluster-autoscaler