使用terraform构建和配置AKS工作负载ID环境的示例,以Azure OpenAI为例

首先

在AKS上运行的应用程序和各个Azure服务之间的认证是如何进行的呢?特别是在配置GitOps时,使用密钥和密码容易泄露,这是一个麻烦的问题。微软Azure在2023年4月为解决这个问题推出了”工作负载标识(workload identity)”,用于AKS(Azure Kubernetes Service)。

因此,本次我打算使用 IaC 工具 Terraform 来构建一个使用此工作负载 ID 的 Azure 环境。
主要是根据以下 Microsoft 文档的步骤,使用 Terraform 进行实施。

    ワークロード ID を使用して Azure Kubernetes Service (AKS) クラスターをデプロイして構成する – Azure Kubernetes Service

有关工作负载ID的特点,请参考以下的GA时的文章。

    一般提供開始: AKS での Azure Active Directory ワークロード ID | Azure の更新情報 | Microsoft Azure

建立环境

这次我们将使用terraform来构建一个简单的环境。请修改terraform的本地变量以更改部署资源的名称。有关详细信息,请参考”关于认证流程”。

デプロイリソース

搭建环境的步骤

在这里,我们将使用Azure CloudShell(bash)。CloudShell默认安装了terraform和kubectl。

请在CloudShell中执行以下命令,并将terraform代码复制到以下内容中。
请注意,根据命令中的注释,第一次执行terraform apply将会失败,所以请再次执行以将清单应用到AKS。

# terraformフォルダを作成し移動
mkdir terraform && cd terraform

# terraformの内容を貼り付けて保存
vi aks-workload-id.tf

# terraformを初期化し、デプロイ実行
terraform init && terraform apply

# 1回目ではKubernetes Configが存在せず失敗するので再実行
terraform apply

请看下方所示的Terraform代码拷贝。

# 必要な外部プロバイダーの定義 ※今回はAzApiを利用します。
terraform {
  required_providers {
    azapi = {
      source = "azure/azapi"
    }
  }
}
# 各プロバイダーの設定
provider "azurerm" {
  features {}
}
provider "kubernetes" {
  config_path = "~/.kube/config"
}
provider "azapi" {
}

# ワークロードID関連で利用するローカル変数を定義
locals {
  resource_group_name                = "example"
  resource_group_locaiton            = "Japan East"

  aks_name                           = "example-aks"
  aks_dns_prefix                     = "exampleaks"

  service_account_name               = "workload-identity-sa"
  service_account_namespace          = "default"
  user_assigned_identity_name        = "myIdentity"
  federated_identity_credential_name = "myFedIdentity"

  kubernetes_pod_name                = "test-pod"
  kubernetes_container_name          = "test"
  
  openai_account_name                = "example-openai"
  gpt_35_turbo_deploy_name           = "chatgpt"
  gpt_35_turbo_model_version         = "0613"
}

# リソースグループを作成
resource "azurerm_resource_group" "example" {
  name     = local.resource_group_name
  location = local.resource_group_locaiton
}

# AKSの作成
resource "azurerm_kubernetes_cluster" "example" {
  name                      = local.aks_name
  location                  = azurerm_resource_group.example.location
  resource_group_name       = azurerm_resource_group.example.name
  dns_prefix                = local.aks_dns_prefix
  oidc_issuer_enabled       = true # OIDC issuerを有効化 
  workload_identity_enabled = true # ワークロードIDを有効化

  default_node_pool {
    name       = "default"
    node_count = 1
    vm_size    = "Standard_D2_v2"
  }

  identity {
    type = "SystemAssigned"
  }

  # デプロイ後、Kubernetes Configを作成
  provisioner "local-exec" {
    command = "az aks get-credentials -n ${self.name} -g ${azurerm_resource_group.example.name}"
  }
}

# ユーザ割当マネージドIDの作成
resource "azurerm_user_assigned_identity" "example" {
  name                = local.user_assigned_identity_name
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
}

# Kubernetesのサービスアカウントを作成
resource "kubernetes_service_account" "example" {
  depends_on = [azurerm_kubernetes_cluster.example]

  metadata {
    name      = local.service_account_name
    namespace = local.service_account_namespace
    annotations = {
      "azure.workload.identity/client-id" = azurerm_user_assigned_identity.example.client_id
    }
  }
}

# フェデレーション ID 資格情報を設定
resource "azurerm_federated_identity_credential" "example" {
  depends_on = [kubernetes_service_account.example]

  name                = local.federated_identity_credential_name
  parent_id           = azurerm_user_assigned_identity.example.id
  resource_group_name = azurerm_resource_group.example.name
  audience            = ["api://AzureADTokenExchange"]
  issuer              = azurerm_kubernetes_cluster.example.oidc_issuer_url
  subject             = "system:serviceaccount:${local.service_account_namespace}:${local.service_account_name}"
}

# テスト用のKubernetes Podを作成
resource "kubernetes_pod" "example" {
  depends_on = [azurerm_federated_identity_credential.example]

  metadata {
    name      = local.kubernetes_pod_name
    namespace = local.service_account_namespace
    labels = {
      "azure.workload.identity/use" = "true"
    }
  }

  spec {
    service_account_name = local.service_account_name
    container {
      name    = local.kubernetes_container_name
      image   = "python:3.11"
      command = ["sleep", "3600"]
    }
  }
}

