
Na początku tego miesiąca, 3 maja, zapowiedziano dużą aktualizację "systemu zarządzania dla rozproszonych magazynów danych w Kubernetes" — . Ponad rok temu już ogólny przegląd Rook. Wtedy też proszono nas, abyśmy podzielili się doświadczeniem w jego praktycznym użyciu — i oto, z okazji tej znaczącej chwili w historii projektu, z radością dzielimy się zebranymi wrażeniami.
Krótko mówiąc, Rook jest zestawem dla Kubernetes, które w pełni przejmują kontrolę nad wdrażaniem, zarządzaniem, automatycznym przywracaniem takich rozwiązań do przechowywania danych jak Ceph, EdgeFS, Minio, Cassandra, CockroachDB.
Obecnie najbardziej rozwiniętym (i do stabilnej na tym etapie) rozwiązaniem jest .
Uwaga: wśród ważnych zmian w wersji Rook 1.0.0 związanych z Ceph można odnotować wsparcie dla Ceph Nautilus oraz możliwość użycia NFS dla CephFS lub RGW-bucketów. Z innych wyróżnia się "dojrzałość" wsparcia EdgeFS do poziomu bety.
Tak więc, w tym artykule:
- odpowiemy na pytanie, jakie korzyści dostrzegamy w używaniu Rook do wdrażania Ceph w klastrze Kubernetes;
- podzielimy się doświadczeniem i wrażeniami z użytkowania Rook w produkcji;
- powiemy, dlaczego mówimy Rook’owi "Tak!", oraz o swoich planach na niego.
Zacznijmy od ogólnych koncepcji i teorii.
„Mam przewagę w jedną Wieżę!” (nieznany szachista)

