在 Azure Kubernetes 服务 (AKS) 群集上部署和配置工作负荷标识

Azure Kubernetes 服务 (AKS) 是可用于快速部署和管理 Kubernetes 群集的托管式 Kubernetes 服务。 本文介绍如何:

  • 使用 Azure CLI 部署一个包含 OpenID Connect 颁发者和 Microsoft Entra 工作负载 ID 的 AKS 群集。
  • 创建 Microsoft Entra 工作负载 ID 和 Kubernetes 服务帐户。
  • 配置托管标识以进行令牌联合身份验证。
  • 部署工作负载并使用工作负载标识验证身份。
  • (可选)向群集中的 Pod 授予访问 Azure Key Vault 中机密的权限。

本文假定你对 Kubernetes 概念有基本的了解。 有关详细信息,请参阅 Azure Kubernetes 服务 (AKS) 的 Kubernetes 核心概念。 如果你不熟悉 Microsoft Entra Workload ID,请参阅下面的概述一文。

先决条件

  • 如果没有 Azure 试用版订阅,请在开始前创建一个
  • 本文需要 2.47.0 或更高版本的 Azure CLI。
  • 确保用于创建群集的标识具有合适的的最低权限。 有关 AKS 的访问和标识的详细信息,请参阅 Azure Kubernetes 服务 (AKS) 的访问和标识选项
  • 如果有多个 Azure 订阅,请使用 az account set 命令选择应在其中计收资源费用的相应订阅 ID。

备注

可以使用服务连接器自动配置某些步骤

创建资源组

Azure 资源组是用于部署和管理 Azure 资源的逻辑组。 创建资源组时,系统会提示你指定一个位置。 此位置是资源组元数据的存储位置,也是资源在 Azure 中运行的位置(如果你在创建资源期间未指定其他区域)。

通过调用 az group create 命令创建资源组。

export RANDOM_ID="$(openssl rand -hex 3)"
export RESOURCE_GROUP="myResourceGroup$RANDOM_ID"
export LOCATION="eastus"
az group create --name "${RESOURCE_GROUP}" --location "${LOCATION}"

以下输出示例显示成功创建资源组:

结果:

{
  "id": "/subscriptions/<guid>/resourceGroups/myResourceGroup",
  "location": "eastus",
  "managedBy": null,
  "name": "myResourceGroup",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}

创建 AKS 群集

使用带有 参数的 --enable-oidc-issuer 命令创建 AKS 群集,以启用 OIDC 颁发者。 以下示例会创建具有一个节点的群集:

export CLUSTER_NAME="myAKSCluster$RANDOM_ID"
az aks create \
    --resource-group "${RESOURCE_GROUP}" \
    --name "${CLUSTER_NAME}" \
    --enable-oidc-issuer \
    --enable-workload-identity \
    --generate-ssh-keys

片刻之后,该命令将会完成,并返回有关群集的 JSON 格式信息。

备注

创建 AKS 群集时,会自动创建另一个资源组来存储 AKS 资源。 有关详细信息,请参阅为何使用 AKS 创建两个资源组?

更新现有的 AKS 群集

可以通过调用带有 --enable-oidc-issuer 参数的 --enable-workload-identity 命令更新 AKS 群集,以使用 OIDC 颁发者并启用工作负载标识。

检索 OIDC 颁发者 URL

若要获取 OIDC 颁发者 URL 并将其保存到环境变量,请运行以下命令:

export AKS_OIDC_ISSUER="$(az aks show --name "${CLUSTER_NAME}" \
    --resource-group "${RESOURCE_GROUP}" \
    --query "oidcIssuerProfile.issuerUrl" \
    --output tsv)"

环境变量应包含类似于以下示例的证书颁发者 URL:

https://eastus.oic.prod-aks.azure.com/00000000-0000-0000-0000-000000000000/11111111-1111-1111-1111-111111111111/

默认情况下,颁发者设置为使用基 URL https://{region}.oic.prod-aks.azure.com/{tenant_id}/{uuid},其中 {region} 的值与部署 AKS 群集的位置相匹配。 值 {uuid} 表示 OIDC 密钥,它是为每个集群随机生成并且不可变的唯一标识符。

创建管理标识

调用 az identity create 命令创建托管标识。

export SUBSCRIPTION="$(az account show --query id --output tsv)"
export USER_ASSIGNED_IDENTITY_NAME="myIdentity$RANDOM_ID"
az identity create \
    --name "${USER_ASSIGNED_IDENTITY_NAME}" \
    --resource-group "${RESOURCE_GROUP}" \
    --location "${LOCATION}" \
    --subscription "${SUBSCRIPTION}"

