Praktyczny przykład połączenia pamięci masowej opartej na Ceph w klastrze Kubernetes

Container Storage Interface (CSI) – to zunifikowany interfejs współpracy Kubernetes z systemami pamięci masowej. Krótko o nim już opowiadaliśmy, a dziś dokładniej przyjrzymy się powiązaniu CSI z Ceph: pokażemy, jak podłączyć pamięć Ceph 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?

Praktyczny przykład połączenia pamięci masowej opartej na Ceph w klastrze Kubernetes

Zatem masz pod ręką klaster Kubernetes, na przykład kubespray. Obok działa klaster Ceph — można go również zainstalować, na przykład przy użyciu tego zestawu playbooków. 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 -s

Następnie tutaj utworzymy pulę dla dysków RBD:

ceph osd pool create kube 32
ceph osd pool application enable kube rbd

Przechodzimy 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.yml

Teraz 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ów

Otrzymane 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: true

Nastę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.rbdkube

Polecenie 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.yaml

Nastę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:
  - discard

Należy wprowadzić clusterID, który już poznaliśmy za pomocą polecenia ceph fsid, i zastosować ten manifest w klastrze Kubernetes:

kubectl apply -f storageclass.yaml

Aby 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-sc

Od razu sprawdźmy, jak Kubernetes utworzył w Ceph żądany wolumen:

kubectl get pvc
kubectl get pv

Wydaje 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 poleceniu

Teraz 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.yaml

Poczekajmy, 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 pvc

Widzimy, ż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 yaml

A 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: false

A teraz przyjrzyjmy się PVC:

kubectl get pvc

Rozmiar 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.yml

Ponownie musimy wypełnić plik cephfs.yml. Jak wcześniej, pomogą nam polecenia Ceph:

ceph fsid
ceph mon dump

Wypeł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: true

Zauważ, ż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-namespace

Przechodzimy 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.fs

Utworzymy 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.yaml

A 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:
  - debug

Wypełnimy to tutaj clusterID i zastosujemy w Kubernetes:

kubectl apply -f storageclass.yaml

Weryfikacja

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-sc

I sprawdzimy obecność PVC/PV:

kubectl get pvc
kubectl get pv

Jeś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/cephfs

Oczywiście, takie montowanie FS na węźle Ceph jest odpowiednie tylko do celów edukacyjnych, jakimi się teraz zajmujemy w naszych kursach Slurm. 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.yaml

Zobaczymy, jak zmieniła się kwota w zamontowanym katalogu:

getfattr -n ceph.quota.max_bytes

Aby 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:

Ogólne zasady działania Kubernetes z wolumenami
Dokumentacja RBD
Integracja RBD i Kubernetes z perspektywy Ceph
Integracja RBD i Kubernetes z perspektywy CSI
Ogólna dokumentacja CephFS
Integracja CephFS i Kubernetes z perspektywy CSI

Na kursie Slurma Kubernetes Podstawy 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 nowy kurs dotyczący Ceph. 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 Southbridge, Certified Kubernetes Administrator, autor i twórca kursów Slurma.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster