👻

LonghornでおうちラボのKubernetesクラスタにストレージを用意する

に公開
1

この記事では、Kubernetes環境で利用できるストレージソリューションLonghornのセットアップ方法を解説します。

Longhornは、バックアップやスナップショット、レプリケーションといった機能を持つ多機能なストレージシステムですが、最も重要な役割はPersistent Volumeの管理です。KubernetesでPodがデータを永続化するために必要なPersistent Volume Claim(PVC)を作成し、「2GBのストレージが欲しい」「10GBのストレージが欲しい」とリクエストするだけで、Longhornが自動的にディスク容量を割り当ててくれます。このシンプルな機能によって、Kubernetesのストレージ管理が格段に楽になります。

Longhorn

Cloud native distributed block storage for Kubernetes

なおウェブサイトのトップには分散ブロックストレージと記載がありますが、ブロックストレージとして構築しようとするとKernelのバージョンやマシンリソースの要件が上がり、私のおうちラボでは厳しいです。今回はファイルシステム型のストレージとしてセットアップしていきます。

Kubernetesクラスタはcontrol plane 3台、worker 3台で組んであります。今年の別のポストではetcdクラスタを別の3台で用意し、そこにcontrol plane 3台とworker 1台でやっていましたが、今年にこれ以降Kubernetes関連でポストする際はx3 cp, x3 workerでやっていくと思います。

environment check script

Longhornを載せるのにKubernetesクラスタの各ノードは要件を満たしているのか、これをチェックするスクリプトが用意されています。

実施ログは以下の通りで、これら3点のエラー、警告がありました。

  • kernel module iscsi_tcp missing
  • cryptsetup package missing
  • warning in regard with multipathd on Ubuntu hosts
$ curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.8.1/scripts/environment_check.sh | bash
[INFO]  Required dependencies 'kubectl jq mktemp sort printf' are installed.
[INFO]  All nodes have unique hostnames.
[INFO]  Waiting for longhorn-environment-check pods to become ready (0/0)...
[INFO]  Waiting for longhorn-environment-check pods to become ready (0/6)...
[INFO]  All longhorn-environment-check pods are ready (6/6).
[INFO]  MountPropagation is enabled
[INFO]  Checking kernel release...
[INFO]  Checking iscsid...
[ERROR] kernel module iscsi_tcp is not enabled on lab-cp3
[ERROR] kernel module iscsi_tcp is not enabled on lab-cp1
[ERROR] kernel module iscsi_tcp is not enabled on lab-worker2
[ERROR] kernel module iscsi_tcp is not enabled on lab-worker1
[ERROR] kernel module iscsi_tcp is not enabled on lab-cp2
[ERROR] kernel module iscsi_tcp is not enabled on lab-worker3
[INFO]  Checking multipathd...
[WARN]  multipathd is running on lab-cp3 known to have a breakage that affects Longhorn.  See description and solution at https://longhorn.io/kb/troubleshooting-volume-with-multipath
[INFO]  Checking packages...
[ERROR] cryptsetup is not found in lab-cp1.
[ERROR] cryptsetup is not found in lab-worker2.
[ERROR] cryptsetup is not found in lab-worker1.
[ERROR] cryptsetup is not found in lab-cp2.
[ERROR] cryptsetup is not found in lab-worker3.
[INFO]  Checking nfs client...
[INFO]  Cleaning up longhorn-environment-check pods...
[INFO]  Cleanup completed.

multipathd remed on lab-cp3

WARNメッセージ内に参照すべきURLも記載されていますが、/etc/multipath.confファイルに以下を記載し、サービスリスタートしておきました。

blacklist {
    devnode "^sd[a-z0-9]+"
}

cryptsetup package

cryptsetupが見つからないという問題に関してはaptなりdnfなり、パッケージマネージャでインストールしました。

kernel modules

Kernelモジュールに監視て、/etc/modules-load.d/longhorn-requirements.confというファイルを用意し、以下の2行を記載してシステムを再起動してあります。

事前チェックスクリプトではiscsi_tcpしか挙げられていませんが、実際にセットアップした後、Longhornのダッシュボードで各ノードを確認するとdm_cryptに関しても見つからないというメッセージが出ていたので加えています。

iscsi_tcp
dm_crypt

その他の今回除外した要件について

Storage Performance Developer Kit, SPDKを利用したLonghorn V2 Data Engineではブロックストレージが使えるようですが、先にも触れたとおり要件が重いので今回は見送りました。

  • SSE4.2 instruction set support
    • grep sse4_2 /proc/cpuinfo
  • 5.19 or later is required for NVMe over TCP support
    • v6.7 or later is recommended for improved system stability
  • linux kernel modules
    • vfio_pci
    • uio_pci_generic
    • nvme-tcp
  • Huge page support, 2 GiB of 2 MiB-sized pages

Longhornのインストール

ステップとしては次の通りです。

  • longhorn-system namespace作成
  • longhornのhelm chartのバージョン確認
  • valuesファイルに必要な変更を書いてGitOpsレポジトリに載せ
  • fluxのHelmRepositoryおよびHelmReleaseマニフェストを用意し、infra-controllers kustomizationに追加

Longhornのhelm chartをfluxcdで導入する手順となっています。

longhorn-system namespace作成

HelmReleaseにnamespaceを作ってくれるオプションがありますが、namespaceは独立して管理したいのでflux-system kustomization下のディレクトリにマニフェストを用意します。

Cilium gatewayを利用してlonghornのダッシュボードへのアクセスを用意する予定ですので、必要なラベルを書き加えておきます。

# ./clusters/lab-hlv3/namespaces/longhorn-system.yaml
---
kind: Namespace
apiVersion: v1
metadata:
  name: longhorn-system
  labels:
    service: longhorn
    type: infrastructure
    gateway: cilium

Longhornのhelmチャートとvalues

Helmレポジトリを追加し、バージョンを確認します。ついでにvaluesファイルもダウンロードします。

helm repo add longhorn https://charts.longhorn.io
helm search repo longhorn
# helm repo update

# store values file locally
helm show values --version 1.8.1 longhorn/longhorn > longhorn-1.8.1-values.yaml

valuesファイルへの変更点

私の場合、次の内容を変更しました。

  • 各イメージをローカルのHarborより取得するよう更新
  • longhorn ui replicaを2から1へ
  • persistence settings
    • レプリカカウントを3から2へ
    • data localityをbest-effortへ

Flux helmrepo & helmrelease

詳細はスキップします。fluxコマンドでfluxのhelmrepoおよびhelmreleaseマニフェストが作成できるのですが、シェルスクリプトとしてコマンドを用意しています。このスクリプトで生成されたマニフェストをinfra-controllers kustomizationに追加するとインストールが進みます。複数のマイクロサービスが実行され、VMオンリーのラボ環境ではインストール開始から20分くらいで落ち着きました。

cert-manager導入時の具体例がこちらにあります.

以下はインストール後のflux tree出力の一部です。

