Um exemplo prático de conexão de armazenamento baseado em Ceph a um cluster Kubernetes

Container Storage Interface (CSI) é uma interface unificada entre Kubernetes e sistemas de armazenamento. Já falamos sobre isso brevemente contado, e hoje veremos mais de perto a combinação de CSI e Ceph: mostraremos como conectar o armazenamento Ceph para o cluster Kubernetes.
O artigo fornece exemplos reais, embora um pouco simplificados, para facilitar a percepção. Não consideramos instalar e configurar clusters Ceph e Kubernetes.

Você está se perguntando como isso funciona?

Um exemplo prático de conexão de armazenamento baseado em Ceph a um cluster Kubernetes

Então você tem um cluster Kubernetes ao seu alcance, implantado, por exemplo, Kubespray. Há um cluster Ceph funcionando próximo - você também pode instalá-lo, por exemplo, com este um conjunto de manuais. Espero que não seja necessário mencionar que para produção entre eles deve haver uma rede com largura de banda de pelo menos 10 Gbit/s.

Se você tem tudo isso, vamos lá!

Primeiro, vamos até um dos nós do cluster Ceph e verificamos se está tudo em ordem:

ceph health
ceph -s

A seguir, criaremos imediatamente um pool para discos RBD:

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

Vamos passar para o cluster Kubernetes. Lá, em primeiro lugar, instalaremos o driver Ceph CSI para RBD. Instalaremos, conforme esperado, através do Helm.
Adicionamos um repositório com um gráfico, obtemos um conjunto de variáveis ​​para o gráfico ceph-csi-rbd:

helm repo add ceph-csi https://ceph.github.io/csi-charts
helm inspect values ceph-csi/ceph-csi-rbd > cephrbd.yml

Agora você precisa preencher o arquivo cephrbd.yml. Para fazer isso, descubra o ID do cluster e os endereços IP dos monitores no Ceph:

ceph fsid  # так мы узнаем clusterID
ceph mon dump  # а так увидим IP-адреса мониторов

Inserimos os valores obtidos no arquivo cephrbd.yml. Ao mesmo tempo, possibilitamos a criação de políticas PSP (Pod Security Policies). Opções em seções plug-in de nó и provedor já no arquivo, eles podem ser corrigidos conforme mostrado abaixo:

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

A seguir, só nos resta instalar o gráfico no cluster Kubernetes.

helm upgrade -i ceph-csi-rbd ceph-csi/ceph-csi-rbd -f cephrbd.yml -n ceph-csi-rbd --create-namespace

Ótimo, o driver RBD funciona!
Vamos criar um novo StorageClass no Kubernetes. Novamente, isso requer alguns ajustes no Ceph.

Criamos um novo usuário no Ceph e damos a ele direitos para gravar no pool Kube:

ceph auth get-or-create client.rbdkube mon 'profile rbd' osd 'profile rbd pool=kube'

Agora vamos ver se a chave de acesso ainda está lá:

ceph auth get-key client.rbdkube

O comando produzirá algo assim:

AQCO9NJbhYipKRAAMqZsnqqS/T8OYQX20xIa9A==

Vamos adicionar esse valor ao Secret no cluster Kubernetes – onde precisarmos dele chave do usuário:

---
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: ceph-csi-rbd
stringData:
  # Значения ключей соответствуют имени пользователя и его ключу, как указано в
  # кластере Ceph. ID юзера должен иметь доступ к пулу,
  # указанному в storage class
  userID: rbdkube
  userKey: <user-key>

E criamos nosso segredo:

kubectl apply -f secret.yaml

A seguir, precisamos de um manifesto StorageClass mais ou menos assim:

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: <cluster-id>
   pool: kube

   imageFeatures: layering

   # Эти секреты должны содержать данные для авторизации
   # в ваш пул.
   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

Precisa ser preenchido ID do cluster, o que já aprendemos pela equipe ceph-fside aplique este manifesto ao cluster Kubernetes:

kubectl apply -f storageclass.yaml

Para verificar como os clusters funcionam juntos, vamos criar o seguinte PVC (Persistent Volume Claim):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc

Vamos ver imediatamente como o Kubernetes criou o volume solicitado no Ceph:

kubectl get pvc
kubectl get pv

Tudo parece estar ótimo! Como é isso no lado do Ceph?
Obtemos uma lista de volumes no pool e visualizamos informações sobre nosso volume:

rbd ls -p kube
rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653  # тут, конечно же, будет другой ID тома, который выдала предыдущая команда

Agora vamos ver como funciona o redimensionamento de um volume RBD.
Altere o tamanho do volume no manifesto pvc.yaml para 2Gi e aplique-o:

kubectl apply -f pvc.yaml

Vamos esperar que as alterações tenham efeito e observar novamente o tamanho do volume.

rbd -p kube info csi-vol-eb3d257d-8c6c-11ea-bff5-6235e7640653

kubectl get pv
kubectl get pvc

Vemos que o tamanho do PVC não mudou. Para descobrir o porquê, você pode consultar o Kubernetes para obter uma descrição YAML do PVC:

kubectl get pvc rbd-pvc -o yaml

Aqui está o problema:

mensagem: Aguardando que o usuário (re) inicie um pod para concluir o redimensionamento do volume do sistema de arquivos no nó. tipo: FileSystemResizePending

Ou seja, o disco cresceu, mas o sistema de arquivos nele não.
Para aumentar o sistema de arquivos, você precisa montar o volume. Em nosso país, o PVC/PV criado atualmente não é utilizado de forma alguma.

Podemos criar um pod de teste, por exemplo assim:

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

E agora vamos dar uma olhada no PVC:

kubectl get pvc

O tamanho mudou, está tudo bem.

Na primeira parte trabalhamos com o dispositivo de bloco RBD (significa Rados Block Device), mas isso não pode ser feito se diferentes microsserviços precisarem trabalhar com este disco simultaneamente. CephFS é muito mais adequado para trabalhar com arquivos do que com imagens de disco.
Usando o exemplo dos clusters Ceph e Kubernetes, configuraremos o CSI e outras entidades necessárias para trabalhar com o CephFS.

Vamos obter os valores do novo gráfico Helm que precisamos:

helm inspect values ceph-csi/ceph-csi-cephfs > cephfs.yml

Novamente você precisa preencher o arquivo cephfs.yml. Como antes, os comandos do Ceph ajudarão:

ceph fsid
ceph mon dump

Preencha o arquivo com valores como este:

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

Observe que os endereços dos monitores são especificados no formato simples endereço:porta. Para montar cephfs em um nó, esses endereços são transferidos para o módulo do kernel, que ainda não sabe trabalhar com o protocolo monitor v2.
Alteramos a porta para httpMetrics (o Prometheus irá lá para monitorar métricas) para que não entre em conflito com o nginx-proxy, que é instalado pelo Kubespray. Você pode não precisar disso.

Instale o gráfico Helm no cluster Kubernetes:

helm upgrade -i ceph-csi-cephfs ceph-csi/ceph-csi-cephfs -f cephfs.yml -n ceph-csi-cephfs --create-namespace

Vamos ao armazenamento de dados do Ceph para criar um usuário separado. A documentação afirma que o provisionador CephFS requer direitos de acesso de administrador de cluster. Mas criaremos um usuário separado fs com direitos limitados:

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'

E vamos dar uma olhada imediata em sua chave de acesso, precisaremos dela mais tarde:

ceph auth get-key client.fs

Vamos criar Secret e StorageClass separados.
Nada de novo, já vimos isso no exemplo do RBD:

---
apiVersion: v1
kind: Secret
metadata:
  name: csi-cephfs-secret
  namespace: ceph-csi-cephfs
stringData:
  # Необходимо для динамически создаваемых томов
  adminID: fs
  adminKey: <вывод предыдущей команды>

Aplicando o manifesto:

kubectl apply -f secret.yaml

E agora - um StorageClass separado:

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-cephfs-sc
provisioner: cephfs.csi.ceph.com
parameters:
  clusterID: <cluster-id>

  # Имя файловой системы CephFS, в которой будет создан том
  fsName: cephfs

  # (необязательно) Пул Ceph, в котором будут храниться данные тома
  # pool: cephfs_data

  # (необязательно) Разделенные запятыми опции монтирования для Ceph-fuse
  # например:
  # fuseMountOptions: debug

  # (необязательно) Разделенные запятыми опции монтирования CephFS для ядра
  # См. man mount.ceph чтобы узнать список этих опций. Например:
  # kernelMountOptions: readdir_max_bytes=1048576,norbytes

  # Секреты должны содержать доступы для админа и/или юзера 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

  # (необязательно) Драйвер может использовать либо ceph-fuse (fuse), 
  # либо ceph kernelclient (kernel).
  # Если не указано, будет использоваться монтирование томов по умолчанию,
  # это определяется поиском ceph-fuse и mount.ceph
  # mounter: kernel
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
  - debug

Vamos preencher aqui ID do cluster e aplicável no Kubernetes:

kubectl apply -f storageclass.yaml

Проверка

Para verificar, como no exemplo anterior, vamos criar um PVC:

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-cephfs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: csi-cephfs-sc

E verifique a presença de PVC/PV:

kubectl get pvc
kubectl get pv

Se quiser ver arquivos e diretórios no CephFS, você pode montar esse sistema de arquivos em algum lugar. Por exemplo, conforme mostrado abaixo.

Vamos para um dos nós do cluster Ceph e realizar as seguintes ações:

# Точка монтирования
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

Obviamente, montar o FS em um nó Ceph como esse só é adequado para fins de treinamento, que é o que fazemos em nosso Cursos de Slurm. Não acho que alguém faria isso na produção; há um alto risco de apagar acidentalmente arquivos importantes.

E por fim, vamos verificar como funciona o redimensionamento de volumes no caso do CephFS. Vamos voltar ao Kubernetes e editar nosso manifesto para PVC - aumente o tamanho lá, por exemplo, para 7Gi.

Vamos aplicar o arquivo editado:

kubectl apply -f pvc.yaml

Vejamos o diretório montado para ver como a cota mudou:

getfattr -n ceph.quota.max_bytes <каталог-с-данными>

Para que este comando funcione, pode ser necessário instalar o pacote em seu sistema atr.

Os olhos estão com medo e as mãos estão fazendo

Todos esses feitiços e longos manifestos YAML parecem complicados superficialmente, mas na prática, os alunos do Slurm pegam o jeito rapidamente.
Neste artigo não nos aprofundamos na selva - existe documentação oficial para isso. Se você estiver interessado nos detalhes de configuração do armazenamento Ceph com um cluster Kubernetes, estes links o ajudarão:

Princípios gerais do Kubernetes trabalhando com volumes
Documentação RBD
Integrando RBD e Kubernetes da perspectiva do Ceph
Integrando RBD e Kubernetes de uma perspectiva CSI
Documentação geral do CephFS
Integrando CephFS e Kubernetes de uma perspectiva CSI

No curso Slurm Base do Kubernetes você pode ir um pouco mais longe e implantar um aplicativo real no Kubernetes que usará o CephFS como armazenamento de arquivos. Por meio de solicitações GET/POST, você poderá transferir arquivos e recebê-los do Ceph.

E se você estiver mais interessado em armazenamento de dados, inscreva-se no novo curso sobre Ceph. Enquanto o teste beta estiver em andamento, o curso pode ser obtido com desconto e você pode influenciar seu conteúdo.

Autor do artigo: Alexander Shvalov, engenheiro praticante Southbridge, Administrador certificado do Kubernetes, autor e desenvolvedor de cursos Slurm.

Fonte: habr.com