k3sとheadscaleでlonghornを使う

に公開

構成とポリシー

  • WSLがansibleおよびデプロイ関連作業端末
  • 登場するPCはすべてheadscaleに接続済。
  • やろうと思えば物理LAN内でk3sを完結することは可能。
  • メリット
    • 複数拠点でのk3sクラスタ構築ができる。
    • 将来的にAWS側に端末を増やしても、VPS、GCPを増やしてもいいわけです。headscaleへの参加は必要。

失敗談

方法だけ知りたい人向け:本題

想定

  • k3sをheadscale上で構築しようとしていた。
  • vpn-authとwireguard-nativeで行けると思った。

起動コマンドは以下の通り。(Ansibleから持ってきたので、一行か\がいります。)

server
--tls-san={{ inventory_hostname }}
--node-ip={{ tailscale_ip }}
--node-external-ip={{ tailscale_ip }}
--advertise-address={{ tailscale_ip }}
--flannel-backend=wireguard-native
--flannel-external-ip
--flannel-iface=tailscale0
--vpn-auth=name=tailscale,joinKey={{ tailscale_join_key }},controlServerURL={{ headscale_url }}

組み込みetcd非対応

結果としてうまくいっていないのでなんともいえないが、今となってはheadscaleであっても複数のネットワークに分散されている判定だった可能性もある。経路的には同じLAN内ではあるんですけどね。

このタイプのデプロイメントでは、組み込みのetcdはサポートされていません。組み込みのetcdを使用する場合、すべてのサーバーノードはプライベートIPを介して相互に到達可能でなければなりません。エージェントは複数のネットワークに分散することができますが、すべてのサーバーは同じ場所にある必要があります。

k3sとheadscaleとの統合失敗

結論:このページに書いてあることはできなかった。正確にはMTUを調整しないとできないとみられる。できたという記事もある。
k3sとheadscaleの統合(Experimental)

検証時系列

  1. MetricServerを立てて、kubectl top nodeで各ノードの情報取得はできる。
  2. longhornを入れると各ノード間の同期でcontent deadline exceedが発生する。
  3. ICMPの相互通信はできるので、Path MTU Discoveryでは対応できない場所なのか。
  4. TailscaleのMTU変更できるらしいのですが、本環境では効いていないのかうまくいかず。(1週間経過)
  5. TailscaleのMTU変更がDEBUG機能なのであまり信用したくはなく、CNIの変更に踏み切る。
  6. あっさりうまくいった。

本題

k3sを起動する。

起動コマンドは以下の通り。(Ansibleから持ってきたので、一行か\がいります。)

server
--cluster-init
--tls-san={{ inventory_hostname }}
--node-ip={{ tailscale_ip }}
--flannel-backend=none
--disable-network-policy

flannel-backend=noneのため、vxlanも使いません。tailnetへのログインもなくなります。
disable-network-policyはciliumで必要になります。ドキュメント通りです。

Ciliumインストール

上記ではCNI(Container Network Interface)がありませんので、Pod間の通信はできないです。

したがって、Metrics Serverを入れてもこうなります。

$ kubectl top node
error: Metrics API not available

wireguard-nativeとvxlanをあきらめたので、CalicoとCilliumの選択肢で、最終的にCilliumをCNIとして使うことをにした。インストール方法は公式ドキュメントに書いてあるまま。$CILIUM_NAMESPACEを書き換えないと怒られます。kube-systemにしてます。

helm install cilium cilium/cilium --version 1.18.1 \
   --namespace $CILIUM_NAMESPACE \
   --set operator.replicas=1

ここまでいっていれば、見えるはず。(少し時間はかかる)

$ kubectl get node -o wide
NAME       STATUS   ROLES                       AGE    VERSION        INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                        KERNEL-VERSION                 CONTAINER-RUNTIME
callis     Ready    <none>                      105m   v1.33.4+k3s1   100.64.0.6    <none>        Ubuntu 24.04.3 LTS              6.8.0-78-generic               containerd://2.0.5-k3s2
digginos   Ready    control-plane,etcd,master   106m   v1.33.4+k3s1   100.64.0.7    <none>        Ubuntu 24.04.3 LTS              6.8.0-71-generic               containerd://2.0.5-k3s2
ramulus    Ready    control-plane,etcd,master   105m   v1.33.4+k3s1   100.64.0.9    <none>        Ubuntu 24.04.3 LTS              6.8.0-1032-raspi               containerd://2.0.5-k3s2
truncus    Ready    control-plane,etcd,master   107m   v1.33.4+k3s1   100.64.0.2    <none>        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.27.1.el10_0.x86_64   containerd://2.0.5-k3s2

$ kubectl top node
NAME       CPU(cores)   CPU(%)      MEMORY(bytes)   MEMORY(%)
callis     74m          0%          1029Mi          143%
digginos   447m         5%          1901Mi          12%
ramulus    635m         15%         1748Mi          46%
truncus    123m         0%          2426Mi          7%

Longhoenのインストール

こちらも公式ドキュメント通り

helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace --version 1.9.1

Lognhorn UI

事前準備:ingressを使うか、ポートフォワーディング
確認用のため、ポートフォワーディングにします。

kubectl port-forward -n longhorn-system svc/longhorn-frontend 8081:80

Keycloakで使う。

正直面倒だったので、geminiにansible playbookを書かせております。PWはAnsible Vaultです。
厳密にはIngress Controllerでやるべきでしょうが、そこは仮ということで。

---
- name: Deploy Keycloak on K3s Cluster
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Add Bitnami Helm repository
      kubernetes.core.helm_repository:
        name: bitnami
        repo_url: "https://charts.bitnami.com/bitnami"

    - name: Install Keycloak via Helm chart
      kubernetes.core.helm:
        name: keycloak
        chart_ref: bitnami/keycloak
        release_namespace: keycloak
        create_namespace: yes
        wait: yes
        timeout: 10m
        values:
          # --- 管理者ユーザーとパスワード ---
          auth:
            adminUser: keycloak-admin
            adminPassword: "{{ vault_keycloak_admin_password }}"
          # --- 永続化の設定 (Longhornを利用) ---
          postgresql:
            persistence:
              enabled: true
              storageClass: "longhorn"
              size: 5Gi

          # --- 外部アクセスの設定 ---
          service:
            type: NodePort
            nodePorts:
              http: 30001

これをデプロイすると、こうなる。
ノードのIPはheadscaleネットワークのため、100.64.0.0/10のIPです。

余談

  • Headscaleの使用感がよすぎるので変えづらい。magicDNSとか。
  • k3sのメリットはラズパイで動くというだけになりました。
  • CNIをいれるならkubernatesでもよかったなと思います。

戒め

これ系の生成AIはマジであてにならない。余計迷子になる。
あと最近頑固になったのか?なかなか考えを改めてくれない。

Discussion