自社開発サービスSaaSTipsクラウド

Infrastructure as a Code実践!!ZenOneのインフラ環境をTerraformでコード化してみた1

TEDが提供している「運用保守DXプラットフォームZenOne(ゼンワン)」のインフラ環境のIaC化に関する内容になります。

みなさん、こんにちは。東京エレクトロンデバイス(以下TED)で開発エンジニアをしている T.Tanakaです。

今回はTEDが提供している「運用保守DXプラットフォームZenOne(ゼンワン)」のインフラ環境に関する内容になります。

ZenOneのインフラ環境は、クラウド上に構築しています。開発を始めた当初から、GUIを使って環境を作っていましたが、以下のような課題点を抱えていました。

  • 環境を構築する度に設定漏れが出てくる

  • 用途別(本番環境 or 開発環境 or 検証環境)で環境を作成する際に工数がかかる

  • GUIでの環境構築ではレビューが入らない

これら課題点を解決するため、インフラ環境のコード化に取り組み始めました(コード化することのメリットに関してはこちらで詳細に解説しています: Terraformとは?メリットやおすすめの形態を徹底解説

とはいえ、周りにTerraformに関して詳しい人は多かったものの、私自身Terraformを書いた経験は一切なく、なにから始めたらいいのか全くわからない状況でした。また、ZenOneのインフラは以下のようなサービスを含む40以上のリソースを使って構築されており、各リソースには多数の設定項目があります。

  • サーバーレス関数

  • NoSQL型データベース

  • リレーショナルデータベース

  • ストレージ

  • 仮想マシン

  • 仮想ネットワーク

  • シークレット管理

  • デバイスマネジメント

試行錯誤しながらこれらリソースのコード化を進めているので、その中で工夫した点などをご紹介していきます。今回は、ループ処理を使ってコードを簡潔にする方法をご紹介します。

Terraformのコードの書き方を学習していると、resourceブロックで作りたいリソースを定義し、設定を記入すればリソースを作成できるということがわかるのですが、ひたすらこのresourceブロックを書いていくとコードが冗長になる場合があります。例えば、同じサービスで複数のリソースを作成する場合、中身がほとんど同じものを複数回書くことになります。

以下のコードは、Azure上に仮想マシンを3台作ろうとした場合の例です(ネットワークの設定とOS Diskの設定は既に終了していると仮定します)。

resource "azurerm_virtual_machine" "vm_1" {
  location                     = "japaneast"
  resource_group_name          = azurerm_resource_group.this.name
  name                         = "vm_1"
  network_interface_ids        = [azurerm_network_interface.nic_1.id]
  primary_network_interface_id = azurerm_network_interface.nic_1.id 

  vm_size = "Standard_B1ls"
  storage_os_disk {
    create_option     = "Attach"
    os_type           = "Linux"
    name              = azurerm_managed_disk.disk_1.name
    caching           = "ReadWrite"
    disk_size_gb      = "30"
    managed_disk_id   = azurerm_managed_disk.disk_1.id
    managed_disk_type = "Standard_LRS"
  }
}

resource "azurerm_virtual_machine" "vm_2" {
  location                     = "japaneast"
  resource_group_name          = azurerm_resource_group.this.name
  name                         = "vm_2"
  network_interface_ids        = [azurerm_network_interface.nic_2.id]
  primary_network_interface_id = azurerm_network_interface.nic_2.id

  vm_size = "Standard_B1ls"
  storage_os_disk {
    create_option     = "Attach"
    os_type           = "Linux"
    name              = azurerm_managed_disk.disk_2.name
    caching           = "ReadWrite"
    disk_size_gb      = "30"
    managed_disk_id   = azurerm_managed_disk.disk_2.id
    managed_disk_type = "Standard_LRS"
  }
}

resource "azurerm_virtual_machine" "vm_3" {
  location                     = "japaneast"
  resource_group_name          = azurerm_resource_group.this.name
  name                         = "vm_3"
  network_interface_ids        = [azurerm_network_interface.nic_3.id]
  primary_network_interface_id = azurerm_network_interface.nic_3.id

  vm_size = "Standard_B1ls"
  storage_os_disk {
    create_option     = "Attach"
    os_type           = "Linux"
    name              = azurerm_managed_disk.disk_3.name
    caching           = "ReadWrite"
    disk_size_gb      = "30"
    managed_disk_id   = azurerm_managed_disk.disk_3.id
    managed_disk_type = "Standard_LRS"
  }
}

3台の仮想マシンの設定で異なるのは、14項目あるうちの以下の5項目のみです

  • name

  • network_interface_ids

  • primary_network_interface_id

  • storage_os_diskのname

  • storage_os_diskのmanaged_disk_id

もちろんこのままの書き方でも仮想マシンを3台作ることは可能ですが、共通設定が多いのでもう少し簡潔に書きたいところです。

Terraformではプログラミング言語で使用するようなループ処理を行うことができるので、この機能を使えば1つのresourceブロックで複数のリソースを作ることができます。ただし、リソース毎に異なる値を設定しなくてはいけない項目もあるので、ただループ処理をすればよいというわけでもありません。そこで、リソース毎に異なる値は、ローカル変数という同じモジュール内でのみ使える変数に定義します。Terraformではlocalsに続いてローカル変数を定義できます。Pythonで言うところの配列や辞書のような形式で記述できます。


locals {
  vm_confs = {
    conf1 = {
      name = "vm_1"
      nic = azurerm_network_interface.nic_1
      os_disk_name = "vm_disk_1"
    }
    conf2 = {
      name = "vm_2"
      nic = azurerm_network_interface.nic_2
      os_disk_name = "vm_disk_2"
    }
    conf3 = {
      name = "vm_3"
      nic = azurerm_network_interface.nic_3
      os_disk_name = "vm_disk_3"
    }
  }
}

ループ処理は、ローカル変数で定義した設定の数だけ行われます。上記の例ではvm_confs内に3つの設定が記入されているため、3回ループが回ることになります。resourceブロック内ではループ処理したローカル変数の値を利用することができます。


resource "azurerm_virtual_machine" "this" {
  for_each = local.vm_confs

  location                     = "japaneast"
  resource_group_name          = azurerm_resource_group.this.name
  name                         = each.value.name
  network_interface_ids        = [each.value.nic.id]
  primary_network_interface_id = each.value.nic.id

  vm_size = "Standard_B1ls"
  storage_os_disk {
    create_option     = "Attach"
    os_type           = "Linux"
    name              = azurerm_managed_disk.this[each.value.os_disk_name].name
    caching           = "ReadWrite"
    disk_size_gb      = "30"
    managed_disk_id   = azurerm_managed_disk.this[each.value.os_disk_name].id
    managed_disk_type = "Standard_LRS"
  }
}

このように複数のリソースを作成する際はループ処理を使うことでコードをスッキリさせることができます。ZenOneのコードでも、ローカル変数とループ処理を使ってコードを見やすく管理しています。こうすることで、コードで定義しているリソースの数や、リソース毎に異なる項目を把握しやすくなります。

次回はモジュール間の値の受け渡しについてご紹介します。

この記事に関連する製品・サービス