$ flux tree ks infra-controllers
Kustomization/flux-system/infra-controllers
├── HelmRelease/flux-system/longhorn
│   ├── PriorityClass/longhorn-critical
│   ├── ServiceAccount/longhorn-system/longhorn-service-account
│   ├── ServiceAccount/longhorn-system/longhorn-ui-service-account
│   ├── ServiceAccount/longhorn-system/longhorn-support-bundle
│   ├── ConfigMap/longhorn-system/longhorn-default-resource
│   ├── ConfigMap/longhorn-system/longhorn-default-setting
│   ├── ConfigMap/longhorn-system/longhorn-storageclass
│   ├── CustomResourceDefinition/backingimagedatasources.longhorn.io
│   ├── CustomResourceDefinition/backingimagemanagers.longhorn.io
│   ├── CustomResourceDefinition/backingimages.longhorn.io
│   ├── CustomResourceDefinition/backupbackingimages.longhorn.io
│   ├── CustomResourceDefinition/backups.longhorn.io
│   ├── CustomResourceDefinition/backuptargets.longhorn.io
│   ├── CustomResourceDefinition/backupvolumes.longhorn.io
│   ├── CustomResourceDefinition/engineimages.longhorn.io
│   ├── CustomResourceDefinition/engines.longhorn.io
│   ├── CustomResourceDefinition/instancemanagers.longhorn.io
│   ├── CustomResourceDefinition/nodes.longhorn.io
│   ├── CustomResourceDefinition/orphans.longhorn.io
│   ├── CustomResourceDefinition/recurringjobs.longhorn.io
│   ├── CustomResourceDefinition/replicas.longhorn.io
│   ├── CustomResourceDefinition/settings.longhorn.io
│   ├── CustomResourceDefinition/sharemanagers.longhorn.io
│   ├── CustomResourceDefinition/snapshots.longhorn.io
│   ├── CustomResourceDefinition/supportbundles.longhorn.io
│   ├── CustomResourceDefinition/systembackups.longhorn.io
│   ├── CustomResourceDefinition/systemrestores.longhorn.io
│   ├── CustomResourceDefinition/volumeattachments.longhorn.io
│   ├── CustomResourceDefinition/volumes.longhorn.io
│   ├── ClusterRole/longhorn-role
│   ├── ClusterRoleBinding/longhorn-bind
│   ├── ClusterRoleBinding/longhorn-support-bundle
│   ├── Service/longhorn-system/longhorn-backend
│   ├── Service/longhorn-system/longhorn-frontend
│   ├── Service/longhorn-system/longhorn-conversion-webhook
│   ├── Service/longhorn-system/longhorn-admission-webhook
│   ├── Service/longhorn-system/longhorn-recovery-backend
│   ├── DaemonSet/longhorn-system/longhorn-manager
│   ├── Deployment/longhorn-system/longhorn-driver-deployer
│   └── Deployment/longhorn-system/longhorn-ui
└── HelmRepository/flux-system/longhorn

Longhorn UIへのアクセス

Longhornをhelmチャートでインストールするとlonghorn-frontendというserviceと、その接続先であるlonghorn-uiのpodが作られます。

Cilium gatewayの設定変更、HTTPRouteの追加をしてダッシュボードへのアクセスを用意します。

$ kubectl get svc longhorn-frontend -n longhorn-system
NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
longhorn-frontend   ClusterIP   10.96.107.178   <none>        80/TCP    23h

Cilium gateway

こちらが更新後のgatewayのマニフェストです。変更がfluxに渡されると対応するTLS証明書がcert-managerによって取得され、gatewayには"longhorn.lab.blink-1x52.net"でアクセスできるlistenerが追加されます。

# ./infrastructure/lab-hlv3/configs/cilium/gateway.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cilium-gateway
  namespace: gateway
  annotations:
    cert-manager.io/issuer: issuer
spec:
  gatewayClassName: cilium
  addresses:
    - type: IPAddress
      value: 192.0.2.79
  listeners:
    - name: whoami-kube-http
      hostname: whoami-kube.lab.blink-1x52.net
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway: cilium

    - name: whoami-kube-https
      hostname: whoami-kube.lab.blink-1x52.net
      port: 443
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway: cilium
      tls:
        mode: Terminate
        certificateRefs:
          - name: tls-whoami-kube
            kind: Secret
            namespace: gateway

    - name: longhorn-https
      hostname: longhorn.lab.blink-1x52.net
      port: 443
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway: cilium
      tls:
        mode: Terminate
        certificateRefs:
          - name: tls-longhorn
            kind: Secret
            namespace: gateway

HTTPRoute

こちらがgatewayとlonghorn-frontendサービスを繋ぐHTTPRouteです。

