Workload Clusterの構築

# はじめに
　本記事では、[Management Clusterの構築](https://qiita.com/kikutak/items/c581b3e2518477d8631f)で構築したManagementClusterを使ってWorkload Clusterを構築します。構築の前提となる環境は[ClusterAPIによるKubernetesクラスタの操作](https://qiita.com/kikutak/items/b39c271c6eb2c9a3e525)を参照してください。
　本記事では下図のようにControl planeノード３台(master1~3)、Workerノード２台(worker1~2)のworkload-clusterを構築します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2633524/e6568834-8c9f-0573-c786-e2071a0937fe.png)

# Management Clusterの再構築
　本連載ではManagement Clusterをkindのデフォルト構成で構築していますが、kindで構築したkubernetesクラスタはDockerコンテナ上にあり、ローカルホストアドレスをbindしているため、Workload Clusterを構築する他のVMからManagement Clusterにアクセスできません。ClusterAPIのBYOHプロバイダを使用する場合Workload Clusterを構成するVMにBYOHエージェントをインストールし、エージェントがManagement Clusterにアクセスするようにしなければならないため再構築が必要です。

```shell
$ sudo docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                       NAMES
da5882fa79d9   kindest/node:v1.25.3   "/usr/local/bin/entr…"   4 minutes ago   Up 4 minutes   127.0.0.1:35311->6443/tcp   management-cluster-control-plane
```

　まずManegement Clusterを削除し、外部公開できるようにするため現在のクラスタを削除します。

```shell
$ sudo kind delete cluster --name=management-cluster
Deleting cluster "management-cluster" ...
```

　その後、apiServerを外部公開するための設定ファイルを作成して再作成します。

```shell
$ vi kind.yaml
```
```kind.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  apiServerAddress: "10.100.2.38"
  apiServerPort: 6443
```

```shell
$ sudo kind create cluster --name=management-cluster --config=kind.yaml
Creating cluster "management-cluster" ...
 ✓ Ensuring node image (kindest/node:v1.25.3) 🖼 
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-management-cluster"
You can now use your cluster with:

kubectl cluster-info --context kind-management-cluster

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

$ sudo docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED              STATUS              PORTS                        NAMES
7d15e2787a63   kindest/node:v1.25.3   "/usr/local/bin/entr…"   About a minute ago   Up About a minute   10.100.2.38:6443->6443/tcp   management-cluster-control-plane
```

これで外部からアクセスできるようになったので、BYOHをインストールしてManagement Clusterの再作成は完了です。

```shell
$ sudo clusterctl init --infrastructure=byoh --kubeconfig=/root/.kube/config
Fetching providers
Installing cert-manager Version="v1.11.0"
Waiting for cert-manager to be available...
Installing Provider="cluster-api" Version="v1.4.1" TargetNamespace="capi-system"
Installing Provider="bootstrap-kubeadm" Version="v1.4.1" TargetNamespace="capi-kubeadm-bootstrap-system"
Installing Provider="control-plane-kubeadm" Version="v1.4.1" TargetNamespace="capi-kubeadm-control-plane-system"
Installing Provider="infrastructure-byoh" Version="v0.3.2" TargetNamespace="byoh-system"

Your management cluster has been initialized successfully!

You can now create your first workload cluster by running the following:

  clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -
```


# Workload Clusterの構築

## BYOHエージェントのインストール

　まずBYOHエージェントに必要なパッケージをインストールします。

```shell
$ sudo apt-get install -y socat ebtables ethtool conntrack
```

　BYOHエージェントを[公式ページのリリース](https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/tag/v0.3.2)からダウンロードし、実行権限を付与するだけでインストールは完了です。Workload Clusterを構成する全てのVMにインストールする必要があります。

```shell: workload Cluster用VM
$ wget https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/download/v0.3.2/byoh-hostagent-linux-amd64
$ chmod +x byoh-hostagent-linux-amd64 
$ ls -l
total 37324
-rwxrwxr-x 1 kikutak kikutak 38215680 Apr  6 18:33 byoh-hostagent-linux-amd64
```

## Management Clusterのkubeconfigの配布

　BYOHエージェントがManagement Clusterにアクセスできるように、Workload Clusterを構成する全てのVMにManagement Clusterのkubeconfigを配布します。kubeconfigはManagement Cluster用VMの`/root/.kube/config`で以下のような内容です。

