Selección de CEPH. Parte 1
Tenía cinco racks, diez switches ópticos, BGP configurado, un par de decenas de SSD y una multitud de discos SAS de todos los colores y tamaños, además de proxmox y el deseo de almacenar toda la estática en un almacenamiento S3 propio. No era que todo esto fuera necesario para la virtualización, pero dado que empecé a usar opensource, debía seguir con mi pasión hasta el final. Lo único que me preocupaba era BGP. En el mundo no hay nada más impotente, irresponsable e inmoral que el enrutamiento interno a través de BGP. Y sabía que pronto nos sumergiríamos en ello.

La tarea era bastante sencilla: teníamos CEPH que no funcionaba muy bien. Necesitábamos hacerlo funcionar "bien".
El clúster que me tocó era heterogéneo, configurado apresuradamente y prácticamente sin ajustar. Estaba compuesto por dos grupos de nodos diferentes, con una red común que servía tanto de red de cluster como de red pública. Los nodos estaban equipados con cuatro tipos de discos: dos tipos de SSD, organizados en dos reglas de colocación separadas, y dos tipos de HDD de diferentes tamaños, organizados en un tercer grupo. El problema con los diferentes tamaños se resolvió con diferentes pesos OSD.
La configuración se dividió en dos partes: ajuste del sistema operativo y ajuste del propio CEPH y sus configuraciones.
Optimización del SO
Red
La alta latencia afectaba tanto a la escritura como al equilibrio. En la escritura, porque el cliente no recibe una respuesta de éxito hasta que las réplicas de datos en otros grupos de colocación confirman el éxito. Dado que las reglas de distribución de réplicas en el mapa CRUSH eran de una réplica por host, la red se utilizaba siempre.
Por eso, primero decidí ajustar ligeramente la red actual, mientras intentaba convencer para mudarnos a redes separadas.
Para empezar, jugué con la configuración de las tarjetas de red. Comencé ajustando las colas:
lo que había:
ethtool -l ens1f1
root@ceph01:~# ethtool -l ens1f1
Parámetros del canal para ens1f1:
Máximos preestablecidos:
RX: 0
TX: 0
Otro: 1
Combinado: 63
Configuraciones de hardware actuales:
RX: 0
TX: 0
Otro: 1
Combinado: 1
root@ceph01:~# ethtool -g ens1f1
Parámetros del anillo para ens1f1:
Máximos preestablecidos:
RX: 4096
RX Mini: 0
RX Jumbo: 0
TX: 4096
Configuraciones de hardware actuales:
RX: 256
RX Mini: 0
RX Jumbo: 0
TX: 256
root@ceph01:~# ethtool -l ens1f1
Parámetros del canal para ens1f1:
Máximos preestablecidos:
RX: 0
TX: 0
Otro: 1
Combinado: 63
Configuraciones de hardware actuales:
RX: 0
TX: 0
Otro: 1
Combinado: 1Es evidente que los parámetros actuales están lejos de los máximos. Aumenté:
root@ceph01:~#ethtool -G ens1f0 rx 4096
root@ceph01:~#ethtool -G ens1f0 tx 4096
root@ceph01:~#ethtool -L ens1f0 combined 63Siguiendo un excelente artículo
aumenté la longitud de la cola de envío txqueuelen de 1000 a 10 000
root@ceph01:~#ip link set ens1f0 txqueuelen 10000Siguiendo la documentación de ceph
aumenté MTU a 9000.
root@ceph01:~#ip link set dev ens1f0 mtu 9000Lo añadí en /etc/network/interfaces para que todo lo mencionado anteriormente se cargue al iniciar
cat /etc/network/interfaces
root@ceph01:~# cat /etc/network/interfaces
auto lo
iface lo inet loopback
auto ens1f0
iface ens1f0 inet manual
post-up /sbin/ethtool -G ens1f0 rx 4096
post-up /sbin/ethtool -G ens1f0 tx 4096
post-up /sbin/ethtool -L ens1f0 combined 63
post-up /sbin/ip link set ens1f0 txqueuelen 10000
mtu 9000
auto ens1f1
iface ens1f1 inet manual
post-up /sbin/ethtool -G ens1f1 rx 4096
post-up /sbin/ethtool -G ens1f1 tx 4096
post-up /sbin/ethtool -L ens1f1 combined 63
post-up /sbin/ip link set ens1f1 txqueuelen 10000
mtu 9000Después de lo cual, siguiendo este mismo artículo, comencé a ajustar cuidadosamente los parámetros del núcleo 4.15. Teniendo en cuenta que los nodos tienen 128G de RAM, se obtuvo un archivo de configuración para sysctl
cat /etc/sysctl.d/50-ceph.conf
net.core.rmem_max = 56623104
# Tamaño máximo del búfer de recepción de datos para todas las conexiones 54M
net.core.wmem_max = 56623104
# Tamaño máximo del búfer de envío de datos para todas las conexiones 54M
net.core.rmem_default = 56623104
# Tamaño del búfer de recepción de datos por defecto para todas las conexiones. 54M
net.core.wmem_default = 56623104
# Tamaño del búfer de envío de datos por defecto para todas las conexiones 54M
# para cada socket
net.ipv4.tcp_rmem = 4096 87380 56623104
# Variable vectorial (mínimo, por defecto, máximo) en el archivo tcp_rmem
# contiene 3 números enteros que definen el tamaño del búfer de recepción de los sockets TCP.
# Mínimo: cada socket TCP tiene derecho a usar esta memoria desde
# su creación. La posibilidad de usar tal búfer
# está garantizada incluso al alcanzar el umbral de limitación (presión de memoria moderada).
# El tamaño mínimo del búfer por defecto es de 8 Kbytes (8192).
# Valor por defecto: cantidad de memoria permitida para el búfer
# de envío del socket TCP por defecto. Este valor se aplica en lugar
# del parámetro /proc/sys/net/core/rmem_default, utilizado por otros protocolos.
# El valor del búfer utilizado por defecto generalmente (por defecto)
# es de 87830 bytes. Esto define el tamaño de ventana 65535 con
# el valor por defecto de tcp_adv_win_scale y tcp_app_win = 0,
# un poco menor que el valor por defecto de tcp_app_win.
# Máximo: tamaño máximo del búfer que puede ser automáticamente
# asignado para recibir en el socket TCP. Este valor no anula el máximo,
# establecido en el archivo /proc/sys/net/core/rmem_max. En la asignación "estática"
# de memoria mediante SO_RCVBUF, este parámetro no tiene valor.
net.ipv4.tcp_wmem = 4096 65536 56623104
net.core.somaxconn = 5000
# Número máximo de sockets abiertos esperando conexiones.
net.ipv4.tcp_timestamps=1
# Permite el uso de marcas de tiempo (timestamps), según RFC 1323.
net.ipv4.tcp_sack=1
# Permitir reconocimientos selectivos del protocolo TCP
net.core.netdev_max_backlog=5000 (por defecto 1000)
# número máximo de paquetes en la cola de procesamiento, si
# la interfaz recibe paquetes más rápido de lo que el núcleo puede procesarlos.
net.ipv4.tcp_max_tw_buckets=262144
# Número máximo de sockets en estado TIME-WAIT al mismo tiempo.
# Al exceder este umbral, el socket "excedente" se destruye y se escribe
# un mensaje en el registro del sistema.
net.ipv4.tcp_tw_reuse=1
# Permitimos la reutilización de sockets TIME-WAIT en casos,
# si el protocolo lo considera seguro.
net.core.optmem_max=4194304
# Aumentar el tamaño máximo del búfer de memoria ALLOCATABLE
# medido en unidades de páginas (4096 bytes)
net.ipv4.tcp_low_latency=1
# Permite que la pila TCP/IP dé prioridad a un menor tiempo de espera
# sobre mayor ancho de banda.
net.ipv4.tcp_adv_win_scale=1
# Esta variable influye en el cálculo del volumen de memoria en el búfer del socket,
# asignado para el tamaño de la ventana TCP y para el búfer de la aplicación.
# Si el valor de tcp_adv_win_scale es negativo, se utiliza la siguiente expresión para calcular el tamaño:
# Bytes - bytes2 elevado a la potencia -tcp_adv_win_scale
# Donde bytes – es el tamaño de la ventana en bytes. Si el valor de tcp_adv_win_scale
# es positivo, para determinar el tamaño se utiliza la siguiente expresión:
# Bytes - bytes2 elevado a la potencia tcp_adv_win_scale
# La variable toma un valor entero. El valor por defecto es 2,
# es decir, se asigna ¼ del volumen definido por la variable
# tcp_rmem para el búfer de la aplicación.
net.ipv4.tcp_slow_start_after_idle=0
# mecánico de reinicio de inicio lento que restablece el valor de la ventana
# de congestión si la conexión no ha sido utilizada durante un período de tiempo determinado.
# Mejor desactivar SSR en el servidor para mejorar el rendimiento de
# conexiones de larga duración.
net.ipv4.tcp_no_metrics_save=1
# No guardar resultados de las mediciones de la conexión TCP en la caché al cerrarla.
net.ipv4.tcp_syncookies=0
# Deshabilitar el mecanismo de envío de syncookie
net.ipv4.tcp_ecn=0
# Explicit Congestion Notification (Notificación Explícita de Congestión) en
# conexiones TCP. Se utiliza para notificar la aparición de un "embotellamiento"
# en la ruta hacia el host o red designada. Puede utilizarse para notificar al
# host emisor sobre la necesidad de reducir la velocidad de transmisión de paquetes a través de
# un enrutador o cortafuegos específico.
net.ipv4.conf.all.send_redirects=0
# desactiva la emisión de ICMP Redirect … a otros hosts. Esta opción debe
# estar habilitada si el host actúa como enrutador de cualquier tipo.
# No tenemos enrutamiento.
net.ipv4.ip_forward=0
# Desactivación del reenvío. No somos un gateway, no se levantan contenedores en las máquinas,
# no lo necesitamos.
net.ipv4.icmp_echo_ignore_broadcasts=1
# No respondemos a las solicitudes ICMP ECHO enviadas en paquetes de broadcast
net.ipv4.tcp_fin_timeout=10
# define el tiempo que mantiene el socket en estado FIN-WAIT-2 después de su
# cierre por la parte local. Por defecto 60
net.core.netdev_budget=600 # (por defecto 300)
# Si la ejecución de interrupciones de software no se realiza durante un tiempo suficiente,
# entonces la tasa de aumento de datos entrantes puede exceder la capacidad del núcleo
# para vaciar el búfer. Como resultado, los búferes NIC se desbordarán y el tráfico se perderá.
# A veces, es necesario aumentar la duración de funcionamiento de los SoftIRQs
# (interrupciones de software) con el CPU. Esto es manejado por netdev_budget.
# El valor por defecto es 300. El parámetro obligará al proceso SoftIRQ a procesar
# 300 paquetes de NIC antes de liberar el CPU
net.ipv4.tcp_fastopen=3
# TFO TCP Fast Open
# si tanto el cliente como el servidor tienen soporte para TFO, que se indica
# a través de un flag especial en el paquete TCP. En nuestro caso es un placebo, simplemente
# se ve bien)Conred de lustre se asignó a interfaces de red separadas de 10Gbps en una red plana separada. Cada máquina tenía tarjetas de red de dos puertos instaladas mellanox 10/25 Gbps, conectadas a dos switches de 10Gbps separados. La agregación se llevó a cabo mediante OSPF, ya que la vinculación con lacp mostró una capacidad total máxima de 16 Gbps, mientras que ospf utilizó con éxito completamente ambos diez en cada máquina. En futuros planes se planeaba utilizar ROCE en estos mellanox para reducir la latencia. Cómo se configuró esta parte de la red:
- Dado que las máquinas tienen direcciones IP externas en BGP, necesitamos el software — (específicamente en el momento de escribir este artículo era ) ya estaba instalado.
- En total había dos interfaces de red en las máquinas, es decir, 4 puertos en total. Una tarjeta de red con dos puertos se conectaba a la fábrica y en ella se configuró BGP, la otra — con dos puertos se conectaba a dos switches diferentes y a ella se le asignó OSPF
Más detalles sobre la configuración de OSPF: La tarea principal es agregar dos enlaces y tener tolerancia a fallos.
dos interfaces de red configuradas en dos redes planas simples — 10.10.10.0/24 y 10.10.20.0/24
1: ens1f0: mtu 9000 qdisc mq state UP group default qlen 1000
inet 10.10.10.2/24 brd 10.10.10.255 scope global ens1f0
2: ens1f1: mtu 9000 qdisc mq state UP group default qlen 1000
inet 10.10.20.2/24 brd 10.10.20.255 scope global ens1f1por los cuales las máquinas se ven entre sí.
DISCO
El siguiente paso fue optimizar el funcionamiento de los discos. Para SSD cambié el planificador a noop, para HDD — deadline. En términos simples, NOOP funciona según el principio de «el primero en llegar, primero en ser servido», que en inglés se expresa como «FIFO (First In, First Out)». Las solicitudes se colocan en cola a medida que llegan. DEADLINE está más orientado a la lectura, y además el proceso de la cola obtiene acceso prácticamente monopolístico al disco en el momento de la operación. Para nuestro sistema, esto es ideal, ya que solo un proceso trabaja con cada disco — el daemón OSD.
(Los interesados en profundizar en el planificador de entrada/salida pueden leer sobre él aquí:
Los que prefieren leer en ruso: )
En las recomendaciones para optimizar Linux también se sugiere aumentar nr_request
nr_requests
El valor de nr_requests determina la cantidad de solicitudes de E/S que se almacenan en búfer antes de que el programador de E/S envíe/reciba datos al dispositivo de bloque. Si está utilizando una tarjeta RAID/dispositivo de bloque que puede manejar una cola más grande de lo que el programador de E/S está configurado, aumentar el valor de nr_requests puede ayudar a mejorar el rendimiento y reducir la carga del servidor cuando ocurren grandes cantidades de E/S en el servidor. Si está utilizando Deadline o CFQ como el programador, se sugiere que establezca el valor de nr_requests en 2 veces el valor de la profundidad de la cola.
¡NO! Los propios ciudadanos desarrolladores de CEPH nos convencen de que su sistema de prioridades funciona mejor.