# ./infrastructure/lab-hlv3/configs/longhorn/httproutes.yaml
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: longhorn-https
  namespace: longhorn-system
spec:
  parentRefs:
    - name: cilium-gateway
      sectionName: longhorn-https
      namespace: gateway
  hostnames:
    - "longhorn.lab.blink-1x52.net"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: longhorn-frontend
          port: 80

Longhorn UIとディスク

ダッシュボードより各ノードとディスクが確認できます。最初からある程度のディスクスペースは予約済みとして、残りは利用可能な状態となっています。

Longhorn Dashboard

ルートファイルシステムとは別に追加ディスクを用意し、そちらを利用してデモへと進みます。

lab-worker2とlab-worker3それぞれに80GBディスクを追加します。

追記:追加ディスクはxfsファイルシステムとしてフォーマットします。のちにMinio S3で利用する予定だからです。

lab-worker2へのディスク追加

手順は次の通りです。

  • 新しいディスクの作成
  • 新しいiscsiコントローラの作成
  • ディスクを接続

lab-worker2はHyper-V上で動かしているVMです。Powershellでの作業は以下の通りです。

# create a new vhdx
New-VHD -Path "F:\hyperv\vhd\lab-worker2-disk2.vhdx" -SizeBytes 80GB -Dynamic

# confirm the existing disks connected
Get-VMHardDiskDrive -VMName lab-worker2 -ControllerType SCSI

# confirm the controllers
# there should be a couple for disks and DVD drive
Get-VMScsiController -VMName lab-worker2

# add one controller
# and confirm the controller ID newly created and open for the new disk connection
Add-VMScsiController -VMName lab-worker2
Get-VMScsiController -VMName lab-worker2

# add the newly created vhdx to the VM using the available controller
Add-VMHardDiskDrive -VMName lab-worker2 -ControllerType SCSI -ControllerNumber 2 -Path "F:\hyperv\vhd\lab-worker2-disk2.vhdx"

lab-worker2上での作業は次の通りです。

  • パーティション作成
  • ext4でフォーマット
  • マウント
# identify the device
sudo fdisk -l

# assuming the newly attached disk is on /dev/sda
sudo fdisk /dev/sda

# on fdisk menu,
# "p" to print existing partitions
# "d" to delete if there is any existing one
# "n" to create new partition, create "p" primary, one big partition
# and then "w" to write the change and exit fdisk

# confirm the result and should see /dev/sda1
sudo fdisk -l

# format /dev/sda1
# sudo apt install xfsprogs  # if mkfs.xfs command is missing
sudo mkfs.xfs /dev/sda1

# confirm UUID of the formatted disk space
sudo blkid

# prepare directory to mount this new disk
sudo mkdir -p /mnt/disk2

システム起動時に自動的にマウントされるよう、/etc/fstabファイルに一行追記します。

UUID=UUID_FOR_DEV_SDA1_CONFIRMED_ABOVE /mnt/disk2 xfs defaults,noatime 0 2

問題がないか確認し、再起動します。

sudo findmnt --verify --verbose

sudo systemctl reboot

lab-worker3へのディスク追加

こちらのVMはProxmox上で稼働しています。Proxmox上での操作は以下の通りです。

# shutdown the target VM
# on this VM) sudo systemctl poweroff, or sudo shutdown -h now
# or on proxmox) qm stop VMID

export VMID=1234 # VM ID

# see the status of each available storage on proxmox ve
pvesm status

# list the disks on specific storage
pvesm list local-lvm

# create and allocate 80GB disk
pvesm alloc local-lvm $VMID vm-$VMID-disk2 80G

# confirm vm config
qm config $VMID

# attach the created disk at scsi1
qm set $VMID --scsi1 local-lvm:vm-$VMID-disk2

# verify and start the VM
pvesm list local-lvm
qm config $VMID
qm start $VMID

ディスクをVMに接続した後は、lab-worker2で実施した同じ作業をこちらのVMでも実施します。