```yaml: /root/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <snip>
    server: https://10.100.2.38:6443
  name: kind-management-cluster
contexts:
- context:
    cluster: kind-management-cluster
    user: kind-management-cluster
  name: kind-management-cluster
current-context: kind-management-cluster
kind: Config
preferences: {}
users:
- name: kind-management-cluster
  user:
    client-certificate-data: <snip> 
    client-key-data: <snip>
```

　本記事では上記ファイルをWorkload Cluster用VMのBYOHエージェントと同じディレクトリに格納しました。

## BYOHエージェントの起動

　Workload Cluster用VMでBYOHエージェントを起動します。c-planeとworkerで使用するサーバを分けたいので、エージェント起動時に`--label`パラメータを使ってラベルを付与しています。

```shell: master1,2,3
$ sudo -E ./byoh-hostagent-linux-amd64 --bootstrap-kubeconfig config --label type=master > byoh-agent.log 2>&1 &
[1] 10750
$ ps ax | grep byoh
  10732 pts/0    S      0:00 sudo -E ./byoh-hostagent-linux-amd64 --bootstrap-kubeconfig bootstrap-kubeconfig.conf --label type=master
  10733 pts/0    Sl     0:00 ./byoh-hostagent-linux-amd64 --bootstrap-kubeconfig bootstrap-kubeconfig.conf --label type=master
  10758 pts/0    S+     0:00 grep --color=auto byoh
```

```shell: worker1,2
$ sudo -E ./byoh-hostagent-linux-amd64 --bootstrap-kubeconfig config --label type=worker > byoh-agent.log 2>&1 &
[1] 10224
$ ps ax | grep byoh
  10206 pts/1    S      0:00 sudo -E ./byoh-hostagent-linux-amd64 --bootstrap-kubeconfig bootstrap-kubeconfig.conf --label type=worker
  10207 pts/1    Sl     0:00 ./byoh-hostagent-linux-amd64 --bootstrap-kubeconfig bootstrap-kubeconfig.conf --label type=worker
  10232 pts/1    S+     0:00 grep --color=auto byoh
```

　Management Clusterで`byohosts.infrastructure.cluster.x-k8s.io`リソースを確認するとWorkload Cluster用VMが登録されmaster, worker用それぞれにラベル付与されていることが分かります。

```shell: Management Cluster用VM
$ kubectl get byohost -L type
NAME                 OSNAME   OSIMAGE              ARCH    TYPE
kikuta-k8s-master1   linux    Ubuntu 20.04.6 LTS   amd64   master
kikuta-k8s-master2   linux    Ubuntu 20.04.6 LTS   amd64   master
kikuta-k8s-master3   linux    Ubuntu 20.04.6 LTS   amd64   master
kikuta-k8s-worker1   linux    Ubuntu 20.04.6 LTS   amd64   worker
kikuta-k8s-worker2   linux    Ubuntu 20.04.6 LTS   amd64   worker
```

## Workload Clusterマニフェストの作成

Workload Clusterのマニフェストは以下のコマンドで作成します。`BUNDLE_LOOKUP_TAG`でkubernetesのバージョンを指定し、`CONTROL_PLANE_ENDPOINT_IP`でWorkload Clusterのcontrol-planeのエンドポイントを指定します。またclusterctlコマンドでcluster名としてworkload-clusterを指定してまいます。

```shell: Management Cluster用VM
$ sudo -E BUNDLE_LOOKUP_TAG=v1.25.11 CONTROL_PLANE_ENDPOINT_IP=10.100.3.31 \
  clusterctl generate cluster workload-cluster \
  --kubeconfig config \
  --infrastructure byoh\
  --kubernetes-version v1.25.11 \
  --control-plane-machine-count 3 \
  --worker-machine-count 2 > cluster.yaml
```
　
c-planeを`type=master`のVMに、workerを`type=worker`のVMにデプロイするため作成したマニフェストのByoMachineTemplateにselectorを追加します。

