A la torre o no a la torre: esa es la cuestión

A la torre o no a la torre: esa es la cuestión

A principios de este mes, el 3 de mayo, se anunció un lanzamiento importante de un "sistema de gestión para el almacenamiento de datos distribuidos en Kubernetes": torre 1.0.0. Hace más de un año ya publicado descripción general de Torre. Luego nos pidieron que habláramos sobre su experiencia. uso en la practica — y ahora, justo a tiempo para un hito tan importante en la historia del proyecto, estamos felices de compartir nuestras impresiones acumuladas.

En resumen, Torre es un conjunto operadores para Kubernetes, que toma el control total de la implementación, administración y recuperación automática de soluciones de almacenamiento de datos como Ceph, EdgeFS, Minio, Cassandra, CockroachDB.

En estos momentos los más desarrollados (y el unico в estable etapa) la solución es operador-ceph-torre.

Nota: Entre los cambios significativos en la versión Rook 1.0.0 relacionados con Ceph, podemos destacar la compatibilidad con Ceph Nautilus y la capacidad de usar NFS para depósitos CephFS o RGW. Lo que se destaca entre otros es la maduración del soporte de EdgeFS al nivel beta.

Entonces, en este artículo nosotros:

  • Respondamos la pregunta sobre qué ventajas vemos en el uso de Rook para implementar Ceph en un clúster de Kubernetes;
  • Compartiremos nuestra experiencia e impresiones sobre el uso de Rook en producción;
  • Te contamos por qué le decimos "¡Sí!" a Rook y cuáles son nuestros planes para él.

Comencemos con conceptos y teoría generales.

"¡Tengo una ventaja de una torre!" (ajedrecista desconocido)

A la torre o no a la torre: esa es la cuestión

Una de las principales ventajas de Rook es que la interacción con los almacenes de datos se realiza mediante mecanismos de Kubernetes. Esto significa que ya no necesita copiar los comandos para configurar Ceph desde la hoja a la consola.

— ¿Quiere implementar CephFS en un clúster? ¡Simplemente escriba un archivo YAML!
- ¿Qué? ¿También desea implementar un almacén de objetos con la API de S3? ¡Simplemente escriba un segundo archivo YAML!

La torre se crea de acuerdo con todas las reglas de un operador típico. La interacción con él ocurre usando CRD (Definiciones de recursos personalizados), en el que describimos las características de las entidades Ceph que necesitamos (dado que esta es la única implementación estable, de forma predeterminada este artículo hablará sobre Ceph, a menos que se indique explícitamente lo contrario). Según los parámetros especificados, el operador ejecutará automáticamente los comandos necesarios para la configuración.

Veamos los detalles usando el ejemplo de creación de un Almacén de objetos, o mejor dicho: 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 }}

Los parámetros indicados en el listado son bastante estándar y apenas necesitan comentarios, pero merece la pena prestar especial atención a los asignados a las variables de plantilla.

El esquema general de trabajo se reduce al hecho de que "ordenamos" recursos a través de un archivo YAML, para lo cual el operador ejecuta los comandos necesarios y nos devuelve un secreto "no tan real" con el que podemos seguir trabajando. (vea abajo). Y a partir de las variables enumeradas anteriormente, se compilará el comando y el nombre secreto.

¿Qué clase de equipo es este? Al crear un usuario para el almacenamiento de objetos, el operador de Rook dentro del pod hará lo siguiente:

radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}"

El resultado de ejecutar este comando será una estructura JSON:

{
    "user_id": "rook-user",
    "display_name": "{{ .Values.s3.username }}",
    "keys": [
        {
           "user": "rook-user",
           "access_key": "NRWGT19TWMYOB1YDBV1Y",
           "secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
        }
    ],
    ...
}

Keys - qué aplicaciones futuras necesitarán para acceder al almacenamiento de objetos a través de la API de S3. El operador de Rook amablemente los selecciona y los coloca en su espacio de nombres en forma de secreto con el nombre rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.

Para utilizar los datos de este secreto, simplemente agréguelos al contenedor como variables de entorno. Como ejemplo, daré una plantilla para Trabajo, en la que creamos automáticamente depósitos para cada entorno de usuario:

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

Todas las acciones enumeradas en este trabajo se realizaron dentro del marco de Kubernetes. Las estructuras descritas en los archivos YAML se almacenan en un repositorio Git y se reutilizan muchas veces. Consideramos que esto es una gran ventaja para los ingenieros de DevOps y el proceso de CI/CD en su conjunto.

