TKG 2.2 で Linux Custom Image をビルドしてみる

# はじめに

Tanzu Kubernetes Grid (TKG) では、Kubernetes (K8s) の Node OS として、Photon OS や Ubuntu を同梱した形で、OVA を提供しています。
しかし、この他の Linux ディストリビューションも持ち込める仕組みを提供しています。

https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2/about-tkg/tkr.html#target-os

上記のドキュメントの通り、TKG 2.2.0 では、vSphere 環境向けに、下記の OS を同梱した形で OVA を配布しています。

* Ubuntu 20.04
* Photon OS 3

これに加えて、独自のカスタマイズを含めて、自身で OVA をビルドすることで、下記の OS が持ち込み可能です。

* Ubuntu 20.04
* Ubuntu 18.04
* RHEL 8
* Photon OS 3
* Windows 2019

その他、最新の情報や、AWS 向けは？Azure 向けは？に関しては、上記の公式ドキュメントをご参照ください。

では、具体的に、どんなツールを使ってビルドするのか？どんな手順か？については、下記が公式ドキュメントになります。

https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/tkg-deploy-mc-22/mgmt-byoi-index.html

[Kubernete Image Builder](https://github.com/kubernetes-sigs/image-builder) プロジェクトの提供するツールを利用しており、技術要素としては、Ansible や Packer を利用しています。

今回は、公式ドキュメントに従って、実際に、Ubuntu 20.04 ベースの OVA をビルドしてみたいと思います。


# 前提

執筆時点では、TKG 2.2.0 を利用しています。

https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/tkg-deploy-mc-22/mgmt-release-notes.html

クライアント端末として、そこまで制約がある訳ではありませんが、それぞれ下記の通りです。

* Ubuntu 22.04.2 LTS
* Docker CE 24.0.2

また、イメージをビルドする過程で、実際に VM をデプロイを行い、カスタマイズするため、対象の VM に何らかの方法で IP アドレスが割り当てられる仕組みが必要になります。
TKG をデプロイする際に、DHCP が必要になるので、その仕組みをそのまま使う形で問題ないと思います。


# 手順

基本的には、下記の公式ドキュメントに従って、実施していきます。

https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/tkg-deploy-mc-22/mgmt-byoi-linux-for-cc.html

ただ、記載が複数のセクションに跨っていたりするため、その内容を噛み砕いた形で記載しており、公式サポートの手順でない部分があることはご留意ください。

:::note warn
**ご注意**

公式ドキュメントには、TKG 2.1 以降の ClasterClass による K8s クラスタのデプロイを前提とした方法と、TKG 1.6 以前の Plan-Based (Legacy) によるデプロイを前提とした方法の 2種類が記載されています。
しかし、TKG 2.1 以降では、ClasterClass によるデプロイ向けに OVA をビルドする必要があり、この記事でもこちらの方法を記載します。
:::


## (参考) 全体の流れ

イメージのビルドから K8s クラスタのデプロイまでの流れは、大まかに下記の通りです。

* OS Image Version の取得
* Linux Image のビルド
* OVA を vCenter から登録
* K8s クラスタのデプロイ

Plan-Based (Legacy) によるデプロイ向けの方法では、Linux Image のビルド後に、Tanzu Kubernetes releases (TKr) の登録というステップが必要になります。しかし、ClasterClass によるデプロイ向けの方法では、不要です。
また、OVA を vCenter から登録する以降の手順は、VMware から提供されている OVA を利用する際の手順において、少しパラメータを追加するくらいで、ほぼ同じ手順になります。


## OS Image Version の取得

イメージをビルドするターゲットの OS 毎に、OS Image Version なるものが決まっているので、その情報を取得します。

まず、公式ドキュメントでは、下記の方法が記載されています。

* 既に Manageme Cluster をデプロイしている場合
    * `kubectl` コマンドを使って、ターゲットの TKr (TKG に含まれる各 K8s リリース) に紐づく `OSImage` の情報からバージョンを取得。
* まだ Management Cluster をデプロイしていない場合
    * VMware から提供されている OVA ファイルの中身を開けて、バージョンを取得。
    * または、OVA を既に vSphere 環境にアップロード済みであれば、`govc` コマンドを使って、バージョンを取得。

いずれの方法も、極端に難易度が高いわけではないですが、もし、Tanzu CLI をインストール済みであれば、下記のファイルからも、バージョン情報を取得することができます。

```yaml: ${HOME}/.config/tanzu/tkg/bom/tkr-bom-v1.25.7+vmware.2-tkg.1.yaml
# vSphere 環境向けには "ova" という部分を参照
ova:
- name: ova-photon-3
  osinfo:
    name: photon
    version: "3"
    arch: amd64
  version: v1.25.7+vmware.2-tkg.1-8795debf8031d8e671660af83b673daa
- name: ova-ubuntu-2004
  osinfo:
    name: ubuntu
    version: "20.04"
    arch: amd64
  version: v1.25.7+vmware.2-tkg.1-8a74b9f12e488c54605b3537acb683bc
- name: ova-rhel-8
  osinfo:
    name: rhel
    version: "8"
    arch: amd64
  version: v1.25.7+vmware.2-tkg.1-226b7a84930e5368c38aa867f998ce33
#...(略)...
```

上記の例では、Ubuntu 20.04 ベースのイメージをビルドする場合、`v1.25.7+vmware.2-tkg.1-8a74b9f12e488c54605b3537acb683bc` が欲しかった情報になります。


## Linux Image のビルド

いよいよ本題部分になります。


### TKG Image Builder の資材の取得

まず、VMware {code} Sample Exchange より、TKG Image Builder の資材をダウンロードしてくる必要があります。

https://developer.vmware.com/samples

ビルドする K8s のバージョンおよび、TKG のバージョン毎に資材が異なるので、今回の場合、↓こんな感じのキーワードで検索します。

```text
TKG-Image-Builder-for-Kubernetes-v1.25.7-on-TKG-v2.2.0
```

![Screenshot 2023-06-29 at 23.18.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245234/a5b7c134-b39a-d249-adad-24d459af57cf.png)

取得した Zip ファイルは、どこか適当な場所に展開しておいてください。
展開した先で、`tkg.json` というファイルが格納されているディレクトリが、今回の作業ディレクトリになります。

```bash: 出力例
$ ls -1
goss
tkg
tkg.json
```

### vCenter ログイン情報の設定

作業ディレクトリに、`CREDENTIALS.json` という名前で、vCenter へのログイン情報、およびイメージをビルドする際に使うリソースプールや、ストレージ、ネットワーク等を設定していきます。

```json: CREDENTIALS.json
{
"cluster": "/<DATACENTER-NAME>/host/<CLUSTER-NAME>",
"convert_to_template": "false",
"create_snapshot": "true",
"datacenter": "/<DATACENTER-NAME>",
"datastore": "/<DATACENTER-NAME>/datastore/<DATASTORE-NAME>",
"folder": "/<DATACENTER-NAME>/vm/<FOLDER-NAME>",
"insecure_connection": "true",
"linked_clone": "true",
"network": "/<DATACENTER-NAME>/network/<NETWORK-NAME>",
"password": "<PASSWORD>",
"resource_pool": "/<DATACENTER-NAME>/host/<CLUSTER-NAME>/Resources/<RESOURCE-POOL-NAME>",
"template": "",
"username": "<USERNAME>",
"vcenter_server": "<VCENTER-FQDN-OR-IP>"
}
```

### アーティファクトコンテナの起動

アーティファクトコンテナという名称では、役割がよく分からないと思いますが、イメージをビルドする過程で必要な資材を固めたコンテナイメージがあります。
そのコンテナから資材を配布できるよう、Docker を使って起動しておきます。

まず、VMware のコンテナレジストリ `projects.registry.vmware.com` に [VMware Tanzu Network](https://network.tanzu.vmware.com/) のアカウントでログインします。

```sh
docker login projects.registry.vmware.com
```

ログインできたら、目的のコンテナイメージを取得します。

```sh
docker pull projects.registry.vmware.com/tkg/linux-resource-bundle:v1.25.7_vmware.2-tkg.1
```

3000番ポートで、待ち受けられるよう `-p` オプションを指定して、コンテナを起動しておきます。

```sh
docker run -d -p 3000:3000 projects.registry.vmware.com/tkg/linux-resource-bundle:v1.25.7_vmware.2-tkg.1
```

この手順からも伺えますが、資材を取得するために、vCenter や VM から、操作をしているクライアント端末に通信できる必要がありますので、ご注意ください。


### `tkg.json` ファイルの構成

作業ディレクトリにある `tkg.json` に最低限必要な情報、およびカスタマイズに関する情報を設定していきます。
まず、デフォルトで `tkg.json` は、下記のようになっていると思います。

```json: tkg.json
{
  "build_version": "{{user `build_name`}}-kube-v1.25.7_vmware.2",
  "pause_image": "projects.registry.vmware.com/tkg/pause:3.8",
  "containerd_sha256": "48116ae36c9125fe644f5b0adc20f0520741db47f6a28e261ec7b21dba2448a7",
  "containerd_url": "http://<IP>:<PORT>/files/containerd/cri-containerd-v1.6.18+vmware.1.linux-amd64.tar",
  "containerd_version": "v1.6.18+vmware.1",
  "crictl_sha256": "",
  "crictl_url": "",
  "custom_role": "true",
  "custom_role_names": "/home/imagebuilder/tkg",
  "extra_debs": "nfs-common unzip apparmor apparmor-utils sysstat",
  "extra_rpms": "sysstat",
  "goss_vars_file": "",
  "goss_tests_dir": "/home/imagebuilder/goss",
  "goss_entry_file": "goss/goss.yaml",
  "goss_download_path": "/tkg-tmp/goss-linux-amd64",
  "goss_remote_folder": "/tkg-tmp",
  "goss_remote_path": "/tkg-tmp/goss",
  "kubernetes_series": "v1.25",
  "kubernetes_semver": "v1.25.7+vmware.2",
  "kubernetes_source_type": "http",
  "kubernetes_http_source": "http://<IP>:<PORT>/files/kubernetes",
  "kubernetes_container_registry": "projects.registry.vmware.com/tkg",
  "kubernetes_cni_semver": "v1.1.1+vmware.20",
  "kubernetes_cni_source_type": "http",
  "kubernetes_cni_http_source": "http://<IP>:<PORT>/files/cni_plugins",
  "kubernetes_cni_http_checksum": "",
  "kubernetes_load_additional_imgs": "true"
}
```

`<IP>` および `<PORT>` の部分は、先ほど起動したコンテナへ通信するための IP アドレスと、ポート番号 (3000番) を設定します。

もし、HTTP Proxy を設定する場合には、下記のように追記します。

```json: tkg.json
{
  "http_proxy": "http://proxy.acme.com:80",
  "https_proxy": "http://proxy.acme.com:80",
  "no_proxy": "localhost, 127.0.0.1, acme.com, 10.0.0.0/8"
}
```

:::note warn
JSON ファイルなので、追記する場合には、カンマの忘れに注意してください。
:::


### `metadata.json` ファイルの構成

冒頭、取得した OS Image Version を使って、下記の内容で、`metadata.json` ファイルを作成します。

```json: metadata.json
{
  "VERSION": "v1.25.7+vmware.2-tkg.1-8a74b9f12e488c54605b3537acb683bc"
}
```


### OUTPUT フォルダの作成

ビルドした OVA を格納するフォルダを、例えば `output` 等の名前で作成しておいてください。

```sh
mkdir output
chmod 777 output
```

### ビルドの実行

ここまでで、下記のようなファイル・ディレクトリが作成されていることを確認してください。

```sh
$ ls -1
CREDENTIALS.json
goss/
metadata.json
output/
tkg
tkg.json
```

上記のように準備していて、かつ Ubutu 20.04 ベースのイメージをビルドするためのコマンドは、下記のようになります。

```sh
docker run -it --rm \
  -v $(pwd)/CREDENTIALS.json:/home/imagebuilder/vsphere.json \
  -v $(pwd)/tkg.json:/home/imagebuilder/tkg.json \
  -v $(pwd)/tkg:/home/imagebuilder/tkg \
  -v $(pwd)/goss/vsphere-ubuntu-v1.25.7+vmware.2-tkg_v2_2_0-goss-spec.yaml:/home/imagebuilder/goss/goss.yaml \
  -v $(pwd)/metadata.json:/home/imagebuilder/metadata.json \
  -v $(pwd)/output:/home/imagebuilder/output \
  --env PACKER_VAR_FILES="tkg.json vsphere.json" \
  --env OVF_CUSTOM_PROPERTIES=/home/imagebuilder/metadata.json \
  --env IB_OVFTOOL=1 \
  projects.registry.vmware.com/tkg/image-builder:v0.1.13_vmware.3 \
  build-node-ova-vsphere-ubuntu-2004
```

:::note warn
パラメータが非常に多いので、Docker の Volume (`-v` オプション) で渡しているファイルについて、それぞれパスが間違っていないか、確認しておくことをお勧めします。
:::

ご参考までに、正常にビルドできた際の出力は下記のようなイメージであり、完了していれば、`output` ディレクトリに、OVA が作成されているはずです。

```text: 出力例
...(略)...
==> vsphere (shell-local): Running local shell script: /tmp/packer-shell3571337521
Build 'vsphere' finished after 36 minutes 44 seconds.

==> Wait completed after 36 minutes 44 seconds

==> Builds finished. The artifacts of successful builds are:
--> vsphere: ubuntu-2004-kube-v1.25.7_vmware.2
--> vsphere: ubuntu-2004-kube-v1.25.7_vmware.2
--> vsphere: ubuntu-2004-kube-v1.25.7_vmware.2
--> vsphere: ubuntu-2004-kube-v1.25.7_vmware.2
```


## OVA を vCenter から登録

このステップは、VMware から提供されいる OVA を vCenter 経由で展開し、テンプレート化する手順と同様なので、詳細は割愛します。

https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/tkg-deploy-mc-22/mgmt-reqs-prep-vsphere.html#import-base


## K8s クラスタのデプロイ

このステップも、VMware から提供されている OVA を利用した手順と、ほぼ同じ手順になるので、差分になる部分に絞って記載します。


### Management Cluster のデプロイ

Custom Image を正しくビルドし、正常にテンプレート化できていれば、UI Installer で OS Image を選択する箇所で、作成したイメージが選択肢として表示されるはずです。

![Screenshot 2023-06-30 at 0.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245234/898dc7a5-683c-5734-780e-566e0b67796e.png)

もし、表示されない場合、正しくイメージがビルドできていない可能性がありますので、複数ある構成ファイルの記述に間違いがないか？Docker の Volume (`-v` オプション) で渡しているパスに間違いがないか？など、確認をしてみてください。


### Workload Cluster のデプロイ

Workload Cluster の展開時に利用する OS Image を設定するには、Cluster Config ファイルにおいて、`VSPHERE_TEMPLATE` というパラメータを指定します。

https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/using-tkg-22/workload-clusters-configure-vsphere.html

```yaml: cluster-config.yaml
# ...(略)...

# 下記は不要
#OS_ARCH:
#OS_NAME:
#OS_VERSION:

# ...(略)...

# 作成したテンプレートを指定
VSPHERE_TEMPLATE: "/<DATACENTER-NAME>/vm/<FOLDER-NAME>/ubuntu-2004-kube-v1.25.7+vmware.2"
```

ただし、上記のフォーマットは、Plan-Based (Legacy) の形式なので、これを ClasterClass 形式に変換します。
もし、記述に誤りがあれば、ここでバリデーションされます。

```sh
tanzu cluster create --dry-run -f cluster-config.yaml > tkc01.yaml
```

バリデーションを含めて、変換が成功すれば、`tkc01.yaml` として ClusterClass 形式のファイルが作成されますので、これを使って、Workload Cluster をデプロイします。

```sh
tanzu cluster create -f tkc01.yaml
```