```yaml: cluster.yaml
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: workload-cluster-md-0
  namespace: default
spec:
  template:
    spec: {}
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  labels:
    cni: workload-cluster-crs-0
    crs: "true"
  name: workload-cluster
  namespace: default
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
      - 192.168.0.0/16
    serviceDomain: cluster.local
    services:
      cidrBlocks:
      - 10.128.0.0/12
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: workload-cluster-control-plane
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: ByoCluster
    name: workload-cluster
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: workload-cluster-md-0
  namespace: default
spec:
  clusterName: workload-cluster
  replicas: 2
  selector:
    matchLabels: null
  template:
    metadata:
      labels:
        nodepool: pool1
    spec:
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: workload-cluster-md-0
      clusterName: workload-cluster
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: ByoMachineTemplate
        name: workload-cluster-md-0
      version: v1.25.11
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  labels:
    nodepool: pool0
  name: workload-cluster-control-plane
  namespace: default
spec:
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        certSANs:
        - localhost
        - 127.0.0.1
        - 0.0.0.0
        - host.docker.internal
      controllerManager:
        extraArgs:
          enable-hostpath-provisioner: "true"
    files:
    - content: |
        apiVersion: v1
        kind: Pod
        metadata:
          creationTimestamp: null
          name: kube-vip
          namespace: kube-system
        spec:
          containers:
          - args:
            - manager
            env:
            - name: cp_enable
              value: "true"
            - name: vip_arp
              value: "true"
            - name: vip_leaderelection
              value: "true"
            - name: vip_address
              value: 10.100.3.31
            - name: vip_interface
              value: {{ .DefaultNetworkInterfaceName }}
            - name: vip_leaseduration
              value: "15"
            - name: vip_renewdeadline
              value: "10"
            - name: vip_retryperiod
              value: "2"
            image: ghcr.io/kube-vip/kube-vip:v0.5.0
            imagePullPolicy: IfNotPresent
            name: kube-vip
            resources: {}
            securityContext:
              capabilities:
                add:
                - NET_ADMIN
                - NET_RAW
            volumeMounts:
            - mountPath: /etc/kubernetes/admin.conf
              name: kubeconfig
          hostNetwork: true
          hostAliases:
            - hostnames:
                - kubernetes
              ip: 127.0.0.1
          volumes:
          - hostPath:
              path: /etc/kubernetes/admin.conf
              type: FileOrCreate
            name: kubeconfig
        status: {}
      owner: root:root
      path: /etc/kubernetes/manifests/kube-vip.yaml
    initConfiguration:
      nodeRegistration:
        criSocket: /var/run/containerd/containerd.sock
        ignorePreflightErrors:
        - Swap
        - DirAvailable--etc-kubernetes-manifests
        - FileAvailable--etc-kubernetes-kubelet.conf
    joinConfiguration:
      nodeRegistration:
        criSocket: /var/run/containerd/containerd.sock
        ignorePreflightErrors:
        - Swap
        - DirAvailable--etc-kubernetes-manifests
        - FileAvailable--etc-kubernetes-kubelet.conf
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: ByoMachineTemplate
      name: workload-cluster-control-plane
      namespace: default
  replicas: 3
  version: v1.25.11
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: ByoCluster
metadata:
  name: workload-cluster
  namespace: default
spec:
  bundleLookupBaseRegistry: projects.registry.vmware.com/cluster_api_provider_bringyourownhost
  controlPlaneEndpoint:
    host: 10.100.3.31
    port: 6443
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: ByoMachineTemplate
metadata:
  name: workload-cluster-control-plane
  namespace: default
spec:
  template:
    spec:
      selector: # 追加
        matchLables:
          "type": "master"
      installerRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: K8sInstallerConfigTemplate
        name: workload-cluster-control-plane
        namespace: default
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: ByoMachineTemplate
metadata:
  name: workload-cluster-md-0
  namespace: default
spec:
  template:
    spec:
      selector: # 追加
        matchLables:
          "type": "worker"
      installerRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: K8sInstallerConfigTemplate
        name: workload-cluster-md-0
        namespace: default
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: K8sInstallerConfigTemplate
metadata:
  name: workload-cluster-control-plane
  namespace: default
spec:
  template:
    spec:
      bundleRepo: projects.registry.vmware.com/cluster_api_provider_bringyourownhost
      bundleType: k8s
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: K8sInstallerConfigTemplate
metadata:
  name: workload-cluster-md-0
  namespace: default
spec:
  template:
    spec:
      bundleRepo: projects.registry.vmware.com/cluster_api_provider_bringyourownhost
      bundleType: k8s
```

