[k8s] kubernetesのマルチノードクラスタでpodにアクセスできなくて困った件について

この記事はKMCアドベントカレンダー2023の24日目です。

https://adventar.org/calendars/8840

この記事ではインフラ初心者がkubernetesクラスタを立ててみたときに詰まった点をまとめてみました。

## はじめに

最近、VPS上で公開していた[ポートフォリオ](https://www.piny940.com)や[歌枠データベース](https://song-list.piny940.com)をDocker上で立てられるようにし、自宅サーバーへと完全移行しました。

(自宅サーバーへサーバーを移行した話はこの辺りの記事にまとめました。)

https://qiita.com/piny940/items/606a6b03c390c136c1f7

https://qiita.com/piny940/items/e41c2709692195f823b5

https://qiita.com/piny940/items/602316d913938db01c81

しかし、自宅サーバーは諸々の理由で頻繁に電源が落ちるため、kubernetesを使って冗長構成にしたいと思うようになりました。また、毎回sshでログインしてデプロイをするのが手間だったので自動デプロイの基盤をkubernetesで作りたいと思うようになり、意を決してkubernetesのお勉強を始めました！
というわけで、これから先「お家Kubernetes環境を作ろう」シリーズとしてk8sのお勉強記録をつけていこうと思います。

今回はその第一段階として、VPS上にkubernetesのマルチノードクラスタを構成してみました。


## 環境

- lemon: Kagoya Cloud VPS 2コア 2GB
- lime: Kagoya Cloud VPS 2コア 2GB

（lemonやlimeはマシン名です）

- デプロイツール: kubeadm
- CRI: cri-dockerd
- CNI: Calico（で上手くいかなかったためWeave netにした(後述)）

## ネタバレ

細かいことは置いといてとりあえず結論を知りたい！と言う方へ

マルチノードクラスタ上でpodを作成し、port-forwardしてpodにアクセスしようとしたのですが、表題の通りpodにアクセスできないという問題が生じました。

```bash
$ kubectl port-forward hello-node-7c97b7fcf5-rj259 8080:8080
error: error upgrading connection: unable to upgrade connection: pod does not exist
```

結論から言うと、Calicoに必要なポートが使えないことが原因でした。VPSをローカルネットで繋いだりしたものの179番が通らなかったため、Calicoは諦めてWeaveworksを使うことにしました。

## 問題発生までの流れ

ざっくり言うと：

1. kubeadm init
2. Calicoのインストール
3. kubeadm join
4. hello-nodeのデプロイ・サービス作成

です。詳細に興味がない方は次章まで飛ばして大丈夫です。

### Docker・コンテナランタイムのインストール

[ドキュメント](https://kubernetes.io/ja/docs/setup/production-environment/container-runtimes/)を眺めて、雑に馴染みのある[Docker Engine](https://kubernetes.io/ja/docs/setup/production-environment/container-runtimes/#docker)を選んでインストールしました。

（強いお友達はCRI-Oとかを勧めていた気がするが初心者の私には違いが分からないため一旦このまま進めます。）

### kubeadm-config.yamlを記述

CRIにcri-dockerdを選んだため、kubeadm initするときに読み込むconfigファイルに次のように記述しました。後でCalicoを使うために`ClusterConfiguration`の`networking.podSubnet`もついでに設定しておきます。

```yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
nodeRegistration:
  criSocket: unix:///run/cri-dockerd.sock
===
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: stable
networking:
  podSubnet: "192.168.0.0/16"
```

:::note warn
criSocketは`unix://`をつけないとエラーで怒られたので注意が必要です。
:::

### kubeadm init

マスターノードにしたいVPS上で

```bash
$ sudo kubeadm init --config=kubeadm-config.yaml
```

としました。表示されるコマンド（mkdir ...とか）をコピーして実行します。また、kubeadm joinコマンドは後で使うためコピーしてどこかにメモっておきます。

### Calicoをインストール

[Install Calico](https://docs.tigera.io/calico/latest/getting-started/kubernetes/quickstart#install-calico)のコマンドを順に実行しました。

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

### kubeadm join

ワーカーノードにしたいVPS上にもdockerやcri-dockerdなどをインストールし、さっきメモったコマンドを実行します。

```bash
$ sudo kubeadm join ...(略) --cri-socket=unix:///var/run/cri-dockerd.sock
```

:::note warn
ワーカーノードでもCRIにcri-dockerdを使用しているため、`kubeadm join ...`にオプションで`--cri-socket=unix:///var/run/cri-dockerd.sock`を指定する必要があります。これを忘れるとCRIが複数あって云々と怒られました。
:::

マスターノードに戻ってnodeを確認してみると、正常に追加できていそうです。

```bash
$ kubectl get nodes
NAME    STATUS   ROLES           AGE   VERSION
lemon   Ready    control-plane   27m   v1.28.2
lime    Ready    <none>          19m   v1.28.2
```

### hello-nodeをデプロイ

[チュートリアル](https://kubernetes.io/ja/docs/tutorials/hello-minikube)に従って、hello-nodeのデプロイメント・サービスを作成します。

```bash
$ kubectl create deployment hello-node --image=registry.k8s.io/echoserver:1.4
$ kubectl expose deployment hello-node --port=8080
```

:::note warn


チュートリアルから単純にコピペするとserviceのtypeはLoadBalancerになっていますが、これを正しく使うにはMetalLbなどを入れる必要があるため、--type=LoadBalancerは消して実行します。
:::

## 問題の内容

前章でマルチノードクラスタ上にhello-nodeのpod・serviceを作成することができました。

`get pods`やらをすると、確かにpodやserviceが作成できていそうです。

```bash
$ kubectl get pods
NAME                          READY   STATUS    RESTARTS   AGE
hello-node-7c97b7fcf5-rj259   1/1     Running   0          21m
$ kubectl get deployment
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
hello-node   1/1     1            1           21m
$ kubectl get svc/hello-node
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
hello-node   ClusterIP   10.111.221.203   <pending>     8080:31781/TCP   18m
```

そこで、実際にpodに対してport-forwardしようとすると、

```bash
$ kubectl port-forward hello-node-7c97b7fcf5-rj259 8080:8080
error: error upgrading connection: unable to upgrade connection: pod does not exist
```

`pod does not exist`と言われてしまいました。

## 試したこと

- namespaceが`default`になっているか確認→なっている。

```
$ kubectl get pod -A -o wide
NAMESPACE          NAME                                      READY   STATUS    RESTARTS        AGE   IP                NODE    NOMINATED NODE   READINESS GATES
calico-apiserver   calico-apiserver-68644496f8-4w6wp         1/1     Running   0               46m   192.168.232.6     lemon   <none>           <none>
calico-apiserver   calico-apiserver-68644496f8-h5v4r         1/1     Running   0               46m   192.168.232.5     lemon   <none>           <none>
calico-system      calico-kube-controllers-69bbc55b7-d5slw   1/1     Running   0               46m   192.168.232.4     lemon   <none>           <none>
calico-system      calico-node-4vwxs                         0/1     Running   0               46m   10.150.11.1       lemon   <none>           <none>
calico-system      calico-node-hrhhl                         0/1     Running   1 (8m43s ago)   40m   10.150.11.1       lime    <none>           <none>
calico-system      calico-typha-6c4fc595ff-kdqwn             1/1     Running   0               46m   10.150.11.1       lemon   <none>           <none>
calico-system      csi-node-driver-d2fzq                     2/2     Running   0               46m   192.168.232.2     lemon   <none>           <none>
calico-system      csi-node-driver-z9xm7                     2/2     Running   0               40m   192.168.124.129   lime    <none>           <none>
default            hello-node-7c97b7fcf5-rj259               1/1     Running   0               39m   192.168.124.130   lime    <none>           <none>
kube-system        coredns-5dd5756b68-6twqb                  1/1     Running   0               48m   192.168.232.3     lemon   <none>           <none>
kube-system        coredns-5dd5756b68-rkwdv                  1/1     Running   0               48m   192.168.232.1     lemon   <none>           <none>
kube-system        etcd-lemon                                1/1     Running   0               48m   10.150.11.1       lemon   <none>           <none>
kube-system        kube-apiserver-lemon                      1/1     Running   0               48m   10.150.11.1       lemon   <none>           <none>
kube-system        kube-controller-manager-lemon             1/1     Running   0               48m   10.150.11.1       lemon   <none>           <none>
kube-system        kube-proxy-8n2r9                          1/1     Running   0               48m   10.150.11.1       lemon   <none>           <none>
kube-system        kube-proxy-m8qlt                          1/1     Running   0               40m   10.150.11.1       lime    <none>           <none>
kube-system        kube-scheduler-lemon                      1/1     Running   0               48m   10.150.11.1       lemon   <none>           <none>
tigera-operator    tigera-operator-7f8cd97876-pm4hz          1/1     Running   0               46m   10.150.11.1       lemon   <none>           <none>
```

- serviceで指定すると？→できない。
```
$ kubectl port-forward svc/hello-node 8080:8080
error: error upgrading connection: unable to upgrade connection: pod does not exist
```

- podSubnetが被っている説？を検証
`kubeadm-config.yaml`のpodSubnetを次のように修正。
```
networking:
  podSubnet: 10.244.0.0/16
```

この変更を加えて`kubeadm reset`、`kubeadm init`をしたところ、色々と苦戦したのですが結局上手くいきませんでした。
この部分で詰まった点は記事1つ分くらいになりそうなのでここでは省略することにします。

- Calicoの必須ポートを確認
CalicoのNetwork requirementsに記載のポートが公開されていないことに気づきました。
179番を公開するのはあまり良くないと考え、Kagoya Cloud VPSのローカルネットワークを使ってVPSを同一ネットワーク上に配置しました。

:::note info
Kagoyaのマニュアルに従ってIPを10.0.0.1/16, 10.0.0.2/16にしました。
:::
しかし、同一ネットワークに配置しても179番が使えず、Calicoの使用を断念しました。

- Weave netをいれてみる
すぐには上手くいきませんでした。

ログを見てみたところ、設定したIPで通信されていませんでした。

- localAPIEndpointを設定する
`kubeadm-config.yaml`の`InitConfiguration`を次のように修正しました。
```kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
nodeRegistration:
  criSocket: unix:///run/cri-dockerd.sock
localAPIEndpoint:
  advertiseAddress: 10.0.0.1
  bindPort: 6443
...
```
これでkubeadm reset + kubeadm initを再度行ったのですが、なお上手くいきませんでした。

- Calicoの設定を抹消
`/etc/cni/net.d`、`/opt/cni/bin`にcalicoの設定が残っていたためすべて削除して再起動したところ、上手くいきました！！

## 教訓
- インストールするパッケージ等のrequirementsはちゃんと確認する！
必須ポートが開いていることを確認できていなかったのが最大の原因
- エラーが起きたらちゃんとログを読もう！

## 最後に
今回エラーを解消するに当たり、[Walnuts](https://github.com/walnuts1018)と[Southball](https://github.com/southball)、他KMCの先輩方にたくさん助けていただきました。ありがとうございます！！

この次は手元のminikube上でnextサーバー構築に挑戦していきたいと思います。