# OpenAIアカウントを作成
resource "azurerm_cognitive_account" "example" {
  name                  = local.openai_account_name
  location              = azurerm_resource_group.example.location
  resource_group_name   = azurerm_resource_group.example.name
  custom_subdomain_name = local.openai_account_name

  kind     = "OpenAI"
  sku_name = "S0"

  identity {
    type = "SystemAssigned"
  }
}

# gpt-35-turbo(ChatGPT)モデルをデプロイ
resource "azapi_resource" "chatgpt" {
  type      = "Microsoft.CognitiveServices/accounts/deployments@2023-05-01"
  name      = local.gpt_35_turbo_deploy_name
  parent_id = azurerm_cognitive_account.example.id

  body = jsonencode({
    properties = {
      model = {
        format  = "OpenAI"
        name    = "gpt-35-turbo"
        version = local.gpt_35_turbo_model_version
      }
      versionUpgradeOption = "NoAutoUpgrade"
    }
    sku = {
      capacity = 120
      name     = "Standard"
    }
  })
}

# ユーザ割当マネージドIDにアクセス権を付与
resource "azurerm_role_assignment" "openai_role_user" {
  scope                = azurerm_cognitive_account.example.id
  role_definition_name = "Cognitive Services OpenAI User"
  principal_id         = azurerm_user_assigned_identity.example.principal_id
}

关于工作负载ID的配置设定

在Azure中的设置

请将`azurerm_kubernetes_cluster`的`oidc_issuer_enabled`和`workload_identity_enabled`设置为`true`,以启用在Terraform中工作负载ID的功能。接下来,需要进行的配置是设置`azurerm_federated_identity_credential`作为联合身份验证凭据。通过此配置,将实现用户分配托管ID与AKS和服务账号的关联。

Kubernetes资源的配置

在Kubernetes资源中,需要将用户分配的托管标识客户端ID设置为服务账户(sa),并将该sa与Pod关联起来,将标签azure.workload.identity/use设置为true。这样,在部署Pod时将注入所需的使用工作负载ID进行身份验证的配置。有关详细信息,请参阅下文的”关于身份验证流程”。

通过示例应用程序对工作负载ID进行认证的示例

接下来,请在Azure Cloud Shell中执行以下命令并进入Pod。

kubectl exec -it test-pod -- /bin/bash

进入Pod之后,请使用pip安装所需的库,并使用apt安装编辑器(如vi等)。

pip install azure-identity openai
apt install vi

请使用安装的编辑器将以下Python代码保存下来。
如果您在terraform中更改了OpenAI资源名称,请将第6行上面的”example-openai”更改为您更改的资源名称;如果您更改了OpenAI的部署名称,请将第12行的engine=”<部署名称>”修改为您更改的名称。
运行此Python代码后,将自动使用工作负载ID进行身份验证,并从Azure OpenAI服务获取到响应。
如您所见,代码中无需进行工作负载ID的任何说明,全部由DefaultAzureCredential处理。

import openai
from azure.identity import DefaultAzureCredential

default_credential = DefaultAzureCredential()

openai.api_base = "https://example-openai.openai.azure.com/"
openai.api_version = "2023-05-15"
openai.api_type = "azure_ad"
openai.api_key = default_credential.get_token("https://cognitiveservices.azure.com/.default").token

response = openai.ChatCompletion.create(
    engine="chatgpt",
    messages=[
        {"role": "user", "content": "こんにちは!"},
    ],
)

print(response.choices[0].message.content)

有关认证流程

使用工作负载ID进行身份验证的流程如下:
当将前述的oidc_issuer_enabled设置为true时,OIDC Provider URL将被公开,从而可以与AzureAD进行集成。
一旦启用OIDC Issuer,就无法禁用它。要禁用它,需要重新创建集群。
另外,将workload_identity_enabled设置为true时,对于标签中azure.workload.identity/use为true的Pod,将注入所需的工作负载ID设置。
注入将仅在创建Pod时执行,所以要进行修改请重新创建Pod本身。

ワークロードID 認証概要

请查阅以下官方文档以获取有关认证流程的详细信息。

    Azure Kubernetes Service (AKS) で Azure AD ワークロード ID を使用する – Azure Kubernetes Service

另外,在制作上述图表时,我们还参考了以下网站。

    • Lab – Workload Identity

 

    Connect your Kubernetes application to your database without any credentials (and securely) – Alexis Plantin – Blog

最后

您认为怎样?由于在官方文档中从认证流程中描述,可能会让人感觉有些难以理解,但是一旦了解了设置,我觉得并不是很困难。
同时,如果您使用Azure Identity的DefaultAzureCredential进行认证,那么在不对现有代码进行更改的情况下,可以将工作负载迁移到工作标识中。我认为您已经知道了这一点。
特别是正如您之前提到的那样,随着日益增加的密钥和密码泄漏风险,我感觉到您有必要考虑迁移,如果您正在使用密钥或密码,请务必考虑迁移。

请提供更多上下文信息。”参考”和”記事”之间是否有关联性?还有其他相关的内容吗?

    • ワークロード ID を使用して Azure Kubernetes Service (AKS) クラスターをデプロイして構成する – Azure Kubernetes Service

 

    Azure Kubernetes Service でAzure AD ワークロード IDを使ってみた – APC 技術ブログ
广告
将在 10 秒后关闭
bannerAds