## Workload Clusterの作成

Workload Clusterを作成するには先ほど作成したcluster.yamlをManagement ClusterにApplyします。

```shell:Management Cluster
$ kubectl apply -f cluster.yaml 
kubeadmconfigtemplate.bootstrap.cluster.x-k8s.io/workload-cluster-md-0 created
cluster.cluster.x-k8s.io/workload-cluster created
machinedeployment.cluster.x-k8s.io/workload-cluster-md-0 created
kubeadmcontrolplane.controlplane.cluster.x-k8s.io/workload-cluster-control-plane created
byocluster.infrastructure.cluster.x-k8s.io/workload-cluster created
byomachinetemplate.infrastructure.cluster.x-k8s.io/workload-cluster-control-plane created
byomachinetemplate.infrastructure.cluster.x-k8s.io/workload-cluster-md-0 created
k8sinstallerconfigtemplate.infrastructure.cluster.x-k8s.io/workload-cluster-control-plane created
k8sinstallerconfigtemplate.infrastructure.cluster.x-k8s.io/workload-cluster-md-0 created
```

Applyした後、Control-planeノードが1台ずつ、workerノードは並列でデプロイされていきます。以下はControl-planeノードの1台目の構築が終わり、2台目とworkerノードが2台並列でデプロイ中であることを示しています。

```shell:Management Cluster
$ kubectl get machine -owide
NAME                                           CLUSTER            NODENAME             PROVIDERID                         PHASE          AGE     VERSION
workload-cluster-control-plane-j996c           workload-cluster   kikuta-k8s-master2   byoh://kikuta-k8s-master2/4kmce9   Running        2m13s   v1.25.11
workload-cluster-control-plane-nh4wm           workload-cluster                                                           Provisioning   34s     v1.25.11
workload-cluster-md-0-7dd6cd8845x4ccbd-4tc57   workload-cluster                                                           Provisioning   2m45s   v1.25.11
workload-cluster-md-0-7dd6cd8845x4ccbd-bvbnn   workload-cluster                                                           Provisioning   2m45s   v1.25.11
```

最終的に以下のような状態になります。ラベル付けした通り、master1~3にcontrol-planeノード、worker1,2にworkerノードがデプロイされています。

```shell:Management Cluster
$ kubectl get machine -owide
NAME                                           CLUSTER            NODENAME             PROVIDERID                         PHASE     AGE     VERSION
workload-cluster-control-plane-j996c           workload-cluster   kikuta-k8s-master2   byoh://kikuta-k8s-master2/4kmce9   Running   5m41s   v1.25.11
workload-cluster-control-plane-lm59v           workload-cluster   kikuta-k8s-master3   byoh://kikuta-k8s-master3/slyfth   Running   99s     v1.25.11
workload-cluster-control-plane-nh4wm           workload-cluster   kikuta-k8s-master1   byoh://kikuta-k8s-master1/w64ivn   Running   4m2s    v1.25.11
workload-cluster-md-0-7dd6cd8845x4ccbd-4tc57   workload-cluster   kikuta-k8s-worker1   byoh://kikuta-k8s-worker1/frdboh   Running   6m13s   v1.25.11
workload-cluster-md-0-7dd6cd8845x4ccbd-bvbnn   workload-cluster   kikuta-k8s-worker2   byoh://kikuta-k8s-worker2/0vr4rh   Running   6m13s   v1.25.11
```

## Workload Clusterへのアクセス

Workload Clusterのkubeconfigを取得してkubectlコマンドでアクセスします。
kubeconfig取得にはWorkload Clusterのマニフェスト作成時に指定したクラスタ名が必要ですが、以下のコマンドで調べることもできます。

```shell: Management Cluster
$ kubectl get cluster
NAME               PHASE         AGE   VERSION
workload-cluster   Provisioned   24m
```

kubeconfigを取得するコマンドは以下の通りです。

```shell: Management Cluster
$ sudo clusterctl get kubeconfig workload-cluster | tee workload-kubeconfig.yaml
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <snip>
    server: https://10.100.3.31:6443
  name: workload-cluster
contexts:
- context:
    cluster: workload-cluster
    user: workload-cluster-admin
  name: workload-cluster-admin@workload-cluster
current-context: workload-cluster-admin@workload-cluster
kind: Config
preferences: {}
users:
- name: workload-cluster-admin
  user:
    client-certificate-data: <snip>
    client-key-data: <snip>
```

