# Install Longhorn on Talos Kubernetes ## PreRequest ### System Extensions 要額外在 Talos K8S 的 Worker Node 上安裝 `iscsi-tools` 和 `util-linux-tools` 這兩個套件 ``` customization: systemExtensions: officialExtensions: - siderolabs/iscsi-tools - siderolabs/util-linux-tools ``` > `siderolabs/iscsi-tools`: this extension enables iscsid daemon and iscsiadm to be available to all nodes for the Kubernetes persistent volumes operations. > `siderolabs/util-linux-tools`: this extension enables linux tool to be available to all nodes. For example, the fstrim binary is used for Longhorn volume trimming. ### Pod Security Longhorn 要求在 `longhorn-system` Namespace 要有 `enforce: "privileged"` 的權限 ### Data Path Mounts You need provide additional data path mounts to be accessible to the Kubernetes Kubelet container. These mount is necessary to provide access to the host directories and attaching volumes required by the Longhorn components. ``` machine: kubelet: extraMounts: - destination: /var/lib/longhorn type: bind source: /var/lib/longhorn options: - bind - rshared - rw ``` ## 環境準備 ### 獲得 Talos System Extensions 的 image ID ```bash! ## create the schematic file $ cat <<EOF > longhorn.yaml customization: systemExtensions: officialExtensions: - siderolabs/iscsi-tools - siderolabs/util-linux-tools - siderolabs/gvisor - siderolabs/qemu-guest-agent EOF ## 到 Talos 官網的 Image Factory 查詢包含指定套件的 Image ID: $ curl -X POST --data-binary @longhorn.yaml https://factory.talos.dev/schematics {"id":"c527b6b20fb22847304656677e9bd4c4055dfcce95f3385da5db80e35f5fa1dc"} ``` > Image Factory is a service that generates Talos boot assets on-demand. Image Factory allows to generate boot assets for the official Talos Linux releases and official Talos Linux system extensions. ### Controlplane node patch yaml 以下是 Controlplane node 使用 machine config 檔需要的 patch yaml ```yaml! machine: install: image: factory.talos.dev/installer/c527b6b20fb22847304656677e9bd4c4055dfcce95f3385da5db80e35f5fa1dc:v1.6.5 cluster: inlineManifests: - name: namespace-longhorn-system contents: |- apiVersion: v1 kind: Namespace metadata: name: longhorn-system ``` ### Worker node patch yaml 以下是 Worker node 使用 machine config 檔需要的 patch yaml ```yaml! machine: kubelet: extraMounts: - destination: /var/lib/longhorn type: bind source: /var/lib/longhorn options: - bind - rshared - rw install: image: factory.talos.dev/installer/c527b6b20fb22847304656677e9bd4c4055dfcce95f3385da5db80e35f5fa1dc:v1.6.5 ``` ### 透過 patch yaml 修改 machineconfig ```bash! ## 修改 k1m1 $ talosctl machineconfig patch /home/bigred/k1/v1.6.5/controlplane.yaml --patch @/home/bigred/k1/v1.6.5/k1m1.patch --output /home/bigred/k1/v1.6.5/k1m1.yaml ## 修改 k1w1 $ talosctl machineconfig patch /home/bigred/k1/v1.6.5/worker.yaml --patch @/home/bigred/k1/v1.6.5/k1w1.patch --output /home/bigred/k1/v1.6.5/k1w1.yaml ## 修改 k1w2 $ talosctl machineconfig patch /home/bigred/k1/v1.6.5/worker.yaml --patch @/home/bigred/k1/v1.6.5/k1w2.patch --output /home/bigred/k1/v1.6.5/k1w2.yaml ``` ### 建立 Talos K8S ``` $ 1m2w.sh Talos Control Plane (172.22.1.11) config waiting 120 k1m1(172.22.1.11) config waiting 300 k1w1(172.22.1.15) config k1w2(172.22.1.16) config waiting 120 NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k1m1 Ready control-plane 5m56s v1.29.2 172.22.1.11 <none> Talos (v1.6.5) 6.1.78-talos containerd://1.7.13 k1w1 Ready worker 63s v1.29.2 172.22.1.15 <none> Talos (v1.6.5) 6.1.78-talos containerd://1.7.13 k1w2 Ready worker 56s v1.29.2 172.22.1.16 <none> Talos (v1.6.5) 6.1.78-talos containerd://1.7.13 ``` ### 確認 System Extension 是否安裝成功 ```bash! $ talosctl -n 172.22.1.11,172.22.1.15,172.22.1.16 --talosconfig=/home/bigred/k1/v1.6.5/talosconfig get extensions NODE NAMESPACE TYPE ID VERSION NAME VERSION 172.22.1.11 runtime ExtensionStatus 0 1 iscsi-tools v0.1.4 172.22.1.11 runtime ExtensionStatus 1 1 util-linux-tools $VERSION 172.22.1.11 runtime ExtensionStatus 2 1 schematic 613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245 172.22.1.15 runtime ExtensionStatus 0 1 iscsi-tools v0.1.4 172.22.1.15 runtime ExtensionStatus 1 1 util-linux-tools $VERSION 172.22.1.15 runtime ExtensionStatus 2 1 schematic 613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245 172.22.1.16 runtime ExtensionStatus 0 1 iscsi-tools v0.1.4 172.22.1.16 runtime ExtensionStatus 1 1 util-linux-tools $VERSION 172.22.1.16 runtime ExtensionStatus 2 1 schematic 613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245 ``` ## 安裝 Longhorn ```bash! $ curl -s https://raw.githubusercontent.com/longhorn/longhorn/v1.6.0/deploy/longhorn.yaml | sed 's/numberOfReplicas: "3"/numberOfReplicas: "2"/' | kubectl apply -f - ``` ### 檢查是否部屬成功 ```bash! $ kubectl get pods --namespace longhorn-system NAME READY STATUS RESTARTS AGE csi-attacher-57689cc84b-btwsj 1/1 Running 0 12m csi-attacher-57689cc84b-h7zw9 1/1 Running 0 12m csi-attacher-57689cc84b-zsb4f 1/1 Running 0 12m csi-provisioner-6c78dcb664-pc2f5 1/1 Running 0 12m csi-provisioner-6c78dcb664-vjw8z 1/1 Running 0 12m csi-provisioner-6c78dcb664-xfvc6 1/1 Running 0 12m csi-resizer-7466f7b45f-b8qq9 1/1 Running 0 12m csi-resizer-7466f7b45f-pbhwp 1/1 Running 0 12m csi-resizer-7466f7b45f-pdqr5 1/1 Running 0 12m csi-snapshotter-58bf69fbd5-54w76 1/1 Running 0 12m csi-snapshotter-58bf69fbd5-8vznv 1/1 Running 0 12m csi-snapshotter-58bf69fbd5-ps7nj 1/1 Running 0 12m engine-image-ei-acb7590c-6jq2h 1/1 Running 0 12m engine-image-ei-acb7590c-bhjs5 1/1 Running 0 12m instance-manager-45ef10875bdd08b79ebfe014faeb1722 1/1 Running 0 12m instance-manager-55c27a0ae5e3ee437940cd838ea3ccef 1/1 Running 0 12m longhorn-csi-plugin-f8zqf 3/3 Running 0 12m longhorn-csi-plugin-g22hx 3/3 Running 0 12m longhorn-driver-deployer-576d574c8-j2lzq 1/1 Running 0 12m longhorn-manager-2jf5d 1/1 Running 1 (12m ago) 12m longhorn-manager-tfh54 1/1 Running 0 12m longhorn-ui-556f7bb76c-fwf5d 1/1 Running 0 12m longhorn-ui-556f7bb76c-ljflg 1/1 Running 0 12m ``` ### 測試 mysql database 飄移在不同節點上不會遺失資料 使用以下 yaml 測試 ```yaml! $ cat <<EOF > test-mysql.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 2Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: mysql labels: app: mysql spec: selector: matchLabels: app: mysql # has to match .spec.template.metadata.labels strategy: type: Recreate template: metadata: labels: app: mysql spec: restartPolicy: Always containers: - image: mysql:5.6 name: mysql livenessProbe: exec: command: - ls - /var/lib/mysql/lost+found initialDelaySeconds: 5 periodSeconds: 5 env: - name: MYSQL_ROOT_PASSWORD value: changeme ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-volume mountPath: /var/lib/mysql volumes: - name: mysql-volume persistentVolumeClaim: claimName: mysql-pvc EOF ``` ### 建立 mysql ``` $ kubectl apply -f test-mysql.yaml ``` ### 檢查部屬狀態 ```! $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-7b79bcfb7c-2zrxh 1/1 Running 0 33s 10.244.2.18 k1w2 <none> <none> ``` ### 在 mysql 產生資料 ```! $ mysql -u root -p -h 10.244.2.18 Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.6.51 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> create database haha; Query OK, 1 row affected (0.001 sec) MySQL [(none)]> use haha; Database changed MySQL [haha]> CREATE TABLE customer ( -> ID int NOT NULL, -> NAME varchar(100) NOT NULL, -> AGE int, -> ADDRESS varchar(255), -> SALARY DECIMAL(10,2), -> PRIMARY KEY (ID) -> ); Query OK, 0 rows affected (0.028 sec) MySQL [haha]> INSERT INTO customer -> VALUES (1,'Ramesh',32,'Ahmedabad','2000.00'), -> (2,'Khilan',25,'Delhi','1500.00'), -> (3,'kaushik',23,'kota','2000.00'), -> (4,'Chaitali',25,'Mumbai','6500.00'), -> (5,'Hardik',27,'Bhopal','8500.00'), -> (6,'Komal',22,'MP','4500.00'), -> (7,'Muffy',24,'Indore','10000.00'); Query OK, 7 rows affected (0.005 sec) Records: 7 Duplicates: 0 Warnings: 0 MySQL [haha]> select * from customer; +----+----------+------+-----------+----------+ | ID | NAME | AGE | ADDRESS | SALARY | +----+----------+------+-----------+----------+ | 1 | Ramesh | 32 | Ahmedabad | 2000.00 | | 2 | Khilan | 25 | Delhi | 1500.00 | | 3 | kaushik | 23 | kota | 2000.00 | | 4 | Chaitali | 25 | Mumbai | 6500.00 | | 5 | Hardik | 27 | Bhopal | 8500.00 | | 6 | Komal | 22 | MP | 4500.00 | | 7 | Muffy | 24 | Indore | 10000.00 | +----+----------+------+-----------+----------+ 7 rows in set (0.001 sec) ``` ### 將 Pod 轉移到其他節點上,並觀察資料是否還在 ``` $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-7b79bcfb7c-2zrxh 1/1 Running 0 5m 10.244.2.18 k1w2 <none> <none> bigred@kube-kadm:~$ kubectl cordon k1w2 node/k1w2 cordoned bigred@kube-kadm:~$ kubectl scale deploy mysql --replicas=0 deployment.apps/mysql scaled bigred@kube-kadm:~$ kubectl cordon k1w2 node/k1w2 already cordoned bigred@kube-kadm:~$ kubectl get pods -o wide No resources found in default namespace. bigred@kube-kadm:~$ kubectl scale deploy mysql --replicas=1 deployment.apps/mysql scaled $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-7b79bcfb7c-fz7pg 1/1 Running 0 30s 10.244.1.24 k1w1 <none> <none> bigred@kube-kadm:~$ mysql -u root -p -h 10.244.1.24 Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.6.51 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> select * from haha.customer; +----+----------+------+-----------+----------+ | ID | NAME | AGE | ADDRESS | SALARY | +----+----------+------+-----------+----------+ | 1 | Ramesh | 32 | Ahmedabad | 2000.00 | | 2 | Khilan | 25 | Delhi | 1500.00 | | 3 | kaushik | 23 | kota | 2000.00 | | 4 | Chaitali | 25 | Mumbai | 6500.00 | | 5 | Hardik | 27 | Bhopal | 8500.00 | | 6 | Komal | 22 | MP | 4500.00 | | 7 | Muffy | 24 | Indore | 10000.00 | +----+----------+------+-----------+----------+ 7 rows in set (0.005 sec) ``` --- ### Block Volume 測試 * 建立 pvc ``` $ echo 'apiVersion: v1 kind: PersistentVolumeClaim metadata: name: longhorn-block-vol spec: accessModes: - ReadWriteOnce volumeMode: Block storageClassName: longhorn resources: requests: storage: 3Gi' | kubectl apply -f - ``` * 建立 pod 並且掛載 pvc ``` $ echo 'apiVersion: apps/v1 kind: Deployment metadata: name: pod-with-raw-block-volume labels: os: alpine spec: replicas: 1 selector: matchLabels: os: alpine template: metadata: labels: os: alpine spec: containers: - name: alpine image: taiwanese/alpine:stable imagePullPolicy: IfNotPresent command: ["/bin/sleep", "infinity"] volumeDevices: - name: data devicePath: /dev/sdd securityContext: privileged: true lifecycle: postStart: exec: command: - /bin/sh - -c - | set -e mkdir /longhorn checkformat=$(blkid | grep -w /dev/sdd | cut -d ':' -f1) [[ "$checkformat" != /dev/sdd ]] && (mkfs.ext4 /dev/sdd && mount /dev/sdd /longhorn) || mount /dev/sdd /longhorn preStop: exec: command: - /bin/sh - -c - | umount /dev/sdd volumes: - name: data persistentVolumeClaim: claimName: longhorn-block-vol' | kubectl apply -f - ``` ``` $ kubectl get po NAME READY STATUS RESTARTS AGE pod-with-raw-block-volume-57b7bf4d5d-nmrzn 1/1 Running 0 20s ``` ``` $ kubectl exec -it pod-with-raw-block-volume-57b7bf4d5d-5wl9l -- bash bash-5.1# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS loop0 7:0 0 4K 1 loop loop1 7:1 0 28K 1 loop loop2 7:2 0 2.3M 1 loop loop3 7:3 0 53.8M 1 loop loop4 7:4 0 16M 0 loop loop5 7:5 0 3G 0 loop sda 8:0 0 800G 0 disk ├─sda1 8:1 0 100M 0 part ├─sda2 8:2 0 1M 0 part ├─sda3 8:3 0 1000M 0 part ├─sda4 8:4 0 1M 0 part ├─sda5 8:5 0 100M 0 part └─sda6 8:6 0 798.8G 0 part /etc/resolv.conf /etc/hostname /dev/termination-log /etc/hosts sdb 8:16 0 120G 0 disk sdc 8:32 0 120G 0 disk sdd 8:48 0 3G 0 disk /longhorn sr0 11:0 1 81.4M 0 rom bash-5.1# echo 123 > /longhorn/test bash-5.1# cat /longhorn/test 123 ``` * 測試 pod 移轉 ``` $ kubectl get po -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-with-raw-block-volume-57b7bf4d5d-5wl9l 1/1 Running 0 59s 10.244.2.24 k1w1 <none> <none> $ kubectl scale deploy pod-with-raw-block-volume --replicas=0 $ kubectl cordon k1w1 $ kubectl scale deploy pod-with-raw-block-volume --replicas=1 $ kubectl get no NAME STATUS ROLES AGE VERSION k1m1 Ready control-plane 47m v1.29.2 k1w1 Ready,SchedulingDisabled worker 42m v1.29.2 k1w2 Ready worker 42m v1.29.2 $ kubectl uncordon k1w1 $ kubectl get po -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-with-raw-block-volume-6cfcd9889-t6pzt 1/1 Running 0 25s 10.244.1.21 k1w2 <none> <none> $ kubectl exec -it pod-with-raw-block-volume-6cfcd9889-t6pzt -- cat /longhorn/test 123 ``` ## 參考資料 - [System Extensions - Talos Docs](https://www.talos.dev/v1.6/talos-guides/configuration/system-extensions/) - [Boot Assets - Talos Docs](https://www.talos.dev/v1.6/talos-guides/install/boot-assets/#image-factory) - [Talos Linux Image Factory](https://factory.talos.dev/)