📝

自宅k3sクラスタにGCEのワーカーノードを追加してみる

2024/08/30に公開

ゴール(はじめに):k3sクラスタにGCEのワーカーノードを追加する方法をまとめる

自宅に構築しているk3sクラスタにワーカーノードを追加したくなったのでその方法をまとめました。
クラウドのインスタンスを使えば必要な時に必要な分だけリソースを追加できて便利ということで、今回はGoogle Compute Engine(GCE)をワーカーノードとして追加してみようと思います。
また、この構成だと各ノードは共通のプライベートネットワーク内にあるわけではないので、通信時のセキュリティ面も考慮した方法にします。

やったこと

tailscaleをk3sに統合することができるので、この機能を使って簡単にノードを追加することが可能になります。
やり方は公式ドキュメントに記載されているやり方で(まだ実験的機能のようですが)、この通りやれば動きました。
https://docs.k3s.io/networking/distributed-multicloud#integration-with-the-tailscale-vpn-provider-experimental
tailscaleを使うことでクラウドのインスタンスとプライベートなネットワークを構築できるので通信のためのポートの穴あけなどセキュリティリスクになるような手順が不要になります。
それでは順番に手順を説明していきます。

事前準備

  • k3sクラスタ構築
    k3sでKubernetesのクラスタを構築する前提とします。
    k3sの基本的なインストール方法や仕様については言及しませんので、適宜公式ページを参照してください。
    https://docs.k3s.io/quick-start
    私の環境では既に自宅のマシンをマスターノードとして起動していたので、それを利用した手順となってます。
  • tailscaleのインストール
    今回はtailscaleというサービスを利用します。tailscaleというのは無料で利用できてかつ簡単に構築できるVPNサービスです。詳しくは以下ページから確認してください。
    最初にアカウント登録が必要です。
    https://tailscale.com/kb/1151/what-is-tailscale

GCEインスタンスの作成

まずはワーカーノードとして利用するGCEインスタンスを作成します。
スペックは適当な値で大丈夫です。ちなみにk3sの最小ではCPU:1 core, RAM: 512MBあれば動くようです。(GCEの無料枠でも動きます)
また作成したインスタンスはネットワークに接続できる必要があります。
今回はtest-agent-1という名前で作成してます。

tailscaleの設定

以下を参考に必要な初期設定を行なっていきます。
https://docs.k3s.io/networking/distributed-multicloud#integration-with-the-tailscale-vpn-provider-experimental

  1. tailscaleにログイン
    https://login.tailscale.com/admin/machines

  2. Settings > KeysGenerate Auth Keyでkeyを作成します。k3sのインストール時に利用するので控えておいてください。

  3. Access Control のjsonに以下を追加します。
    cidrはclusterの設定に合わせて修正してください。デフォルトだと以下で大丈夫です。

    "autoApprovers": {
		"routes": {
			"10.42.0.0/16":      ["your_account@xyz.com"],
			"2001:cafe:42::/56": ["your_account@xyz.com"],
		},
	},

既存ノードにtailscaleを導入

既に存在するノードにもtailscaleの設定を追加する必要があります。
この作業は各マシンで行います。
tailscaleがインストールされてない場合はまずインストールします。

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

tailscaleをk3sに統合するにはk3sのインストールコマンドに--vpn-auth="name=tailscale,joinKey=$AUTH-KEY"のオプションをつけることで可能です。$AUTH-KEYは上で作成したtailscaleのauth keyを設定してください。

k3sに設定を適用します。すでにk3sのインストールがされている場合も以下のコマンドをそのまま実行すればtailscaleの設定が追加されるはずです。
--vpn-auth以外の設定はそれぞれの設定に合わせてください。

curl -sfL https://get.k3s.io | sh -s - --write-kubeconfig-mode 644 --disable=traefik --vpn-auth="name=tailscale,joinKey=$AUTH-KEY"

GCEをワーカーノードとして追加