Workload Clusterの状態をpodを確認してみるとcontrol-planeがmaster1~3にデプロイされていることが分かります。

```shell: Management Cluster
$ kubectl --kubeconfig workload-kubeconfig.yaml get pod -A -owide
NAMESPACE     NAME                                         READY   STATUS    RESTARTS      AGE   IP            NODE                 NOMINATED NODE   READINESS GATES
kube-system   coredns-565d847f94-v4snk                     0/1     Pending   0             31m   <none>        <none>               <none>           <none>
kube-system   coredns-565d847f94-wx4ds                     0/1     Pending   0             31m   <none>        <none>               <none>           <none>
kube-system   etcd-kikuta-k8s-master1                      1/1     Running   0             30m   10.100.2.33   kikuta-k8s-master1   <none>           <none>
kube-system   etcd-kikuta-k8s-master2                      1/1     Running   0             32m   10.100.2.35   kikuta-k8s-master2   <none>           <none>
kube-system   etcd-kikuta-k8s-master3                      1/1     Running   0             28m   10.100.2.36   kikuta-k8s-master3   <none>           <none>
kube-system   kube-apiserver-kikuta-k8s-master1            1/1     Running   2 (30m ago)   30m   10.100.2.33   kikuta-k8s-master1   <none>           <none>
kube-system   kube-apiserver-kikuta-k8s-master2            1/1     Running   1             32m   10.100.2.35   kikuta-k8s-master2   <none>           <none>
kube-system   kube-apiserver-kikuta-k8s-master3            1/1     Running   1             27m   10.100.2.36   kikuta-k8s-master3   <none>           <none>
kube-system   kube-controller-manager-kikuta-k8s-master1   1/1     Running   1             29m   10.100.2.33   kikuta-k8s-master1   <none>           <none>
kube-system   kube-controller-manager-kikuta-k8s-master2   1/1     Running   1             32m   10.100.2.35   kikuta-k8s-master2   <none>           <none>
kube-system   kube-controller-manager-kikuta-k8s-master3   1/1     Running   1             28m   10.100.2.36   kikuta-k8s-master3   <none>           <none>
kube-system   kube-proxy-285s4                             1/1     Running   0             31m   10.100.2.35   kikuta-k8s-master2   <none>           <none>
kube-system   kube-proxy-csmmm                             1/1     Running   0             30m   10.100.2.33   kikuta-k8s-master1   <none>           <none>
kube-system   kube-proxy-khvsl                             1/1     Running   0             30m   10.100.2.46   kikuta-k8s-worker2   <none>           <none>
kube-system   kube-proxy-xlgpf                             1/1     Running   0             28m   10.100.2.36   kikuta-k8s-master3   <none>           <none>
kube-system   kube-proxy-z65nd                             1/1     Running   0             30m   10.100.2.42   kikuta-k8s-worker1   <none>           <none>
kube-system   kube-scheduler-kikuta-k8s-master1            1/1     Running   1             30m   10.100.2.33   kikuta-k8s-master1   <none>           <none>
kube-system   kube-scheduler-kikuta-k8s-master2            1/1     Running   1             32m   10.100.2.35   kikuta-k8s-master2   <none>           <none>
kube-system   kube-scheduler-kikuta-k8s-master3            1/1     Running   1             28m   10.100.2.36   kikuta-k8s-master3   <none>           <none>
kube-system   kube-vip-kikuta-k8s-master1                  1/1     Running   1             29m   10.100.2.33   kikuta-k8s-master1   <none>           <none>
kube-system   kube-vip-kikuta-k8s-master2                  1/1     Running   1             32m   10.100.2.35   kikuta-k8s-master2   <none>           <none>
kube-system   kube-vip-kikuta-k8s-master3                  1/1     Running   1             27m   10.100.2.36   kikuta-k8s-master3   <none>           <none>
```

## まとめ

本記事ではCluster APIを使ってWorkload Clusterを構築しました。次回はWorkload Clusterで障害が発生した際の動作を確認していきます。