Dev > CaaS > Kubernetes > K3s

今まで自宅サーバーにはMicrok8sをインストールしてKubernetesクラスターを構築していましたが、今回はK3sをインストールしてみたので、そのメモ。
Synology CSI Driverを使いたかったのですが、Microk8sだとうまく動作しなかったので、実績のあるK3sを試してみました。

Controlplane(兼Worker)ノード1台(192.168.100.50)、Workerノード2台(192.168.100.51,192.168.100.52)の計3台構成でセットアップしました。

Controlplaneノードのセットアップ

まずはControlplaneノードにK3sをインストールします。192.168.11.50のサーバー上で

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.34.2+k3s1 \
  K3S_TOKEN=CHANGEME \
  sh -s - server \
  --node-ip=192.168.100.50 \
  --node-name=192.168.100.50 \
  --advertise-address=192.168.100.50 \
  --disable=traefik \
  --disable=local-storage

状態を確認します。

$ sudo systemctl status k3s | cat
● k3s.service - Lightweight Kubernetes
     Loaded: loaded (/etc/systemd/system/k3s.service; enabled; preset: enabled)
     Active: active (running) since Thu 2025-11-27 14:05:52 UTC; 10s ago
       Docs: https://k3s.io
    Process: 1036001 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Process: 1036002 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
   Main PID: 1036004 (k3s-server)
      Tasks: 48
     Memory: 540.8M (peak: 550.0M)
        CPU: 15.836s
     CGroup: /system.slice/k3s.service
             ├─1036004 "/usr/local/bin/k3s server"
             ├─1036025 "containerd "
             ├─1036367 /var/lib/rancher/k3s/data/96d729345c5cd40a1f306326c270ddd621a6acdf003606e91630f8c8147ba6ac/bin/containerd-shim-runc-v2 -namespace k8s.io -id ae731258755b088fe830f46096c8651229a363412a8ad972d4053c1fdc067761 -address /run/k3s/containerd/containerd.sock
             └─1036373 /var/lib/rancher/k3s/data/96d729345c5cd40a1f306326c270ddd621a6acdf003606e91630f8c8147ba6ac/bin/containerd-shim-runc-v2 -namespace k8s.io -id 15269957327d39ec3f431b632afdc71ecee76e185ad672ab204501e37e59e632 -address /run/k3s/containerd/containerd.sock

Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Started tunnel to 192.168.100.50:6443"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Connecting to proxy" url="wss://192.168.100.50:6443/v1-k3s/connect"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Stopped tunnel to 127.0.0.1:6443"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Proxy done" err="context canceled" url="wss://127.0.0.1:6443/v1-k3s/connect"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="error in remotedialer server [400]: websocket: close 1006 (abnormal closure): unexpected EOF"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Handling backend connection request [192.168.100.50]"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Connected to proxy" url="wss://192.168.100.50:6443/v1-k3s/connect"
Nov 27 14:05:59 apricot k3s[1036004]: time="2025-11-27T14:05:59Z" level=info msg="Remotedialer connected to proxy" url="wss://192.168.100.50:6443/v1-k3s/connect"
Nov 27 14:06:02 apricot k3s[1036004]: I1127 14:06:02.892827 1036004 kuberuntime_manager.go:1828] "Updating runtime config through cri with podcidr" CIDR="10.42.0.0/24"
Nov 27 14:06:02 apricot k3s[1036004]: I1127 14:06:02.894417 1036004 kubelet_network.go:47] "Updating Pod CIDR" originalPodCIDR="" newPodCIDR="10.42.0.0/24"

kubeconfigをコピーします。

sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
chmod 600 ~/.kube/config

NodeとPodの状態を確認します。

$ kubectl get node,pod -owide -A
NAME                  STATUS   ROLES           AGE   VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
node/192.168.100.50   Ready    control-plane   35s   v1.34.2+k3s1   192.168.100.50   <none>        Ubuntu 24.04.2 LTS   6.8.0-87-generic   containerd://2.1.5-k3s1

NAMESPACE     NAME                                  READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES
kube-system   pod/coredns-7f496c8d7d-q2qw6          1/1     Running   0          29s   10.42.0.3   192.168.100.50   <none>           <none>
kube-system   pod/metrics-server-7b9c9c4b9c-xwq2r   1/1     Running   0          29s   10.42.0.2   192.168.100.50   <none>           <none>

Workerノードのセットアップ

Worker 1台目 (192.168.11.51)をセットアップします。192.168.11.51のサーバー上で次を実行します。

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.34.2+k3s1 \
  K3S_URL=https://192.168.100.50:6443 \
  K3S_TOKEN=CHANGEME \
  sh -s - agent \
  --node-ip=192.168.100.51 \
  --node-name=192.168.100.51

Controlplane(192.168.11.50)のサーバー上でNodeとPodの状態を確認します。

$ kubectl get node,pod -owide -A
NAME                  STATUS   ROLES           AGE   VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
node/192.168.100.50   Ready    control-plane   85s   v1.34.2+k3s1   192.168.100.50   <none>        Ubuntu 24.04.2 LTS   6.8.0-87-generic   containerd://2.1.5-k3s1
node/192.168.100.51   Ready    <none>          8s    v1.34.2+k3s1   192.168.100.51   <none>        Ubuntu 24.04.2 LTS   6.8.0-87-generic   containerd://2.1.5-k3s1

NAMESPACE     NAME                                  READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES
kube-system   pod/coredns-7f496c8d7d-q2qw6          1/1     Running   0          79s   10.42.0.3   192.168.100.50   <none>           <none>
kube-system   pod/metrics-server-7b9c9c4b9c-xwq2r   1/1     Running   0          79s   10.42.0.2   192.168.100.50   <none>           <none>

Worker 2台目 (192.168.11.52)をセットアップします。192.168.11.52のサーバー上で次を実行します。

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.34.2+k3s1 \
  K3S_URL=https://192.168.100.50:6443 \
  K3S_TOKEN=CHANGEME \
  sh -s - agent \
  --node-ip=192.168.100.52 \
  --node-name=192.168.100.52

Controlplane(192.168.11.50)のサーバー上でNodeとPodの状態を確認します。

$ kubectl get node,pod -owide -A
NAME                  STATUS   ROLES           AGE     VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
node/192.168.100.50   Ready    control-plane   2m10s   v1.34.2+k3s1   192.168.100.50   <none>        Ubuntu 24.04.2 LTS   6.8.0-87-generic   containerd://2.1.5-k3s1
node/192.168.100.51   Ready    <none>          53s     v1.34.2+k3s1   192.168.100.51   <none>        Ubuntu 24.04.2 LTS   6.8.0-87-generic   containerd://2.1.5-k3s1
node/192.168.100.52   Ready    <none>          9s      v1.34.2+k3s1   192.168.100.52   <none>        Ubuntu 24.04.2 LTS   6.8.0-87-generic   containerd://2.1.5-k3s1

NAMESPACE     NAME                                  READY   STATUS    RESTARTS   AGE    IP          NODE             NOMINATED NODE   READINESS GATES
kube-system   pod/coredns-7f496c8d7d-q2qw6          1/1     Running   0          2m4s   10.42.0.3   192.168.100.50   <none>           <none>
kube-system   pod/metrics-server-7b9c9c4b9c-xwq2r   1/1     Running   0          2m4s   10.42.0.2   192.168.100.50   <none>           <none>

Synology CSI Driverのインストール

次にSynology CSI Driverをインストールします。次のようにマニフェストを用意します。

git clone https://github.com/SynologyOpenSource/synology-csi.git -b v1.2.1
cd synology-csi
cp config/client-info-template.yml config/client-info.yml
vim config/client-info.yml

client-info.ymlにSynology NASの情報を設定します。

Storage Classの設定ファイルを編集します。

vim deploy/kubernetes/v1.20/storage-class.yml 

次の設定にします。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: synology-iscsi-storage
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: csi.san.synology.com
parameters:
  fsType: 'btrfs'
  dsm: '192.168.100.200'
  location: '/volume1'
  formatOptions: '--nodiscard'
reclaimPolicy: Delete
allowVolumeExpansion: true

次のコマンドでSynology CSI Driverをインストールします。

./scripts/deploy.sh install --basic

次のような出力が得られます。