Feliz con Rook y Rados

El uso de la combinación Ceph + RBD impone ciertas restricciones a la hora de montar volúmenes en pods.

En particular, el espacio de nombres debe contener un secreto para acceder a Ceph para que funcionen las aplicaciones con estado. Está bien si tienes 2 o 3 entornos en sus espacios de nombres: puedes copiar el secreto manualmente. Pero, ¿qué pasa si para cada función se crea un entorno independiente con su propio espacio de nombres para los desarrolladores?

Resolvimos este problema nosotros mismos usando operador de shell, que copia automáticamente secretos a nuevos espacios de nombres (un ejemplo de este tipo de gancho se describe en este artículo).

#! /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 -
fi

Sin embargo, cuando se utiliza Rook, este problema simplemente no existe. El proceso de montaje se produce mediante controladores propios basados ​​en volumen flexible o CSI (aún en etapa beta) y por lo tanto no requiere secretos.

Rook resuelve automáticamente muchos problemas, lo que nos anima a utilizarlo en nuevos proyectos.

Asedio de la torre

Completemos la parte práctica implementando Rook y Ceph para que podamos realizar nuestros propios experimentos. Para facilitar el asalto a esta torre inexpugnable, los desarrolladores han preparado un paquete Helm. Vamos a descargarlo:

$ helm fetch rook-master/rook-ceph --untar --version 1.0.0

En archivo rook-ceph/values.yaml Puedes encontrar muchas configuraciones diferentes. Lo más importante es especificar tolerancias para los agentes y la búsqueda. Describimos en detalle para qué se puede utilizar el mecanismo de contaminación/tolerancia en este artículo.

En resumen, no queremos que los pods de aplicaciones cliente estén ubicados en los mismos nodos que los discos de almacenamiento de datos. La razón es simple: de esta forma el trabajo de los agentes de Rook no afectará a la aplicación en sí.

Entonces, abre el archivo. rook-ceph/values.yaml con tu editor favorito y agrega el siguiente bloque al final:

discover:
  toleration: NoExecute
  tolerationKey: node-role/storage
agent:
  toleration: NoExecute
  tolerationKey: node-role/storage
  mountSecurityMode: Any

Para cada nodo reservado para almacenamiento de datos, agregue la contaminación correspondiente:

$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecute

Luego instale el gráfico Helm con el comando:

$ helm install --namespace ${ROOK_NAMESPACE} ./rook-ceph

Ahora necesita crear un clúster y especificar la ubicación. OSD:

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"

Comprobando el estado de Ceph: espere verlo 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 -s

Al mismo tiempo, comprobemos que los pods con la aplicación cliente no acaben en nodos reservados para Ceph:

$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName

Además, se pueden configurar componentes adicionales según se desee. Más detalles sobre ellos se indican en documentación. Para la administración, recomendamos encarecidamente instalar el panel y la caja de herramientas.

Torre y ganchos: ¿la torre es suficiente para todo?

Como puedes ver, el desarrollo de Rook está en pleno apogeo. Pero todavía existen problemas que no nos permiten abandonar por completo la configuración manual de Ceph:

  • Sin conductor de torre no puede exportar métricas sobre el uso de bloques montados, lo que nos priva de seguimiento.
  • Volumen flexible y CSI no se como cambia el tamaño de los volúmenes (a diferencia del mismo RBD), por lo que Rook se ve privado de una herramienta útil (¡y a veces muy necesaria!).
  • Rook todavía no es tan flexible como Ceph normal. Si queremos configurar el grupo para que los metadatos de CephFS se almacenen en SSD y los datos en sí se almacenen en HDD, necesitaremos registrar grupos separados de dispositivos en mapas CRUSH manualmente.
  • A pesar de que rook-ceph-operator se considera estable, actualmente existen algunos problemas al actualizar Ceph de la versión 13 a 14.

Hallazgos

"En este momento, Rook está aislada del mundo exterior por peones, ¡pero creemos que algún día jugará un papel decisivo en el juego!" (cita inventada específicamente para este artículo)

Sin duda, el proyecto Rook se ha ganado nuestro corazón; creemos que [con todos sus pros y sus contras] definitivamente merece su atención.

Nuestros planes futuros se reducen a hacer de rook-ceph un módulo para operador-adicional, lo que hará que su uso en nuestros numerosos clústeres de Kubernetes sea aún más sencillo y conveniente.

PS

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario