本文发表已超过一年。较旧的文章可能包含过时内容。请检查页面信息自发布以来是否已过时。

支持 Azure VMSS、Cluster-Autoscaler 和用户分配标识

引言

借助 Kubernetes v1.12,Azure 虚拟机规模集 (VMSS) 和集群自动扩缩器 (cluster-autoscaler) 已达到通用可用性 (GA),而用户分配标识 (User Assigned Identity) 已作为预览功能提供。

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

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

v1.12 带来的另一个令人兴奋的功能是能够在 Kubernetes 集群中使用用户分配标识 (User Assigned Identities) [12]

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

VMSS (虚拟机规模集)

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

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

借助 Kubernetes v1.12,客户可以基于 VMSS 实例创建 Kubernetes 集群并利用 VMSS 功能。

Azure 上的集群组件

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

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

计算

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

网络

在 Azure Kubernetes 集群中,各种网络组件被组合在一起,以提供用户所需的功能。通常包括网络接口、网络安全组、公共 IP 资源、VNET(虚拟网络)、负载均衡器等。

存储

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

云提供商组件

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

Kubernetes 的云提供商实现主要可以分为以下需要实现的组件接口:

  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 用于创建集群的各种配置。这里的 API 模型 [5] 提供了一个设置 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

我们看到的作为 agent 的节点(在 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,主节点使用可用性集 (Availability set) 的组合。
  3. 每虚拟机磁盘附加
  4. Azure Disk 和 Azure File 支持
  5. 可用性区域 (Alpha)

未来将支持以下功能:

  1. 支持 VMSS 的 AKS
  2. 每虚拟机实例公共 IP

集群自动扩缩器 (Cluster Autoscaler)

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

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

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

由于 Cluster Autoscaler 是 Kubernetes 集群中的一个 Pod,它可以利用集群内配置和 Kubernetes go client [10] 与 API server 通信。

内部机制

API server 是管理 Kubernetes 集群状态的中心服务,它利用后端存储(etcd 数据库),运行在管理节点或云中(对于托管服务如 AKS)。Kubernetes 集群内的任何组件要了解集群状态,例如集群中注册的节点,都必须通过 API server 进行通信。

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

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

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

进行扩容的决策基于未调度的 Pod 和各种检查与平衡。可以缩减的节点将从集群中删除,并从云中本身删除。集群自动扩缩器在扩容和缩容之前会进行检查和平衡 - 例如,对最近添加的节点会给予特殊考虑。在删除过程中,节点会被排空 (drain),以确保不会对正在运行的 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 已部署在 system 命名空间中:

$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 的其余部分表明自动扩缩器目前没有采取进一步行动。

当前状态和未来

Cluster Autoscaler 当前支持四种虚拟机类型:standard (VMAS)、VMSS、ACS 和 AKS。将来,Cluster Autoscaler 将集成到 AKS 产品中,以便用户可以通过一键启用它。

用户分配标识 (User Assigned Identity)

为了让 Kubernetes 集群组件能够安全地与云服务通信,它需要向云提供商进行身份验证。在 Azure Kubernetes 集群中,到目前为止,这是通过两种方式完成的 - 服务主体 (Service Principals) 或托管标识 (Managed Identities)。在使用服务主体的情况下,凭据存储在集群内,用户需要承担密码轮换和其他挑战来适应这种模式。托管服务标识减轻了用户的这一负担,直接管理服务实例 [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)

此调用会请求实例元数据服务或 VM 扩展 [12] 来获取令牌,然后使用此令牌访问各种资源。

使用用户分配的标识设置集群

随着 v1.12 版本上游对用户分配的标识的支持,acs-engine 现在也支持使用用户分配的标识创建集群。这里的 JSON 配置文件 [13] 可用于创建带有用户分配的标识的集群。创建 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 特定讨论,请访问 Azure SIG 页面:[6],并加入 #sig-azure Slack 频道以获取更多信息。

有关 CA,请查看 Autoscaler 项目:[7],并加入 #sig-autoscaling Slack 进行更多讨论。

关于 Azure 上的 acs-engine(非托管类型)文档可以在这里找到:[9]。关于 Azure Kubernetes 服务 (AKS) 托管服务的更多详细信息在这里:[5]

参考

  1. https://learn.microsoft.com/en-us/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/en-us/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/en-us/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