==== Creates namespace and secrets, then installs synology-csi ====
Current Server Version: v1.34.2+k3s1
Deploy Version: v1.20
namespace/synology-csi created
secret/client-info-secret created
serviceaccount/csi-controller-sa created
clusterrole.rbac.authorization.k8s.io/synology-csi-controller-role created
clusterrolebinding.rbac.authorization.k8s.io/synology-csi-controller-role created
statefulset.apps/synology-csi-controller created
csidriver.storage.k8s.io/csi.san.synology.com created
namespace/synology-csi unchanged
serviceaccount/csi-node-sa created
clusterrole.rbac.authorization.k8s.io/synology-csi-node-role created
clusterrolebinding.rbac.authorization.k8s.io/synology-csi-node-role created
daemonset.apps/synology-csi-node created
storageclass.storage.k8s.io/synology-iscsi-storage created

synology-csi NamespaceのPodの状態を確認します。

$ kubectl get pod -owide -n synology-csi
NAME                        READY   STATUS    RESTARTS   AGE    IP               NODE             NOMINATED NODE   READINESS GATES
synology-csi-controller-0   4/4     Running   0          116s   192.168.100.52   192.168.100.52   <none>           <none>
synology-csi-node-bhcpf     2/2     Running   0          115s   192.168.100.50   192.168.100.50   <none>           <none>
synology-csi-node-p6mq8     2/2     Running   0          115s   192.168.100.52   192.168.100.52   <none>           <none>
synology-csi-node-tz6hl     2/2     Running   0          115s   192.168.100.51   192.168.100.51   <none>           <none>

Persistent Volumeの動作確認

次のPVCを作成して、動作を確認します。

cat <<EOF > /tmp/pvc.yaml
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-dynamic-volume-claim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: synology-iscsi-storage
---
EOF

kubectl apply -f /tmp/pvc.yaml

次のようにPVCがBound状態になれば成功です。

$ kubectl get pvc
NAME                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS             VOLUMEATTRIBUTESCLASS   AGE
test-dynamic-volume-claim   Bound    pvc-6df54034-c251-4184-9d58-cf7b2fa1c039   1Gi        RWO            synology-iscsi-storage   <unset>                 7s

Synology NAS側のSAN ManagerでLUNとiSCSI Targetも作成されていることを確認できます。

image

image

このPVCをマウントするPodを作成し、読み書きの動作確認を行います。

cat <<EOF > /tmp/pvc-test-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: pvc-test-pod
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 100
    fsGroup: 100
  containers:
  - name: test-container
    image: busybox:latest
    command: ["/bin/sh"]
    args:
      - -c
      - |
        echo "=== PVC Read/Write Test Start ==="
        echo "Mount point check:"
        df -h /mnt/test
        echo ""
        
        echo "=== Write Test ==="
        echo "Hello from PVC test - $(date)" >> /mnt/test/test-file.txt
        echo "File write completed"
        echo ""
        
        echo "=== File Verification ==="
        ls -lah /mnt/test/
        echo ""
        
        echo "=== Read Test ==="
        cat /mnt/test/test-file.txt
        echo ""
        
        echo "=== Test Completed ==="
        sleep infinity
    volumeMounts:
    - name: test-volume
      mountPath: /mnt/test
  volumes:
  - name: test-volume
    persistentVolumeClaim:
      claimName: test-dynamic-volume-claim
  restartPolicy: Never
---
EOF

kubectl apply -f /tmp/pvc-test-pod.yaml

次のようにPodがReadyになれば成功です。

$ kubectl  get pod -owide
NAME           READY   STATUS    RESTARTS   AGE   IP          NODE            NOMINATED NODE   READINESS GATES
pvc-test-pod   1/1     Running   0          24s   10.42.1.4   192.168.100.51  <none>           <none>

ログを確認し、読み書きができていることを確認します。

$ kubectl logs pvc-test-pod 
=== PVC Read/Write Test Start ===
Mount point check:
Filesystem                Size      Used Available Use% Mounted on
/dev/sdd                  1.0G      5.8M    904.6M   1% /mnt/test

=== Write Test ===
File write completed

=== File Verification ===
total 24K    
drwxrwsr-x    1 root     users         26 Nov 27 14:22 .
drwxr-xr-x    3 root     root        4.0K Nov 27 14:22 ..
-rw-r--r--    1 1000     users         51 Nov 27 14:22 test-file.txt

