在 Kubernetes 中使用 JuiceFS
相比于相比普通云盘(如 AWS EBS、阿里云弹性云盘等),JuiceFS 支持更多适合云环境使用的特性:
- 作为分布式共享存储,JuiceFS 支持同时挂载到多个 Pod 并发读写
- 文件系统挂载不受可用区限制,可以在云上与本地同时挂载
- JuiceFS 元数据服务默认跨两个可用区部署,相当于同城双活,不受公有云单可用区故障影响
- 几乎无限的存储空间(取决于所使用的的对象存储),无需进行扩容操作
在 Kubernetes 中有各种不同方式来使用 JuiceFS,请针对不同使用场景来选择:
- hostPath,适用于所有 Kubernetes 版本。
- CSI,适用于 Kubernetes v1.13 及以上版本。
- FlexVolume,适用于 Kubernetes v1.2 及以上版本,官方已经不推荐,建议使用 CSI。
hostPath 存储卷
如果你仅仅需要在 Kubernetes 容器中简单使用 JuiceFS,没有其他任何复杂要求(比如隔离性、权限控制),那么完全可以以 hostPath
卷 的方式使用 JuiceFS,搭建起来也十分简单:
-
在 Kubernetes 节点上统一安装、挂载 JuiceFS,如果节点众多,考虑自动化部署。
-
在 pod 定义中使用
hostPath
卷,直接将宿主机上的 JuiceFS 子目录挂载到容器中:apiVersion: v1
kind: Pod
metadata:
name: juicefs-app
spec:
containers:
- ...
volumeMounts:
- name: jfs-data
mountPath: /opt/app-data
volumes:
- name: jfs-data
hostPath:
# 假设挂载点为 /jfs
path: "/jfs/myapp/"
type: Directory
相比以 CSI 驱动的方式来使用 JuiceFS,hostPath
更为简单直接,出问题也更易排查,但也要注意:
-
为求管理方便,一般所有容器都在使用同一个宿主机挂载点,缺乏隔离可能导致数据安全问题,未来也无法在不同应用中单独调整 JuiceFS 挂载参数。请谨慎评估。
-
所有节点都需要提前挂载 JuiceFS,因此集群加入新节点,需要在初始化流程里进行安装和挂载,否则新节点没有 JuiceFS 挂载点,容器将无法创建。
-
宿主机上的 JuiceFS 挂载进程所占用的系统资源(如 CPU、内存等)不受 Kubernetes 控制,有可能占用较多宿主机资源。可以考虑用
system-reserved
来适当调整 Kubernetes 的系统资源预留值,为 JuiceFS 挂载进程预留更多资源。 -
如果宿主机上的 JuiceFS 挂载进程意外退出,将会导致应用 pod 无法正常访问挂载点,此时需要重新挂载 JuiceFS 文件系统并重建应用 pod。作为对比,JuiceFS CSI 驱动提供「挂载点自动恢复」功能来解决这个问题。
-
JuiceFS 挂载点的生命周期与宿主机绑定,如需重新挂载文件系统,可以借助 JuiceFS 客户端的平滑重启功能来确保重新挂载以后不会影响应用 pod 访问挂载点。详见「客户端升级与平滑重启」。
-
如果你使用 Docker 作为 Kubernetes 容器运行环境,最好令 JuiceFS 先于 Docker 启动,否则在节点重启的时候,偶尔可能出现容器启动时,JuiceFS 尚未挂载好的情况,此时便会因该依赖问题启动失败。以 systemd 为例,可以用下方 unit file 来配置启动顺序:
/etc/systemd/system/docker.service.d/override.conf[Unit]
# 请使用下方命令确定 JuiceFS 挂载服务的名称(例如 jfs.mount):
# systemctl list-units | grep "\.mount"
After=network-online.target firewalld.service containerd.service jfs.mount
CSI 存储卷
如果对灵活性、可观测性有要求,推荐以 CSI 存储卷的方式来使用 JuiceFS,这也是 Kubernetes 下的标准用法。JuiceFS CSI 有着自己的文档体系,请参考 JuiceFS CSI 驱动文档。
FlexVolume 存储卷
FlexVolume 已经不再被 Kubernetes 官方推荐,如有可能,请选择 CSI 存储卷方式。
设置 FlexVolume 首先需要安装 JuiceFS 客户端 juicefs
,这同时也是 FlexVolume 的驱动,支持 init
, mount
and unmount
操作。
将 juicefs
放到所有主机的 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/juicedata~juicefs/juicefs
位置,并且将它改成可执行的(需要 Python>=2.7)。
wget https://juicefs.com/static/juicefs
chmod +x juicefs
cp juicefs /usr/libexec/kubernetes/kubelet-plugins/volume/exec/juicedata~juicefs/juicefs
在配置文件中使用明文密钥
你可以像下面这样定义一个 Flex 卷并使用 juicedata/juicefs
作为驱动:
volumes:
- name: test
flexVolume:
driver: "juicedata/juicefs"
options:
name: "myjfs"
token: "TOKEN"
accesskey: "ACCESSKEY"
secretkey: "SECRETKEY"
你还可以把其他挂载参数加入到 options
里,比如 cacheSize
和 cacheDir
完整的参数列表请参考 juicefs mount -h
。
每个 Pod 会使用一个独立的挂载点,跟 Pod 有相同的生命周期。
Kubernetes 密钥管理
或许你不想把密钥的明文放到 Pod 配置文件中,使用 Kubernetes 的密钥管理可以做到。
apiVersion: v1
kind: Secret
metadata:
name: myjfs
type: juicedata/juicefs
data:
token: BASE64(TOKEN)
accesskey: BASE64(ACCESSKEY)
secretkey: BASE64(SECRETKEY)
用下面的方法可以生成秘密的 base64 编码:
echo -n TOKEN | base64
然后你可以在 Pod 配置文件中使用它:
volumes:
- name: test
flexVolume:
driver: "juicedata/juicefs"
secretRef:
name: “myjfs”
options:
name: "myjfs"
使用 JuiceFS 的配置文件
不管是使用明文还是 Kubernetes 密钥,Flex 卷都会把它们通过命令行参数的方式传递给驱动,比如:
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/juicedata~juicefs/juicefs mount /var/lib/kubelet/pods/aa8ef19f-ba1f-11e7-ab55-0800279245af/volumes/juicedata~juicefs/test '{"kubernetes.io/fsType":"","kubernetes.io/pod.name":"test","kubernetes.io/pod.namespace":"default","kubernetes.io/pod.uid":"aa8ef19f-ba1f-11e7-ab55-0800279245af","kubernetes.io/pvOrVolumeName":"test","kubernetes.io/readwrite":"rw","kubernetes.io/secret/accesskey":"","kubernetes.io/secret/secretkey":"","kubernetes.io/secret/token":"TOKEN","kubernetes.io/serviceAccount.name":"default","name":"NAME"}'
这样看起来也不是很安全,其他人通过 ps
等方法可以看到密钥明文或者编码后的,通过 JuiceFS 的配置文件(/root/.juicefs/myjfs.conf
)我们可以把这些从命令行中隐藏起来,它是一个有如下内容的 JSON 文件:
{"token": "TOKEN", "accesskey": "ACCESSKEY", "secretkey": "SECRETKEY"}
一旦你把这个配置文件部署到所有主机的 /root/.juicefs/myjfs.conf
,就不需要在 Pod 配置文件中使用明文或者密码了,如下:
volumes:
- name: test
flexVolume:
driver: "juicedata/juicefs"
options:
name: "NAME"