cloud-initとOVAファイルでESXi上にUbuntuを構築する

# やりたいこと

- ESXiでUbuntu(Ubuntu Server 22.04 LTS)を構築したい
- 頻繁に作り直したり、追加したりするため、毎回インタラクティブなインストーラーで設定するのは面倒
- 事前にコードとして定義しておくことで(IaC)、バージョン管理や構築作業の簡略化を行いたい
- そこで採用したのがcloud-initとUbuntu Cloud Images(OVAファイル)を用いたプロビジョニング

# 使用技術

### Ubuntu Cloud Images

- [Ubuntu 22.04 (Jammy Jellyfish) Daily Build](https://cloud-images.ubuntu.com/jammy/current/)から取得可能
- 「Ubuntu Cloud Images」という名称だが、実際には各仮想環境向けにインストール済みイメージを提供しているものなので、クラウドではない環境であっても利用可能となっている
- VHDやIMG、Vagrant Box、OVA、VMDK等、様々なファイル形式で提供されている
- 今回はOVAファイルを利用する

### OVF

- 今回、直接的には取り扱わないが、OVAと密接に関係している技術なのでついでに紹介する
- OVFはOpen Virtualization Formatの略称
- DMTF(Distrivuted Management Task Force)のOpen Virtualization Format Working Group(OVFWG)というワーキンググループで標準化された仮想環境向けの規格(ファイル形式)
- このワーキンググループにはNovell、Dell、 ヒューレット・パッカード、IBM、Microsoft、VMware、XenSource等が参画していた
- この規格はISOによってISO/IEC 17203:2017としても標準化されている
- 2007年にOVF 0.9、2009年にOVF 1.0、2010年にOVF 1.1、2013年にOVF 2.0が策定されている
- 元々はベンダに依存しないサーバ仮想化のファイル形式を提供することが目的だったが、実際にはVMware製品で利用されることが多い模様
- OVFは単一のファイル形式を指す規格ではなく、複数のファイルから構成される
    - `.ovf`ファイル
        - 仮想マシンのメタデータがXMLで記述されている
        - 仮想マシンの構成情報、仮想ディスクイメージの情報、ハードウェア要件、アプリケーションの構成情報、ライセンス情報等が含まれている
    - 仮想ディスクファイル
        - VMDKやVHD等、複数のファイル形式がサポートされている
            - VMDKはVMwareが開発し、主にVMwareの製品に採用されている
            - VHDはConnectix(現在はMicrosoftに買収)が開発し、Hyper-VやXen、VirtualBox等に採用されている
        - ただし、仮想化ソフトウェアがその仮想ディスクのファイル形式をサポートしている必要がある
    - `.mf`ファイル
        - マニフェスト
        - 各ファイルの完全性を証明するためにSHA-1のチェックサムが記述されている
    - `.cert`ファイル
        - X.509証明書
        - 仮想マシンが信頼できるソースから提供されていることを証明する

### OVA

- OVAはOpen Virtualization Format Archiveの略称
- OVFの各ファイルをtarで１つに固めたもの
- 拡張子は`.ova`
- Ubuntu Cloud Imagesで提供されているOVAファイルのみを使用した場合、デフォルトのユーザも存在しない(つまりログインできない)、まともな設定がされていない仮想マシンが作成されるため、別途cloud-initで設定を行う必要がある

### cloud-init

- 仮想マシンの初期設定を行うツール
- 多様なプラットフォームに対応しているため、ベンダロックされないのも利点の一つ
- 仕組みとしてはメタデータやユーザデータ等を何らかの形で受け取り、それを元に初期設定を行う
- Ubuntu Serverはデフォルトでcloud-initがインストールされている
- UbuntuのサポートやLXD、MAAS(Metal as a Service)等を開発しているカノニカル社が開発した
- GitHub: https://github.com/canonical/cloud-init
- 公式ドキュメント: https://cloudinit.readthedocs.io/en/latest/

# Ubuntuの構築

### cloud-initのコンフィグの作成

- cloud-initのコンフィグは基本的に`meta-data`、`user-data`、`network-config`、`vendor-data`から構成される
    - このうち、`meta-data`、`user-data`は必須
    - `network-config`、`vendor-data`は任意
    - `vendor-data`はESXiでは使用できない

```yaml:meta-data
# インスタンスID(任意の名称)
instance-id: foo-bar-baz
# ホスト名
local-hostname: foo-bar-baz
```

```yaml:user-data
#cloud-config
users:
  - name: username
    plain_text_passwd: password
    # falseにしないとパスワードでログインできないらしい
    lock_passwd: false
    # sudoする際にパスワードを聞かれないようにする
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      # 必要があれば事前にSSHキーを生成しておく
      - ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX foo@example.com
```

```yaml:network-config
version: 2
ethernets:
  # 設定の対象とするネットワークインタフェース
  ens192:
    # 静的IPアドレスを割り当てたいため、DHCPをオフにする
    dhcp4: false
    # 静的IPアドレスの割り当て
    addresses:
      - 192.168.0.X/24
    # デフォルトゲートウェイの指定
    gateway4: 192.168.0.1
    # DNSキャッシュサーバの指定
    # Google Public DNS(8.8.8.8)等のPublic DNSを指定しても良い
    nameservers:
      addresses:
        - 192.168.0.1
```

### ISOの作成

- cloud-initの各コンフィグからISOを作成する
    - 作成したISOをCD/DVDドライブからマウントして使用するため
- ISOの作成に必要なコマンドのインストール
    - Linux環境であれば`genisoimage`コマンド(cdrkitパッケージに含まれる)が使用できるが、macOS環境では使用できない
    - そのため、ほぼ同等の機能とコマンド体系を有する`mkisofs`コマンド(cdrtoolsパッケージに含まれる)を使用する

```bash
# mkisofs(cdrtools)のインストール
$ brew install cdrtools

# mkisofsのバージョンの確認
$ mkisofs -version 
mkisofs 3.02a09 (x86_64-apple-macosx22.1.0)

Copyright (C) 1993-1997 Eric Youngdale
Copyright (C) 1997-2017 Joerg Schilling
Copyright (C) 1997-2001 James Pearson
Copyright (C) 2006      HELIOS Software GmbH
```

- ISOを作成する
    - `-joliet`と`-rock`オプション
        - JolietとRock Ridgeフォーマットを有効にする
        - 長いファイル名、大文字と小文字を区別したファイル名、日本語を含んだファイル名等を使用することが可能になる
    - `-volid`オプション
        - ISOのボリュームIDを指定している
        - `cidata`という値を指定することで、Ubuntuがメタデータやユーザデータ等をcloud-initに渡すことができるようになる

```bash
$ mkisofs -output seed.iso -volid cidata -joliet -rock meta-data user-data network-config
Total translation table size: 0
Total rockridge attributes bytes: 457
Total directory bytes: 0
Path table size(bytes): 10
Max brk space used 0
184 extents written (0 MB)
```

### ISOのアップロード

- ストレージ > <任意のデータストア> > データストア ブラウザ を開く

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/a2afb2be-3777-69b5-ab39-ffb5e409e718.png)

- 「アップロード」から作成したISOをアップロード

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/0b158241-0747-37d9-da96-c5ebc745fa2f.png)

### 仮想マシンの作成

- 事前に[Ubuntu 22.04 (Jammy Jellyfish) Daily Build](https://cloud-images.ubuntu.com/jammy/current/)からOVAファイルをダウンロードしておく
- 仮想マシン > 仮想マシンの作成/登録 を開く
- 「OVF ファイルまたは OVA ファイルから ...」を選択

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/3707a056-7514-d9d4-726b-aa1e8827a168.png)

- 仮想マシンの名前を適当に設定
- Ubuntu Cloud ImagesからダウンロードしたOVAファイルをアップロード

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/16b0883f-fb77-d543-4e23-6034fe3e096b.png)

- ストレージを選択

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/8837c834-8f0c-f490-babd-e843862df530.png)

- デプロイのオプションを選択する

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/3bf16740-7576-e716-55d9-e9c2d4715911.png)

- 設定を確認し、完了する

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/5801a1d3-5a0f-4518-c34f-5a1e4eb1db24.png)

- 一旦、作成した仮想マシンの電源をオフにする

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/140f28fc-a2ae-f7f5-3f42-49116e51ddc9.png)

### ISOのマウント

- アクション > 設定の編集 > 仮想ハードウェア を開く
- 「CD/DVD ドライブ 1」で「データストア ISO ファイル」を選択

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/bc0534fc-29a5-86e6-0fda-d36c5bb43fe0.png)

- 先程作成したISOを選択

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/fe2cdbc7-cafc-ac73-2ba9-00cb4dc84a77.png)

- 「パワーオン時に接続」にチェックを入れる
- 保存し、仮想マシンの電源をもう一度入れる

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/b28a960e-a5de-9677-c864-0e7031e98794.png)

### 設定の確認

- コンソールに入る
- ネットワークインタフェースとIPアドレスの確認

```bash
$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:84:b3:bd brd ff:ff:ff:ff:ff:ff
    altname enp11s0
    inet 192.168.0.5/24 brd 192.168.0.255 scope global ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe84:b3bd/64 scope link
       valid_lft forever preferred_lft forever
```

- デフォルトゲートウェイの確認

```bash
$ ip route
default via 192.168.0.1 dev ens192 proto static
192.168.0.0/24 dev ens192 proto kernel scope link src 192.168.0.5
```

- DNSキャッシュサーバの確認

```bash
$ cat /run/systemd/resolve/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 192.168.0.1
search .
```

- パスワード認証でSSH接続ができることを確認

```bash
$ ssh kazukichi@192.168.0.5
kazukichi@192.168.0.5's password:

kazukichi@homelab-srv-01:~
```