⚙️

Incus VMでMicroK8sクラスタを組んでみた (1of2) 構築編

に公開

List of documents

Incus VMでMicroK8sクラスタを組んでみた (1of2) 構築編
Incus VMでMicroK8sクラスタを組んでみた (2of2) 検証編

MicroK8sをクラスタ構成で構築してみたメモです。
実際にHA (High Avaiability)がどう機能するかは検証編に続きます。

はじめに

物理マシン1台の上にIncusでVMインスタンスを4台を立てて、各VMインスタンスをMicroK8sのノードとするクラスタを組みました。
物理マシンはほとんどいじっていない素のUbuntuサーバで、自宅ホームネットワークに設置済みです。今回、これをリモートのクライアントPCから操作・管理できるようにしたいと考え、そのためにVPNのTailscaleと、MicroK8sのアドオンのMetalLB (ロードバランサ)も導入しました。

MicroK8sの構築は下記のmicrok8s公式サイトを主に参考にしました。
LXDとIncusの違いはありますが、ほぼほぼ同じ手順で構築できました。

  • LXDのVM上にmicrok8sをインストールする手順

https://microk8s.io/docs/install-lxd

  • microk8sでクラスタを組む手順

https://microk8s.io/docs/high-availability

事前に用意するもの

  • 適当な物理マシン 1台
    Incus MVのホストマシンです。
     - OS: Ubuntu 24.04.2
     - 自宅ホームネットワーク内からInternet reachable
     - openssh-serverインストール済

  • 適当なクライアントPC 1台
    Incusホストマシンを操作する作業用ノートPCです。
     - OS: Ubuntu 24.04.2
     - 自宅ホームネットワーク内または宅外ではFree WiFi等からInternet reachable
     - SSHクライアント

  • 論理ネットワーク
     - home_private: 192.168.0.0/24
      DefaultGW: ホームルータ (192.168.0.1)
     - incusbr0_private: 10.57.140.0/24
      DefautGW: Incusホストマシンのincusbr0 (10.57.140.1)

1 Incusのインストール

作業対象: Incusホストマシン

Incus公式のインストール手順に沿ってインストールします。

https://linuxcontainers.org/incus/docs/main/tutorial/first_steps/

インストールのコマンドだけ抜粋すると以下のとおりです。

Incusホストマシン
## パッケージインストール
$ sudo apt install incus
$ sudo apt install qemu-system

## カレントユーザをincus-adminグループに追加
$ sudo adduser $USER incus-admin
$ newgrp incus-admin

## 初期化
$ incus admin init --minimal

初期化まで実行すると、ip aコマンドでincusbr0ブリッジとそのIPアドレス(10.57.140.1/24)を確認できます。

Incusホストマシン
$ ip a show incusbr0
8: incusbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 10.57.140.1/24 scope global incusbr0
(...)

このincusbr0ブリッジは、Incusのデフォルト設定でVMインスタンスを作成した場合に、VMインスタンスのNICインタフェースが接続されるブリッジです。

incusbr0ブリッジのデフォルト設定ではDHCPサーバが有効になっており、VMのNICインタフェースに対してIPアドレスを動的に割り当てます。特に何も考えずVMインスタンスを作成しても、MVインスタンスはこのincusbr0ブリッジ (NAT)を経由してインターネットにアクセスできるため便利なのですが、デフォルト設定ではDHCPで払い出すIPアドレスプールの範囲が無制限となっています。今回、DHCPのIPアドレスプールの範囲を明示的に.199までに狭めることにします。これは、後で設定するmetallbが使用するIPアドレスプールとの重複を避けるためです。

Incusホストマシン
$ incus network set incusbr0 ipv4.dhcp.ranges="10.57.140.2-10.57.140.199"
$ incus network show incusbr0

2 Tailscaleのインストール

作業対象: クライアントPC

Tailscale公式の下記インストール手順に沿ってインストールします。

https://tailscale.com/download/linux

作業対象: Incusホストマシン

Tailscale公式の下記インストール手順に沿ってサブネットルータとしてインストールします。

https://tailscale.com/kb/1019/subnets

Tailscaleインストール後の正常性確認

  • クライアントPCからping疎通することを確認します。
     - 確認1: Incusホストマシンのtailnet0のIPv4アドレスへping
     - 確認2: Incusホストマシンのincusbr0のIPv4アドレス(10.57.140.1)へping

  • クライアントPCからIncusホストマシンへSSHログインできることを確認します。
     - 確認3: Incusホストマシンのtailnet0のIPアドレスへssh

3 Incus VMでMicroK8sクラスタを構築

3.1 Incus VMインスタンスの作成

microk8sの仕様上、HA (High Availability)機能を利用するためには複数のVMインスタンス(ノード)が必要となります。

  • VMインスタンス(ノード)の必要台数:
     - Master node 3台
     - Standby node 1台 (任意)

Standby nodeは、いずれか1台のMaster nodeが障害となった際にMaster nodeに昇格するノードで、構築は任意ですが、今回は昇格による障害リカバリの動作を試したいためVMインスタンス 4台をIncusで作成します。

Incusホストマシン
$ incus launch images:ubuntu/noble/desktop k8s0-vm --vm -c limits.cpu=2 -c limits.memory=4GiB
$ incus launch images:ubuntu/noble/desktop k8s1-vm --vm -c limits.cpu=2 -c limits.memory=4GiB
$ incus launch images:ubuntu/noble/desktop k8s2-vm --vm -c limits.cpu=2 -c limits.memory=4GiB
$ incus launch images:ubuntu/noble/desktop k8s3-vm --vm -c limits.cpu=2 -c limits.memory=4GiB
$ incus list

launchはVMインスタンスの作成と起動(start)を兼ねていて、incus listコマンドにより、MVインスタンス 4台がRUNNINGの状態になっていることが確認できます。

3.2 microk8sのインストール

microk8sのcurrentバージョンを確認します。
(下記は、2025年6月時点のバージョン表示例)

Incus VMインスタンス
$ incus exec k8s0-vm -- snap info microk8s
(...)
channels:
  1.32/stable:           v1.32.3  2025-04-22 (8148) 172MB classic
  1.32/candidate:        v1.32.3  2025-04-22 (8148) 172MB classic
  1.32/beta:             v1.32.3  2025-04-22 (8148) 172MB classic
  1.32/edge:             v1.32.6  2025-06-19 (8266) 172MB classic
  latest/stable:         v1.32.3  2025-04-07 (7964) 172MB classic
  latest/candidate:      v1.32.3  2025-03-12 (7890) 179MB classic
  latest/beta:           v1.32.3  2025-03-12 (7890) 179MB classic
  latest/edge:           v1.33.2  2025-06-19 (8267) 177MB classic
  1.33/stable:           v1.33.0  2025-04-24 (8205) 177MB classic
  1.33/candidate:        v1.33.0  2025-04-24 (8205) 177MB classic
  1.33/beta:             v1.33.0  2025-04-24 (8205) 177MB classic
  1.33/edge:             v1.33.2  2025-06-18 (8244) 177MB classic
(...)

microk8s公式サイトのインストールガイドを参考に、どのバージョン(チャンネル)をインストールするか決定します。

https://microk8s.io/docs/setting-snap-channel

今回は、current latest/stableより先の1.33/stableをインストールしてみます。

Incus VMインスタンス
$ incus exec k8s0-vm -- snap install microk8s --classic --channel=1.33/stable
$ incus exec k8s1-vm -- snap install microk8s --classic --channel=1.33/stable
$ incus exec k8s2-vm -- snap install microk8s --classic --channel=1.33/stable
$ incus exec k8s3-vm -- snap install microk8s --classic --channel=1.33/stable
(...)
microk8s (1.33/stable) v1.33.0 from Canonical installed

各VMノードでmicrok8sが起動状態になっていることを確認します。(表示例: microk8s is running)
まだクラスタを組んでいないため、master nodesの表示は自ノードのみです。(表示例: 127.0.0.1)

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s status --wait-ready
$ incus exec k8s1-vm -- microk8s status --wait-ready
$ incus exec k8s2-vm -- microk8s status --wait-ready
$ incus exec k8s3-vm -- microk8s status --wait-ready
(...)
microk8s is running
high-availability: no
  datastore master nodes: 127.0.0.1:19001
  datastore standby nodes: none
addons:
  enabled:
    dns                  # (core) CoreDNS
    ha-cluster           # (core) Configure high availability on the current node
    helm                 # (core) Helm - the package manager for Kubernetes
    helm3                # (core) Helm 3 - the package manager for Kubernetes
  disabled:
    cert-manager         # (core) Cloud native certificate management
    cis-hardening        # (core) Apply CIS K8s hardening
    community            # (core) The community addons repository
    dashboard            # (core) The Kubernetes dashboard
    gpu                  # (core) Alias to nvidia add-on
    host-access          # (core) Allow Pods connecting to Host services smoothly
    hostpath-storage     # (core) Storage class; allocates storage from host directory
    ingress              # (core) Ingress controller for external access
    kube-ovn             # (core) An advanced network fabric for Kubernetes
    mayastor             # (core) OpenEBS MayaStor
    metallb              # (core) Loadbalancer for your Kubernetes cluster
    metrics-server       # (core) K8s Metrics Server for API access to service metrics
    minio                # (core) MinIO object storage
    nvidia               # (core) NVIDIA hardware (GPU and network) support
    observability        # (core) A lightweight observability stack for logs, traces and metrics
    prometheus           # (core) Prometheus operator for monitoring and logging
    rbac                 # (core) Role-Based Access Control for authorisation
    registry             # (core) Private image registry exposed on localhost:32000
    rook-ceph            # (core) Distributed Ceph storage using Rook
    storage              # (core) Alias to hostpath-storage add-on, deprecated

各VMノードで、どのノードとクラスタを組んでいるか確認します。
まだクラスタを組んでいないため、get nodesの表示は自ノードのみです。(表示例: k8s0-vm)

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl get nodes
$ incus exec k8s1-vm -- microk8s kubectl get nodes
$ incus exec k8s2-vm -- microk8s kubectl get nodes
$ incus exec k8s3-vm -- microk8s kubectl get nodes
(...)
NAME      STATUS   ROLES    AGE     VERSION
k8s0-vm   Ready    <none>   9m18s   v1.33.0

3.3 microk8s VMノードのクラスタを組む (add-nodeとjoin)

add-nodeとjoinによりVMノード 4台でクラスタを組みます。

  • k8s0-vmのクラスタにadd-nodeするためのトークンをk8s0-vm上で発行します。
  • トークンを使って他のVMノードをk8s0-vmのクラスタにjoinさせます。
  • トークンはjoin 1回しか使えず、joinごとにトークンを発行します。
  1. k8s1-vmをjoinさせます。
Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 10.57.140.189:25000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx
(...)

$ incus exec k8s1-vm -- microk8s join 10.57.140.189:25000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx
Successfully joined the cluster.
  1. 同様に、k8s2-vmをjoinさせます。
Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 10.57.140.189:25000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx
(...)

$ incus exec k8s2-vm -- microk8s join 10.57.140.189:25000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx
Successfully joined the cluster.
  1. 同様に、k8s3-vmをjoinさせます。
Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 10.57.140.189:25000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx

$ incus exec k8s3-vm -- microk8s join 10.57.140.189:25000/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx
Successfully joined the cluster.

クラスタを組み終わったため、get nodesの表示は4台となります。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl get nodes
NAME      STATUS   ROLES    AGE   VERSION
k8s0-vm   Ready    <none>   29m   v1.33.0
k8s1-vm   Ready    <none>   13m   v1.33.0
k8s2-vm   Ready    <none>   12m   v1.33.0
k8s3-vm   Ready    <none>   10m   v1.33.0

