Amazon EKS Anywhere ベアメタルサポートを仮想環境 (VMware ESXi) で検証する

[Amazon EKS Anywhere (EKS-A)](https://aws.amazon.com/jp/eks/eks-anywhere/) は、Amazon EKS で利用されている Kubernetes ディストリビューション「[Amazon EKS Distro](https://aws.amazon.com/jp/eks/eks-distro/)」の環境をオンプレミスで管理できる、EKS のデプロイオプションの一つです。
[GitHub で開発、公開されていて](https://github.com/aws/eks-anywhere)、いつでも無料でダウンロードして利用できます。
EKS-A は現在、 [ベアメタル環境もサポート](https://aws.amazon.com/jp/about-aws/whats-new/2022/06/bare-metal-support-amazon-eks-anywhere/)していますが、いざ検証しようとするとどこから始めるべきかとっつきにくいと感じることがあるかもしれません。
本記事では、 EKS-A の構築手順や機能を、仮想環境 (VMware ESXi) を使ってお手軽に検証してしてみます。vCenter で操作していますが、本手順は ESXi の Host Client でも実施できます。また、記事を通して、以下の仮想マシンを作成しますので、その分のマシンスペックが必要です。

* ルーター
    * vCPU: 2, Memory: 2GB
* EKS Anywhere Admin Machine
    * vCPU: 2, Memory: 16GB
* EKS Anywhere ノード (シングルノード)
    * vCPU:2, Memory: 8GB

## 専用の仮想ネットワークを構築する

EKS-A のベアメタルサポートでは、EKS Anywhere が提供する DHCP で PXE ブートを行い Kubernetes のノードを構築していきます。

![EKS-A PXE boot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/6091a027-57d4-ae97-e803-6960fa540798.jpeg)

ここでは、既存 DHCP の影響を避けるため、**DHCP が存在しない新しい仮想ネットワーク**を検証環境用に用意することとします。
本記事のステップ全体で、以下の環境の構築を目指します。
![EKS-A ESXi Network](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/482c3795-0a2f-43ca-94a5-c2e55a5f50d0.jpeg)

### ネットワークを追加

ESXi に新しいネットワークを追加します。

![Add Network for EKS-A](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/f289cc68-1025-3283-7e25-c353d298d7b8.jpeg)

ここでは、以下の設定でネットワークを作成しています。VLAN ID は適当なユニークなものを指定してください。

![Add Network for EKS-A](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/fed5b67b-311c-b045-3cb5-bbf4262cef58.jpeg)

### ルーターの作成

この手順では、 Ubuntu でルーターを作成します。

![EKS-A ESXi Network Router](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/758c908e-85b8-537e-2355-dc36387b64af.jpeg)

#### Ubuntu Server ISO イメージを ESXi にアップロード

[Ubuntu 22.04 のイメージをダウンロード](https://ubuntu.com/download/server)して、ESXi のデータストアにアップロードしておきます。

![Upload Ubuntu iso](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/4976cf20-e81c-9f2a-a945-485891e438d9.jpeg)

#### VM の作成と起動

以下の設定で VM を作成します。
![VM for router](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/c0567569-5deb-3ebf-2e42-516af04ebe85.jpeg)

CD/DVD ドライブには前手順でアップロードした Ubuntu Server の iso イメージを設定してください。
VM の作成が終わったら、そのまま UI から起動しましょう。

#### Ubuntu Server のインストール

インストーラーの指示に従い、Ubuntu Server をインストールします。この手順では、Minimized 版を選択してインストールしました。

#### ネットワークの設定

ネットワーク設定は 以下の netplan の設定ファイルを参考にしてください。

```yaml:/etc/netplan/00-installer-config.yaml 
network:
  ethernets:
    # WAN 側の設定は IPv4 を想定
    # IP アドレスを固定する
    ens33:
      addresses:
        - 192.168.1.200/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers: # WAN 側のルーターに合わせて設定
        addresses:
          - 192.168.1.1
    # LAN 側の設定
    ens34:
      addresses:
        - 10.8.0.1/16
  version: 2
```

#### ルーティング設定

`/etc/sysctl.conf` を以下の通り編集し、IPv4 のフォワーディングを設定します。

```
# コメントアウトを外す
net.ipv4.ip_forward=1
```

以下コマンドで設定を適用します。

```bash
sysctl -p
```

さらに、IPv4 のルーターとしてパケットのルーティングを設定します。ここでは ufw を使用するので、前もってインストールしておきます。

```bash
apt install ufw

ufw allow 22
ufw enable
```

`/etc/default/ufw` の修正し、Forward ポリシーを有効にします。

```
DEFAULT_FORWARD_POLICY="ACCEPT"
```

`/etc/ufw/before.rules` に以下を追加し、 LAN 側のマシンがインターネットと通信できるようにします。

```
# NAT
*nat
:POSTROUTING ACCEPT [0:0]
-F
-A POSTROUTING -s 10.8.0.0/16 -o ens33 -j MASQUERADE

COMMIT
```

以下コマンドで設定を適用します。

```bash
ufw reload
```

## Amazon EKS Anywhere の構築

ネットワークの設定が完了したので、いよいよ EKS-A の構築に入ります。

### Admin Machine の構築

Amazon EKS Anywhere のインストールコマンドを実行する、[Admin Machine](https://anywhere.eks.amazonaws.com/docs/getting-started/install/) を作成します。

![EKS-A ESXi Network Admin Machine](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/3bb18a84-f5d7-2756-3c1b-9f208785d279.jpeg)

このマシンは、前述の通り、ネットワークブートのインフラや Kubernetes 環境構築のコントロールプレーンをホストするマシンです。
 `EKS Anywhere` ネットワークに、Ubuntu の VM を作成します。ルーター作成の手順とほぼ同じですが、今回はマシンにログインした後様々な操作を実行するので、 Minimized 版ではなく通常の Ubuntu Server を構築しています。

#### ネットワーク設定、ポートの開放

ネットワークの設定は、以下の netplan の情報を参考にしてください。

```yaml
network:
  ethernets:
    ens192: # DHCP はないため、固定でアドレスを設定
      addresses:
      - 10.8.10.1/16
      nameservers:
        addresses: # OpenDNS
        - 208.67.222.222
        - 208.67.220.220
        search: []
      routes:
      - to: default
        via: 10.8.0.1
  version: 2
```

また、図で記載されている通り、構築先 Kubernetes ノードから DHCP や TFTP などのリクエストを受信します。 ufw などでポートをブロックしている場合は、[ドキュメントの仕様通り](https://anywhere.eks.amazonaws.com/docs/getting-started/ports/#bare-metal-provider)にポートを開放してください。

#### ツール類のインストール

[ドキュメント記載の手順](https://anywhere.eks.amazonaws.com/docs/getting-started/install/)に従い、ツール類をインストールします。まず、以下のツールをインストールしてください。

* [Docker](https://docs.docker.com/engine/install/ubuntu/)
* [yq](https://github.com/mikefarah/yq/#install)
* [curl](https://everything.curl.dev/get/linux)

その後、EKS-A のインストーラーやツール類をインストールします。

```bash
curl "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" \
    --silent --location \
    | tar xz -C /tmp
sudo install -m 0755 /tmp/eksctl /usr/local/bin/eksctl

RELEASE_VERSION=$(curl https://anywhere-assets.eks.amazonaws.com/releases/eks-a/manifest.yaml --silent --location | yq ".spec.latestVersion")
EKS_ANYWHERE_TARBALL_URL=$(curl https://anywhere-assets.eks.amazonaws.com/releases/eks-a/manifest.yaml --silent --location | yq ".spec.releases[] | select(.version==\"$RELEASE_VERSION\").eksABinary.$(uname -s | tr A-Z a-z).uri")
curl $EKS_ANYWHERE_TARBALL_URL \
    --silent --location \
    | tar xz ./eksctl-anywhere
sudo install -m 0755 ./eksctl-anywhere /usr/local/bin/eksctl-anywhere

export OS="$(uname -s | tr A-Z a-z)" ARCH=$(test "$(uname -m)" = 'x86_64' && echo 'amd64' || echo 'arm64')
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/${OS}/${ARCH}/kubectl"
sudo install -m 0755 ./kubectl /usr/local/bin/kubectl
```

### クラスター用の VM を作成

今回は、シングルノードで クラスターを作成します。クラスターのノードとなる VM を作成しましょう。
![EKS-A ESXi Network k8s nodes](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/ef6346aa-a10e-e2e0-3469-37abf86242f6.jpeg)

ESXi で以下の設定で空の VM を作成してください。

* ゲスト OS
    * Linux
    * Other Linux 5.x
* ネットワーク
    * MAC アドレス
        * `00:50:56:bd:ba:da`
* ハードディスク
    * 仮想デバイスノード
        * SCSI コントローラーから IDE へ変更

![Node VM Settings](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/c392d85f-090c-7210-1c75-258e0eb345e7.jpeg)

### クラスター用の インベントリファイル (hardware.csv) を作成

EKS-A のインストーラーは、[ノードとなるマシンのリストを CSV 形式のファイルを渡すことで設定](https://anywhere.eks.amazonaws.com/docs/getting-started/baremetal/bare-preparation/#prepare-hardware-inventory)します。
以下のファイルを Admin Machine に用意してください。

```plaintext:expt-eksa-mgmt-hardware.csv
hostname,bmc_ip,bmc_username,bmc_password,mac,ip_address,netmask,gateway,nameservers,labels,disk
expt-eksa-mgmt-cp01,,,,00:50:56:bd:ba:da,10.8.20.5,255.255.0.0,10.8.0.1,10.8.0.1,type=cp,/dev/sda
```

### クラスターの設定ファイルを作成

さらに、Kubernetes のバージョンなど、[構築するクラスターの設定を yaml ファイルで定義](https://anywhere.eks.amazonaws.com/docs/getting-started/baremetal/bare-spec/)します。以下のコマンドで、定義ファイルを生成することができます。

```bash
export CLUSTER_NAME=expt-eksa-mgmt
eksctl anywhere generate clusterconfig $CLUSTER_NAME --provider tinkerbell > expt-eksa-mgmt-cluster.yaml
```

今回は、以下の設定でクラスターの定義ファイルを作成します。

```yaml:expt-eksa-mgmt-cluster.yaml
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: Cluster
metadata:
  name: expt-eksa-mgmt
spec:
  clusterNetwork:
    cniConfig:
      cilium: {}
    pods:
      cidrBlocks:
      - 10.20.0.0/16
    services:
      cidrBlocks:
      - 10.30.0.0/16
  controlPlaneConfiguration:
    count: 1
    endpoint:
      host: "10.8.10.20"
    machineGroupRef:
      kind: TinkerbellMachineConfig
      name: expt-eksa-mgmt-cp
  datacenterRef:
    kind: TinkerbellDatacenterConfig
    name: expt-eksa-mgmt
  kubernetesVersion: "1.27"
  managementCluster:
    name: expt-eksa-mgmt

---
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: TinkerbellDatacenterConfig
metadata:
  name: expt-eksa-mgmt
spec:
  tinkerbellIP: "10.8.10.10"

---
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: TinkerbellMachineConfig
metadata:
  name: expt-eksa-mgmt-cp
spec:
  hardwareSelector:
    type: cp
  osFamily: bottlerocket
  templateRef: {}
  users:
  - name: ec2-user
```

Pod の CIDR や Kubernetes API の IP などは想定ネットワークの通りに作成しています。
Pod、Serviceの CIDR は、マシンのネットワークと重複しないプライベートアドレスを指定します。

![EKS-A Network k8s](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/6d9c650c-378e-ba20-9d7f-b992787dc7c6.jpeg)

### クラスター作成コマンドの実行

ここまで出来たら、以下コマンドを実行し、クラスター作成フローを開始します。

コマンドを実行すると、[Kind](https://kind.sigs.k8s.io/) でローカルに Kubernetes クラスター (Bootstrap クラスターと呼ばれます) が作成されます。Bootstrap クラスターには EKS Anywhere クラスターを構築するためのコントロールプレーンがデプロイされます。
後述の手順でノードとなるマシンを立ち上げると、マシンはネットワークブートによりこのコントロールプレーンにアクセスしながら、ノードの OS や Kubernetes 環境を構築していきます。フローを示す図を以下に再掲します。

![EKS-A PXE boot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91422/6091a027-57d4-ae97-e803-6960fa540798.jpeg)

フローについての詳細は以下のドキュメントもご参照ください。
https://anywhere.eks.amazonaws.com/docs/getting-started/baremetal/overview/

```bash
eksctl anywhere create cluster --hardware-csv expt-eksa-mgmt-hardware.csv -f expt-eksa-mgmt-cluster.yaml

Warning: The recommended number of control plane nodes is 3 or 5
Warning: No configurations provided for worker node groups, pods will be scheduled on control-plane nodes
Warning: The recommended number of control plane nodes is 3 or 5
Warning: No configurations provided for worker node groups, pods will be scheduled on control-plane nodes
Performing setup and validations
✅ Tinkerbell Provider setup is valid
✅ Validate OS is compatible with registry mirror configuration
✅ Validate certificate for registry mirror
✅ Validate authentication for git provider
✅ Validate cluster's eksaVersion matches EKS-A version
Creating new bootstrap cluster
Provider specific pre-capi-install-setup on bootstrap cluster
Installing cluster-api providers on bootstrap cluster
Provider specific post-setup
Creating new workload cluster
```

`Creating new workload cluster` が表示されたら、次の手順を実施します。

### クラスターの VM を起動する

作成した クラスター用の VM を起動すると、PXE ブートによりクラスター構築が開始されます。
起動したノード内のフローを簡単に補足すると、PXE ブートでまず起動するのは、[Hook](https://docs.tinkerbell.org/hook/) というオンメモリで起動する小さな OS です。この Hook では [Tink Worker](https://docs.tinkerbell.org/services/tink-worker/) というエージェントが稼働しています。Tink Worker が、Admin Machine で稼働しているコントロールプレーンと通信しながら構築ワークフローを実行します。ワークフローでは、ノードの OS となる Bottlerocket ( Ubuntu や RHEL もサポート) をデバイスに書き込んだり、ネットワークを設定したりします。
その後、ノードで cloud-init が起動し、クラスターの構築、設定が実行されます。
このフローは、[Tinkerbell](https://tinkerbell.org/) というベアメアル環境のセットアップを自動化する OSS ツールで実行されています。

Admin Machine から以下のコマンドで構築のログを確認出来ます。

```bash
docker logs boots -f
```

### Kubernetes クラスターへのアクセスを確認する

正常にクラスターの構築が終了すると、 Admin Machine の `eksctl anywhere create cluster` コマンドが完了し、クラスターへアクセスするための kubeconfig ファイルも作成されます。
以下のコマンドで、クラスターへアクセスできることを確認します。

```bash
export KUBECONFIG=$(pwd)/${CLUSTER_NAME}/${CLUSTER_NAME}-eks-a-cluster.kubeconfig
kubectl get nodes
```

## まとめ

この記事では、Amazon EKS Anywhere のベアメタルサポートについてクラスターを構築するまでの手順を ESXi で実際に体験してみました。
Amazon EKS Anywhere にはベアメタルだけではなく、ネイティブの vSphere サポートもありますので、実際に ESXi でベアメタルサポートを利用することはないかもしれません。
しかし、仮想環境で使うことで、実際の動作を簡単に検証することができます。ぜひ、EKS Anywhere で色々試してみてください。