Google Cloud Platformで勉強用Kubernetesクラスタを作る

筆者は先日Certified Kubernetes Administrator(以下CKA)を受験しました。
CKAではkubeadmによるクラスタの作成やバージョンアップが出題されます。
勉強用にCompute Engine上でKubernetesクラスタを作成したので手順を残します。

# 構成
- コントローラー 1台
- ワーカー 1台
- k8sバージョン 1.25(※)

(※)最新版は1.26です。
k8sのバージョンアップもCKAの出題範囲のため、[続編](https://qiita.com/hsmtkk/items/cf13be375fb61fe863bd)で1.25から1.26へのバージョンアップを練習してみます。

# コントローラー、ワーカーの構築

k8sのインストール最低要件は以下のとおりです。
- メモリ 2GB以上 (ただし追加のアプリを動作させる余裕ほぼなし)
- CPU 2コア以上

> 2 GB or more of RAM per machine (any less will leave little room for your apps).
> 2 CPUs or more.

https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#before-you-begin

e2-smallでギリギリ達成しますが、余裕をもたせてe2-mediumとします。

以下がインスタンス設定のスクリーンショットです。

![gce0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/b0497a19-0f0f-4b6f-460c-94e0891a2dd8.png)

ほぼデフォルト値のままですが、1点変更します。

料金節約のため、Spot VMを使用します。
Spot VMはGoogle側の都合で突然に強制停止される可能性があります。
かわりに利用料金が大幅に安くなります。

勉強や実験用でなく、安定的にkubeadmクラスタを運用したい場合には、通常のインスタンスを使用してください。

![gce2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/09d78449-67f9-9cd1-7bdf-6b7bf84d720b.png)

インスタンスが起動したら、SSHで接続します。

![gce3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/2b912378-8b39-cb26-858d-56614d6d7b1e.png)

# コントローラー、ワーカー共通

ここからはk8sのドキュメントに沿って操作します。

https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

## OS設定
```
sudo modprobe br_netfilter
```

以下内容で `/etc/sysctl.d/k8s.conf` を作成する。
```
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
```

```
sudo sysctl --system
```

## kubeadmのインストール

ドキュメントの通りです。
[続編](https://qiita.com/hsmtkk/items/cf13be375fb61fe863bd)で1.25から1.26へのバージョンアップを練習するので、あえて1.25を指定してインストールします。

```shell
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg 

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update
sudo apt-get install -y kubelet=1.25.0-00 kubeadm=1.25.0-00 kubectl=1.25.0-00
sudo apt-mark hold kubelet kubeadm kubectl
```

## CRIのインストール
CRIにCRI-Oを使うことにします。

CRI-Oのドキュメントに従い作業します。
なお、rootユーザーでコマンドを実行する想定になっています。

- バージョン等の指定
```shell
export OS=Debian_11
export OS=1.25
```

- インストール
```shell
echo "deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
echo "deb [signed-by=/usr/share/keyrings/libcontainers-crio-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list

mkdir -p /usr/share/keyrings
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | gpg --dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpg
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/Release.key | gpg --dearmor -o /usr/share/keyrings/libcontainers-crio-archive-keyring.gpg

apt-get update
apt-get install cri-o cri-o-runc
```

- サービス有効化
```shell
systemctl enable crio
systemctl start crio
```

https://github.com/cri-o/cri-o/blob/main/install.md#apt-based-operating-systems

# コントローラーでの作業

CNIにCalicoを使うことにします。

Calicoのドキュメントに記載のとおり `--pod-network-cidr` オプションを指定します。

```shell
sudo kubeadm init --pod-network-cidr=192.168.0.0/16
```

https://projectcalico.docs.tigera.io/getting-started/kubernetes/quickstart

成功すると以下のような出力を得られます。

```
To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.128.0.4:6443 --token z2ktxh.p4pbda2f3r81gaxo \
        --discovery-token-ca-cert-hash sha256:47b511ec98be374763eb1dc571c22bfadd9400be6ccde52b5e55f6b0e03e27c3
```

指示どおり、設定ファイルをコピーします。
```shell
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
```

ノードの情報を取得してみます。
```shell
kubectl get node
```

以下のような出力を得られれば、コントローラーは正常に稼動しています。
```shell
NAME         STATUS   ROLES           AGE   VERSION
controller   Ready    control-plane   85m   v1.25.0
```

# ワーカーでの作業

ワーカーをクラスターに参加させます。
`kubeadm init` 実行時に出力された `kubeadm join` のコマンドを実行します。

```shell
sudo kubeadm join 10.128.0.4:6443 --token z2ktxh.p4pbda2f3r81gaxo \
        --discovery-token-ca-cert-hash sha256:47b511ec98be374763eb1dc571c22bfadd9400be6ccde52b5e55f6b0e03e27c3
```

ふたたびノードの情報を取得してみます。

```shell
kubectl get node
```

以下のような出力を得られれば、ワーカーは正常にクラスターに参加できています。

```shell
NAME         STATUS   ROLES           AGE   VERSION
controller   Ready    control-plane   90m   v1.25.0
worker       Ready    <none>          20s   v1.25.0
```

# Calicoのセットアップ

公式ドキュメントに記載のとおりです。

```shell
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml
```

https://projectcalico.docs.tigera.io/getting-started/kubernetes/quickstart

calico～ namespaceが作成されます。

```
$ kubectl get namespace
NAME               STATUS   AGE
calico-apiserver   Active   53s
calico-system      Active   97s
default            Active   103m
kube-node-lease    Active   103m
kube-public        Active   103m
kube-system        Active   103m
tigera-operator    Active   106s
```

calico-system namespace内で、複数Podが動作します。

```
$ kubectl -n calico-system get pod
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-5f9dc85578-lzp7t   1/1     Running   0          2m25s
calico-node-hg8b7                          1/1     Running   0          2m26s
calico-node-vkjq6                          1/1     Running   0          2m26s
calico-typha-88f68649f-kd2gk               1/1     Running   0          2m26s
csi-node-driver-5zh6x                      2/2     Running   0          2m25s
csi-node-driver-ftw9b                      2/2     Running   0          2m25s
```

# k8sワークロードのデプロイ

適当なワークロードをデプロイし動作確認します。
今回は `traefik/whoami` を使用してみます。

https://hub.docker.com/r/traefik/whoami

以下のようにPod, Serviceが作成されます。

```shell
$ kubectl get pod
NAME                             READY   STATUS    RESTARTS   AGE
whoami-deploy-6d757cc7f6-9fk6b   1/1     Running   0          7m16s
whoami-deploy-6d757cc7f6-srfwl   1/1     Running   0          7m16s

$ kubectl get service
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP        14h
whoami-service   NodePort    10.97.181.224   <none>        80:31859/TCP   6m7s
```

NodePort Serviceのため `workerのIP` と `kubectl get serviceで表示されたポート` の組み合わせで、アクセスできます。

```shell
$ curl http://10.128.0.5:31859
Hostname: whoami-deploy-6d757cc7f6-9fk6b
IP: 127.0.0.1
IP: ::1
IP: 192.168.171.69
IP: fe80::46a:9bff:fe48:c6ef
RemoteAddr: 10.128.0.5:42205
GET / HTTP/1.1
Host: 10.128.0.5:31859
User-Agent: curl/7.74.0
Accept: */*
```

# 料金見積もり

以下条件で合計4.3ドル/月でした。

## Compute Engine

| 項目 | 見積もり条件 |
|-|-|
| スペック | SPOT VM, e2-medium |
| インスタンス数 | 2 |
| 稼動時間 | 2時間/日、7日/週 |

小計 3.22ドル/月

https://cloud.google.com/products/calculator#id=18a3e856-977b-46b6-a8f9-142a13ec6f62

## ネットワーク転送 (GCPからインターネット方向)

GCPからインターネットに出る通信(egress)が課金対象です。
利用者から見ると、作成したk8sクラスタからファイルをダウンロードするなどです。

10GBあるとして、
小計 1.08ドル/月

(k8sクラスタを構築し、少々操作する程度ならば、1GBにも達しないでしょう)

https://cloud.google.com/products/calculator/#id=298cf2f5-a7ff-4e09-a168-13ab58cd299c