Nutmeg advent calendar 2024

IaCのすゝめ

IaCのすゝめ

はじめに

NUTMEGの、のせ(市之瀬)です。 アドカレ2024の22日を担当します。

インターンを通して学んだIaCを最近NUTMEGでも作っていたので、NUTMEG内に宣伝する意味も込めてIaCについての解説と今回作った内容の紹介をします。

コードはこのリポジトリにあります。

IaCの概要

IaCは「Infrastructure as Code」の略で、インフラの構成や設定をコードで管理することです。一昔前まで、サーバやネットワークの設定の管理は手動で行っていましたが。手動なためミスが起こりやすく、行う人の時間が奪われるデメリットがありました。インフラ構成をより早く、再現性のある形にするためにIaCという機能が生まれました。

コードで管理する恩恵として、以下の2つを挙げます。 今回作成したものは、「インフラ構成の一貫性の向上」を網羅します。

  • インフラ構成の一貫性の向上
  • デプロイの高速化

インフラ構成の一貫性の向上

AWS、GCPなどのクラウドサービスのコンソール画面や、NUTMEGで使っているProxmoxのコンソール画面からもインフラを作ることはできます。しかし、何人がまったく同じ構成を作れるでしょうか、今あるインフラの構成を理解しているでしょうか。あまりいないのが正直なところだと思います。 インフラをコードで管理することで、完璧な再現性を作ることができます。これにより、急にインフラエンジニアが飛んでしまっても現状のインフラがどうなっているのか、どう作ればよいかの情報を最低限残すことができます。 私もあと1年でいなくなる立場なので、この気にIaCとしてインフラの情報を残そうと思って今回取り組みました。

デプロイの高速化

現状のNUTMEGのデプロイは、インフラチームのメンバーがproxmoxの中のコンテナに訪れて最新版への更新を行うのが大半です。そのため、即座にデプロイできません。本番環境へ即座に反映できないのはアプリの開発を行う上で非常に面倒です。

インフラがコード化できていれば、この作業は人間以外にもやってもらうことができます。いわゆるDevOpsです。githubでMergeするだけで本番環境へアプリが反映させることができれば、開発効率の改善が行えます。 ここは今回の内容では網羅できていない部分なので、今後作るか、後輩が作ってくれるはずです。

terraformの紹介

今回IaCを行うために利用したものになります。terraformが行うのは、インフラ構成の中でもリソースの管理になります。そのため、先ほど述べていたメリットの「インフラ構成の一貫性の向上」を行うためのものになります。

「デプロイの高速化」をNUTMEGで行うためにはk8sを使う必要があるのですが・・・長くなるので完成したときにでもまたブログにしようと思います。

インストール

今回は、NUTMEGクラウドで使用しているバージョンのインストール方法を載せておきます。

macOSの場合

brew install tfenv
tfenv install 1.10.0
tfenv use 1.10.0

windows(WSL)の場合

このサイトのコピペなので失敗したらごめんなさい

git clone <https://github.com/tfutils/tfenv.git> ~/.tfenv
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
tfenv install 1.10.0
tfenv use 1.10.0

確認

terraform -v

以下のようにでるはずです。

Terraform v1.10.0
on darwin_arm64

Your version of Terraform is out of date! The latest version
is 1.10.1. You can update by downloading from <https://www.terraform.io/downloads.html>

terraformの使い方

ここでは最低限の使い方を紹介します。NUTMEG用のものはMakefileを作ってあるので、そちらを使ってください。

  • init
terraform init

リポジトリをterraform用に初期化するための機能です。terraformにはproviderという概念があり、それに応じて必要なファイルを取得します。NUTMEGではproxmox 3.0.1-rc5を利用しています。基本、このページを見ながら機能を追加しています。

  • plan
terraform plan

作成したterraformのファイルが行う変更差分を出力します。ポイントは、変更差分を出力するのみで、実際に変更自体は行われないことです。

  • apply
terraform apply

planとは異なり、実際に変更まで行う機能です。作成したterraformのコードで実際にリソースを作成することができます。

  • destroy
terraform destroy 

作成していたリソースを削除するための機能です。

開発内容

