🎄

Terraform でディレクトリ構成を意識しながら Azure OpenAI Service の閉域環境を作る

2023/12/21に公開

本記事は、Microsoft Azure Tech Advent Calendar 2023[1] の 21 日目の記事です。

https://qiita.com/advent-calendar/2023/microsoft-azure-tech

はじめに

最近 IaC にハマっており、Bicep では飽き足らず Terraform に手を出し始めた zukako です。今回は、Microsoft Azure Tech Advent Calender 2023 にも関わらず Terraform を触る系の話です。構築する対象としては、Azure OpenAI Service(AOAI) の閉域化環境としました。ネットをいろいろと検索してみたのですが、まだ Terraform x Azure OpenAI Service の記事が少なそうに見えたので、ちょうどいい規模感かな~と思った次第です。閉域化といっても、独自のデータで Chat モデルを利用する、所謂「Add Your Data」の部分は含みませんのでご了承ください。イメージとしては過去の記事[2] で触れているようなシンプルなものです。

また、Terraform を扱うにあたってある程度ディレクトリ構成も意識をしてみました。この規模の環境であればそこまで意識する必要はないかもしれないのですが、ご参考まで。

環境

アーキテクチャ全体像

全体像としては以下のようなイメージになります。とてもシンプルです。

デプロイされるリソース一覧はこちらになります。

ソースコード

今回の環境をデプロイするためのファイルはこちらに配置しております。
https://github.com/zukakosan/terraform-learn/tree/main/20231201-aoai-private

ディレクトリ構成

今回は Terraform らしい構成にするために敢えて以下のような構成としました。

.
├── env
│   ├── dev
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── provider.tf
│   │   ├── variables.tf
│   │   └── dev.tfvars
│   └── prod
│        :(省略)
├── vnet
│   ├── main.tf
│   └── outputs.tf
└── vm
|   ├── main.tf
│   └── outputs.tf
└── aoai
    └── main.tf

実装上のポイント

各種リソースの module 化

これくらいの規模であれば module として読み込む必要があるのか微妙なところですが、なるべく疎結合になるようにしてみました。例えば vnet の作成は以下のように module として呼び出しています。resource_group_name は variable に同じ文字列を持っているため、そこから読み込むこともできるのですが、リソースグループ作成に対して依存関係を示すために azurerm_resource_group のインスタンスから参照しています。

module "vnet" {
  source = "../../vnet"
  resource_group_name = azurerm_resource_group.aoai.name
  location = var.location
  vnet_name = var.vnet_name
  address_space = var.address_space
  jumpbox_subnet_address_space = var.jumpbox_subnet_address_space
  pe_subnet_address_space = var.pe_subnet_address_space
  client_ip = var.client_ip
}

module 間の値の受け渡し

それぞれのパーツを module で作成しているため、作成したリソースの id の参照ができません。そこで output を使って値を渡しています。例えば Azure OpenAI Service 用の module では Private Endpoint や Private DNS Zone のために Virtual Network や Subnet の id が必要になるのですが、そこは vnet/outputs.tf で出力した値を拾ってきています。

module "aoai" {
  source = "../../aoai"
  resource_group_name = azurerm_resource_group.aoai.name
  location = var.location
  suffix = var.env
  vnet_id = module.vnet.vnet_id
  subnet_id = module.vnet.pe_subnet_id
  random_id = random_id.aoai.hex
}

Azure OpenAI Service のデプロイ

azurerm プロバイダーでは、azurerm_coginitive_account というリソースを参照し、kind = OpenAI とすることで Azure Open AI Service をデプロイします。Azure Open AI Service を Azure Portal で利用しているユーザからすると、ここは若干わかりにくいかもしれません。そして、パブリックアクセスを拒否したいので、public_network_access_enabled = false も忘れずに入れておきます。

 resource "azurerm_cognitive_account" "aoai" {
  name                = "aoai-${var.suffix}"
  location            = var.location
  resource_group_name = var.resource_group_name
  kind                = "OpenAI"
  sku_name            = "S0"
  custom_subdomain_name = "aoai-${var.random_id}-${var.suffix}"
  public_network_access_enabled = false
}

また、Azure OpenAI Service のリソースだけでなく、その内部で利用する chat モデルも azurerm_cognitive_deployment リソースでデプロイすることが可能[3]です。

resource "azurerm_cognitive_deployment" "chat" {
  name                 = "aoai-${var.suffix}-chat-model"
  cognitive_account_id = azurerm_cognitive_account.aoai.id
  model {
    format  = "OpenAI"
    name    = "gpt-35-turbo"
    version = "0613"
  }
  scale {
    type = "Standard"
  }
}

subnet における Private Endpoint Policy の有効化

azurerm_subnet リソースでは、subnet に対して Private Endpoint Policy は既定で有効[4]になるようです。よって、jumpbox 用の subnet では Private Endpoint Policy を無効化しています。ここも Azure Portal との違いですね。

resource "azurerm_subnet" "jumpbox" {
  resource_group_name  = var.resource_group_name
  virtual_network_name = azurerm_virtual_network.workload.name
  name                 = "subnet-jumpbox"
  address_prefixes     = [var.jumpbox_subnet_address_space]
  private_endpoint_network_policies_enabled = false
}

動作検証

環境のデプロイ

dev.tfvars は GitHub に載せていないので、以下をサンプルとします。

env                          = "dev"
rg_name                      = "rg-aoai-dev"
vnet_name                    = "vnet-aoai-dev"
address_space                = "172.16.0.0/16"
jumpbox_subnet_address_space = "172.16.0.0/24"
pe_subnet_address_space      = "172.16.1.0/24"
admin_username               = "azureuser"
admin_password               = "P@ssw0rd1234!"
client_ip                    = "x.x.x.x"

/envs/dev/ に移動して以下の流れで実行します。暫らくするとリソースが出来上がります。output として、jumpbox となる VM の Public IP が出力されます。

$ terraform init
$ terraform plan
$ terraform apply -var-file="dev.tfvars"
...
...
...
vm_pip = "x.x.x.x"

ネットワーク閉域構成の確認

手元の PC から Azure OpenAI Studio にアクセスしてチャットをしてみると、ネットワークの外部からの接続に該当するため、想定通りエラーにより応答が得られません。

一方で Terraform でデプロイした jumpbox からの場合は、同じネットワーク内であるためチャットができています。

おわりに

今回は、Microsoft Azure Advent Calender 2023 にかこつけて Terraform で Azure OpenAI Service の閉域お試し環境を(折角なのでディレクトリ構成も少しこだわりながら)作ってみました。 Terraform では Azure OpenAI Service のリソース定義まわりが若干癖があるのが注意点といったところでしょうか。またその他の細かいところについてはソースコードをご参考にいただければと思います。では、よいお年を!

脚注
  1. https://qiita.com/advent-calendar/2023/microsoft-azure-tech ↩︎

  2. https://zenn.dev/microsoft/articles/198989f60eba61 ↩︎

  3. https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment ↩︎

  4. https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet#private_endpoint_network_policies_enabled ↩︎

GitHubで編集を提案
Microsoft (有志)

Discussion