Photon OS で kind + MetaLB を動かす

Photon OS は、VMware が OSS として開発しており、クラウド環境に最適化され、非常に軽量で、ミニマリストな Linux OS です。
最近の新しめな VMware 製品でも、OS 部分に Photon OS が使われていたりします。

https://vmware.github.io/photon/docs/

一方で、kind とは、Kubernetes クラスタを Docker のコンテナとしてデプロイするツールです。
元々は、Kubernetes 自身の開発におけるテストの目的で設計されたものようですが、アプリ開発者が自身のローカルマシン上で Kubernetes 環境を持つためのツールとしても活用されています。
むしろ、Kubernetes のユーザーとしては、そっちがメインかも知れません。

https://kind.sigs.k8s.io/

このままでも良いのですが、デフォルト設定で kind を動かしただけでは、外部からのアクセスのために Service (type=LoadBalancer) を使うことができません。
そこで kind の公式ドキュメントでは、MetalLB を使って、LB を構築方法が紹介されています。

https://kind.sigs.k8s.io/docs/user/loadbalancer/

この MetalLB は、外部の LB 製品を用いずに、Kubernetes クラスタ上で上手くやりくりすることで、外部アクセスのための Service (type=LoadBalancer) を動かすことを目的に実装されています。
ただ、執筆時点で Beta 扱いのプロジェクトであることや、Kubernetes クラスタ単位の運用となるため、実際の運用に載せるには考慮すべき事項があります。
しかし、kind のようなお手元の Kubernetes 環境向けには、非常にお手軽です。

https://metallb.universe.tf/

今回は、この kind と MetalLB を Photon OS 上で動かす方法をご紹介します。


# 前提

執筆時点で、Photon OS 4.0 を利用しています。

```sh
# cat /etc/photon-release
VMware Photon OS 4.0
PHOTON_BUILD_NUMBER=2f5aad892
```

また kind は `sigs.k8s.io/kind@v0.17.0` を利用しており、MetalLB は kind の公式ドキュメントに従って `v0.13.7` を利用します。

# 手順

## (事前準備) Docker サービスの起動

Photon OS では、Docker がデフォルトでインストールされていますが、まだサービスを有効化していない場合には、事前に有効かしておきます。

下記の例では、Docker サービスが動いていません。

```sh
# systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor pr
eset: disabled)
     Active: inactive (dead)
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
```

サービスを有効化していきます。`--now` オプションを付けることで、有効化と起動を同時に行うことができます。

```sh
systemctl enable docker --now
```

## kind のインストール

公式のガイドにも、いくつかの方法がありますが、バイナリからのインストールと Go 言語によるインストールの大きく2つを紹介します。

https://kind.sigs.k8s.io/docs/user/quick-start/


### 方法1: バイナリからのインストール (簡単)

バイナリからのインストールは非常に簡単で、`curl` でバイナリをダウンロードし、実行権限を付けたものを、パスの通ったディレクトリに配置するのみです。
公式のガイドにもある通り、`/usr/local/bin` 配下が一般的でしょうか。

```sh
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
```


### 方法2: Go 言語によるインストール

少し凝った(?)方法として、Go 言語をインストールし、`go install` コマンドで kind をインストールすることもできます。

まず、Go 言語を Photon OS にインストールします。

```sh
tdnf install -y go
```

root ユーザーの場合、既に GOPATH が環境変数として設定されています。

```
# go env
...(snip)...
GOPATH="/root/go"
...(snip)...
```

しかし、個別に作成したユーザーの場合、`GOPATH` を環境変数として設定する必要があります。
私は検証の目的毎に、ユーザーを分けていたりするので、その場合に、設定し忘れると、あれ？ということになりました。
例えば、今回の kind の検証ためには `kind` というユーザーを作成しています。

Photon OS においては、`~/.bash_profile` に設定するので、ご注意ください。

```sh
vi ~/.bash_profile
```

設定例としては、こんな感じです。

```sh:~/.bash_profile
# Begin ~/.bash_profile
# Written for Beyond Linux From Scratch
# by James Robertson <jameswrobertson@earthlink.net>
# updated by Bruce Dubbs <bdubbs@linuxfromscratch.org>

# Personal environment variables and startup programs.

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

# ...(snip)...
```

一旦、シェルと開き直すか、現在のシェルにも、環境変数を反映しておきます。

```sh
source .bash_profile
```

これで Go 言語の準備は整ったので、公式ガイドの通り、`go install` コマンドを実行します。

```sh
go install sigs.k8s.io/kind@v0.17.0
```

これで、`$GOPATH/bin` 配下に kind が配置され、上記の設定でパスが通っていれば、kind が実行できます。

```
# kind version
kind v0.17.0 go1.19.3 linux/amd64
```


## kind クラスタの作成/起動

下記のコマンドを実行すると、`kind` という名前で Kubernetes クラスタが作成されます。

```sh
kind create cluster
```