今回開発した内容は、NUTMEGサーバと呼ばれるproxmoxを利用しているサーバで、terraformを利用してコンテナ(以降LXC)、バーチャルマシン(以降VM)を作成するための機能です。これを利用することで、誰でも簡単にNUTMEGサーバでインフラを用意することができます。

今回は、特に利用が多いであろうLXCについて紹介していきます。

構成

以下のディレクトリ構成になっています。

.
├── Makefile
├── README.md
├── envs
│   └── group-manager-2
│       ├── lxc
│       ├── main.tf
│       ├── terraform.tfstate
│       ├── terraform.tfvars
│       └── variables.tf
├── modules
│   ├── lxc
│   │   ├── main.tf
│   │   └── provider.tf
│   └── vm
│       ├── main.tf
│       └── provider.tf
└── template
    ├── lxc
    │   ├── main.tf
    │   ├── terraform.tfvars
    │   └── variables.tf
    └── vm
        ├── main.tf
        ├── terraform.tfvars
        └── variables.tf
  • envs/
    • インフラ構成を作成する場所。現在は試験的にgroup-manager-2を作成している。
  • modules/
    • インフラを作成するためのコードの本体。このコードをenvsから呼び出す形をとっている。LXCとVMで使用するファイルが異なるため分かれている。
  • template/
    • envsでインフラ構成を作成するためのtemplate。このディレクトリのコードをenvsにコピーして使用する。こちらも、LXCとVMで使用するファイルが異なるため分かれている。
  • Makefile
    • terraformに必要な操作をまとめたファイル。terraformのコマンドを直で叩かず、Makefileの使用を推奨。

コードの中身

それぞれのファイルが何を行うのか解説します。

modules/lxc/main.tf

variable "settings" {
  type = map(object({
    vmid        = number
    target_node = string
    hostname    = string
    ssh_keys    = list(string)
    template    = string
    cores       = number
    memory      = number
    swap        = number
    disk_size   = string
    ip_address  = string
  }))
}

resource "proxmox_lxc" "basic" {
  for_each = var.settings

  vmid        = each.value.vmid
  target_node = each.value.target_node
  hostname    = each.value.hostname

  ssh_public_keys = join("\n", each.value.ssh_keys)

  ostemplate = "local:vztmpl/${each.value.template}.tar.zst"

  cores  = each.value.cores
  memory = each.value.memory
  swap   = each.value.swap

  unprivileged = true

  rootfs {
    storage = "local-lvm"
    size    = each.value.disk_size
  }

  network {
    name     = "eth0"
    bridge   = "vmbr0"
    firewall = true
    ip       = "${each.value.ip_address}/24"
    ip6      = "auto"
  }
}

このファイルがlxcを作成するためのメインの機能になります。後述する変数ファイルから値を受け取り、その値を代入してリソースを作成します。 注目するべきは以下の行です。

