Kubernetes 使用 JuiceFS 持久化数据

JuiceFS 非常适合用作 Kubernetes 集群的存储层,目前有两种常见的用法。

JuiceFS CSI Driver

JuiceFS CSI Driver 遵循 CSI 规范,实现了容器编排系统与 JuiceFS 文件系统之间的接口,支持动态配置 JuiceFS 卷提供给 Pod 使用。

版本要求

  • Kubernetes 1.14+

安装

JuiceFS CSI Driver 有以下两种安装的方式。

通过 Helm 安装

Helm 是 Kubernetes 的包管理器,Chart 是 Helm 管理的包。你可以把它看作是 Homebrew formula,APT dpkg,或 YUM RPM 在 Kubernetes 中的等价物。

该安装方式要求 Helm 3.1.0 及以上版本,具体安装方法请参考「Helm 安装指南」

  1. 准备一个设置存储类基本信息的配置文件,例如:values.yaml,复制并完善下列配置信息。其中,backend 部分是 JuiceFS 文件系统相关的信息,你可以参照 JuiceFS 快速上手指南了解相关内容。如果使用的是已经提前创建好的 JuiceFS 卷,则只需填写 namemetaurl 这两项即可。mountPod 部分可以对使用此驱动的 Pod 设置 CPU 和内存的资源配置。不需要的项可以删除,或者将它的值留空。
  1. storageClasses:
  2. - name: juicefs-sc
  3. enabled: true
  4. reclaimPolicy: Retain
  5. backend:
  6. name: "test"
  7. metaurl: "redis://juicefs.afyq4z.0001.use1.cache.amazonaws.com/3"
  8. storage: "s3"
  9. accessKey: ""
  10. secretKey: ""
  11. bucket: "https://juicefs-test.s3.us-east-1.amazonaws.com"
  12. mountPod:
  13. resources:
  14. limits:
  15. cpu: "<cpu-limit>"
  16. memory: "<memory-limit>"
  17. requests:
  18. cpu: "<cpu-request>"
  19. memory: "<memory-request>"

在支持「角色管理」的云平台,可以通过为 Kubernetes 节点分配「服务角色」,实现对象存储 API 免密钥访问。这种情况下无需设置配置文件中的 accessKeysecretKey

  1. 依次执行以下三条命令,通过 Helm 部署 JuiceFS CSI Driver。
  1. $ helm repo add juicefs-csi-driver https://juicedata.github.io/juicefs-csi-driver/
  2. $ helm repo update
  3. $ helm install juicefs-csi-driver juicefs-csi-driver/juicefs-csi-driver -n kube-system -f ./values.yaml
  1. 检查部署状态
  • 检查 Pods:部署过程会启动一个名为 juicefs-csi-controllerStatefulSet 及一个 replica,以及一个名为 juicefs-csi-nodeDaemonSet。执行命令 kubectl -n kube-system get pods -l app.kubernetes.io/name=juicefs-csi-driver 会看到有 n+1 个 pod 在运行,例如:
  1. $ kubectl -n kube-system get pods -l app.kubernetes.io/name=juicefs-csi-driver
  2. NAME READY STATUS RESTARTS AGE
  3. juicefs-csi-controller-0 3/3 Running 0 22m
  4. juicefs-csi-node-v9tzb 3/3 Running 0 14m
  • 检查 secret:通过命令 kubectl -n kube-system describe secret juicefs-sc-secret 可以看到前面 values.yaml 配置文件中 backend 部分的 secret 信息。
  1. $ kubectl -n kube-system describe secret juicefs-sc-secret
  2. Name: juicefs-sc-secret
  3. Namespace: kube-system
  4. Labels: app.kubernetes.io/instance=juicefs-csi-driver
  5. app.kubernetes.io/managed-by=Helm
  6. app.kubernetes.io/name=juicefs-csi-driver
  7. app.kubernetes.io/version=0.7.0
  8. helm.sh/chart=juicefs-csi-driver-0.1.0
  9. Annotations: meta.helm.sh/release-name: juicefs-csi-driver
  10. meta.helm.sh/release-namespace: default
  11. Type: Opaque
  12. Data
  13. ====
  14. access-key: 0 bytes
  15. bucket: 47 bytes
  16. metaurl: 54 bytes
  17. name: 4 bytes
  18. secret-key: 0 bytes
  19. storage: 2 bytes
  • 检查存储类(storage class)kubectl get sc juicefs-sc 命令将会显示类如下面的存储类:
  1. $ kubectl get sc juicefs-sc
  2. NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
  3. juicefs-sc csi.juicefs.com Retain Immediate false 69m

通过 kubectl 安装

由于 Kubernetes 在版本变更过程中会废弃部分旧的 API,因此需要根据你使用 Kubernetes 版本选择适用的部署文件:

Kubernetes v1.18 及以上版本

  1. $ kubectl apply -f https://raw.githubusercontent.com/juicedata/juicefs-csi-driver/master/deploy/k8s.yaml

Kubernetes v1.18 以下版本

  1. $ kubectl apply -f https://raw.githubusercontent.com/juicedata/juicefs-csi-driver/master/deploy/k8s_before_v1_18.yaml

创建存储类

参考以下内容创建一个配置文件,例如:juicefs-sc.yaml,在 stringData 部分填写 JuiceFS 文件系统的配置信息:

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: juicefs-sc-secret
  5. namespace: kube-system
  6. type: Opaque
  7. stringData:
  8. name: "test"
  9. metaurl: "redis://juicefs.afyq4z.0001.use1.cache.amazonaws.com/3"
  10. storage: "s3"
  11. bucket: "https://juicefs-test.s3.us-east-1.amazonaws.com"
  12. access-key: ""
  13. secret-key: ""
  14. ---
  15. apiVersion: storage.k8s.io/v1
  16. kind: StorageClass
  17. metadata:
  18. name: juicefs-sc
  19. provisioner: csi.juicefs.com
  20. reclaimPolicy: Retain
  21. volumeBindingMode: Immediate
  22. parameters:
  23. csi.storage.k8s.io/node-publish-secret-name: juicefs-sc-secret
  24. csi.storage.k8s.io/node-publish-secret-namespace: kube-system
  25. csi.storage.k8s.io/provisioner-secret-name: juicefs-sc-secret
  26. csi.storage.k8s.io/provisioner-secret-namespace: kube-system

执行命令,部署存储类:

  1. $ kubectl apply -f ./juicefs-sc.yaml

另外,你也可以将上述配置文件中 Secret 部分抽离出来,通过 kubectl 在命令行上创建:

  1. $ kubectl -n kube-system create secret generic juicefs-sc-secret \
  2. --from-literal=name=test \
  3. --from-literal=metaurl=redis://juicefs.afyq4z.0001.use1.cache.amazonaws.com/3 \
  4. --from-literal=storage=s3 \
  5. --from-literal=bucket=https://juicefs-test.s3.us-east-1.amazonaws.com \
  6. --from-literal=access-key="" \
  7. --from-literal=secret-key=""

这样一来,存储类的配置文件 juicefs-sc.yaml 应该像下面这样:

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: juicefs-sc
  5. provisioner: csi.juicefs.com
  6. reclaimPolicy: Retain
  7. parameters:
  8. csi.storage.k8s.io/node-publish-secret-name: juicefs-sc-secret
  9. csi.storage.k8s.io/node-publish-secret-namespace: kube-system
  10. csi.storage.k8s.io/provisioner-secret-name: juicefs-sc-secret
  11. csi.storage.k8s.io/provisioner-secret-namespace: kube-system

然后通过 kubectl apply 部署存储类:

  1. $ kubectl apply -f ./juicefs-sc.yaml

使用 JuiceFS 为 Pod 提供存储

JuiceFS CSI Driver 同时支持静态和动态 PV,你既可以将提前创建的 PV 手动分配给 Pods,也可以在部署 Pods 时通过 PVC 动态的创建卷。

例如,可以使用下面的配置创建一个名为 development.yaml 的配置文件,它通过 PVC 为 Nginx 容器创建持久化卷,并挂载到了容器的 /config 目录:

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: web-pvc
  5. spec:
  6. accessModes:
  7. - ReadWriteMany
  8. resources:
  9. requests:
  10. storage: 10Pi
  11. storageClassName: juicefs-sc
  12. ---
  13. apiVersion: apps/v1
  14. kind: Deployment
  15. metadata:
  16. name: nginx-run
  17. spec:
  18. selector:
  19. matchLabels:
  20. app: nginx
  21. template:
  22. metadata:
  23. labels:
  24. app: nginx
  25. spec:
  26. containers:
  27. - name: nginx
  28. image: linuxserver/nginx
  29. ports:
  30. - containerPort: 80
  31. volumeMounts:
  32. - mountPath: /config
  33. name: web-data
  34. volumes:
  35. - name: web-data
  36. persistentVolumeClaim:
  37. claimName: web-pvc

通过 kubectl apply 部署 Pods:

  1. $ kubectl apply -f ./development.yaml

部署成功以后,查看 pods 状态:

  1. $ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. nginx-run-7d6fb7d6df-cfsvp 1/1 Running 0 21m

我们可以简单的通过 kubectl exec 命令查看容器中的文件系统挂载情况:

  1. $ kubectl exec nginx-run-7d6fb7d6df-cfsvp -- df -Th
  2. Filesystem Type Size Used Avail Use% Mounted on
  3. overlay overlay 40G 7.0G 34G 18% /
  4. tmpfs tmpfs 64M 0 64M 0% /dev
  5. tmpfs tmpfs 3.8G 0 3.8G 0% /sys/fs/cgroup
  6. JuiceFS:jfs fuse.juicefs 1.0P 180M 1.0P 1% /config
  7. ...

从容器中返回的结果可以看到,完全符合预期,JuiceFS 卷已经挂载到了我们指定的 /config 目录。

像上面这样通过 PVC 动态创建 PV 时,JuiceFS 会在文件系统根目录创建与 PV 同名的目录并挂载到容器中。执行下列命令,可以查看集群中所有 PV:

  1. $ kubectl get pv -A
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pvc-b670c8a1-2962-497c-afa2-33bc8b8bb05d 10Pi RWX Retain Bound default/web-pvc juicefs-sc 34m

通过外部主机挂载同一个 JuiceFS 存储,可以看到当前正在使用的 PV 以及曾经创建的 PV。

Kubernetes 使用 JuiceFS - 图1

如果想了解更多关于 JuiceFS CSI Driver 的信息,请参考项目主页

创建更多 JuiceFS 存储类

你可以根据实际需要重复前面的步骤,通过 JuiceFS CSI Driver 创建任意数量的存储类。但要注意修改存储类的名称以及 JuiceFS 文件系统的配置信息,避免与已创建的存储类冲突。例如,使用 Helm 时可以创建一个名为 jfs2.yaml 的配置文件:

  1. storageClasses:
  2. - name: jfs-sc2
  3. enabled: true
  4. reclaimPolicy: Retain
  5. backend:
  6. name: "jfs-2"
  7. metaurl: "redis://example.abc.0001.use1.cache.amazonaws.com/3"
  8. storage: "s3"
  9. accessKey: ""
  10. secretKey: ""
  11. bucket: "https://jfs2.s3.us-east-1.amazonaws.com"

执行 Helm 命令进行部署:

  1. $ helm repo add juicefs-csi-driver https://juicedata.github.io/juicefs-csi-driver/
  2. $ helm repo update
  3. $ helm upgrade juicefs-csi-driver juicefs-csi-driver/juicefs-csi-driver --install -f ./jfs2.yaml

查看集群中存储类的情况:

  1. $ kubectl get sc
  2. NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
  3. juicefs-sc csi.juicefs.com Retain Immediate false 88m
  4. juicefs-sc2 csi.juicefs.com Retain Immediate false 13m
  5. standard (default) k8s.io/minikube-hostpath Delete Immediate false 128m

监控

请查看「监控」文档了解如何收集及展示 JuiceFS 监控指标

在容器中挂载 JuiceFS

某些情况下,你可能需要在容器中直接挂载 JuiceFS 存储,这需要在容器中使用 JuiceFS 客户端,你可以参考以下 Dockerfile 样本将 JuiceFS 客户端集成到应用镜像:

  1. FROM alpine:latest
  2. LABEL maintainer="Juicedata <https://juicefs.com>"
  3. # Install JuiceFS client
  4. RUN apk add --no-cache curl && \
  5. JFS_LATEST_TAG=$(curl -s https://api.github.com/repos/juicedata/juicefs/releases/latest | grep 'tag_name' | cut -d '"' -f 4 | tr -d 'v') && \
  6. wget "https://github.com/juicedata/juicefs/releases/download/v${JFS_LATEST_TAG}/juicefs-${JFS_LATEST_TAG}-linux-amd64.tar.gz" && \
  7. tar -zxf "juicefs-${JFS_LATEST_TAG}-linux-amd64.tar.gz" && \
  8. install juicefs /usr/bin && \
  9. rm juicefs "juicefs-${JFS_LATEST_TAG}-linux-amd64.tar.gz" && \
  10. rm -rf /var/cache/apk/* && \
  11. apk del curl
  12. ENTRYPOINT ["/usr/bin/juicefs", "mount"]

由于 JuiceFS 需要使用 FUSE 设备挂载文件系统,因此在创建 Pod 时需要允许容器在特权模式下运行:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-run
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. template:
  10. metadata:
  11. labels:
  12. app: nginx
  13. spec:
  14. containers:
  15. - name: nginx
  16. image: linuxserver/nginx
  17. ports:
  18. - containerPort: 80
  19. securityContext:
  20. privileged: true

⚠️ 风险提示:容器启用 privileged: true 特权模式以后,就具备了访问宿主机所有设备的权限,即拥有了对宿主机内核的完全控制权限。使用不当会带来严重的安全隐患,请您在使用此方式之前进行充分的安全评估。