WBThrottle y/o nr_requests
WBThrottle y/o nr_requests
El almacenamiento de archivos utiliza operaciones de entrada/salida en búfer para la escritura; esto aporta una serie de ventajas si el registro del almacenamiento de archivos se encuentra en un medio más rápido. Las solicitudes de los clientes reciben una notificación tan pronto como los datos se escriben en el registro, y luego se envían al disco de datos en un momento posterior utilizando la funcionalidad estándar de Linux. Esto permite que los discos OSD de discos giratorios ofrezcan una latencia de escritura similar a la de los SSD al realizar escrituras en paquetes pequeños. Esta escritura diferida también permite que el núcleo reorganice las solicitudes de operaciones de entrada/salida al disco con la esperanza de fusionarlas o permitir que las cabezas del disco elijan un camino más óptimo sobre sus platos. El efecto final es que se puede extraer un poco más de operaciones de entrada/salida de cada disco de lo que sería posible mediante operaciones directas o sincrónicas de entrada/salida.
Sin embargo, surge un problema si la cantidad de registros entrantes en ese clúster Ceph supera todas las capacidades de los discos subyacentes. En tal escenario, el número total de operaciones de entrada/salida pendientes para la escritura en el disco puede crecer de manera incontrolada, resultando en colas de operaciones de entrada/salida que llenan todo el disco y las colas de Ceph. Las solicitudes de lectura se ven especialmente afectadas, ya que quedan atrapadas entre las solicitudes de escritura, que pueden requerir varios segundos para ser enviadas al disco principal.
Para abordar este problema, Ceph cuenta con un mecanismo de limitación de escritura diferida (writeback) llamado WBThrottle, que está integrado en el almacenamiento de archivos. Está diseñado para restringir el volumen total de operaciones de entrada/salida de escritura diferida que pueden acumularse en la cola y comenzar su proceso de descarga antes de que ocurra de forma natural debido a la intervención del núcleo. Desafortunadamente, las pruebas demuestran que los valores predeterminados aún pueden no reducir el comportamiento existente a un nivel que minimice su impacto en la latencia de las operaciones de lectura. Ajustar esta configuración puede cambiar este comportamiento y disminuir las longitudes generales de las colas de escritura, haciendo posible reducir dicho impacto. Sin embargo, existe un compromiso: al disminuir el número máximo de escrituras permitidas en la cola, puede reducir la capacidad del núcleo para maximizar su eficiencia en la ordenación de las solicitudes entrantes. Vale la pena considerar qué es más necesario para su caso de uso específico y la carga de trabajo y ajustar la configuración en consecuencia.
Para gestionar la profundidad de esta cola de escritura diferida, puede reducir el número total máximo de operaciones de entrada/salida pendientes aplicando las configuraciones de WBThrottle o disminuyendo el valor máximo para las operaciones pendientes a nivel de bloque en su núcleo. Ambas opciones pueden gestionar de manera efectiva el mismo comportamiento, y sus preferencias serán la base para implementar esta configuración.
También es importante señalar que el sistema de prioridades de operaciones en Ceph es más eficiente para solicitudes más cortas a nivel de disco. Al reducir la cola general hacia este disco, la posición principal en la cola se traslada a Ceph, donde tiene un mayor control sobre la prioridad de la operación de entrada/salida. Consideremos el siguiente ejemplo:
echo 8 > /sys/block/sda/queue/nr_requestsCOMÚN
Y algunas configuraciones adicionales del núcleo que permiten que su máquina funcione suavemente y extraiga un poco más de rendimiento del hardware
cat /etc/sysctl.d/60-ceph2.conf
kernel.pid_max = 4194303
# Discos en cada máquina son 25, así que calculamos que habría muchos procesos
kernel.threads-max=2097152
# Hilos, por supuesto, también.
vm.max_map_count=524288
# Aumentamos el número de áreas de mapeo de memoria del proceso.
# Como se indica en la documentación sobre variables del kernel
# Las áreas de mapeo de memoria se utilizan como un efecto secundario de la llamada
# malloc, directamente a través de mmap, mprotect y madvise, así como al cargar
# bibliotecas compartidas.
fs.aio-max-nr=50000000
# Ajustamos los parámetros de entrada-salida
# El núcleo de Linux proporciona la función de entrada-salida asíncrona no bloqueante (AIO),
# que permite a un proceso iniciar varias operaciones de entrada-salida
# simultáneamente, sin esperar a que ninguna de ellas termine.
# Esto ayuda a mejorar el rendimiento de las aplicaciones,
# que pueden solapar procesamiento y entrada-salida.
# El parámetro aio-max-nr define el máximo número de
# solicitudes concurrentes permitidas.
vm.min_free_kbytes=1048576
# Tamaño mínimo de memoria libre que se debe mantener.
# Se estableció en 1Gb, lo cual es más que suficiente para el funcionamiento del sistema operativo,
# y permite evitar el OOM Killer para los procesos OSD. Aunque la memoria es
# como si no hubiera un problema, pero un poco de reserva no viene mal.
vm.swappiness=10
# Indicamos usar swap si queda libre el 10% de la memoria.
# En máquinas con 128G de RAM, 10% son 12 Gigas. Más que suficiente para funcionar.
# El parámetro predeterminado del 60% hacía que el sistema se ralentizara, accediendo al swap,
# cuando aún había mucha memoria libre.
vm.vfs_cache_pressure=1000
# Aumentamos de 100 a este parámetro. Hacemos que el núcleo libere
# las páginas de memoria no utilizadas del caché más activamente.
vm.zone_reclaim_mode=0
# Permite establecer enfoques más o menos agresivos para
# recuperar memoria cuando la zona se queda sin memoria.
# Si se establece en cero, no se recupera la zona.
# Para servidores de archivos o cargas de trabajo
# es beneficioso si sus datos están en caché, por lo que se debe
# mantener el zone_reclaim_mode desactivado, ya que el efecto de caché,
# probablemente será más importante que la ubicación de los datos.
vm.dirty_ratio=20
# Porcentaje de memoria RAM que se puede usar para "páginas sucias"
# Calculamos esto a partir de una estimación:
# En el sistema hay 128 gigas de memoria.
# Aproximadamente 20 discos SSD, que han sido configurados
# en CEPH para utilizar 3G de RAM para almacenamiento en caché.
# Aproximadamente 40 discos HDD, para los cuales este parámetro es 1G.
# El 20% de 128 es 25.6 gigas. En total, en caso de uso máximo de memoria,
# quedarán 2.4G de memoria para el sistema. Eso debería ser suficiente para sobrevivir y esperar
# la llegada de la caballería, es decir, el arribo de DevOps que arreglará todo.
vm.dirty_background_ratio=3
# Porcentaje de memoria del sistema que se puede llenar con páginas sucias antes de que
# los procesos de fondo pdflush/flush/kdmflush las escriban en el disco
fs.file-max=524288
# Y probablemente tendremos muchos más archivos abiertos de los que se indica por defecto. Inmersión en CEPH
Configuraciones en las que nos gustaría profundizar:
cat /etc/ceph/ceph.conf
osd:
journal_aio: true # Tres parámetros que incluyen
journal_block_align: true # I/O directo
journal_dio: true # al registro
journal_max_write_bytes: 1073714824 # Expandamos un poco el tamaño máximo
# de la operación de escritura en el registro
journal_max_write_entries: 10000 # Y la cantidad de registros simultáneos
journal_queue_max_bytes: 10485760000
journal_queue_max_ops: 50000
rocksdb_separate_wal_dir: true # Decidimos hacer un WAL separado
# Incluso intentamos obtener uno para esto
# NVMe
bluestore_block_db_create: true # Y un dispositivo por separado para el registro
bluestore_block_db_size: '5368709120 #5G'
bluestore_block_wal_create: true
bluestore_block_wal_size: '1073741824 #1G'
bluestore_cache_size_hdd: '3221225472 # 3G'
# Un gran volumen de RAM permite
# almacenar volúmenes suficientemente grandes
bluestore_cache_size_ssd: '9663676416 # 9G'
keyring: /var/lib/ceph/osd/ceph-$id/keyring
osd_client_message_size_cap: '1073741824 #1G'
osd_disk_thread_ioprio_class: idle
osd_disk_thread_ioprio_priority: 7
osd_disk_threads: 2 # cantidad de hilos del daemon por disco
osd_failsafe_full_ratio: 0.95
osd_heartbeat_grace: 5
osd_heartbeat_interval: 3
osd_map_dedup: true
osd_max_backfills: 2 # número de operaciones de llenado simultáneas por OSD.
osd_max_write_size: 256
osd_mon_heartbeat_interval: 5
osd_op_threads: 16
osd_op_num_threads_per_shard: 1
osd_op_num_threads_per_shard_hdd: 2
osd_op_num_threads_per_shard_ssd: 2
osd_pool_default_min_size: 1 # Particularidades de la codicia. Se volvió muy rápido
osd_pool_default_size: 2 # insuficiente, porque se tomó la
# decisión temporal de reducir la cantidad
# de réplicas de datos
osd_recovery_delay_start: 10.000000
osd_recovery_max_active: 2
osd_recovery_max_chunk: 1048576
osd_recovery_max_single_start: 3
osd_recovery_op_priority: 1
osd_recovery_priority: 1 # parámetro que ajustamos según sea necesario en el transcurso
osd_recovery_sleep: 2
osd_scrub_chunk_max: 4Parte de los parámetros que se probaron en QA en la versión 12.2.12, faltan en la versión ceph 12.2.2, por ejemplo osd_recovery_threads. Por lo tanto, se incluyó en los planes la actualización a 12.2.12 en producción. La práctica mostró compatibilidad en un clúster de versiones 12.2.2 y 12.2.12, lo que permite realizar una actualización continua.
Clúster de prueba
Naturalmente, para las pruebas era necesario tener la misma versión que en producción, pero en el momento en que comencé a trabajar con el clúster, solo había una versión más nueva en el repositorio. Al ver que las diferencias en la versión menor no eran muy grandes (1393 líneas en las configuraciones frente a 1436 en la nueva versión), decidimos comenzar a probar la nueva (de todos modos íbamos a actualizar, ¿por qué seguir con algo antiguo?)
Lo único que intentamos mantener de la versión anterior fue el paquete ceph-deploy, ya que parte de las utilidades (y parte del personal) estaban adaptadas a su sintaxis. La nueva versión era bastante diferente, pero no afectaba el funcionamiento del clúster, así que se mantuvo la versión 1.5.39
Dado que el equipo ceph-disk claramente indica que está obsoleto y recomienda, estimados, usar el comando ceph-volume, comenzamos a crear OSD exactamente con este comando, sin perder tiempo en lo obsoleto.
El plan era crear un espejo de dos discos SSD, donde alojaríamos los registros OSD, que a su vez están en discos SAS de spindle. Así nos protegeríamos de problemas con los datos en caso de fallo del disco con el registro.
Comenzamos a crear el clúster según la documentación
cat /etc/ceph/ceph.conf
root@ceph01-qa:~# cat /etc/ceph/ceph.conf # configuraciones preparadas previamente
[client]
rbd_cache = true
rbd_cache_max_dirty = 50331648
rbd_cache_max_dirty_age = 2
rbd_cache_size = 67108864
rbd_cache_target_dirty = 33554432
rbd_cache_writethrough_until_flush = true
rbd_concurrent_management_ops = 10
rbd_default_format = 2
[global]
auth_client_required = cephx
auth_cluster_required = cephx
auth_service_required = cephx
cluster network = 10.10.10.0/24
debug_asok = 0/0
debug_auth = 0/0
debug_buffer = 0/0
debug_client = 0/0
debug_context = 0/0
debug_crush = 0/0
debug_filer = 0/0
debug_filestore = 0/0
debug_finisher = 0/0
debug_heartbeatmap = 0/0
debug_journal = 0/0
debug_journaler = 0/0
debug_lockdep = 0/0
debug_mon = 0/0
debug_monc = 0/0
debug_ms = 0/0
debug_objclass = 0/0
debug_objectcatcher = 0/0
debug_objecter = 0/0
debug_optracker = 0/0
debug_osd = 0/0
debug_paxos = 0/0
debug_perfcounter = 0/0
debug_rados = 0/0
debug_rbd = 0/0
debug_rgw = 0/0
debug_throttle = 0/0
debug_timer = 0/0
debug_tp = 0/0
fsid = d0000000d-4000-4b00-b00b-0123qwe123qwf9
mon_host = ceph01-q, ceph02-q, ceph03-q
mon_initial_members = ceph01-q, ceph02-q, ceph03-q
public network = 8.8.8.8/28 # dirección cambiada, por supuesto ))
rgw_dns_name = s3-qa.mycompany.ru # y esta dirección también
rgw_host = s3-qa.mycompany.ru # y esta también
[mon]
mon allow pool delete = true
mon_max_pg_per_osd = 300 # más de trescientas grupos de colocación
# no se decidieron en disco
# aunque el parámetro, por supuesto, depende de la cantidad de pools,
# sus tamaños y la cantidad de OSD. Tener pocos pero saludables PG
# también no es la mejor opción: afecta la precisión del balanceo
mon_osd_backfillfull_ratio = 0.9
mon_osd_down_out_interval = 5
mon_osd_full_ratio = 0.95 # por ahora, para discos SSD, el espacio para su
# registro es el mismo dispositivo que para OSD
# decidimos que el 5% del disco (que tiene un tamaño de 1.2Tb)
# debería ser suficiente y correlaciona con el parámetro
# bluestore_block_db_size más la variación en grandes
# grupos de colocación
mon_osd_nearfull_ratio = 0.9
mon_pg_warn_max_per_osd = 520
[osd]
bluestore_block_db_create = true
bluestore_block_db_size = 5368709120 #5G
bluestore_block_wal_create = true
bluestore_block_wal_size = 1073741824 #1G
bluestore_cache_size_hdd = 3221225472 # 3G
bluestore_cache_size_ssd = 9663676416 # 9G
journal_aio = true
journal_block_align = true
journal_dio = true
journal_max_write_bytes = 1073714824
journal_max_write_entries = 10000
journal_queue_max_bytes = 10485760000
journal_queue_max_ops = 50000
keyring = /var/lib/ceph/osd/ceph-$id/keyring
osd_client_message_size_cap = 1073741824 #1G
osd_disk_thread_ioprio_class = idle
osd_disk_thread_ioprio_priority = 7
osd_disk_threads = 2
osd_failsafe_full_ratio = 0.95
osd_heartbeat_grace = 5
osd_heartbeat_interval = 3
osd_map_dedup = true
osd_max_backfills = 4
osd_max_write_size = 256
osd_mon_heartbeat_interval = 5
osd_op_num_threads_per_shard = 1
osd_op_num_threads_per_shard_hdd = 2
osd_op_num_threads_per_shard_ssd = 2
osd_op_threads = 16
osd_pool_default_min_size = 1
osd_pool_default_size = 2
osd_recovery_delay_start = 10.0
osd_recovery_max_active = 1
osd_recovery_max_chunk = 1048576
osd_recovery_max_single_start = 3
osd_recovery_op_priority = 1
osd_recovery_priority = 1
osd_recovery_sleep = 2
osd_scrub_chunk_max = 4
osd_scrub_chunk_min = 2
osd_scrub_sleep = 0.1
rocksdb_separate_wal_dir = true# создаем мониторы
root@ceph01-qa:~#ceph-deploy mon create ceph01-q
# генерируем ключи для аутентификации нод в кластере
root@ceph01-qa:~#ceph-deploy gatherkeys ceph01-q
# Это если поштучно. Если у нас несколько машин доступны - те, которые описаны в конфиге в секции
# mon_initial_members = ceph01-q, ceph02-q, ceph03-q
# можно запустить эти две команды в виде одной
root@ceph01-qa:~#ceph-deploy mon create-initial
# Положим ключи в указанные в конфиге места
root@ceph01-qa:~#cat ceph.bootstrap-osd.keyring > /var/lib/ceph/bootstrap-osd/ceph.keyring
root@ceph01-qa:~#cat ceph.bootstrap-mgr.keyring > /var/lib/ceph/bootstrap-mgr/ceph.keyring
root@ceph01-qa:~#cat ceph.bootstrap-rgw.keyring > /var/lib/ceph/bootstrap-rgw/ceph.keyring
# создадим ключ для управления кластером
root@ceph01-qa:~#ceph-deploy admin ceph01-q
# и менеджер, плагинами управлять
root@ceph01-qa:~#ceph-deploy mgr create ceph01-qLo primero con lo que me encontré al trabajar en esta versión de ceph-deploy con el clúster versión 12.2.12 fue un error al intentar crear OSD con db en RAID por software.
root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sde --block.db /dev/md0
blkid no pudo detectar un PARTUUID para el dispositivo: /dev/md1De hecho, blkid no muestra PARTUUID, tuve que crear las particiones manualmente:
root@ceph01-qa:~#parted /dev/md0 mklabel GPT
# habrá muchas particiones,
# sin GPT no se pueden crear
# el tamaño de la partición lo hemos indicado en la configuración anterior = bluestore_block_db_size: '5368709120 #5G'
# Tengo 20 discos para OSD, no quiero crear particiones manualmente
# así que hice un ciclo
root@ceph01-qa:~#for i in {1..20}; do echo -e "nnnn+5Gnw" | fdisk /dev/md0; doneParece que todo está listo, intentemos crear el OSD nuevamente y obtenemos el siguiente error (que, por cierto, no se reproducía en producción).
al crear OSD tipo bluestore sin indicar la ruta a WAL, pero con la db señalada.
root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sde --block.db /dev/md0
stderr: 2019-04-12 10:39:27.211242 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) _read_fsid uuid no parseable
stderr: 2019-04-12 10:39:27.213185 7eff461b6e00 -1 bdev(0x55824c273680 /var/lib/ceph/osd/ceph-0//block.wal) open abierto obtuvo: (22) Argumento no válido
stderr: 2019-04-12 10:39:27.213201 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) _open_db add block device(/var/lib/ceph/osd/ceph-0//block.wal) returned: (22) Argumento no válido
stderr: 2019-04-12 10:39:27.999039 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) mkfs falló, (22) Argumento no válido
stderr: 2019-04-12 10:39:27.999057 7eff461b6e00 -1 OSD::mkfs: ObjectStore::mkfs falló con error (22) Argumento no válido
stderr: 2019-04-12 10:39:27.999141 7eff461b6e00 -1 ** ERROR: error al crear almacenamiento de objetos vacío en /var/lib/ceph/osd/ceph-0/: (22) Argumento no válidoSin embargo, si en el mismo espejo (o en otro lugar, a elección) creo otra partición para WAL y la indico al crear el OSD, todo funciona sin problemas (excepto por la aparición del WAL separado, que probablemente no quisieras).
Pero, dado que también estaba en los planes a largo plazo mover el WAL a NVMe, la práctica no resultó innecesaria.
root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sdf --block.wal /dev/md0p2 --block.db /dev/md1p2Creamos monitores, gerentes y OSD. Ahora queremos agruparlos de diferentes maneras, ya que planeamos tener discos de diferentes tipos: grupos rápidos en SSD y grandes, pero lentos en discos SAS.
Supongamos que en los servidores hay 20 discos, la primera decena es de un tipo, la segunda de otro.
El mapa inicial, por defecto, se ve así:
ceph osd tree
root@сeph01-q:~# ceph osd tree
ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF
-1 14.54799 root default
-3 9.09200 host ceph01-q
0 ssd 1.00000 osd.0 up 1.00000 1.00000
1 ssd 1.00000 osd.1 up 1.00000 1.00000
2 ssd 1.00000 osd.2 up 1.00000 1.00000
3 ssd 1.00000 osd.3 up 1.00000 1.00000
4 hdd 1.00000 osd.4 up 1.00000 1.00000
5 hdd 0.27299 osd.5 up 1.00000 1.00000
6 hdd 0.27299 osd.6 up 1.00000 1.00000
7 hdd 0.27299 osd.7 up 1.00000 1.00000
8 hdd 0.27299 osd.8 arriba 1.00000 1.00000
9 hdd 0.27299 osd.9 arriba 1.00000 1.00000
10 hdd 0.27299 osd.10 arriba 1.00000 1.00000
11 hdd 0.27299 osd.11 arriba 1.00000 1.00000
12 hdd 0.27299 osd.12 arriba 1.00000 1.00000
13 hdd 0.27299 osd.13 arriba 1.00000 1.00000
14 hdd 0.27299 osd.14 arriba 1.00000 1.00000
15 hdd 0.27299 osd.15 arriba 1.00000 1.00000
16 hdd 0.27299 osd.16 arriba 1.00000 1.00000
17 hdd 0.27299 osd.17 arriba 1.00000 1.00000
18 hdd 0.27299 osd.18 arriba 1.00000 1.00000
19 hdd 0.27299 osd.19 arriba 1.00000 1.00000
-5 5.45599 host ceph02-q
20 ssd 0.27299 osd.20 arriba 1.00000 1.00000
21 ssd 0.27299 osd.21 arriba 1.00000 1.00000
22 ssd 0.27299 osd.22 arriba 1.00000 1.00000
23 ssd 0.27299 osd.23 arriba 1.00000 1.00000
24 hdd 0.27299 osd.24 arriba 1.00000 1.00000
25 hdd 0.27299 osd.25 arriba 1.00000 1.00000
26 hdd 0.27299 osd.26 arriba 1.00000 1.00000
27 hdd 0.27299 osd.27 arriba 1.00000 1.00000
28 hdd 0.27299 osd.28 arriba 1.00000 1.00000
29 hdd 0.27299 osd.29 arriba 1.00000 1.00000
30 hdd 0.27299 osd.30 arriba 1.00000 1.00000
31 hdd 0.27299 osd.31 arriba 1.00000 1.00000
32 hdd 0.27299 osd.32 arriba 1.00000 1.00000
33 hdd 0.27299 osd.33 arriba 1.00000 1.00000
34 hdd 0.27299 osd.34 arriba 1.00000 1.00000
35 hdd 0.27299 osd.35 arriba 1.00000 1.00000
36 hdd 0.27299 osd.36 arriba 1.00000 1.00000
37 hdd 0.27299 osd.37 arriba 1.00000 1.00000
38 hdd 0.27299 osd.38 arriba 1.00000 1.00000
39 hdd 0.27299 osd.39 arriba 1.00000 1.00000
-7 6.08690 host ceph03-q
40 ssd 0.27299 osd.40 arriba 1.00000 1.00000
41 ssd 0.27299 osd.41 arriba 1.00000 1.00000
42 ssd 0.27299 osd.42 arriba 1.00000 1.00000
43 ssd 0.27299 osd.43 arriba 1.00000 1.00000
44 hdd 0.27299 osd.44 arriba 1.00000 1.00000
45 hdd 0.27299 osd.45 arriba 1.00000 1.00000
46 hdd 0.27299 osd.46 arriba 1.00000 1.00000
47 hdd 0.27299 osd.47 arriba 1.00000 1.00000
48 hdd 0.27299 osd.48 arriba 1.00000 1.00000
49 hdd 0.27299 osd.49 arriba 1.00000 1.00000
50 hdd 0.27299 osd.50 arriba 1.00000 1.00000
51 hdd 0.27299 osd.51 arriba 1.00000 1.00000
52 hdd 0.27299 osd.52 arriba 1.00000 1.00000
53 hdd 0.27299 osd.53 arriba 1.00000 1.00000
54 hdd 0.27299 osd.54 arriba 1.00000 1.00000
55 hdd 0.27299 osd.55 arriba 1.00000 1.00000
56 hdd 0.27299 osd.56 arriba 1.00000 1.00000
57 hdd 0.27299 osd.57 arriba 1.00000 1.00000
58 hdd 0.27299 osd.58 arriba 1.00000 1.00000
59 hdd 0.89999 osd.59 arriba 1.00000 1.00000
Crearemos nuestros propios racks y servidores con blackjack y lo demás:
root@ceph01-q:~#ceph osd crush add-bucket rack01 root #creamos un nuevo root
root@ceph01-q:~#ceph osd crush add-bucket ceph01-q host #creamos un nuevo host
root@ceph01-q:~#ceph osd crush move ceph01-q root=rack01 #movimos el servidor a otro rack
root@ceph01-q:~#osd crush add 28 1.0 host=ceph02-q # Agregamos el OSD al servidor
# Si se creó incorrectamente, se puede eliminar
root@ceph01-q:~# ceph osd crush remove osd.4
root@ceph01-q:~# ceph osd crush remove rack01Problemas que encontramos en operativo clúster al intentar crear un nuevo host y moverlo a un rack existente: el comando ceph osd crush move ceph01-host root=rack01 se colgó y los monitores comenzaron a caer uno por uno. Interrumpir el comando con CTRL+C devolvía el clúster al mundo de los vivos.
La búsqueda mostró el siguiente problema:
La solución fue volcar el crushmap y eliminar la sección rule replicated_ruleset
root@ceph01-prod:~#ceph osd getcrushmap -o crushmap.row #Volcando mapa en crudo
root@ceph01-prod:~#crushtool -d crushmap.row -o crushmap.txt #convirtiendo a legible
root@ceph01-prod:~#vim crushmap.txt #editando, eliminando la rule replicated_ruleset
root@ceph01-prod:~#crushtool -c crushmap.txt -o new_crushmap.row #compilando de nuevo
root@ceph01-prod:~#ceph osd setcrushmap -i new_crushmap.row #cargando en el clústerAtención: esta operación puede provocar un rebalanceo del grupo de colocación entre OSD. Esto nos ha ocurrido, pero muy poco.
Y la extrañeza con la que nos encontramos en el clúster de prueba fue que, después de reiniciar el servidor, los OSD olvidaban que habían sido movidos a nuevos servidores y racks, y regresaban al root por defecto.
Al final, al construir el esquema final en el que creamos un root separado para discos SSD y otro para discos de plato, redistribuimos todos los OSD por los racks y simplemente eliminamos el root por defecto. Después de reiniciar, los OSD permanecieron en sus lugares.
Más tarde, indagando en la documentación, encontramos un parámetro que controla este comportamiento. Sobre él en la segunda parte.
Cómo hicimos varios grupos según los tipos de discos.
Primero, creamos dos roots: uno para SSD y otro para HDD.
root@ceph01-q:~#ceph osd crush add-bucket ssd-root root
root@ceph01-q:~#ceph osd crush add-bucket hdd-root rootDado que físicamente los servidores están en diferentes racks, para mayor comodidad, creamos racks y en ellos colocamos los servidores.
# Стойки:
root@ceph01-q:~#ceph osd crush add-bucket ssd-rack01 rack
root@ceph01-q:~#ceph osd crush add-bucket ssd-rack02 rack
root@ceph01-q:~#ceph osd crush add-bucket ssd-rack03 rack
root@ceph01-q:~#ceph osd crush add-bucket hdd-rack01 rack
root@ceph01-q:~#ceph osd crush add-bucket hdd-rack01 rack
root@ceph01-q:~#ceph osd crush add-bucket hdd-rack01 rack
# Сервера
root@ceph01-q:~#ceph osd crush add-bucket ssd-ceph01-q host
root@ceph01-q:~#ceph osd crush add-bucket ssd-ceph02-q host
root@ceph01-q:~#ceph osd crush add-bucket ssd-ceph03-q host
root@ceph01-q:~#ceph osd crush add-bucket hdd-ceph01-q host
root@ceph01-q:~#ceph osd crush add-bucket hdd-ceph02-q host
root@ceph01-q:~#ceph osd crush add-bucket hdd-ceph02-q hosty distribuimos los discos según sus tipos en diferentes servidores.
root@ceph01-q:~# Los discos del 0 al 3 son SSD, están en ceph01-q, los asignamos al servidor
root@ceph01-q:~# ssd-ceph01-q
root@ceph01-q:~#ceph osd crush add 0 1 host=ssd-ceph01-q
root@ceph01-q:~#ceph osd crush add 1 1 host=ssd-ceph01-q
root@ceph01-q:~#ceph osd crush add 2 1 host=ssd-ceph01-q
root@ceph01-q:~#ceph osd crush add 3 1 host=ssd-ceph01-q
root-ceph01-q:~# similar con otros servidoresAl distribuir los discos entre los roots ssd-root y hdd-root, dejamos el root-default vacío, por lo que podemos eliminarlo.
root-ceph01-q:~#ceph osd crush remove defaultA continuación, debemos crear reglas de distribución que vincularemos a los pools que crearemos. En las reglas especificaremos en qué roots se pueden almacenar los datos de nuestro pool y el nivel de unicidad de la réplica. Por ejemplo, las réplicas deben estar necesariamente en servidores diferentes, o en racks diferentes (incluso en diferentes roots, si tenemos tal distribución).
Antes de elegir el tipo, es mejor leer la documentación:
root-ceph01-q:~#ceph osd crush rule create-simple rule-ssd ssd-root host firstn
root-ceph01-q:~#ceph osd crush rule create-simple rule-hdd hdd-root host firstn
root-ceph01-q:~# Hemos especificado dos reglas, en las que los datos se replican
root-ceph01-q:~# entre hosts, es decir, la réplica debe estar en otro host,
root-ceph01-q:~# incluso si están en un mismo rack.
root-ceph01-q:~# En producción, si es posible, es mejor distribuir los hosts
root-ceph01-q:~# por racks y especificar que las réplicas se distribuyan por racks:
root-ceph01-q:~# ##ceph osd crush rule create-simple rule-ssd ssd-root rack firstnY creamos los grupos en los que queremos almacenar en el futuro las imágenes de disco de nuestra virtualización — PROXMOX:
root-ceph01-q:~# #ceph osd pool create {NAME} {pg_num} {pgp_num}
root-ceph01-q:~# ceph osd pool create ssd_pool 1024 1024
root-ceph01-q:~# ceph osd pool create hdd_pool 1024 1024Y les decimos a estos grupos qué reglas de colocación usar.
root-ceph01-q:~#ceph osd crush rule ls # vemos la lista de reglas
root-ceph01-q:~#ceph osd crush rule dump rule-ssd | grep rule_id # seleccionamos el ID necesario
root-ceph01-q:~#ceph osd pool set ssd_pool crush_rule 2
La elección del número de grupos de colocación debe hacerse con una visión previa de su clúster: cuántos OSD habrá aproximadamente, qué cantidad de datos (en porcentaje del volumen total) estará en el grupo, cuánta cantidad de datos en total.
En total, es recomendable no tener más de 300 grupos de colocación por disco, y será más fácil balancear con grupos pequeños de colocación — es decir, si toda su piscina ocupa 10 Tb y tiene 10 PG, será problemático equilibrar transfiriendo ladrillos de terabytes (pg); es más fácil y uniforme pasar arena con un tamaño de grano pequeño en cubos.
Pero hay que recordar que cuanto mayor sea el número de PG, más recursos se gastan en calcular su ubicación — comenzará a utilizarse memoria y CPU.
Una comprensión aproximada puede , proporcionado por los desarrolladores de la documentación de CEPH.
Lista de materiales:
Fuente: habr.com