いよいよGCEをk3sクラスタに追加します。
まずはGCEにssh接続し他マシンと同じようにtailscaleをインストールします。

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

次にワーカーノードとしてk3sを起動します。
ここでK3S_URLはtailscaleのmachine一覧で表示されているserverノードのアドレス、$SEVER-TOKENはサーバーノードの/var/lib/rancher/k3s/server/node-tokenに格納されているagentを追加するためのトークンを設定します。
ここでもこれまでと同じように$AUTH-KEYが必要です。

curl -sfL https://get.k3s.io | K3S_URL=https://100.XX.XX.XX:6443 sh -s - agent --token $SEVER-TOKEN --vpn-auth="name=tailscale,joinKey=$AUTH-KEY"

これでGCEインスタンスがワーカーノードとして追加されていることが確認できると思います。
test-agent-1がReadyになっていれば大丈夫です。

❯  kubectl get node
NAME             STATUS     ROLES                  AGE   VERSION
pollux           Ready      control-plane,master   34d   v1.29.6+k3s2
test-agent-1     Ready      <none>                 38m   v1.29.6+k3s2

動作確認

ちゃんと動くか動作確認してみます。
まず今回追加したノードに適当なpodを起動します。

cat << EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
  - name: example-container
    image: nginx
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - test-agent-1
EOF

ちゃんとtest-agent-1ノードに起動できています。

❯  kubectl get pod -o wide
NAME            READY   STATUS    RESTARTS      AGE     IP            NODE           NOMINATED NODE   READINESS GATES
example-pod     1/1     Running   0             4d23h   10.42.6.4     test-agent-1   <none>           <none>

port-forwardしてレスポンスが返ってくるかも試します。

kubectl port-forward example-pod 8080:80

ちゃんと結果が返ってくれば大丈夫です。

❯  curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

追加で他のノードからpod同士疎通できるかも試します。
マスターノードに新しく疎通用のpodを起動します。

cat << EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: example-pod-2
spec:
  containers:
  - name: example-container
    image: nginx
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - pollux
EOF
❯  k get pod -o wide
NAME            READY   STATUS    RESTARTS      AGE     IP            NODE           NOMINATED NODE   READINESS GATES
example-pod     1/1     Running   0             4d23h   10.42.6.4     test-agent-1   <none>           <none>
example-pod-2   1/1     Running   5 (50s ago)   4d23h   10.42.0.193   pollux         <none>           <none>

example-pod-2に入ってexample-podに対してcurlを投げてみると結果がちゃんと返却されました。
10.42.6.4はget podして得られるIPアドレスです。

❯  k iexec example-pod-2
# curl http://10.42.6.4:80 -vvv
*   Trying 10.42.6.4:80...
* Connected to 10.42.6.4 (10.42.6.4) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.42.6.4
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.27.1
< Date: Fri, 30 Aug 2024 07:25:18 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Mon, 12 Aug 2024 14:21:01 GMT
< Connection: keep-alive
< ETag: "66ba1a4d-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 10.42.6.4 left intact

おわりに

tailscaleを利用して簡単にGCEインスタンスをk3sクラスタに追加することができました。
今までtailscale自体は知っていたのですが、k3s側に統合する機能があることを初めて知り、それを使うことで簡単に実現できました。
実現にあたっては各種公式ドキュメントがまとまっていて、結果としてそれに従ってやってみたらちゃんと動いた、というだけの記事になってます。
セキュリティに関しては素人ですが、tailscaleでプライベートネットワークを構成しているので、ノード間の通信をpublicで行うために穴あけとかをする必要がないので比較的安全なんじゃないかと思っています。
普段自宅でk3sを利用していてコンピュートリソースを追加したいけど、物理マシンを買ってセットアップするのはめんどくさいとかあくまで一時的な追加でいいというユースケースで参考になれば嬉しいです。

GitHubで編集を提案

Discussion