Jedną z głównych zalet Rook jest to, że interakcja z magazynami danych odbywa się przez mechanizmy Kubernetes. Oznacza to, że nie trzeba już kopiować poleceń do konfiguracji Ceph z kartki do konsoli.
— Chcesz wdrożyć CephFS w klastrze? Po prostu napisz plik YAML!
— Co? Chcesz wdrożyć również magazyn obiektów z API S3? Po prostu napisz drugi plik YAML!
Rook został stworzony zgodnie z zasadami typowego operatora. Interakcja z nim odbywa się za pomocą , w których opisujemy wymagane przez nas cechy encji Ceph (ponieważ jest to jedyna stabilna realizacja, w artykule domyślnie mowa będzie o Ceph, jeśli nie zaznaczone zostanie inaczej). Zgodnie z zadanymi parametrami operator automatycznie wykona niezbędne polecenia konfiguracyjne.
Szczegóły omówimy na przykładzie tworzenia Object Store, a dokładniej — CephObjectStoreUser.
apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
name: {{ .Values.s3.crdName }}
namespace: kube-rook
spec:
metadataPool:
failureDomain: host
replicated:
size: 3
dataPool:
failureDomain: host
erasureCoded:
dataChunks: 2
codingChunks: 1
gateway:
type: s3
sslCertificateRef:
port: 80
securePort:
instances: 1
allNodes: false
---
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreUser
metadata:
name: {{ .Values.s3.crdName }}
namespace: kube-rook
spec:
store: {{ .Values.s3.crdName }}
displayName: {{ .Values.s3.username }}Podane w ofercie parametry są dość standardowe i raczej nie wymagają komentarza, jednak warto szczególnie zwrócić uwagę na te, które są wyróżnione jako zmienne szablonów.
Ogólna zasada działania sprowadza się do tego, że za pośrednictwem pliku YAML „zamawiamy” zasoby, w wyniku czego operator wykonuje niezbędne komendy i zwraca nam „nie do końca prawdziwy” sekret, z którym możemy następnie pracować. (patrz poniżej). A z zmiennych, które zostały podane powyżej, zostanie zbudowana komenda oraz nazwa sekretu.
Czym jest więc ta komenda? Podczas tworzenia użytkownika dla obiektowego magazynu operator Rook wewnątrz pod’a wykona następujące działania:
radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}"Wynikiem wykonania tej komendy będzie struktura JSON:
{
"user_id": "rook-user",
"display_name": "{{ .Values.s3.username }}",
"keys": [
{
"user": "rook-user",
"access_key": "NRWGT19TWMYOB1YDBV1Y",
"secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
}
],
...
} Klucze — to, co będzie potrzebne w przyszłości aplikacjom do dostępu do obiektowego magazynu za pośrednictwem S3 API. Operator Rook z nieskrywaną uprzejmością zbiera je i składa w swoim namespace w formie sekretu o nazwie rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.
Aby skorzystać z danych z tego sekretu, wystarczy dodać je do kontenera jako zmienne środowiskowe. Przykładem może być szablon dla Job, w którym automatycznie tworzymy bucket’y dla każdego środowiska użytkownika:
{{- range $bucket := $.Values.s3.bucketNames }}
apiVersion: batch/v1
kind: Job
metadata:
name: create-{{ $bucket }}-bucket-job
annotations:
"helm.sh/hook": post-install
"helm.sh/hook-weight": "2"
spec:
template:
metadata:
name: create-{{ $bucket }}-bucket-job
spec:
restartPolicy: Never
initContainers:
- name: waitdns
image: alpine:3.6
command: ["/bin/sh", "-c", "while ! getent ahostsv4 rook-ceph-rgw-{{ $.Values.s3.crdName }}; do sleep 1; done" ]
- name: config
image: rook/ceph:v1.0.0
command: ["/bin/sh", "-c"]
args: ["s3cmd --configure --access_key=$(ACCESS-KEY) --secret_key=$(SECRET-KEY) -s --no-ssl --dump-config | tee /config/.s3cfg"]
volumeMounts:
- name: config
mountPath: /config
env:
- name: ACCESS-KEY
valueFrom:
secretKeyRef:
name: rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}
key: AccessKey
- name: SECRET-KEY
valueFrom:
secretKeyRef:
name: rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}
key: SecretKey
containers:
- name: create-bucket
image: rook/ceph:v1.0.0
command:
- "s3cmd"
- "mb"
- "--host=rook-ceph-rgw-{{ $.Values.s3.crdName }}"
- "--host-bucket= "
- "s3://{{ $bucket }}"
ports:
- name: s3-no-sll
containerPort: 80
volumeMounts:
- name: config
mountPath: /root
volumes:
- name: config
emptyDir: {}
---
{{- end }}Wszystkie działania opisane w tym Jobie zostały wykonane w obrębie Kubernetes. Struktury opisane w plikach YAML są złożone w repozytorium Git i wielokrotnie wykorzystywane. W tym widzimy ogromny plus dla inżynierów DevOps oraz procesu CI/CD jako całości.
Z Rook i Rados w radości
Użycie kombinacji Ceph + RBD nakłada pewne ograniczenia na montowanie wolumenów do podów.
W szczególności w namespace musi znajdować się sekretny klucz dostępu do Ceph, aby aplikacje stateful mogły funkcjonować. Jest w porządku, jeśli masz 2-3 środowiska w swoich przestrzeniach nazw: można po prostu skopiować sekret ręcznie. Ale co zrobić, jeśli każde feature dla deweloperów ma mieć swoje własne środowisko z własnym namespace?
U nas rozwiązano ten problem przy pomocy , który automatycznie kopiował sekrety do nowych namespace (przykład podobnego hooka opisano w ).
#! /bin/bash
if [[ $1 == “--config” ]]; then
cat <<EOF
{"onKubernetesEvent":[
{"name": "OnNewNamespace",
"kind": "namespace",
"event": ["add"]
}
]}
EOF
else
NAMESPACE=$(kubectl get namespace -o json | jq '.items | max_by( .metadata.creationTimestamp ) | .metadata.name')
kubectl -n ${CEPH_SECRET_NAMESPACE} get secret ${CEPH_SECRET_NAME} -o json | jq ".metadata.namespace="${NAMESPACE}"" | kubectl apply -f -
fiJednak podczas korzystania z Rook ten problem po prostu nie istnieje. Proces montowania odbywa się przy użyciu własnych sterowników opartych na lub (na razie w wersji beta) i dlatego nie wymaga sekretów.
Rook automatycznie rozwiązuje wiele problemów, co oraz skłania nas do jego użycia w nowych projektach.
Oblężenie Rook
Zakończymy część praktyczną wdrażaniem Rook i Ceph, aby mieć możliwość przeprowadzania własnych eksperymentów. Aby łatwiej zdobyć tę nieprzyjazną wieżę, deweloperzy przygotowali pakiet Helm. Pobierzmy go:
$ helm fetch rook-master/rook-ceph --untar --version 1.0.0 W pliku rook-ceph/values.yaml można znaleźć wiele różnych ustawień. Najważniejsze to wskazanie tolerancji dla agentów i wyszukiwania. O tym, do czego można wykorzystać mechanizm taints/tolerations, szczegółowo mówiłem w .
Krótko mówiąc, nie chcemy, aby pod’y z aplikacją kliencką znalazły się na tych samych node’ach, co dyski do przechowywania danych. Powód jest prosty: w ten sposób działanie agentów Rook nie będzie wpływać na samą aplikację.
Zatem otwieramy plik rook-ceph/values.yaml w ulubionym edytorze i dodajemy na końcu następujący blok:
discover:
toleration: NoExecute
tolerationKey: node-role/storage
agent:
toleration: NoExecute
tolerationKey: node-role/storage
mountSecurityMode: AnyNa każdym węźle zarezerwowanym do przechowywania danych dodajemy odpowiedni taint:
$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecuteNastępnie instalujemy wykres Helm poleceniem:
$ helm install --namespace ${ROOK_NAMESPACE} ./rook-cephTeraz musimy utworzyć klaster i wskazać lokalizację :
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
clusterName: "ceph"
finalizers:
- cephcluster.ceph.rook.io
generation: 1
name: rook-ceph
spec:
cephVersion:
image: ceph/ceph:v13
dashboard:
enabled: true
dataDirHostPath: /var/lib/rook/osd
mon:
allowMultiplePerNode: false
count: 3
network:
hostNetwork: true
rbdMirroring:
workers: 1
placement:
all:
tolerations:
- key: node-role/storage
operator: Exists
storage:
useAllNodes: false
useAllDevices: false
config:
osdsPerDevice: "1"
storeType: filestore
resources:
limits:
memory: "1024Mi"
requests:
memory: "1024Mi"
nodes:
- name: host-1
directories:
- path: "/mnt/osd"
- name: host-2
directories:
- path: "/mnt/osd"
- name: host-3
directories:
- path: "/mnt/osd" Sprawdzamy status Ceph — oczekujemy zobaczyć HEALTH_OK:
$ kubectl -n ${ROOK_NAMESPACE} exec $(kubectl -n ${ROOK_NAMESPACE} get pod -l app=rook-ceph-operator -o name -o jsonpath='{.items[0].metadata.name}') -- ceph -sJednocześnie sprawdzimy, czy pod’y z aplikacją kliencką nie trafiają na zarezerwowane węzły Ceph:
$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeNameNastępnie opcjonalnie można skonfigurować dodatkowe komponenty. Szczegóły na ich temat znajdują się w . Zalecamy zainstalowanie dashboardu i toolboxu do zarządzania.
Rook-i-haki: czy Rook wystarcza na wszystko?
Jak widać, rozwój Rook jest w pełnym toku. Jednak wciąż pozostają problemy, które uniemożliwiają nam całkowite odejście od ręcznej konfiguracji Ceph:
- Żaden sterownik Rook eksportować metryk dotyczących wykorzystania zamontowanych bloków, co pozbawia nas monitoringu.
- Flexvolume i CSI zmieniać rozmiaru wolumenów (w przeciwieństwie do RBD), dlatego Rook traci użyteczne (a czasami krytycznie potrzebne!) narzędzie.
- Rook wciąż nie jest tak elastyczny jak tradycyjny Ceph. Jeśli zechcemy skonfigurować, aby pulę dla metadanych CephFS przechowywano na SSD, a same dane na HDD, będzie trzeba ręcznie zapisać oddzielne grupy urządzeń w mapach CRUSH.
- Mimo że rook-ceph-operator uchodzi za stabilny, aktualnie istnieją pewne problemy przy aktualizacji Ceph z wersji 13 do 14.
Wnioski
„Teraz Rook jest zamknięty przed światem przez pionki, ale wierzymy, że pewnego dnia odegra kluczową rolę w rozgrywce!” (cytat wymyślony specjalnie na potrzeby tego artykułu)
Projekt Rook z pewnością zdobył nasze serca — uważamy, że [ze wszystkimi swoimi plusami i minusami] zasługuje również na Państwa uwagę.
Nasze dalsze plany sprowadzają się do tego, aby uczynić rook-ceph modułem dla , co uczyni jego użycie w naszych licznych klastrach Kubernetes jeszcze prostszym i wygodniejszym.
P.S.
Przeczytaj także na naszym blogu:
- «»;
- «»;
- «»;
- «»;
- «».
Źródło: habr.com
