Container Storage Interface (CSI) – to zunifikowany interfejs współpracy Kubernetes z systemami pamięci masowej. Krótko o nim już , a dziś dokładniej przyjrzymy się powiązaniu CSI z Ceph: pokażemy, jak do klastra Kubernetes.
W artykule znajduje się kilka rzeczywistych, choć nieco uproszczonych dla łatwiejszego przyswajania przykładów. Instalacja i konfiguracja klastrów Ceph i Kubernetes nie są omawiane.
Ciekawi cię, jak to działa?

Zatem masz pod ręką klaster Kubernetes, na przykład . Obok działa klaster Ceph — można go również zainstalować, na przykład przy użyciu tego . Mam nadzieję, że nie trzeba wspominać, że dla produkcji między nimi musi być sieć o przepustowości co najmniej 10 Gb/s.
Jeśli to wszystko masz, jedźmy dalej!
Najpierw wejdźmy na jeden z węzłów klastra Ceph i sprawdźmy, czy wszystko w porządku:
ceph health
ceph -sNastępnie tutaj utworzymy pulę dla dysków RBD:
ceph osd pool create kube 32
ceph osd pool application enable kube rbdPrzechodzimy do klastra Kubernetes. Tam najpierw zainstalujemy sterownik Ceph CSI dla RBD. Instalację przeprowadzimy, jak należy, przez Helm.
Dodajemy repozytorium z chartem i otrzymujemy zestaw zmiennych chartu ceph-csi-rbd:
helm repo add ceph-csi https://ceph.github.io/csi-charts
helm inspect values ceph-csi/ceph-csi-rbd > cephrbd.ymlTeraz należy wypełnić plik cephrbd.yml. W tym celu poznajmy identyfikator klastra i adresy IP monitorów w Ceph:
ceph fsid # w ten sposób poznamy clusterID
ceph mon dump # a w ten sposób zobaczymy adresy IP monitorówOtrzymane wartości wprowadzamy do pliku cephrbd.yml. Przy okazji włączamy tworzenie polityk PSP (Pod Security Policies). Opcje w sekcjach nodeplugin i provisioner są już w pliku, można je poprawić w sposób pokazany poniżej:
csiConfig:
- clusterID: "bcd0d202-fba8-4352-b25d-75c89258d5ab"
monitors:
- "v2:172.18.8.5:3300/0,v1:172.18.8.5:6789/0"
- "v2:172.18.8.6:3300/0,v1:172.18.8.6:6789/0"
- "v2:172.18.8.7:3300/0,v1:172.18.8.7:6789/0"
nodeplugin:
podSecurityPolicy:
enabled: true
provisioner:
podSecurityPolicy:
enabled: trueNastępnie wszystko, co musimy zrobić, to zainstalować chart w klastrze Kubernetes.
helm upgrade -i ceph-csi-rbd ceph-csi/ceph-csi-rbd -f cephrbd.yml -n ceph-csi-rbd --create-namespaceŚwietnie, sterownik RBD działa!
Utwórzmy w Kubernetes nową klasę StorageClass. W tym celu znów musimy trochę popracować z Ceph.
Tworzymy nowego użytkownika w Ceph i przyznajemy mu prawa do zapisu w puli kube:
ceph auth get-or-create client.rbdkube mon 'profile rbd' osd 'profile rbd pool=kube'A teraz sprawdźmy klucz dostępu tam samo:
ceph auth get-key client.rbdkubePolecenie zwróci coś podobnego:
AQCO9NJbhYipKRAAMqZsnqqS/T8OYQX20xIa9A==Zapiszemy tę wartość w Sekrecie w klastrze Kubernetes — tam, gdzie jest potrzebna userKey:
---
apiVersion: v1
kind: Secret
metadata:
name: csi-rbd-secret
namespace: ceph-csi-rbd
stringData:
# Wartości kluczy odpowiadają nazwie użytkownika i jego kluczowi, jak podane w
# klastrze Ceph. ID użytkownika musi mieć dostęp do puli,
# podanej w klasie storage
userID: rbdkube
userKey:I tworzymy nasz sekret:
kubectl apply -f secret.yamlNastępnie potrzebujemy mniej więcej takiego manifestu StorageClass:
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
clusterID:
pool: kube
imageFeatures: layering
# Te sekrety powinny zawierać dane do autoryzacji
# w twojej puli.
csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi-rbd
csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi-rbd
csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi-rbd
csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
- discardNależy wprowadzić clusterID, który już poznaliśmy za pomocą polecenia ceph fsid, i zastosować ten manifest w klastrze Kubernetes:
kubectl apply -f storageclass.yamlAby sprawdzić współpracę klastrów, stwórzmy taki PVC (Persistent Volume Claim):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rbd-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: csi-rbd-scOd razu sprawdźmy, jak Kubernetes utworzył w Ceph żądany wolumen:
kubectl get pvc
kubectl get pvWydaje się, że wszystko jest w porządku! A jak to wygląda po stronie Ceph?
Uzyskujemy listę wolumenów w puli i przeglądamy informacje o naszym wolumenie:
rbd ls -p kube
rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653 # tu oczywiście będzie inny ID wolumenu, który podano w poprzednim poleceniuTeraz spójrzmy, jak działa zmiana rozmiaru wolumenu RBD.
Zmienić rozmiar wolumenu w manifeście pvc.yaml na 2Gi i zastosować go:
kubectl apply -f pvc.yamlPoczekajmy, aż zmiany wejdą w życie, i jeszcze raz sprawdźmy rozmiar wolumenu.
rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653
kubectl get pv
kubectl get pvcWidzimy, że rozmiar PVC się nie zmienił. Aby poznać przyczynę, możemy zapytać Kubernetes o opis PVC w formacie YAML:
kubectl get pvc rbd-pvc -o yamlA oto problem:
message: Waiting for user to (re-)start a pod to finish file system resize of volume on node. type: FileSystemResizePending
To znaczy, że dysk się zwiększył, ale system plików na nim — nie.
Aby zwiększyć system plików, trzeba zamontować wolumen. Stworzony PVC/PV obecnie w żaden sposób nie jest używany.
Możemy stworzyć testowy Pod, na przykład tak:
---
apiVersion: v1
kind: Pod
metadata:
name: csi-rbd-demo-pod
spec:
containers:
- name: web-server
image: nginx:1.17.6
volumeMounts:
- name: mypvc
mountPath: /data
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: rbd-pvc
readOnly: falseA teraz przyjrzyjmy się PVC:
kubectl get pvcRozmiar się zmienił, wszystko w porządku.
W pierwszej części pracowaliśmy z urządzeniem blokowym RBD (nazwa pochodzi od Rados Block Device), jednak nie jest to rozwiązanie, jeśli potrzebujemy jednoczesnej pracy z tym dyskiem różnych mikrousług. Do pracy z plikami, a nie obrazem dysku, znacznie lepiej nadaje się CephFS.
Na przykładzie klastrów Ceph i Kubernetes skonfigurujemy CSI oraz inne konieczne zasoby do pracy z CephFS.
Uzyskamy wartości z potrzebnego nam nowego Helm-charta:
helm inspect values ceph-csi/ceph-csi-cephfs > cephfs.ymlPonownie musimy wypełnić plik cephfs.yml. Jak wcześniej, pomogą nam polecenia Ceph:
ceph fsid
ceph mon dumpWypełniamy plik wartości w ten sposób:
csiConfig:
- clusterID: "bcd0d202-fba8-4352-b25d-75c89258d5ab"
monitors:
- "172.18.8.5:6789"
- "172.18.8.6:6789"
- "172.18.8.7:6789"
nodeplugin:
httpMetrics:
enabled: true
containerPort: 8091
podSecurityPolicy:
enabled: true
provisioner:
replicaCount: 1
podSecurityPolicy:
enabled: trueZauważ, że adresy monitorów są podawane w prostym formacie address:port. Aby zamontować cephfs na węźle, te adresy są przekazywane do modułu jądra, który jeszcze nie obsługuje protokołu monitorów v2.
Port dla httpMetrics (tutaj Prometheus będzie zbierać metryki) zmieniamy, aby nie kolidował z nginx-proxy, który jest instalowany przez Kubespray. Możliwe, że nie będziesz tego potrzebować.
Instalujemy Helm-chart w klastrze Kubernetes:
helm upgrade -i ceph-csi-cephfs ceph-csi/ceph-csi-cephfs -f cephfs.yml -n ceph-csi-cephfs --create-namespacePrzechodzimy do magazynu danych Ceph, aby utworzyć tam oddzielnego użytkownika. W dokumentacji wskazano, że prowizjoner CephFS potrzebuje uprawnień administratora klastra. Jednak stworzymy oddzielnego użytkownika fs z ograniczonymi prawami:
ceph auth get-or-create client.fs mon 'allow r' mgr 'allow rw' mds 'allow rws' osd 'allow rw pool=cephfs_data, allow rw pool=cephfs_metadata'I od razu sprawdzimy jego klucz dostępu, przyda nam się później:
ceph auth get-key client.fsUtworzymy oddzielne Secret i StorageClass.
Nic nowego, już to widzieliśmy na przykładzie RBD:
---
apiVersion: v1
kind: Secret
metadata:
name: csi-cephfs-secret
namespace: ceph-csi-cephfs
stringData:
# Wymagane do dynamicznie tworzonych wolumenów
adminID: fs
adminKey:Zastosowujemy manifest:
kubectl apply -f secret.yamlA teraz – oddzielny StorageClass:
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-cephfs-sc
provisioner: cephfs.csi.ceph.com
parameters:
clusterID:
# Nazwa systemu plików CephFS, na którym zostanie utworzony wolumin
fsName: cephfs
# (opcjonalnie) Pula Ceph, w której będą przechowywane dane woluminu
# pool: cephfs_data
# (opcjonalnie) Opcje montowania dla Ceph-fuse, oddzielone przecinkami
# na przykład:
# fuseMountOptions: debug
# (opcjonalnie) Opcje montowania CephFS dla jądra, oddzielone przecinkami
# Zobacz man mount.ceph, aby uzyskać listę tych opcji. Na przykład:
# kernelMountOptions: readdir_max_bytes=1048576,norbytes
# Sekrety muszą zawierać dostęp dla administratora i/lub użytkownika Ceph.
csi.storage.k8s.io/provisioner-secret-name: csi-cephfs-secret
csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi-cephfs
csi.storage.k8s.io/controller-expand-secret-name: csi-cephfs-secret
csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi-cephfs
csi.storage.k8s.io/node-stage-secret-name: csi-cephfs-secret
csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi-cephfs
# (opcjonalnie) Sterownik może używać albo ceph-fuse (fuse),
# albo ceph kernelclient (kernel).
# Jeśli nie określono, używane będzie domyślne montowanie woluminów,
# które określa wyszukiwanie ceph-fuse i mount.ceph
# mounter: kernel
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
- debugWypełnimy to tutaj clusterID i zastosujemy w Kubernetes:
kubectl apply -f storageclass.yamlWeryfikacja
Aby sprawdzić, jak w poprzednim przykładzie, stworzymy PVC:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: csi-cephfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: csi-cephfs-scI sprawdzimy obecność PVC/PV:
kubectl get pvc
kubectl get pvJeśli chcesz zobaczyć pliki i katalogi w CephFS, możesz zamontować ten system plików gdzieś. Na przykład jak pokazano poniżej.
Wejdź na jeden z węzłów klastra Ceph i wykonaj następujące czynności:
# Точка монтирования
mkdir -p /mnt/cephfs
# Создаём файл с ключом администратора
ceph auth get-key client.admin >/etc/ceph/secret.key
# Добавляем запись в /etc/fstab
# !! Изменяем ip адрес на адрес нашего узла
echo "172.18.8.6:6789:/ /mnt/cephfs ceph name=admin,secretfile=/etc/ceph/secret.key,noatime,_netdev 0 2" >> /etc/fstab
mount /mnt/cephfsOczywiście, takie montowanie FS na węźle Ceph jest odpowiednie tylko do celów edukacyjnych, jakimi się teraz zajmujemy w naszych . Nie sądzę, żeby ktoś to robił w produkcji, duże ryzyko przypadkowego nadpisania ważnych plików.
Na koniec sprawdźmy, jak wygląda sytuacja w przypadku CephFS ze zmianą rozmiarów woluminów. Wracamy do Kubernetes i edytujemy nasz manifest dla PVC — zwiększamy rozmiar, na przykład do 7Gi.
Zastosujemy edytowany plik:
kubectl apply -f pvc.yamlZobaczymy, jak zmieniła się kwota w zamontowanym katalogu:
getfattr -n ceph.quota.max_bytesAby użyć tej komendy, możesz potrzebować zainstalować w systemie pakiet attr.
Oczy strach, ręce działają
Na pierwszy rzut oka wszystkie te zaklęcia i długie manifesty YAML wydają się skomplikowane, ale w praktyce studenci Slurma radzą sobie z nimi dość szybko.
W tym artykule nie zanurzyliśmy się w szczegóły — na to jest oficjalna dokumentacja. Jeśli interesują Cię szczegóły konfiguracji przechowywania Ceph w połączeniu z klastrem Kubernetes, pomogą te linki:
Na kursie Slurma możesz pójść jeszcze krok dalej i uruchomić w Kubernetes aplikację, która będzie wykorzystywać CephFS jako magazyn dla plików. Poprzez żądania GET/POST będziesz mógł przesyłać pliki i je odbierać z Ceph.
A jeśli bardziej interesuje Cię przechowywanie danych, zapisz się na . Podczas trwających testów beta kurs można zdobyć ze zniżką i wpłynąć na jego zawartość.
Autor artykułu: Aleksander Szwalow, inżynier praktykujący , Certified Kubernetes Administrator, autor i twórca kursów Slurma.
Źródło: habr.com