resource "proxmox_lxc" "basic" {

今回利用しているproxmox 3.0.1-rc5では、以下の4つの機能があります。

  • proxmox_cloud_init_disk
  • proxmox_pool
  • proxmox_vm_qemu
  • proxmox_lxc

その中でも、今回はlxcを作成するための機能であるをproxmox_lxc利用していることが先ほどの行でわかります。作成する際や修正するときは以下のこちらの内容を読んでください。

modules/lxc/provider.tf

terraform {
  required_providers {
    proxmox = {
      source  = "telmate/proxmox"
      version = "3.0.1-rc5"
    }
  }
}

provider "proxmox" {
  pm_api_token_id     = "hogehoge@pam!hoho-hoho"
  pm_api_token_secret = "examplepmapitokensecret"
  pm_api_url          = "https://example_domain/api2/json"
  pm_tls_insecure     = true
}

このファイルはクレデンシャルファイルのためgithubには上がっていません。 NUTMEGのメンバーはsettingsリポジトリから取得して配置してください。

このファイルでは、前述したproviderの指定と、proxmoxへリソースを作るためのtoken情報を書き込みます。

template/lxc/main.tf

module "vm_module" {
  source = "../../modules/lxc"

  settings = var.settings
}

各環境を作成する際(terraform apply,plan)に、実際に実行されるファイルになります。 内容としては、変数ファイルから変数を受け取り、受け取った変数をmodulesのmain.tfに渡して呼び出しているだけです。

template/lxc/terraform.tfvars

settings = {
  "vm1" = {
    # 作成するVMのID
    # 既存のVMと重複しないようにする
    # 先頭はPVEのIDと同じにする
    # 例:pveがpve02の場合、201,202,203...とする
    vmid        = xxx
    # どのサーバに作成するかを指定する
    # pve02~06のどれかを指定する
    target_node = "pve0x"
    # ディレクトリの名前と同じにする
    hostname    = "プロダクト名"
    # SSHの公開鍵
    # アクセスするPCの公開鍵をListに追加する
    ssh_keys    = [
      "ssh-ed25519 sshed25519examplepublickey hoge@hoge",
    ]
    # 基本的に固定
    template    = "ubuntu-24.04-standard_24.04-2_amd64"
    # 基本的に固定
    username    = "nutmeg"
    # CPUの数
    cores       = 1
    # メモリのサイズ
    memory      = 1024
    # スワップのサイズ
    swap        = 1024
    # ディスクのサイズ
    disk_size   = "16G"
    # IPアドレス
    # /24なので、最後の数字を変える
    ip_address  = "192.168.1.xxx"
  }
}

各環境ごとに設定する変数ファイルです。 各変数はコメントアウトの内容を読みながら設定してください。 この変数ファイルは、複数の環境を用意できます。なので、“vm1"のように"vm2"を用意すれば、一度に複数の環境を用意することができます。 基本は、make init で用意されたこのファイルのみ編集することになります。

template/lxc/variables.tf

variable "settings" {
  type = map(object({
    vmid        = number
    target_node = string
    hostname    = string
    ssh_keys    = list(string)
    template    = string
    cores       = number
    memory      = number
    swap        = number
    disk_size   = string
    ip_address  = string
  }))
  description = "A map of VM settings"
}

上記で紹介した変数ファイルの値の型定義をしているファイルです。

使用例

今回は、新規で環境を作成したい場合の手順になります。

事前準備

事前に、以下を満たす必要があります。

  • nutmeg-cloudへのtailscaleのアクセス権限を持っている
  • providerファイルをsettingsからコピーしてある

ディレクトリとファイルの準備

新規作成するためにtemplateから新たにディレクトリを用意します。以下のコマンドを実行してください。

  • コマンドの引数と説明
    • resource_type
      • vmかlxc
    • pruduct
      • プロダクトの名前を入力(半角英数字で)
# コンテナを作りたいとき
make init resource_type=lxc product=example

# VMを作りたいとき
make init resource_type=vm product=example

exampleにはプロダクト名や用途など、わかるように設定してください。 このコマンドが成功すると、envsディレクトリの中にexampleというディレクトリが用意され、必要なファイルが揃った状況になります。

変数ファイルを編集

/envs/example/terraform.tfvars を修正します。 コメントを読みながら作成してください。 スペックに関する部分(cpu,cores,memory,swap,disksize)は相談しながら決めましょう。

planを実行

下のコマンドを実行します。

make plan product=example

planはドライランなので、本当に実行した際に起きる差分を出してくれます。 この結果をPRやslackに貼り付けてインフラの誰かに見てもらってください。

実際に実行

male apply product=example

terraformで書いた変数の内容で「example」という環境が作成されます。 sshをしてアクセスしてみましょう。

なお、sshするにはproxmoxのサーバを利用して二段sshをする必要があります。以下の設定を各々の~/.ssh/config に書き足してください。

Host pve01-nutmeg
    HostName <tailscaleで割り当てられたIP>
    User root

Host *.nutmeg
    User nutmeg
    IdentityFile ~/.ssh/id_ed25519

Host <好きな名前>.nutmeg
    User root
    HostName 192.168.1.xxx
    ProxyJump pve01-nutmeg

最後に

今回はIaCについて、概要の説明と実際に作成例を紹介しました。 まだまだ完璧なインフラ構成には遠いですが、卒業するまでになるべく良くしていきたいです。