使用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本身。
请查阅以下官方文档以获取有关认证流程的详细信息。
- 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 技術ブログ