=== Read Test ===
Hello from PVC test - Thu Nov 27 14:16:50 UTC 2025

=== Test Completed ===

次のコマンドでPodを再作成し、データが保持されていることを確認します。

kubectl delete pod pvc-test-pod --force
kubectl apply -f /tmp/pvc-test-pod.yaml

ログを確認し、ファイルに行が追記されていることを確認します。

$ kubectl logs pvc-test-pod 
=== PVC Read/Write Test Start ===
Mount point check:
Filesystem                Size      Used Available Use% Mounted on
/dev/sdd                  1.0G      5.8M    904.6M   1% /mnt/test

=== Write Test ===
File write completed

=== File Verification ===
total 24K    
drwxrwsr-x    1 root     users         26 Nov 27 14:22 .
drwxr-xr-x    3 root     root        4.0K Nov 27 14:23 ..
-rw-rw-r--    1 1000     users        102 Nov 27 14:23 test-file.txt

=== Read Test ===
Hello from PVC test - Thu Nov 27 14:16:50 UTC 2025
Hello from PVC test - Thu Nov 27 14:16:50 UTC 2025

=== Test Completed ===

次のコマンドでPodとPVCを削除します。

kubectl delete pod pvc-test-pod --force
kubectl delete pvc test-dynamic-volume-claim

SAN ManagerでLUNとiSCSI Targetも削除されていることを確認できます。

HelmでRedisをインストールして動作確認

次にHelmを使ってRedisをインストールし、動作確認を行います。

helm upgrade --install redis \
  oci://registry-1.docker.io/bitnamicharts/redis \
  -n redis \
  --create-namespace --wait \
  --set auth.enabled=false

次のようにPodとPVCが作成されていることを確認します。

$ kubectl get pod,pvc -owide -n redis 
NAME                   READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
pod/redis-master-0     1/1     Running   0          2m36s   10.42.1.11   192.168.100.51   <none>           <none>
pod/redis-replicas-0   1/1     Running   0          2m36s   10.42.1.12   192.168.100.51   <none>           <none>
pod/redis-replicas-1   1/1     Running   0          114s    10.42.2.5    192.168.100.52   <none>           <none>
pod/redis-replicas-2   1/1     Running   0          72s     10.42.0.4    192.168.100.50   <none>           <none>

NAME                                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS             VOLUMEATTRIBUTESCLASS   AGE     VOLUMEMODE
persistentvolumeclaim/redis-data-redis-master-0     Bound    pvc-5f7792b3-3c49-44d6-8167-0ebc4f4e66fb   8Gi        RWO            synology-iscsi-storage   <unset>                 2m36s   Filesystem
persistentvolumeclaim/redis-data-redis-replicas-0   Bound    pvc-ea541f4a-879a-4ccd-9654-49efc018d87d   8Gi        RWO            synology-iscsi-storage   <unset>                 2m36s   Filesystem
persistentvolumeclaim/redis-data-redis-replicas-1   Bound    pvc-80964ce4-bb0d-4d2d-8b19-881feb82a6ed   8Gi        RWO            synology-iscsi-storage   <unset>                 115s    Filesystem
persistentvolumeclaim/redis-data-redis-replicas-2   Bound    pvc-02e1e1dc-0646-447c-89d5-6f16215d27cf   8Gi        RWO            synology-iscsi-storage   <unset>                 72s     Filesystem

次のコマンドでRedisマスターにポートフォワードし、redis-cliで接続して動作確認を行います。

kubectl port-forward --namespace redis svc/redis-master 6379:6379

次のようにRedisに接続して、データの読み書きができることを確認します。

$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set foo 100
OK
127.0.0.1:6379> get foo
"100"

次のコマンドでRedisをアンインストールし、PVCも削除します。

helm uninstall -n redis redis --wait
kubectl delete pvc -n redis --all
kubectl delete ns redis

K3sにSynology CSI Driverをインストールして、Synology NASのiSCSI LUNをKubernetesのPersistent Volumeとして利用することができました。
K3sは軽量でセットアップも簡単なので、自宅サーバーでKubernetesクラスターを構築するのに便利です。

Found a mistake? Update the entry.
Share this article: