---
# System prepended metadata

title: のせ
tags: [ブログ記事]

---

# yyyymmdd_IaCのすゝめ
(例：20210901_LT会を開催しました)
###### tags: `ブログ記事`

- [ ] 公開（ブログ公開担当者がいじるやつ）


太字斜体で書いてある内容を埋めて行ってください．
文章，画像は太字斜体の下の行に入れてください．
最初に書く時はREADMEを読んだら読むといいと思います．

<br>

## 表示されない情報
***書いた人の名前(自己紹介文と同じ名前)***
{
    のせ
}


***記事の簡単な説明(検索した時にタイトルの下に出てくる文章)***
{
インターンを通して学んだIaCを最近NUTMEGでも作っていたので、NUTMEG内に宣伝する意味も込めてIaCについての解説と今回作った内容の紹介をします。
}


<br>

## 表示される部分
***サムネイル画像***
{
![adocare-2](https://hackmd.io/_uploads/H1RkjIBr1e.png)

}

***カテゴリ***
以下の中から該当しそうなカテゴリを選択してください
※一つだけ選択してください
- [ ] 対外活動
- [ ] 活動の様子
- [ ] メンバーの趣味
- [ ] 実務訓練体験記
- [x] NUTMEG Advent Calendar 2024

***タグ***
以下の中から該当しそうなカテゴリを選択してください．当てはまる物がない場合は適宜追加してください.
言語
- [ ] HTML
- [ ] CSS
- [ ] Python
- [ ] Go
- [ ] Ruby
- [ ] JavaScript
- [ ] TypeScript
- [ ] Dart
- [ ] Rust
- [ ] Kotlin
- [ ] Swift

フレームワーク・ライブラリ
- [ ] Ruby on rails
- [ ] Vue.js
- [ ] Nuxt.js
- [ ] React.js
- [ ] Next.js
- [ ] Gin
- [ ] Flluter

ツール
- [x] GitHub
- [x] ターミナル
- [x] WSL
- [x] Ubuntu
- [ ] Docker
- [ ] Raspberry Pi
- [ ] Figma

分野
- [ ] チームづくり
- [ ] フロントエンド
- [ ] バックエンド
- [x] インフラ
- [ ] Web-design
- [ ] API関係
 


---

***以下に本文を記載してください***

## はじめに

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

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

コードは[このリポジトリ](https://github.com/NUTFes/nutmeg-cloud)にあります。

## 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の場合

```bash
brew install tfenv
tfenv install 1.10.0
tfenv use 1.10.0
```

windows(WSL)の場合

[このサイト](https://zenn.dev/shz/articles/0c237d00267be4)のコピペなので失敗したらごめんなさい

```bash
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

```

確認

```bash
terraform -v
```

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

```bash
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

```bash
terraform init
```

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

- plan

```bash
terraform plan
```

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

- apply

```bash
terraform apply
```

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

- destroy

```bash
terraform destroy 
```

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

## 開発内容

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

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

### 構成

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

```bash
.
├── 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`利用していることが先ほどの行でわかります。作成する際や修正するときは以下の[こちら](https://registry.terraform.io/providers/Telmate/proxmox/latest/docs/resources/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
        - プロダクトの名前を入力（半角英数字で）

```bash
# コンテナを作りたいとき
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を実行

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

```bash
make plan product=example
```

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

### 実際に実行

```bash
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について、概要の説明と実際に作成例を紹介しました。
まだまだ完璧なインフラ構成には遠いですが、卒業するまでになるべく良くしていきたいです。


