使用 Longhorn 作为 K8s 后端储存

简单介绍

Longhorn 是一个分布式存储系统,具有以下特点:

  • 使用 Longhorn 卷作为 K8s 集群的持久存储卷。
  • 使用块存储设备作为基础卷,无需依赖云服务提供商。
  • 通过在多个节点上存储副本来提供高可用性。
  • 支持将 NFS 用作存储系统的备份源,并且可以设置多个备份源。
  • 支持跨集群灾备,方便在 K8s 集群中迁移数据。
  • 可以在不中断持久卷的情况下升级 Longhorn 系统。

官方参考文档:

https://longhorn.io/docs/1.2.6

中文参考文档:

https://blog.51cto.com/u_15168528/3605855

结构原理:

https://jishuin.proginn.com/p/763bfbd73e98

准备工作

在集群中每个节点安装并启动 open-iscsi :

[root@k8s-101 ~]# yum install -y nfs-utils
[root@k8s-101 ~]# yum --setopt=tsflags=noscripts install -y iscsi-initiator-utils
[root@k8s-101 ~]# /sbin/iscsi-iname -p "InitiatorName=iqn.2022-05.org.open-iscsi" > /etc/iscsi/initiatorname.iscsi
[root@k8s-101 ~]# systemctl enable --now iscsid
[root@k8s-101 ~]# lsmod | grep iscsi
scsi_transport_iscsi   110592  1 

或使用官方脚本来安装:

[root@k8s-101 ~]# kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.2.6/deploy/prerequisite/longhorn-iscsi-installation.yaml

部署流程

在主节点上克隆仓库,并检出到 v1.2.6 分支:

[root@k8s-101 ~]# git clone https://github.com/longhorn/longhorn.git
[root@k8s-101 ~]# cd longhorn && git checkout v1.2.6

如果遇到错误:

fatal: unable to access ‘https://github.com/longhorn/longhorn.git/’: TCP connection reset by peer

多试几次,直到匹配上能连通的 Github 服务器 IP 地址。

运行官方环境检测脚本,一般没有报错:

[root@k8s-101 ~]# bash scripts/environment_check.sh
[INFO]  All longhorn-environment-check pods are ready (6/6).
[INFO]  Required packages are installed.
[INFO]  MountPropagation is enabled.
[INFO]  Cleaning up longhorn-environment-check pods...
[INFO]  Cleanup completed.

deploy 目录下的 longhorn.yaml 文件拷贝出来,直接部署:

[root@k8s-101 longhorn]# mkdir -p /hxz393/local/longhorn/apply && cp deploy/longhorn.yaml /hxz393/local/longhorn/apply/
[root@k8s-101 longhorn]# kubectl apply -f /hxz393/local/longhorn/apply/
[root@k8s-101 longhorn]# kubectl get po -n longhorn-system -owide --watch

等待所有组件容器变为 Running 状态。

集群配置

配置应用使用的自定义 SC 和前端访问网址。

建立 SC

自定义 SC 配置好默认自动备份策略,涵盖了每周一次备份、每天一次备份和每三小时一次快照:

[root@k8s-101 ~]# tee /hxz393/local/longhorn/apply/kube-sc.yaml<<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: kube-sc
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: driver.longhorn.io
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: Immediate
parameters:
  numberOfReplicas: "3"
  staleReplicaTimeout: "2880"
  fromBackup: ""
  fsType: "xfs"
  recurringJobSelector: '[
   {
     "name":"kube-snap",
     "task":"snapshot",
     "cron":"0 0/3 * * ?",
     "retain":5
   },
   {
     "name":"kube-backup-daily",
     "task":"backup",
     "cron":"0 2 * * ?",
     "retain":7
   },
   {
     "name":"kube-backup-weekly",
     "task":"backup",
     "cron":"0 3 ? * SAT",
     "retain":4
   }
  ]'
EOF
[root@k8s-101 ~]# kubectl apply -f /hxz393/local/longhorn/apply/kube-sc.yaml

开放端口

修改 longhorn-frontend 服务类型为 nodeport,暴露端口 10005 留作内网访问:

[root@k8s-101 ~]# kubectl -n longhorn-system edit svc longhorn-frontend
...
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: http
    nodePort: 10005
  selector:
    app: longhorn-ui
  sessionAffinity: None
  type: NodePort
...

通过服务器 IP 地址加 10005 端口,能直接打开 Longhorn 控制面板。

配置域名

由于 Longhorn 控制面板默认不带身份验证,如果想通过域名访问,需要自行生成用户密码加密文件:

