Kubernetesクラスターを「クラウド↔自宅ネットワーク」間で作る（Tailscale編）

## はじめに

「Kubernetesクラスターを異なるプライベートネットワーク間で構築」するのが目標。

以前に試行錯誤して実現した記事を以下のように書いたのだが、手順が非常に面倒臭い課題があった...
* [Kubernetesクラスターを「クラウド↔自宅ネットワーク」間で作る（前編）](https://qiita.com/showchan33/items/4c9030ee5ac2ae8a24e8)
* [Kubernetesクラスターを「クラウド↔自宅ネットワーク」間で作る（後編）](https://qiita.com/showchan33/items/abac0221e2b0ca30f3fb)

そんな中、最近[Tailscale](https://tailscale.com/)という端末間でVPN接続してくれるサービスがあることを知り、「これを使ったらもっと簡単にできるんじゃない！？」と思って試してみた内容をまとめる。

## クラスターに追加したいノードの情報

#### Control Plane(Master)にしたいマシン

* OS : Debian GNU/Linux 10 (buster)
* 所属するネットワーク
    * 自宅
    * プライベートIP : 192.168.1.4/24

#### Workerにしたいマシン

* OS : Ubuntu 18.04.6 LTS (Bionic Beaver)
* 所属するネットワーク
    * クラウド上
    * プライベートIP : 10.0.0.4/24

#### Kubernetesのバージョン

v1.26.0

ざっくりと現状（モノクロ）と目標（色つき）を図にしてみる。図を見ておわかりの通り、クラスターを構築したいマシンのプライベートネットワークが別々になっているのが大きな課題となる。
![network-new-1.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370519/2bc1b1ff-0c26-9031-7813-e4c1aa7e95c2.png)

## Tailscaleのセットアップ

実現イメージは図のように、「Tailscaleを使って2つのマシンをプライベートネットワークでつなぐ」こと。各マシンにTailscaleをセットアップすると、下図の水色部分のように100系のプライベートIPが追加され、これを使って通信可能になる。
![network-new-2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370519/42c6947b-b9d5-76e0-14b8-ef45f8495d60.png)

具体的な手順は以下を参照いただきたい。

https://tailscale.com/kb/1017/install/

以下にざっくりとセットアップ方法を説明する。

#### ユーザの登録

[インストール手順](https://tailscale.com/kb/1017/install/)のサイトの「Sign up for a Tailscale account」をクリックしてユーザ登録する。同じことは、[Tailscaleのトップページ](https://tailscale.com/)から「Use Tailscale for free」のボタンを押してもできるはず。

#### マシンをTailscaleのネットワークに追加

Tailscaleクライアントを各マシンにインストールする必要がある。
今回対象にしているLinuxだと、下記サイトを参考にコマンドを実行する。

* https://tailscale.com/download/linux

以下の２つの手順で簡単に設定可能。

* Tailscale(クライアント)のインストール

```
$ curl -fsSL https://tailscale.com/install.sh | sh
```

* `tailscale up`(または`tailscale login`で、端末をTailscaleネットワークに追加


```
$ sudo tailscale up

To authenticate, visit:

	https://login.tailscale.com/a/xxxxxxxxxxxx
```

端末に表示されたURLにWebブラウザからアクセスすると、ログイン画面が表示される。予め登録した「ユーザID/パスワード」を入力して認証完了後、以下のように端末一覧が出てきて、マシンに100系のIPアドレスが追加されているはず。

※たった2つのコマンドで、異なるプライベートネットワークの端末が簡単にVPNで繋げられるのには非常に驚き感動した！！

![](https://www.gwtcenter.com/wp-content/uploads/2022/10/2022-10-09_16h13_42.png)
引用元 : [GWT Center](https://www.gwtcenter.com/how-to-use-tailscale-2)

TailScaleを追加したマシンでも、以下のように100系のネットワークが追加されているのを確認できる。

```
$ ip address show tailscale0
5: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none
    inet 100.xxx.xxx.xxx/32 scope global tailscale0
       valid_lft forever preferred_lft forever
...
```

## Kubernetesクラスターの構築

Tailscaleで追加したネットワークを使って、いよいよKubernetesクラスターを構築していく。
基本的な手順は下記のkubeadmに基づく。

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

今回のクラスターの構築ではTailscaleの100系のネットワークを使うため、一点だけカスタマイズが必要。以下のように、ノードにする各マシンの``/etc/default/kubelet``ファイルに次の設定を追記する。
```text:/etc/default/kubelet
KUBELET_EXTRA_ARGS=--node-ip=100.xxx.xxx.xxx
```
↑``100.xxx.xxx.xxx``の部分には、各マシンにTailscaleで割り当てられたIPアドレスを記載

設定反映のためにリロードと、kubeletの再起動が必要。
```
$ sudo systemctl daemon-reload
$ sudo systemctl restart kubelet
```

Control Planeノードでの``kubeadm init``、その後のWorkerノードでの``kubeadm join``コマンドは上記の設定反映後に行う必要がある。

この時点で、自宅とクラウド間でクラスター構築できているのが確認できる。
```
$ kubectl get node -o wide
NAME          STATUS      ROLES           AGE     VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE                       KERNEL-VERSION       CONTAINER-RUNTIME
node1-home    NotReady    control-plane   30m     v1.26.0   100.xxx.xxx.xxx   <none>        Debian GNU/Linux 10 (buster)   4.9.0-8-amd64        containerd://1.6.14
node2-cloud   NotReady    <none>          5m39s   v1.26.0   100.yyy.yyy.yyy   <none>        Ubuntu 18.04.6 LTS             4.15.0-122-generic   containerd://1.6.10
```

## CNIの追加

上の状態だとノードを跨いだPod間の通信ができないため、それを実現するためのCNIツールをインストールする必要がある。今回はCalicoでCNIを構築してみる。

下記サイトの"Install Calico"に記載されたコマンドで構築可能。

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

"Tigera Calico operator"は公式サイト通りに素直にインストールして問題ない。

:::note warn
下記を代わりに``kubectl apply -f``で行おうとするとマニフェストが長すぎてエラーになってしまうので注意！
:::

```
$ kubectl create -f \
  https://raw.githubusercontent.com/projectcalico/calico/v3.24.5/manifests/tigera-operator.yaml
```

続く``custom-resources.yaml``は編集が必要なため、一旦ローカルにダウンロードする。

```
$ wget https://raw.githubusercontent.com/projectcalico/calico/v3.24.5/manifests/custom-resources.yaml
```

ダウンロードした``custom-resources.yaml``で、以下の箇所を編集。
* (1) ``ipPools``配下のcidrを必要に応じて編集
    * デフォルトの``192.168.0.0/16``は自宅LANのネットワーク範囲と重なる可能性が高いので、例えば``192.168.128.0/24``などのように重複しない範囲に変更する
* (2) ``nodeAddressAutodetectionV4``の追加
    * ``firstFound``は``false``にする
        * デフォルトの``true``だと、各ノードに紐づくIPがTailscaleの100系以外になってしまってCNIの構築に失敗してしまったため
    * ``cidrs``に、クラスター構築に使うIPの範囲を明記する。

```yaml:custom-resources.yaml（一部抜粋）
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 192.168.0.0/16 # (1)必要に応じて変更
      encapsulation: VXLANCrossSubnet
      natOutgoing: Enabled
      nodeSelector: all() 
    #(2)ここから追加
    nodeAddressAutodetectionV4:
      firstFound: false
      cidrs:
        - "100.0.0.0/8"
    #(2)ここまで追加
```

上のように編集した後にマニフェストを実行。

```
$ kubectl apply -f custom-resources.yaml
```

うまくいくと、次のような感じでCalicoが稼働しているのを確認できる。

```
$ kubectl get pod -n calico-system -o wide
NAME                                       READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
calico-kube-controllers-67df98bdc8-nlb8g   1/1     Running   0          23m   192.168.128.1    node1-home    <none>           <none>
calico-node-5l9lb                          1/1     Running   0          17m   100.yyy.yyy.yyy  node2-cloud   <none>           <none>
calico-node-lkpf5                          1/1     Running   0          23m   100.xxx.xxx.xxx  node1-home    <none>           <none>
calico-typha-788df7c947-4qs97              1/1     Running   0          17m   100.yyy.yyy.yyy  node2-cloud   <none>           <none>
```