Longhornへのディスク追加

Longhorn node edit to add disk2

/mnt/disk2をそれぞれのノードで追加します。

ついでに、デモへ進める前にその他のディスクを全て無効にします。

Longhorn disk2 only

PVCのテスト

Longhorn dashboard before volume creation

追加ディスク以外すべて無効にした状態です。

  • 156 Gi のスケジュール可能なストレージ
  • スケジュール可能なストレージを持つノードは2ノード

テストに用いたマニフェストの概要は次の通りです。

  • nginxのdeployment
  • nginxへアクセスするためのservice
  • nginxで使う1GiのPVC

Longhorn dashboard after test volume creation

マニフェストをGitOps用レポジトリに上げるとfluxcdへと渡り、PVCに対応したPVが作成されました。Podもボリュームもlab-worker2上に作成されました。

$ kubectl -n longhorn-system get pods -o wide | grep test
my-longhorn-test-676d6b57dc-gzqtc                   1/1     Running   0                3m40s   10.0.3.202   lab-worker2   <none>           <none>

NginxのdeploymentのnodeSelectorをlab-worker1とすると(そしてdeploymentをリスタートすると)、lab-worker2上のpodは落とされましたがPVCはそのままで、lab-worker1上に新たなpodが作成され、元々のPVCがそのままpodに接続されました。

podがlab-worker2上にある時とlab-worker1上にある時それぞれで、マウントしたボリューム上でファイルを更新しました。以下がcurlで確認したときの出力です。

# edit /usr/share/nginx/html/index.html on the pod running on lab-worker2
$ kubectl exec deploy/tools -n testbed -- curl http://longhorn-test.longhorn-system.svc.lab.blink-1x52.net
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    50  100    50    0     0  21758      0 --:--:-- --:--:-- --:--:-- 50000
<html>
<body>
<h1>hi, world.</h1>
</body>
</html>

# nodeSelector lab-worker1 added to the deployment, pod gets re-created

# edit /usr/share/nginx/html/index.html on the pod running on lab-worker1
$ kubectl exec deploy/tools -n testbed -- curl http://longhorn-test.longhorn-system.svc.lab.blink-1x52.net
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    76  100    76    0     0  30170      0 --:--:-- --:--:-- --:--:-- 38000
<html>
<body>
<h1>hi, world.</h1>
<h2>hi, world at h2.</h2>
</body>
</html>

ダッシュボード上では以下のように見えていました。data localityに関して警告が出ていますが機能していました。

Longhorn volume for pod on lab-worker2

Longhorn volume for pod on lab-worker1

テスト用マニフェスト

テストに用いたマニフェストはこちらです。"nodeSelector"はあれこれ変更し、deploymentをリスタートすると指定のノードにpodを移すといった細かい操作ができます。

---
apiVersion: v1
kind: Service
metadata:
  name: longhorn-test
  namespace: longhorn-system
spec:
  ports:
    - name: http
      targetPort: 80
      port: 80
  selector:
    app: longhorn-test
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-longhorn-test
  namespace: longhorn-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: longhorn-test
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: longhorn-test
    spec:
      containers:
        - name: nginx
          image: nginx:1.27.3-alpine
          ports:
            - containerPort: 80
          volumeMounts:
            - name: test-longhorn-volume
              mountPath: /usr/share/nginx/html
      volumes:
        - name: test-longhorn-volume
          persistentVolumeClaim:
            claimName: test-longhorn-pvc
      nodeSelector:
        kubernetes.io/hostname: lab-worker1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-longhorn-pvc
  namespace: longhorn-system
spec:
  storageClassName: longhorn
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

終わりに

以上です!

次はモニタリングかS3サービスいずれかをセットアップします。

Discussion

おしょうさんおしょうさん

right... minio recommends xfs and the directpv I previously used by default sets up xfs drives. The minio s3 did work alright with longhorn provisioning ext4 PVs, but I guess I'll do some re-work on this