[root@k8s-101 ~]# htpasswd -m -c /hxz393/local/nginx/cert/longhorn_passwd assassing

命令 htpasswdApache httpd 服务所带套件,需要自行安装。

建立 nginx 配置文件:

[root@k8s-101 ~]# vi /hxz393/local/nginx/config/conf.d/longhorn.x2b.net.conf
server {
    listen 80;
    listen 443 ssl;
    # 域名
    server_name longhorn.x2b.net;

    ssl_certificate   /cert/x2b.net.pem;
    ssl_certificate_key  /cert/x2b.net.key;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1.2 TLSv1.3 SSLv3;
    ssl_prefer_server_ciphers on;
    
    # 基本认证
    auth_basic "nginx basic auth for longhorn";
    auth_basic_user_file /cert/longhorn_passwd; 
    location / {
        # k8s服务名
        proxy_pass http://longhorn-frontend.longhorn-system/;
        proxy_set_header Host   $host;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        if ($request_uri ~* "/(.*)"){
            proxy_pass http://longhorn-frontend.longhorn-system/$1;
            break;
        }
    }
}
[root@k8s-101 ~]# for i in $(kubectl get pod -n local|grep nginx|awk '{print $1}');do kubectl exec -it -n local $i -- nginx -s reload; done

通过域名 longhorn.x2b.net 访问,需要输入用户名 assassing 的密码来访问。

节点配置(Node)

根据需要修改默认节点 Node 配置:

  • 删除所有节点默认挂载点。
  • 添加主节点 /ssd 目录作为储存目录 ,开启调度(Scheduling),设置保留磁盘(Storage Reserved)空间为 1 GB。

备份任务(Recurring Job)

创建快照策略 kube-snap,用来自动创建快照:

  • Name:输入名字 kube-snap。
  • Task:任务类型选择 Snapshot。
  • Retain:设置保留副本数量为 5。
  • Concurrency:并发活动数为 3。
  • Cron:设置任务调度表为 0 0/3 * * ?,代表每 3 小时执行一次。

创建备份策略 kube-backup-daily,用来每天自动创建备份。需要先设置好备份 NFS 服务器地址:

  • Name:输入名字 kube-backup-daily。
  • Task:任务类型选择 Backup。
  • Retain:设置保留副本数量为 7。
  • Concurrency:并发活动数为 1。
  • Cron:设置任务调度表为 0 2 * * ?,代表每天的凌晨 2 点执行。

创建备份策略 kube-backup-weekly,用来每周自动创建备份:

  • Name:输入名字 kube-backup-weekly。
  • Task:任务类型选择 Backup。
  • Retain:设置保留副本数量为 4。
  • Concurrency:并发活动数为 1。
  • Cron:设置任务调度表为 0 6 ? * SAT,代表每周六早上 6 点执行。

功能设置(Setting)

遵循最小化配置原则,一般不建议修改默认配置。修改前请一定先查询官方文档。

Orphan

  • Orphan Auto-Deletion

    自动删除孤儿容器。

Backup

  • Backup Target

    填写备份 NFS 服务器地址。例如:nfs://192.168.1.106:/backup

Scheduling

  • Replica Node Level Soft Anti-Affinity

    开启节点级别反亲和策略。同节点部署多个相同的副本没有意义。

  • Storage Over Provisioning Percentage

    设置超量调度百分比为 100%。

本地挂载

可以将活动中的储存卷挂载到本地,来管理卷内文件:

  • 在卷的详细信息(Volume Details)中找到 Attached Node & Endpoint,这是 PVC 挂载到的目标服务器和设备名。
  • 登录目标服务器,使用 mount 命令来挂载 RWX 卷。例如:mount /dev/longhorn/pvc-9a38209a-ca92-44b3-adba-e1c629437409 /root/temp_pvc
  • 维护完毕用 umount 卸载卷:umount /root/temp_pvc

磁盘瘦身

针对 RWO 格式的卷,1.40 以上版本提供清理磁盘空间功能,在卷操作中选择 Trim Filesystem 即可。

对于 RWX 卷需要手动清理。例如:

[root@k8s-101 ~]# mkdir 111 && mount /dev/longhorn/pvc-41795a8f-d3bf-406b-acfd-fa7d88e44e73 111
[root@k8s-101 ~]# fstrim 111
[root@k8s-101 ~]# umount 111 && rmdir 111

版本升级

目前最新版为 1.4.2,根据官方文档说明,从 1.2.X 升级需要先升级到 1.3.3 版本,再升级到 1.4.2。升级时访问控制台会中断一会,但不影响储存卷工作。