以下输出示例演示如何成功创建托管标识:

结果:

{
  "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroupxxxxxx/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentityxxxxxx",
  "location": "eastus",
  "name": "myIdentityxxxxxx",
  "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "resourceGroup": "myResourceGroupxxxxxx",
  "systemData": null,
  "tags": {},
  "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
}

接下来,为托管标识客户端 ID 创建一个变量。

export USER_ASSIGNED_CLIENT_ID="$(az identity show \
    --resource-group "${RESOURCE_GROUP}" \
    --name "${USER_ASSIGNED_IDENTITY_NAME}" \
    --query 'clientId' \
    --output tsv)"

创建 Kubernetes 服务帐户

创建一个 Kubernetes 服务帐户,并使用在上一步创建的托管标识的客户端 ID 对其进行批注。 使用 az aks get-credentials 命令,并替换群集名称和资源组名称的值。

az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}"

将以下多行输入复制粘贴到 Azure CLI 中。

export SERVICE_ACCOUNT_NAMESPACE="default"
export SERVICE_ACCOUNT_NAME="workload-identity-sa$RANDOM_ID"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
  name: "${SERVICE_ACCOUNT_NAME}"
  namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
EOF

成功创建工作负载标识后的输出如下所示:

serviceaccount/workload-identity-sa created

创建联合标识凭据

调用 az identity federated-credential create 命令在托管标识、服务帐户颁发者和使用者之间创建联合标识凭据。

export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity$RANDOM_ID"
az identity federated-credential create \
    --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \
    --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \
    --resource-group "${RESOURCE_GROUP}" \
    --issuer "${AKS_OIDC_ISSUER}" \
    --subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}" \
    --audience api://AzureADTokenExchange

备注

联合标识凭据在添加后需要几秒钟的时间才能传播。 如果在添加联合标识凭据后立即发出令牌请求,则在刷新缓存之前,请求可能会失败。 若要避免此问题,可以在添加联合标识凭据后添加轻微的延迟。

部署应用程序

部署应用程序 Pod 时,清单应引用在创建 Kubernetes 服务帐户步骤中创建的服务帐户。 以下清单演示如何引用帐户,特别是 metadata\namespace 和 spec\serviceAccountName 属性。 请确保为 <image> 指定映像,并为 <containerName> 指定容器名称:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-workload-identity
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}  # Replace with your namespace
  labels:
    azure.workload.identity/use: "true"  # Required. Only pods with this label can use workload identity.
spec:
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}  # Replace with your service account name
  containers:
    - name: rabbitmq  # Replace with your container name
      image: mcr.azk8s.cn/mirror/docker/library/rabbitmq:3.10-management-alpine  # Replace with your Docker image
      ports:
        - containerPort: 5672
          name: rabbitmq-amqp
        - containerPort: 15672
          name: rabbitmq-http
      env:
        - name: RABBITMQ_DEFAULT_USER
          value: "username"
        - name: RABBITMQ_DEFAULT_PASS
          value: "password"
      resources:
        requests:
          cpu: 10m
          memory: 128Mi
        limits:
          cpu: 250m
          memory: 256Mi
EOF

重要

确保使用工作负载标识的应用程序 Pod 在 Pod 规范中包含标签 azure.workload.identity/use: "true"。否则,Pod 在重启后将失败。

授予访问 Azure Key Vault 的权限

此步骤中的说明演示如何从 Pod 访问 Azure Key Vault 中的机密、密钥或证书。 本部分中的示例为工作负载标识配置对密钥保管库中机密的访问权限,但你可以执行类似的步骤来配置对密钥或证书的访问权限。