初回は、コンテナイメージの取得があるので、少し長めですが、非常に簡単に Kubernetes クラスタが作成されます。
デフォルトでは、シングルノードの Kubernetes クラスタが Docker コンテナとして作成されます。

```sh
# kind get clusters
kind

# kind get nodes
kind-control-plane

# docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED              STATUS              PORTS                       NAMES
4e94a151bed0   kindest/node:v1.25.3   "/usr/local/bin/entr…"   About a minute ago   Up About a minute   127.0.0.1:38959->6443/tcp   kind-control-plane
```

`kubeconfig` ファイルも、自動で配置されます。

```sh
# ls .kube/config
.kube/config
```


## kind クラスタへのアクセス

もし、まだ `kubectl` をインストールしていなければ、Kubernetes の公式ドキュメントに従ってインストールし、Kubernetes クラスタにアクセスしていきましょう。

https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/

まずは、最新版のバイナリをダウンロードします。

```sh
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
```

取得したバイナリに実行権限を付与します。

```sh
chmod +x ./kubectl
```

あとはお好みで PATH の通った場所に配置します。
大抵は `/usr/local/bin` でしょうか。

```sh
mv ./kubectl /usr/local/bin/kubectl
```

下記のように、`kubectl` 側のクライアントと kind 側のサーバーのバージョンが表示されることを確認します。

```sh
# kubectl version
WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short.  Use --output=yaml|json to get the full version.
Client Version: version.Info{Major:"1", Minor:"26", GitVersion:"v1.26.1", GitCommit:"8f94681cd294aa8cfd3407b8191f6c70214973a4", GitTreeState:"clean", BuildDate:"2023-01-18T15:58:16Z", GoVersion:"go1.19.5", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v4.5.7
Server Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.3", GitCommit:"434bfd82814af038ad94d62ebe59b133fcb50506", GitTreeState:"clean", BuildDate:"2022-10-25T19:35:11Z", GoVersion:"go1.19.2", Compiler:"gc", Platform:"linux/amd64"}
```


## MetalLB のインストール

冒頭で述べた通り、このままでも良いのですが、せっかくなので Service (type=LoacBalancer) が使えるように MetalLB をインストールします。

kind の公式ドキュメントにある [MetalLB のインストール手順](https://kind.sigs.k8s.io/docs/user/loadbalancer/) に従って、まずは MetalLB のインストール用のマニフェストを適用していきますが、後からどんなマニフェストを適用したのか記録する意味で、一旦、マニフェストをダウンロードします。

```sh
curl -OL https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
```

そして、このマニフェストを適用します。

```sh
kubectl apply -f metallb-native.yaml
```

数分、このまま待機でも良いですが、ドキュメントにあるコマンドで、MetalLB の準備ができるのを確認します。

```sh
kubectl wait --namespace metallb-system \
                --for=condition=ready pod \
                --selector=app=metallb \
                --timeout=90s
```

↓こんな感じで、`condition met` が返ってくれば、OK です。

```sh
# kubectl wait --namespace metallb-system \
>                 --for=condition=ready pod \
>                 --selector=app=metallb \
>                 --timeout=90s
pod/controller-84d6d4db45-grfv9 condition met
pod/speaker-jpzx5 condition met
```

次に、MetalLB の設定ファイルを作成していきますが、MetalLB が Service 用に使っても良い IP アドレス帯を指定する必要があります。
その際、Docker が kind を構築する際に、どの IP アドレス帯を使って Bridge ネットワークを作成しているのか確認しておく必要があります。

```sh
docker network inspect -f '{{.IPAM.Config}}' kind
```

私の手元の環境では、↓こんな感じの結果だったので、`172.18.0.0/16` のネットワークから、MetalLB が使うアドレス帯を指定すれば良いことが分かります。

```sh
# docker network inspect -f '{{.IPAM.Config}}' kind
[{172.18.0.0/16  172.18.0.1 map[]} {fc00:f853:ccd:e793::/64   map[]}]
```

ここでは、`172.18.255.200-172.18.255.250` を MetalLB に割り当てることにしていますが、必要に応じて、割り当てを変更してください。

```yaml: metallb-config.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: example
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.200-172.18.255.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: empty
  namespace: metallb-system
```

作成した設定ファイルを適用してきます。

```sh
kubectl apply -f metallb-config.yaml
```

参考までに、適当な Service を作成して、MetalLB の動作を確認します。

```sh
# kubectl create service loadbalancer my-lbs --tcp=80:8080
service/my-lbs created
# kubectl get svc
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1     <none>           443/TCP        18m
my-lbs       LoadBalancer   10.96.93.40   172.18.255.200   80:32467/TCP   6s
```

アプリの実体を伴わない、空のサービスなので、実際にアクセスしても確認できませんが、指定したアドレス帯から、`EXTERNAL-IP` が振られていることが確認できます。


## クラスタの停止/削除

使い終わったクラスタは、下記のコマンドで簡単に削除できます。

```sh
kind delete cluster
```

こんな感じで、簡単に使い捨てできちゃうのが、実にコンテナらしいですよね。