依然可以用安装时克隆的仓库来操作。或则删除后重新克隆仓库:

[root@k8s-101 ~]# cd longhorn && git checkout v1.3.3
Previous HEAD position was f936f6e... Release 1.2.6
HEAD is now at f57838c... release: 1.3.3

后续步骤和安装一样:

[root@k8s-101 longhorn]# cp deploy/longhorn.yaml /hxz393/local/longhorn/apply/
[root@k8s-101 longhorn]# kubectl apply -f /hxz393/local/longhorn/apply/
[root@k8s-101 longhorn]# kubectl get po -n longhorn-system -owide --watch

如果网络不错,可以直接指定 Github 上的发布文件来升级:

[root@k8s-101 longhorn]# kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.4.2/deploy/longhorn.yaml

升级后需要重新修改前端服务类型:

[root@k8s-101 ~]# kubectl -n longhorn-system edit svc longhorn-frontend

数据恢复

通过控制面板正常恢复卷备份操作不在本段讨论之列。这里要假设的是一个极端的情况:整个 K8s 集群或者 Longhorn 系统失败且无法恢复,要怎么取出 Longhrn 卷的数据。

由于 Longhorn 在磁盘中,以 img 格式来封装数据:

[root@k8s-250 ~]# ll /ssd/replicas/pvc-10efcd2b-df0d-4af3-b9cc-3b61368bbdb2-8d14ac1d/
total 1312920
-rw------- 1 root root       4096 May 21 21:28 revision.counter
-rw-r--r-- 1 root root 5368709120 May 21 21:28 volume-head-007.img
-rw-r--r-- 1 root root        187 May 21 08:00 volume-head-007.img.meta
-rw-r--r-- 1 root root        203 May 21 08:00 volume.meta
-rw-r--r-- 1 root root 5368709120 May 21 08:00 volume-snap-kube-bac-3fe04efe-14ed-4dc9-a3d6-e938e20bb8e3.img
-rw-r--r-- 1 root root        193 May 21 08:00 volume-snap-kube-bac-3fe04efe-14ed-4dc9-a3d6-e938e20bb8e3.img.meta
-rw-r--r-- 1 root root 5368709120 May 21 08:00 volume-snap-kube-sna-427d25c2-4eea-4e6a-a6c0-791e931451ef.img
-rw-r--r-- 1 root root        210 May 21 08:00 volume-snap-kube-sna-427d25c2-4eea-4e6a-a6c0-791e931451ef.img.meta

所以即使找到了 PV 所在目录也无法直接操作。先查看卷目录下的 volume.meta 文件内容:

[root@k8s-250 ~]# cat /ssd/replicas/pvc-10efcd2b-df0d-4af3-b9cc-3b61368bbdb2-8d14ac1d/volume.meta
{"Size":5368709120,"Head":"volume-head-007.img","Dirty":true,"Rebuilding":false,"Error":"","Parent":"volume-snap-kube-sna-427d25c2-4eea-4e6a-a6c0-791e931451ef.img","SectorSize":512,"BackingFilePath":""}

通过文件内容,无法得知卷的用途。只知道卷的大小为 5368709120 Bytes(5 GB)。

接着使用 longhorn-engine 镜像手动创建卷容器,命令参数为:docker run -v /dev:/host/dev -v /proc:/host/proc -v 卷实际路径:/volume --privileged longhornio/longhorn-engine:v1.4.1 launch-simple-longhorn 卷名 卷大小

在上面例子中,卷路径是 /ssd/replicas/pvc-10efcd2b-df0d-4af3-b9cc-3b61368bbdb2-8d14ac1d/,卷大小为 5368709120。而卷名,也就是 PV 名字 pvc-10efcd2b-df0d-4af3-b9cc-3b61368bbdb2 在路径名中也有体现,统一去掉最后一段 8 位长度 UUID 名 8d14ac1d 即可,PV 名字长度是固定的 40 位。

所以实际要运行的命令是:

[root@k8s-250 ~]# docker run -v /dev:/host/dev -v /proc:/host/proc -v /ssd/replicas/pvc-10efcd2b-df0d-4af3-b9cc-3b61368bbdb2-8d14ac1d:/volume --privileged longhornio/longhorn-engine:v1.4.1 launch-simple-longhorn pvc-10efcd2b-df0d-4af3-b9cc-3b61368bbdb2 5368709120

如果运行顺利,可以通过 mount 命令来挂载块设备 /dev/longhorn/卷名。挂载步骤参考:本地挂载