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

支持 Azure VMSS、集群自动缩放器和用户分配的标识

引言

在 Kubernetes v1.12 中,Azure 虚拟机规模集 (VMSS) 和集群自动缩放器已达到正式发布 (GA) 状态,并且用户分配的身份作为预览功能提供。

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

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

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

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

VMSS

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

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

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

Azure 上的集群组件

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

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

计算

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

网络

在 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 用于创建集群的各种配置。此处的 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

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

$ 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

节点名称与 VM 规模集中的名称不匹配,但是如果我们运行以下命令列出 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. 每个 VM 磁盘连接
  4. Azure 磁盘和 Azure 文件支持
  5. 可用区(Alpha)

未来将支持以下功能

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

集群自动缩放器

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

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

集群自动缩放器本身作为 Kubernetes 集群内的 Pod 运行。下图说明了关于 k8s 集群的设置的高级视图

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

内部

API 服务器是管理 k8s 集群状态的中心服务,它利用后备存储(etcd 数据库),在管理节点上运行或在云中运行(在 AKS 等托管服务的情况下)。对于 Kubernetes 集群中的任何组件来说,要确定集群的状态,例如集群中注册的节点,与 API 服务器联系是唯一的方法。

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

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 中的配置部分可用于为集群自动伸缩器 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 指示自动伸缩器目前没有采取进一步的操作。

当前状态和未来

集群自动伸缩器目前支持四种 VM 类型:标准 (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,但情况可能如此。用于在 Azure 上为 Kubernetes 进行身份验证的代码可以在这里找到 [14]。此代码使用 Azure adal 包来获取身份验证以访问云中的各种资源。在用户分配的身份的情况下,会调用以下 API 调用来获取新令牌

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

这会调用实例元数据服务或虚拟机扩展 [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/zh-cn/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/zh-cn/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/zh-cn/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