正常にクラスタを組み終わった状態では、microk8sは以下のステータス表示となります。

  • high-availability: yes
  • master nodes: k8s0-vm, k8s1-vm, k8s2-vmのIPアドレス(3個)
  • standby nodes: 最後にjoinさせたk8s3-vmのIPアドレス(1個)
Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s status --wait-ready
microk8s is running
high-availability: yes
  datastore master nodes: 10.57.140.189:19001 10.57.140.40:19001 10.57.140.98:19001
  datastore standby nodes: 10.57.140.147:19001
(...)

$ incus list | grep k8s
| k8s0-vm         | RUNNING | 10.57.140.189 (enp5s0)      | fdxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx (enp5s0) | VIRTUAL-MACHINE | 0         |
| k8s1-vm         | RUNNING | 10.57.140.40 (enp5s0)       | fdxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx (enp5s0) | VIRTUAL-MACHINE | 0         |
| k8s2-vm         | RUNNING | 10.57.140.98 (enp5s0)       | fdxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx (enp5s0) | VIRTUAL-MACHINE | 0         |
| k8s3-vm         | RUNNING | 10.57.140.147 (enp5s0)      | fdxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx (enp5s0) | VIRTUAL-MACHINE | 0         |

3.4 microkl8sのアドオン追加 (dashboardとmetalld)

dashboardアドオンを有効化します。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s enable dashboard
(...)
NOTES:
*************************************************************************************************
*** PLEASE BE PATIENT: Kubernetes Dashboard may need a few minutes to get up and become ready ***
*************************************************************************************************

Congratulations! You have just installed Kubernetes Dashboard in your cluster.

To access Dashboard run:
  kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-proxy 8443:443

NOTE: In case port-forward command does not work, make sure that kong service name is correct.
      Check the services in Kubernetes Dashboard namespace using:
        kubectl -n kubernetes-dashboard get svc

Dashboard will be available at:
  https://localhost:8443
Applying manifest
secret/microk8s-dashboard-token created

If RBAC is not enabled access the dashboard using the token retrieved with:

microk8s kubectl describe secret -n kube-system microk8s-dashboard-token

Use this token in the https login UI of the kubernetes-dashboard service.

In an RBAC enabled setup (microk8s enable RBAC) you need to create a user with restricted
permissions as shown in:
https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md

有効化すると、RBACを有効化していない場合の、ダッシュボードへのログインに必要なトークンの表示コマンドが表示されます。

