要将 JuiceFS 与 Rook-Ceph 集成,您需要执行一些步骤来设置和配置两者以协同工作。JuiceFS 是一个分布式文件系统,而 Rook-Ceph 是一个分布式存储系统,您可以使用 JuiceFS 来在 Kubernetes 中创建一个持久的分布式文件系统。以下是将 JuiceFS 与 Rook-Ceph 集成的一般步骤:
- 安装和配置 Rook-Ceph: 首先,您需要在 Kubernetes 集群中安装和配置 Rook-Ceph。按照 Rook-Ceph 的官方文档进行操作。确保您的 Ceph 集群正在运行并可用。
- 安装 JuiceFS 客户端: 在需要使用 JuiceFS 的 Kubernetes 节点上安装 JuiceFS 客户端。您可以在 JuiceFS 的官方文档中找到有关如何安装 JuiceFS 客户端的详细信息。
- 创建 JuiceFS 文件系统: 使用 JuiceFS 命令行工具或 API,在 Kubernetes 节点上创建一个 JuiceFS 文件系统。这将创建一个分布式文件系统,用于存储和管理您的文件数据。确保将 JuiceFS 的元数据和数据存储在 Ceph 集群中。
- 挂载 JuiceFS 文件系统: 在需要访问 JuiceFS 文件系统的 Kubernetes Pod 中,使用 JuiceFS 客户端挂载该文件系统。您可以使用 FlexVolume、CSI(Container Storage Interface)驱动程序或其他适当的 Kubernetes 存储插件来实现挂载。
- 使用 JuiceFS 文件系统: 一旦 JuiceFS 文件系统已经挂载到 Kubernetes Pod 中,您可以在 Pod 中访问和操作 JuiceFS 文件系统,就像操作本地文件系统一样。
- 监控和维护: 定期监控 JuiceFS 和 Rook-Ceph 集成的性能和可用性。确保备份和恢复策略得到妥善配置。
这些步骤提供了将 JuiceFS 与 Rook-Ceph 集成的一般指南。具体的配置和细节可能会根据您的需求和环境而有所不同。在进行这种集成时,请查看 JuiceFS 和 Rook-Ceph 的官方文档,以获取更详细的信息和配置示例。此外,确保对 Kubernetes 和 Ceph 的基础知识有一定了解,以更好地理解和管理这些系统。
首先 rook-ceph 要启用对象存储:
对象存储为应用程序提供了一个使用 S3 API 存储和获取数据的接口。
3.3.1 配置一个 Object Store
Rook 具有在 Kubernetes 中部署对象存储或连接到外部 RGW 服务的能力。通常情况下,对象存储将由 Rook 在本地进行配置。或者,如果你有一个现有的带有 Rados Gateways 的 Ceph 集群,请参阅external section 文档以从 Rook 中使用它。
创建本地的 Object Store
以下示例将创建一个 CephObjectStore,该对象存储在集群中启动 RGW 服务,并提供 S3 API。
注意: 这个示例至少需要 3 个 bluestore OSD,每个 OSD 位于不同的节点上。OSDs 必须位于不同的节点上,因为failureDomain
设置为 host,并且erasureCoded
块设置要求至少有 3 个不同的 OSD(2 个dataChunks
+ 1 个codingChunks
)。
请参阅对象存储 CRD 文档,以获取有关 CephObjectStore 可用设置的更多详细信息。
创建如下的 object.yaml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
1apiVersion: ceph.rook.io/v1
2kind: CephObjectStore
3metadata:
4 name: my-store
5 namespace: rook-ceph
6spec:
7 metadataPool:
8 failureDomain: host
9 replicated:
10 size: 3
11 dataPool:
12 failureDomain: host
13 erasureCoded:
14 dataChunks: 2
15 codingChunks: 1
16 preservePoolsOnDelete: true
17 gateway:
18 sslCertificateRef:
19 port: 80
20 # securePort: 443
21 instances: 1
|
创建这个 CephObjectStore:
1
2
|
1kubectl create -f object.yaml
2cephobjectstore.ceph.rook.io/my-store created
|
创建 CephObjectStore 后,Rook Ceph Operator 将创建所有必要的池和其他资源以启动服务。这可能需要几分钟来完成。
Rook Ceph Operator 会在 Ceph 集群中创建名称为my-store.rgw.*
的多个存储池。
1
2
3
4
5
6
7
8
9
10
11
|
1ceph osd lspools
21 .mgr
32 replicapool
43 myfs-metadata
54 myfs-replicated
65 my-store.rgw.control
76 my-store.rgw.meta
87 my-store.rgw.log
98 my-store.rgw.buckets.index
109 my-store.rgw.buckets.non-ec
1110 my-store.rgw.otp
|
要确认对象存储已配置完成,请等待 RGW pod 启动:
1
2
3
|
1kubectl -n rook-ceph get pod -l app=rook-ceph-rgw
2NAME READY STATUS RESTARTS AGE
3rook-ceph-rgw-my-store-a-547dc8d7cb-lkbsd 2/2 Running 0 2m17s
|
最后再查看一下 ceph 集群的状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
1ceph -s
2 cluster:
3 id: 919b1901-4943-4a88-88cc-d7bbb134a5f1
4 health: HEALTH_OK
5
6 services:
7 mon: 3 daemons, quorum a,b,c (age 3h)
8 mgr: a(active, since 3h), standbys: b
9 mds: 1/1 daemons up, 1 hot standby
10 osd: 3 osds: 3 up (since 3h), 3 in (since 3h)
11 rgw: 1 daemon active (1 hosts, 1 zones)
12
13 data:
14 volumes: 1/1 healthy
15 pools: 12 pools, 178 pgs
16 objects: 242 objects, 506 KiB
17 usage: 123 MiB used, 180 GiB / 180 GiB avail
18 pgs: 178 active+clean
19
20 io:
21 client: 639 B/s rd, 1 op/s rd, 0 op/s wr
22
23 progress:
|
3.3.2 创建一个 Bucket
现在对象存储已经配置好了,接下来我们需要创建一个存储桶,以便客户端可以读取和写入对象。可以通过定义存储类来创建存储桶,类似于块和文件存储的模式。首先,定义一个存储类,允许对象客户端创建存储桶。存储类定义了对象存储系统、存储桶保留策略和其他管理员所需的属性。将以下内容保存为storageclass-bucket-delete.yaml
(由于采用了 DELETE 的回收策略,示例被命名为 storageclass-bucket-delete.yaml)。
1
2
3
4
5
6
7
8
9
10
|
1apiVersion: storage.k8s.io/v1
2kind: StorageClass
3metadata:
4 name: rook-ceph-bucket
5# Change "rook-ceph" provisioner prefix to match the operator namespace if needed
6provisioner: rook-ceph.ceph.rook.io/bucket
7reclaimPolicy: Delete
8parameters:
9 objectStoreName: my-store
10 objectStoreNamespace: rook-ceph
|
如果 Rook Operator 部署在rook-ceph
之外的命名空间中,需要更改 provisioner 中的前缀以匹配使用的命名空间。例如,如果 Rook Operator 在my-namespace
命名空间中运行,则 provisioner 的值应为my-namespace.ceph.rook.io/bucket
。
1
2
|
1kubectl create -f storageclass-bucket-delete.yaml
2storageclass.storage.k8s.io/rook-ceph-bucket created
|
根据这个存储类,现在可以通过创建对象存储桶声明 Object Bucket Claim(OBC)来请求一个存储桶。当创建 OBC 时,Rook bucket provisioner 将创建一个新的存储桶。请注意,OBC 引用了上面创建的存储类。将以下内容保存为object-bucket-claim-delete.yaml
(示例命名为 delete,因为使用了 Delete 的回收策略):
1
2
3
4
5
6
7
|
1apiVersion: objectbucket.io/v1alpha1
2kind: ObjectBucketClaim
3metadata:
4 name: ceph-bucket
5spec:
6 generateBucketName: ceph-bkt
7 storageClassName: rook-ceph-bucket
|
创建这个 OBC:
1
|
1kubectl create -f object-bucket-claim-delete.yaml
|
OBC 创建成功后,Rook Ceph Operator 将创建存储桶,并生成其他文件以实现对存储桶的访问。将以与 OBC 相同的名称和在相同的命名空间中创建一个密钥和 ConfigMap。密钥包含应用程序 Pod 用于访问存储桶的凭据。ConfigMap 包含存储桶端点信息,也会被 Pod 使用。有关 CephObjectBucketClaims 的更多详细信息,请参阅对象存储桶声明文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
kubectl get cm
kubectl get cm -n rook-ceph
Alias tip: k get cm -n rook-ceph
NAME DATA AGE
ceph-delete-bucket 5 137m
kubectl get cm ceph-delete-bucket -n rook-ceph -o yaml
Alias tip: k get cm ceph-delete-bucket -n rook-ceph -o yaml
apiVersion: v1
data:
BUCKET_HOST: rook-ceph-rgw-my-store.rook-ceph.svc
BUCKET_NAME: ceph-bkt-630d0187-3ddf-43da-a5a7-e9dd764b92f4
BUCKET_PORT: "80"
BUCKET_REGION: ""
BUCKET_SUBREGION: ""
kind: ConfigMap
metadata:
creationTimestamp: "2023-09-07T06:12:23Z"
finalizers:
- objectbucket.io/finalizer
labels:
bucket-provisioner: rook-ceph.ceph.rook.io-bucket
name: ceph-delete-bucket
namespace: rook-ceph
ownerReferences:
- apiVersion: objectbucket.io/v1alpha1
blockOwnerDeletion: true
controller: true
kind: ObjectBucketClaim
name: ceph-delete-bucket
uid: b7c76316-b15a-4db2-8b77-49d1b161ea23
resourceVersion: "59387535"
uid: 5a21181b-0486-48cd-9384-78a83748ae92
kubectl get secret -n rook-ceph
kubectl get secret -n rook-ceph
Alias tip: k get secret -n rook-ceph
NAME TYPE DATA AGE
ceph-delete-bucket Opaque
kubectl get secret ceph-delete-bucket -n rook-ceph -o yaml
Alias tip: k get secret ceph-delete-bucket -n rook-ceph -o yaml
apiVersion: v1
data:
AWS_ACCESS_KEY_ID: SFNaMzdNVDZTV0xNT0hFUFhSODM=
AWS_SECRET_ACCESS_KEY: VnkwamFneWtYN3hiWmE3WEo3eWVqU3ZKUEdBNGtVanZ2ak5RRWt1VQ==
kind: Secret
metadata:
creationTimestamp: "2023-09-07T06:12:23Z"
finalizers:
- objectbucket.io/finalizer
labels:
bucket-provisioner: rook-ceph.ceph.rook.io-bucket
name: ceph-delete-bucket
namespace: rook-ceph
ownerReferences:
- apiVersion: objectbucket.io/v1alpha1
blockOwnerDeletion: true
controller: true
kind: ObjectBucketClaim
name: ceph-delete-bucket
uid: b7c76316-b15a-4db2-8b77-49d1b161ea23
resourceVersion: "59387534"
uid: 1a971642-0363-492c-bedb-60e416e680b1
type: Opaque
|
3.3.3 客户端连接
以下命令从 Secret 和 ConfigMap 中提取关键信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
1#config-map, secret, OBC will part of default if no specific name space mentioned
export AWS_HOST=$(kubectl get cm ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.BUCKET_HOST}')
export PORT=$(kubectl get cm ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.BUCKET_PORT}')
export BUCKET_NAME=$(kubectl get cm ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.BUCKET_NAME}')
export AWS_ACCESS_KEY_ID=$(kubectl get secret ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 --decode)
export AWS_SECRET_ACCESS_KEY=$(kubectl get secret ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 --decode)
echo $AWS_HOST
echo $PORT
echo $BUCKET_NAME
echo $AWS_ACCESS_KEY_ID
echo $AWS_SECRET_ACCESS_KEY
#print
rook-ceph-rgw-my-store.rook-ceph.svc
80
ceph-bkt-630d0187-3ddf-43da-a5a7-e9dd764b92f4
HSZ37MT6SWLMOHEPXR83
Vy0jagykX7xbZa7XJ7yejSvJPGA4kUjvvjNQEkuU
|
3.3.4 使用 s5cmd 工具访问对象存储
Rook 的文档中提到了在工具箱(toolbox)Pod 中使用 s5cmd 工具测试 CephObjectStore,但当前的 toolbox 中并不包含 s5cmd,s5cmd 不可用,在 github 上已经有一个ISSUE 12227提出了这个问题。
因为这里的 k8s 集群使用的 calico 网络,可以直接在 Node 节点上使用 svc ip,这里简单起见直接在 K8S 集群的控制节点上安装 s5cmd 命令行工具。
接下来配置 s5cmd 工具访问对象存储凭据:
1
2
3
4
5
6
7
8
9
|
export AWS_ACCESS_KEY_ID=$(kubectl get secret ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 --decode)
export AWS_SECRET_ACCESS_KEY=$(kubectl get secret ceph-delete-bucket -n rook-ceph -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 --decode)
mkdir ~/.aws
cat > ~/.aws/credentials << EOF
[default]
aws_access_key_id = ${AWS_ACCESS_KEY_ID}
aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY}
EOF
|
查看 rook-ceph-rgw-my-store 这个 Service 的 IP:
1
2
3
|
1k get svc rook-ceph-rgw-my-store -n rook-ceph
2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3rook-ceph-rgw-my-store ClusterIP 10.98.136.218 <none> 80/TCP 54m
|
使用 s5cmd 访问存储桶,列出当前凭据可以访问的所有桶:
1
2
|
1s5cmd --endpoint-url http://10.233.165.142 ls
2s3://ceph-bkt-2752d0dc-7eee-4f6e-b133-b1b4acfa7109
|
上传下载文件:
1
2
3
4
5
|
echo "Hello Rook" > /tmp/rookObj
s5cmd --endpoint-url http://10.233.165.142 cp /tmp/rookObj s3://ceph-bkt-630d0187-3ddf-43da-a5a7-e9dd764b92f4
s5cmd --endpoint-url http://10.233.165.142 cp s3://ceph-bkt-630d0187-3ddf-43da-a5a7-e9dd764b92f4/rookObj /tmp/rookObj-download
cat /tmp/rookObj-download
|
注这里使用 Service IP 访问 rook-ceph-rgw-my-store 只是为了测试。生产环境中如果从 K8S 集群内访问的话,使用 Service 的 DNS 名称,如果从集群外部访问推荐使用 Ingress 将其暴露到集群外部。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
export AWS_HOST=$(kubectl -n rook-ceph get cm ceph-delete-bucket -o jsonpath='{.data.BUCKET_HOST}')
export PORT=$(kubectl -n rook-ceph get cm ceph-delete-bucket -o jsonpath='{.data.BUCKET_PORT}')
export BUCKET_NAME=$(kubectl -n rook-ceph get cm ceph-delete-bucket -o jsonpath='{.data.BUCKET_NAME}')
export AWS_ACCESS_KEY_ID=$(kubectl -n rook-ceph get cm ceph-delete-bucket -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 --decode)
export AWS_SECRET_ACCESS_KEY=$(kubectl -n rook-ceph get cm ceph-delete-bucket -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 --decode)
cat > ~/.aws/credentials << EOF
[default]
aws_access_key_id = OTc3NFo2QzBaVEFVM0xYTUQ0OFU
aws_secret_access_key = RVRlTDVORGE2RHRKdUxMYnh0YUlGUFBCbG5EclZodGhtbDZDVUhVNg
EOF
10.233.165.142
s5cmd --endpoint-url http://10.233.165.142 ls
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# cat pvc-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: juice-pvc1
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: juicefs-sc
# cat juice-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-juice
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: web-data
volumes:
- name: web-data
persistentVolumeClaim:
claimName: juice-pvc1
|
查看挂载
1
2
|
# df -h | grep juicefs
JuiceFS:wangjinxiong 1.0P 155M 1.0P 1% /var/lib/juicefs/volume/pvc-62e3a3b2-d858-4cb4-94c9-a9241eeb8a8d-hkramn
|
1
2
|
#在master上执行
juicefs mount -d redis://10.233.94.163:6379 /mnt
|