以下示例演示如何使用 Azure 基于角色的访问控制 (Azure RBAC) 权限模型向 Pod 授予访问密钥保管库的权限。 有关 Azure Key Vault 的 Azure RBAC 权限模型的详细信息,请参阅使用 Azure RBAC 授予应用程序访问 Azure Key Vault 的权限

  1. 创建启用了清除保护功能和 RBAC 授权的新密钥保管库。 如果为清除保护功能和 RBAC 授权配置了现有密钥保管库,也可以使用它:

    export KEYVAULT_NAME="keyvault-workload-id$RANDOM_ID"
    # Ensure the key vault name is between 3-24 characters
    if [ ${#KEYVAULT_NAME} -gt 24 ]; then
        KEYVAULT_NAME="${KEYVAULT_NAME:0:24}"
    fi
    az keyvault create \
        --name "${KEYVAULT_NAME}" \
        --resource-group "${RESOURCE_GROUP}" \
        --location "${LOCATION}" \
        --enable-purge-protection \
        --enable-rbac-authorization 
    
  2. 将 RBAC 密钥保管库机密主管角色分配给自己,以便可以在新的密钥保管库中创建机密:

    export KEYVAULT_RESOURCE_ID=$(az keyvault show --resource-group "${KEYVAULT_RESOURCE_GROUP}" \
        --name "${KEYVAULT_NAME}" \
        --query id \
        --output tsv)
    
    export CALLER_OBJECT_ID=$(az ad signed-in-user show --query id -o tsv)
    
    az role assignment create --assignee "${CALLER_OBJECT_ID}" \
        --role "Key Vault Secrets Officer" \
        --scope "${KEYVAULT_RESOURCE_ID}"
    
  3. 在密钥保管库中创建机密:

    export KEYVAULT_SECRET_NAME="my-secret$RANDOM_ID"
    az keyvault secret set \
        --vault-name "${KEYVAULT_NAME}" \
        --name "${KEYVAULT_SECRET_NAME}" \
        --value "Hello\!"
    
  4. 密钥保管库机密用户角色分配给之前创建的用户托管标识。 此步骤授予托管标识从密钥保管库读取机密的权限:

    export IDENTITY_PRINCIPAL_ID=$(az identity show \
        --name "${USER_ASSIGNED_IDENTITY_NAME}" \
        --resource-group "${RESOURCE_GROUP}" \
        --query principalId \
        --output tsv)
    
    az role assignment create \
        --assignee-object-id "${IDENTITY_PRINCIPAL_ID}" \
        --role "Key Vault Secrets User" \
        --scope "${KEYVAULT_RESOURCE_ID}" \
        --assignee-principal-type ServicePrincipal
    
  5. 为密钥保管库 URL 创建环境变量:

    export KEYVAULT_URL="$(az keyvault show \
        --resource-group ${RESOURCE_GROUP} \
        --name ${KEYVAULT_NAME} \
        --query properties.vaultUri \
        --output tsv)"
    
  6. 部署一个引用服务帐户和密钥保管库 URL 的 Pod:

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
        name: sample-workload-identity-key-vault
        namespace: ${SERVICE_ACCOUNT_NAMESPACE}
        labels:
            azure.workload.identity/use: "true"
    spec:
        serviceAccountName: ${SERVICE_ACCOUNT_NAME}
        containers:
          - image: ghcr.io/azure/azure-workload-identity/msal-go
            name: oidc
            env:
              - name: KEYVAULT_URL
                value: ${KEYVAULT_URL}
              - name: SECRET_NAME
                value: ${KEYVAULT_SECRET_NAME}
        nodeSelector:
            kubernetes.io/os: linux
    EOF
    

若要检查 webhook 是否正确注入了所有属性,请使用 kubectl describe 命令:

kubectl wait --namespace ${SERVICE_ACCOUNT_NAMESPACE} --for=condition=Ready pod/sample-workload-identity-key-vault --timeout=120s
kubectl describe pod sample-workload-identity-key-vault | grep "SECRET_NAME:"

如果成功,输出应类似于以下示例:

      SECRET_NAME:                 ${KEYVAULT_SECRET_NAME}

若要验证 Pod 是否能够获取令牌和访问资源,请使用 kubectl logs 命令:

kubectl logs sample-workload-identity-key-vault

如果成功,输出应类似于以下示例:

I0114 10:35:09.795900       1 main.go:63] "successfully got secret" secret="Hello\\!"

重要

Azure RBAC 角色分配可能需要最多十分钟的时间进行传播。 如果该 Pod 无法访问机密,则可能需要等待角色分配生效。 有关详细信息,请参阅 Azure RBAC 疑难解答

禁用工作负载标识

若要禁用已启用和配置的 AKS 群集上的 Microsoft Entra 工作负荷 ID,请使用--disable-workload-identity命令设置az aks update参数来更新 AKS 群集。

后续步骤

在本文中,你部署了 Kubernetes 群集并将其配置为使用工作负荷标识,为应用程序工作负荷使用该凭据进行身份验证做好准备。 现在,你已准备好部署应用程序并将其配置为将工作负荷标识与最新版 Azure 标识客户端库配合使用。 如果无法重写应用程序以使用最新的客户端库版本,则可以设置应用程序 Pod,以使用托管标识和工作负荷标识作为短期迁移解决方案进行身份验证。