今すぐトークンを表示する必要はありませんが、後ほどログインすることになるので、今ここでトークンの表示コマンドを実行してtoken:の値をメモしておきます。(以下の/////部分)

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl describe secret -n kube-system microk8s-dashboard-token
Name:         microk8s-dashboard-token
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1123 bytes
namespace:  11 bytes
token:      ////////////////////////////////////////////////////////////////////////////////////////

metallbアドオンを有効化します。
metallb(ロードバランサ)をmicrok8sサービスに紐付けると、外部から該当microk8sサービスにアクセスできるようになります。有効化する際に、引数として、metallbが外部向けのExternal-IPとして用いるIPアドレスプールの範囲を指定します。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s enable metallb:10.57.140.200-10.57.140.219
MetalLB is enabled

3.5 ダッシュボードへのアクセス設定

get all -Aでダッシュボードのネームスペース名とサービス名を特定します。
i.e. 443/TCPポートを持っているサービスを特定します。

特定した結果は、
 - ネームスペース名: kubernetes-dashboard
 - サービス名: kubernetes-dashboard-kong-proxy

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl get all -A
(...)
NAMESPACE              NAME                                           TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                  AGE
kubernetes-dashboard   service/kubernetes-dashboard-kong-proxy        ClusterIP      10.152.183.31    <none>          443/TCP                  25m

ダッシュボードのサービス名とネームスペース名を指定して、サービス設定の編集を行います。
edit svcを実行すると編集画面(vi画面)になるので、そこでtype:の値をClusterIPからLoadBalancerに書き換えて保存します。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl edit svc kubernetes-dashboard-kong-proxy -n kubernetes-dashboard
(..)
編集前 type: ClusterIP
編集後 type: LoadBalancer

保存してvi画面を抜けると設定変更が即時反映されます。
get all -Aで設定変更が反映されたことを確認できます。
 - TYPE: LoadBalancer
 - EXTERNAL-IP: metallbが割り当てたIPアドレス (表示例: 10.57.140.200)

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl get all -A
(...)
NAMESPACE              NAME                                           TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                  AGE
kubernetes-dashboard   service/kubernetes-dashboard-kong-proxy        LoadBalancer   10.152.183.31    10.57.140.200   443:31034/TCP            31m

設定は以上です。
クライアントPCのWebブラウザからダッシュボードにアクセスしてみましょう。

  • アクセスURL: https://10.57.140.200

下記のようなWarning画面が表示されれば成功です。
Continue to 10.57.140.200 (unsafe)をクリックして続行します。

ダッシュボードへログインするためのトークンの入力が求められます。

あらかじめメモしておいたトークン値を入力するか、または、今ここでトークン表示コマンドを実行して表示されたトークン値を入力します。

ダッシュボード画面が表示されます。

3.6 microbotへのアクセス設定

お試しサービスとして、microk8sの公式サイト (Installing - MicroK8s in LXD)でも紹介されているmicrobotをインストールしてみます。

https://microk8s.io/docs/install-lxd

まず、microbotをデプロイします。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl create deployment microbot --image=dontrebootme/microbot:v1
deployment.apps/microbot created

get allでデプロイできたことを確認します。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl get all
(...)
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.152.183.1   <none>        443/TCP   88m

デプロイした素の状態だと外部からサービスへアクセスできません。外部からロードバランサを介してサービスへアクセスできるようにLoadBalancerタイプでmicrobotサービスをexposeします。

Incus VMインスタンス
$ incus exec k8s0-vm -- microk8s kubectl expose deployment microbot --type=LoadBalancer --port=80 --name=microbot-service
service/microbot-service exposed

get allでmicrobotサービスの状態を確認します。
 - TYPE: LoadBalancer
 - EXTERNAL-IP: metallbが割り当てたIPアドレス (表示例: 10.57.140.201)
 - PORT(S): 内部ポート:外部ポート/TCP (表示例: 80:30740/TCP)

incus exec k8s0-vm -- microk8s kubectl get all
(...)
NAME                       TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
service/microbot-service   LoadBalancer   10.152.183.246   10.57.140.201   80:30740/TCP   15s

設定は以上です。
クライアントPCのWebブラウザからmicrobotにアクセスしてみましょう。

  • アクセスURL: http://10.57.140.201/

以下のような画像とhostnameが表示されれば成功です。

Webブラウザからに限らず、CLIのcurlコマンドからでもmicrobotにアクセスできます。

クライアントPC
$ curl 10.57.140.201:80
<!DOCTYPE html>
<html>
  <style type="text/css">
    .centered
      {
      text-align:center;
      margin-top:0px;
      margin-bottom:0px;
      padding:0px;
      }
  </style>
  <body>
    <p class="centered"><img src="microbot.png" alt="microbot"/></p>
    <p class="centered">Container hostname: microbot-7d987d6fc6-8m2xz</p>
  </body>
</html>

なお、microbotサービスが実際にどのVMノード上で動いているかmicrok8sのコマンドで確認することもできます。(表示例: k8s3-vm上で動作している)

Incus VMインスタンス
incus exec k8s0-vm -- microk8s kubectl describe pod microbot | grep Node:
Node:             k8s3-vm/10.57.140.147

構築編は以上です。

The next document:
Incus VMでMicroK8sクラスタを組んでみた (2of2) 検証編

Discussion