La scelta di CEPH. Parte 1
Avevamo cinque rack, dieci switch ottici, BGP configurato, una manciata di SSD e una miriade di dischi SAS di tutti i colori e dimensioni, con Proxmox e il desiderio di inserire tutta la staticità nel nostro storage S3. Non che tutto questo fosse necessario per la virtualizzazione, ma dato che avevo iniziato a utilizzare open source, dovevo portare avanti la mia passione fino in fondo. L'unica cosa che mi preoccupava era il BGP. Non esiste nulla di più impotente, irresponsabile e immorale della routing interno tramite BGP. E sapevo che molto presto ci saremmo stati dentro.

Il compito era banale — avevamo un CEPH che non funzionava molto bene. Dovevamo farlo funzionare 'bene'.
Il cluster che mi era stato assegnato era eterogeneo, configurato in modo approssimativo e praticamente non ottimizzato. Era composto da due gruppi di nodi diversi, con una rete comune che svolgeva il ruolo sia di rete cluster che di rete pubblica. I nodi erano caricati con quattro tipi di dischi — due tipi di SSD, organizzati in due regole di posizionamento separate, e due tipi di HDD di dimensioni diverse, raggruppati in un terzo insieme. Il problema delle dimensioni diverse è stato risolto con pesi OSD differenti.
La configurazione stessa è stata suddivisa in due parti — ottimizzazione del sistema operativo e ottimizzazione del CEPH stesso e delle sue impostazioni.
Miglioramento dell'OS
Network log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats timeout 30s user haproxy group haproxy daemondefaults log global mode http option httplog option dontlognull timeout connect 5000 timeout client 50000 timeout server 50000frontend http_front bind *:80 stats uri /haproxy?stats default_backend http_backbackend http_back balance roundrobin server server_name1 private_ip1:80 check server server_name2 private_ip2:80 check
L'elevata latenza si manifestava sia durante la scrittura che durante il bilanciamento. Durante la scrittura, infatti, il cliente non riceveva conferma di scrittura riuscita finché le repliche dei dati in altri gruppi non confermavano il successo. Poiché le regole di distribuzione delle repliche nella mappa CRUSH prevedevano una replica per host, la rete era sempre utilizzata.
Pertanto, ho deciso innanzitutto di ottimizzare leggermente la rete attuale, cercando parallelamente di convincere a passare a reti separate.
Per cominciare, ho modificato le impostazioni delle schede di rete. Ho iniziato con la configurazione delle code:
quello che c'era:
ethtool -l ens1f1
root@ceph01:~# ethtool -l ens1f1
Parametri del canale per ens1f1:
Massimi preimpostati:
RX: 0
TX: 0
Altro: 1
Combinato: 63
Impostazioni hardware attuali:
RX: 0
TX: 0
Altro: 1
Combinato: 1
root@ceph01:~# ethtool -g ens1f1
Parametri della coda per ens1f1:
Massimi preimpostati:
RX: 4096
RX Mini: 0
RX Jumbo: 0
TX: 4096
Impostazioni hardware attuali:
RX: 256
RX Mini: 0
RX Jumbo: 0
TX: 256
root@ceph01:~# ethtool -l ens1f1
Parametri del canale per ens1f1:
Massimi preimpostati:
RX: 0
TX: 0
Altro: 1
Combinato: 63
Impostazioni hardware attuali:
RX: 0
TX: 0
Altro: 1
Combinato: 1Si può notare che i parametri attuali sono lontani dai massimi. Ho aumentato:
root@ceph01:~#ethtool -G ens1f0 rx 4096
root@ceph01:~#ethtool -G ens1f0 tx 4096
root@ceph01:~#ethtool -L ens1f0 combined 63Seguendo un articolo eccellente
ho aumentato la lunghezza della coda di invio txqueuelen da 1000 a 10 000
root@ceph01:~#ip link set ens1f0 txqueuelen 10000E seguendo la documentazione di Ceph
ho aumentato MTU fino a 9000.
root@ceph01:~#ip link set dev ens1f0 mtu 9000Ho aggiunto a /etc/network/interfaces per far sì che tutto quanto sopra si carichi all'avvio
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 9000Dopo di che, seguendo lo stesso articolo, ho iniziato a regolare attentamente i parametri del kernel 4.15. Considerando che sui nodi ci sono 128G di RAM, è risultata una sorta di file di configurazione per sysctl
cat /etc/sysctl.d/50-ceph.conf
net.core.rmem_max = 56623104
#Dimensione massima del buffer di ricezione dati per tutte le connessioni 54M
net.core.wmem_max = 56623104
#Dimensione massima del buffer di trasmissione dati per tutte le connessioni 54M
net.core.rmem_default = 56623104
#Dimensione predefinita del buffer di ricezione dati per tutte le connessioni 54M
net.core.wmem_default = 56623104
#Dimensione predefinita del buffer di trasmissione dati per tutte le connessioni 54M
# per ogni socket
net.ipv4.tcp_rmem = 4096 87380 56623104
#Variabile vettoriale (minimo, predefinito, massimo) nel file tcp_rmem
# contiene 3 numeri interi che definiscono la dimensione del buffer di ricezione dei socket TCP.
# Minimo: ogni socket TCP ha il diritto di utilizzare questa memoria al momento
# della sua creazione. La possibilità di utilizzare tale buffer
# è garantita anche al raggiungimento della soglia limite (moderate memory pressure).
# La dimensione minima del buffer predefinita è di 8 Kbyte (8192).
#Valore predefinito: quantità di memoria consentita per il buffer
# di trasmissione del socket TCP predefinito. Questo valore viene applicato al posto
# del parametro /proc/sys/net/core/rmem_default, utilizzato da altri protocolli.
# Il valore del buffer predefinito è generalmente (predefinito)
# di 87830 byte. Questo definisce la dimensione della finestra 65535 con
# il valore predefinito tcp_adv_win_scale e tcp_app_win = 0,
# leggermente inferiore rispetto al valore predefinito di tcp_app_win.
# Massimo: dimensione massima del buffer che può essere automaticamente
# allocato per la ricezione da un socket TCP. Questo valore non annulla il massimo,
# impostato nel file /proc/sys/net/core/rmem_max. Con l'allocazione "statica"
# della memoria usando SO_RCVBUF questo parametro non ha significato.
net.ipv4.tcp_wmem = 4096 65536 56623104
net.core.somaxconn = 5000
# Numero massimo di socket aperti in attesa di connessioni.
net.ipv4.tcp_timestamps=1
# Abilita l'uso delle timestamp, in conformità con RFC 1323.
net.ipv4.tcp_sack=1
# Abilita l'uso di conferme selettive nel protocollo TCP
net.core.netdev_max_backlog=5000 (predefinito 1000)
# numero massimo di pacchetti in coda per l'elaborazione, se
# l'interfaccia riceve pacchetti più rapidamente di quanto il kernel riesca a elaborarli.
net.ipv4.tcp_max_tw_buckets=262144
# Numero massimo di socket in stato TIME-WAIT simultaneamente.
# Superato questo limite, il socket "in più" viene distrutto e viene scritto
# un messaggio nel registro di sistema.
net.ipv4.tcp_tw_reuse=1
#Permettiamo il riutilizzo dei socket TIME-WAIT nei casi in cui
# il protocollo lo considera sicuro.
net.core.optmem_max=4194304
# Aumenta la dimensione massima totale allocabile del buffer
# misurata in unità di pagina (4096 byte)
net.ipv4.tcp_low_latency=1
# Permette allo stack TCP/IP di dare priorità a una bassa latenza
# rispetto a una maggiore larghezza di banda.
net.ipv4.tcp_adv_win_scale=1
# Questa variabile influisce sul calcolo della quantità di memoria nel buffer del socket,
# allocata per la dimensione della finestra TCP e per il buffer dell'applicazione.
# Se il valore di tcp_adv_win_scale è negativo, per calcolare la dimensione
# si usa la seguente espressione:
# Bytes - bytes2 elevato alla -tcp_adv_win_scale
# Dove bytes è la dimensione della finestra in byte. Se il valore di tcp_adv_win_scale
# è positivo, per determinare la dimensione si usa la seguente espressione:
# Bytes - bytes2 elevato a tcp_adv_win_scale
# La variabile accetta un intero. Il valore predefinito è 2,
# cioè il buffer dell'applicazione riceve ¼ della capacità definita dalla variabile
# tcp_rmem.
net.ipv4.tcp_slow_start_after_idle=0
# meccanismo di riavvio lento, che resetta il valore della finestra
# di sovraccarico se la connessione non è stata utilizzata per un periodo di tempo definito.
# È meglio disattivare l'SSR sul server per migliorare le prestazioni
# delle connessioni di lunga durata.
net.ipv4.tcp_no_metrics_save=1
#Non memorizzare i risultati delle misurazioni TCP nella cache alla chiusura della connessione.
net.ipv4.tcp_syncookies=0
#Disabilita il meccanismo di invio delle syncookie
net.ipv4.tcp_ecn=0
#Explicit Congestion Notification (Notifica Esplicita di Congestione) nelle
#connessioni TCP. Viene utilizzato per segnalare l'insorgere di un 'ingorgo'
#sul percorso verso un dato host o rete. Può essere utilizzato per informare
#l'host mittente della necessità di ridurre la velocità di invio dei pacchetti attraverso
#un determinato router o firewall.
net.ipv4.conf.all.send_redirects=0
#disabilita l'emissione di ICMP Redirect … ad altri host. Questa opzione deve essere
#attivata se l'host funge da router di qualsiasi tipo.
# Non abbiamo routing.
net.ipv4.ip_forward=0
#Praticamente disabilitare il forwarding. Non siamo un gateway, Docker sulle macchine non è avviato,
# non ne abbiamo bisogno.
net.ipv4.icmp_echo_ignore_broadcasts=1
#Non rispondiamo alle richieste ICMP ECHO trasmesse come pacchetti broadcast
net.ipv4.tcp_fin_timeout=10
#definisce il tempo di permanenza del socket in stato FIN-WAIT-2 dopo la sua
#chiusura da parte della parte locale. Predefinito 60
net.core.netdev_budget=600 # (predefinito 300)
# Se l'esecuzione delle interruzioni software non dura a lungo,
# allora la velocità di crescita dei dati in arrivo può superare la possibilità del kernel
# di svuotare il buffer. Di conseguenza, i buffer NIC si riempiranno e il traffico sarà perso.
# A volte è necessario aumentare la durata di funzionamento dei SoftIRQs
# (interruzioni software) con la CPU. Questo è gestito da netdev_budget.
# Il valore predefinito è 300. Il parametro costringe il processo SoftIRQ a gestire
# 300 pacchetti da NIC prima di liberare la CPU
net.ipv4.tcp_fastopen=3
# TFO TCP Fast Open
# se sia il client che il server supportano TFO, segnalato tramite
# un flag speciale nel pacchetto TCP. Nel nostro caso è placebo, sembra solo
# bello)Crete luster è stata assegnata a interfacce di rete da 10Gbps separate in una rete piatta dedicata. Su ogni macchina sono state installate schede di rete a due porte mellanox 10/25 Gbps, collegate a due switch da 10Gbps separati. L'aggregazione è stata effettuata tramite OSPF, poiché il bonding con LACP ha mostrato una larghezza di banda massima di 16 Gbps, mentre OSPF ha sfruttato con successo entrambi i dieci su ogni macchina. I piani futuri erano di utilizzare ROCE su questi Mellanox per ridurre la latenza. Come è stata configurata questa parte della rete:
- Poiché le stesse macchine hanno indirizzi IP esterni su BGP, ci serve un software — ( più precisamente al momento della scrittura dell'articolo era ) già installato.
- In totale, sulle macchine c'erano due interfacce di rete — in totale 4 porte. Una scheda di rete a due porte era collegata al fabric ed era configurata per BGP, l'altra — con due porte era collegata a due switch diversi ed era sotto OSPF.
Ulteriori dettagli sulla configurazione di OSPF: L'obiettivo principale è aggregare due link e avere tolleranza ai guasti.
due interfacce di rete sono configurate in due reti piatte semplici — 10.10.10.0/24 e 10.10.20.0/24
1: ens1f0: mtu 9000 qdisc mq stato UP gruppo default qlen 1000
inet 10.10.10.2/24 brd 10.10.10.255 scope global ens1f0
2: ens1f1: mtu 9000 qdisc mq stato UP gruppo default qlen 1000
inet 10.10.20.2/24 brd 10.10.20.255 scope global ens1f1che le macchine si vedono tra loro.
DISK
Il passo successivo è stato ottimizzare le prestazioni dei dischi. Per l'SSD ho cambiato il pianificatore in noop, per l'HDD — deadline. In termini semplici, NOOP funziona secondo il principio "chi arriva primo si siede", ciò che in inglese si traduce come "FIFO (First In, First Out)". Le richieste si mettono in coda man mano che arrivano. DEADLINE è più orientato alla lettura, e il processo nella coda ottiene praticamente accesso monopolistico al disco al momento dell'operazione. Questo si adatta perfettamente al nostro sistema — poiché solo un processo lavora con ogni disco — il daemon OSD.
(Coloro che desiderano approfondire il pianificatore di input-output possono leggerne qui:
Preferiscono leggere in russo: )
Nelle raccomandazioni per il tuning di Linux si consiglia di aumentare anche nr_request
nr_requests
Il valore di nr_requests determina la quantità di richieste I/O che vengono memorizzate nella cache prima che lo scheduler I/O invii/riceva dati al dispositivo di blocco. Se si utilizza una scheda RAID o un dispositivo di blocco in grado di gestire una coda più grande rispetto a quella impostata dallo scheduler I/O, aumentare il valore di nr_requests può contribuire a migliorare l'efficienza e ridurre il carico del server quando si verificano grandi quantità di I/O. Se si utilizza Deadline o CFQ come scheduler, si consiglia di impostare il valore di nr_request a 2 volte il valore della profondità della coda.
NO! Gli stessi sviluppatori di CEPH ci assicurano che il loro sistema di priorità funziona meglio.

WBThrottle e/o nr_requests
WBThrottle e/o nr_requests
L'archiviazione dei file utilizza operazioni di input/output in buffer per la scrittura; ciò porta una serie di vantaggi quando il registro di archiviazione dei file si trova su un supporto più veloce. Le richieste dei clienti ricevono notifiche non appena i dati vengono registrati nel registro e successivamente vengono scaricati sul disco dati in un secondo momento, sfruttando la funzionalità standard di Linux. Questo consente agli OSD di dischi rigidi di offrire latenze di scrittura simili a quelle degli SSD per scritture a pacchetto ridotto. Tale scrittura ritardata permette anche al core di riordinare le richieste di operazioni di input/output sul disco con la speranza di unire le operazioni o di consentire alle testine di leggere percorrenze più ottimali sui dischi. L'effetto finale è che è possibile ottenere leggermente più operazioni di input/output da ciascun disco rispetto a quanto sarebbe possibile con operazioni di input/output dirette o sincrone.
Tuttavia, sorge un problema se il volume delle registrazioni in arrivo a questo cluster Ceph supera le capacità dei dischi sottostanti. In questo scenario, il numero totale delle operazioni di input/output in attesa di scrittura su disco può crescere in modo incontrollato, portando a code di operazioni di input/output che riempiono completamente il disco e le code di Ceph. Le richieste di lettura sono particolarmente colpite, poiché si bloccano tra le richieste di scrittura, che possono richiedere diversi secondi per essere scritte sul disco principale.
Per affrontare questa problematica, Ceph offre un meccanismo di throttling della scrittura in coda (writeback) denominato WBThrottle, integrato nel suo sistema di archiviazione. Questo strumento è progettato per limitare il numero complessivo di operazioni di input/output in attesa di scrittura, che possono accodarsi e avviare il processo di scrittura prima di quanto avverrebbe naturalmente a causa dell'attivazione del kernel stesso. Purtroppo, i test mostrano che i valori predefiniti possono comunque non ridurre il comportamento attuale fino a un livello che possa diminuire l'impatto sulla latenza delle operazioni di lettura. Una regolazione appropriata può modificare questo comportamento, riducendo la lunghezza complessiva delle code di scrittura e limitando l'impatto. Tuttavia, esiste un certo compromesso: diminuendo il numero massimo di scritture consentite in coda, si potrebbe ridurre la capacità del kernel di massimizzare l'efficienza nell'ordinamento delle richieste in arrivo. È opportuno riflettere su cosa sia più necessario per il vostro caso d'uso specifico e i carichi di lavoro e regolare di conseguenza.
Per gestire la profondità di una coda di scrittura differita, puoi ridurre il numero massimo totale di operazioni di input/output non completate, applicando le impostazioni di WBThrottle, o diminuire il valore massimo per le operazioni non completate a livello di blocco del tuo kernel. Entrambi possono gestire efficacemente lo stesso comportamento e le tue preferenze guideranno l'implementazione di questa impostazione.
È inoltre importante notare che il sistema di priorità delle operazioni esistente in Ceph è più efficiente per le richieste più brevi a livello di disco. Riducendo la coda totale a questo disco, la posizione principale nella coda si sposta in Ceph, dove ha un maggiore controllo su quale priorità ha l'operazione di input/output. Consideriamo il seguente esempio:
echo 8 > /sys/block/sda/queue/nr_requestsCOMUNE
E altre impostazioni del kernel che permettono di rendere la tua macchina più fluida e setosa, spremendo ancora un po' di prestazioni dall'hardware
cat /etc/sysctl.d/60-ceph2.conf
kernel.pid_max = 4194303
#Dischi in ogni macchina 25, quindi abbiamo calcolato che ci saranno molti processi
kernel.threads-max=2097152
# Anche i thread, naturalmente.
vm.max_map_count=524288
# Aumentato il numero di aree della mappa di memoria del processo.
# Come indicato nella documentazione sulle variabili del kernel
# Le aree della mappa di memoria sono utilizzate come effetto collaterale delle chiamate
# malloc, direttamente tramite mmap, mprotect e madvise, e anche nel caricamento
# di librerie condivise.
fs.aio-max-nr=50000000
# Ottimizzare i parametri di input-output
# Il kernel Linux offre la funzione di input-output asincrono non bloccante (AIO),
# che consente a un processo di avviare più operazioni di input-output
# contemporaneamente, senza attendere il completamento di una qualsiasi di esse.
# Questo aiuta a migliorare le prestazioni delle applicazioni,
# che possono sovrapporre il trattamento con input-output.
# Il parametro aio-max-nr determina il numero massimo di
# richieste simultanee consentite.
vm.min_free_kbytes=1048576
# dimensione minima di memoria libera che deve essere mantenuta.
# Impostato a 1Gb, più che sufficiente per il funzionamento del sistema operativo,
# e permette di evitare l'OOM Killer per i processi OSD. Anche se la memoria è abbondante,
# un po' di riserva non fa male.
vm.swappiness=10
# Si utilizza lo swap se rimane libero il 10% della memoria.
# Su macchine con 128G di RAM, 10% sono 12 Gigabyte. Più che sufficiente per il funzionamento.
# Il parametro standard del 60% faceva rallentare il sistema, attivando lo swap,
# quando c'era ancora molta memoria libera.
vm.vfs_cache_pressure=1000
# Aumentiamo dallo standard di 100. Facciamo sì che il kernel scarichi
# più attivamente le pagine di memoria inutilizzate dalla cache.
vm.zone_reclaim_mode=0
# Consente di impostare approcci più o meno aggressivi al
# recupero della memoria, quando la zona sta esaurendo la memoria.
# Se impostato a zero, non avviene il recupero della zona.
# Per i server di file o i carichi di lavoro
# è vantaggioso se i loro dati sono memorizzati nella cache, quindi lasciare
# disabilitato zone_reclaim_mode, poiché l'effetto di caching,
# probabilmente sarà più importante della posizione dei dati.
vm.dirty_ratio=20
# Percentuale di memoria RAM che può essere riservata per le "pagine sporche"
# Calcolata sulla base di una stima approssimativa:
# Il sistema ha 128 gigabyte di memoria.
# Circa 20 dischi SSD, dei quali nelle impostazioni di CEPH è indicato
# riservare 3G di RAM per la cache.
# Circa 40 dischi HDD, per i quali questo parametro è pari a 1G
# il 20% di 128 è 25.6 gigabyte. Quindi, nel caso di massima utilizzazione della memoria,
# resteranno 2.4G di memoria per il sistema. Sarà sufficiente per sopravvivere e attendere
# l'arrivo della cavalleria – cioè del DevOps che riparerà tutto.
vm.dirty_background_ratio=3
# percentuale di memoria del sistema che può essere occupata da pagine sporche fino a quando
# i processi di background pdflush/flush/kdmflush le scriveranno su disco
fs.file-max=524288
# E probabilmente avremo molte più file aperti di quante indicate per impostazione predefinita. Introduzione a CEPH
Impostazioni su cui vorremmo soffermarci di più:
cat /etc/ceph/ceph.conf
osd:
journal_aio: true # Tre parametri che includono
journal_block_align: true # I/O diretto
journal_dio: true # sul journal
journal_max_write_bytes: 1073714824 # Allunghiamo un po' la dimensione massima
# dell'operazione di scrittura singola nel journal
journal_max_write_entries: 10000 # E il numero di scritture simultanee
journal_queue_max_bytes: 10485760000
journal_queue_max_ops: 50000
rocksdb_separate_wal_dir: true # Abbiamo deciso di creare un WAL separato
# Abbiamo persino provato a farlo
# NVMe
bluestore_block_db_create: true # E un dispositivo separato per il journal
bluestore_block_db_size: '5368709120 #5G'
bluestore_block_wal_create: true
bluestore_block_wal_size: '1073741824 #1G'
bluestore_cache_size_hdd: '3221225472 # 3G'
# Un grande quantitativo di RAM consente di
# memorizzare volumi piuttosto elevati
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 # numero di thread del demone per un disco
osd_failsafe_full_ratio: 0.95
osd_heartbeat_grace: 5
osd_heartbeat_interval: 3
osd_map_dedup: true
osd_max_backfills: 2 # numero di operazioni di riempimento simultanee per 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 # Caratteristiche di avidità. È diventato molto veloce
osd_pool_default_size: 2 # trovarsi a corto di spazio, perché la soluzione temporanea
# adottata è stata quella di ridurre il numero
# delle repliche dei dati
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 # parametri regolabili a seconda delle necessità
osd_recovery_sleep: 2
osd_scrub_chunk_max: 4Alcuni parametri testati su QA nella versione 12.2.12 mancano nella versione ceph 12.2.2, ad esempio osd_recovery_threads. Pertanto, era previsto l'aggiornamento in produzione a 12.2.12. La pratica ha dimostrato che le versioni 12.2.2 e 12.2.12 possono coesistere nel medesimo cluster, permettendo un aggiornamento rolling.
Cluster di test
Naturalmente, per i test era necessario avere la stessa versione di quella in produzione, ma al momento dell'inizio della mia attività con il cluster era disponibile solo una versione più recente nel repository. Dopo aver osservato che le differenze nella versione minore erano limitate (1393 righe nei file di configurazione contro 1436 nella nuova versione), abbiamo deciso di iniziare a testare la nuova (comunque dovevamo aggiornare, quindi perché restare su un vecchio rottame)
L'unica cosa che abbiamo cercato di mantenere dalla vecchia versione è stato il pacchetto ceph-deploy, dato che parte degli strumenti (e alcuni membri dello staff) erano adattati alla sua sintassi. La nuova versione differiva abbastanza, ma non influiva sul funzionamento del cluster stesso, quindi è stata mantenuta la versione 1.5.39
Poiché il comando ceph-disk è chiaramente indicato come deprecato e raccomanda di utilizzare il comando ceph-volume — abbiamo iniziato a creare OSD usando proprio questo comando, senza perdere tempo con il vecchio.
Il piano era di creare uno specchio costituito da due dischi SSD, sui quali avremmo posizionato i log OSD, che sono a loro volta ospitati su dischi SAS spinning. In questo modo ci assicuriamo contro problemi di dati in caso di guasto del disco con il log.
Abbiamo iniziato a creare il cluster seguendo la documentazione
cat /etc/ceph/ceph.conf
root@ceph01-qa:~# cat /etc/ceph/ceph.conf # abbiamo caricato in anticipo la configurazione
[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 # indirizzo modificato, ovviamente ))
rgw_dns_name = s3-qa.mycompany.ru # e anche questo indirizzo è stato modificato
rgw_host = s3-qa.mycompany.ru # e anche questo
[mon]
mon allow pool delete = true
mon_max_pg_per_osd = 300 # non ci siamo spinti oltre trecento gruppi di collocamento
# su disco non ci siamo decisi
# anche se il parametro dipende dal numero di pool,
# delle loro dimensioni e del numero di OSD. Avere pochi ma sani PG
# non è nemmeno la scelta migliore - ne risente la bilanciatura
mon_osd_backfillfull_ratio = 0.9
mon_osd_down_out_interval = 5
mon_osd_full_ratio = 0.95 # per SSD l'area per il loro
# log è lo stesso dispositivo degli OSD
# abbiamo deciso che il 5% del disco (che è grande 1.2Tb)
# dovrebbe essere sufficiente, e corrisponde al parametro
# bluestore_block_db_size più la variabilità su gruppi di collocamento più grandi
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-qLa prima difficoltà riscontrata con questa versione di ceph-deploy su un cluster della versione 12.2.12 è stata un'errore nel tentativo di creare un OSD con db su un RAID software —
root@ceph01-qa:~# ceph-volume lvm create --bluestore --data /dev/sde --block.db /dev/md0
blkid non è riuscito a rilevare un PARTUUID per il dispositivo: /dev/md1Infatti, blkid non mostra PARTUUID, ho dovuto creare le partizioni manualmente:
root@ceph01-qa:~# parted /dev/md0 mklabel GPT
# ci saranno molte partizioni,
# senza GPT non sarà possibile crearle
# la dimensione della partizione è stata indicata nella configurazione sopra = bluestore_block_db_size: '5368709120 #5G'
# Ho 20 dischi per OSD, creare partizioni manualmente è noioso
# quindi ho fatto un ciclo
root@ceph01-qa:~# for i in {1..20}; do echo -e "nnnn+5Gnw" | fdisk /dev/md0; doneSembra che sia tutto pronto, proviamo a creare l'OSD ancora una volta e otteniamo il seguente errore (che, tra l'altro, non si è riprodotto in produzione)
durante la creazione di un OSD di tipo bluestore senza specificare il percorso per il WAL, ma specificando il db
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 unparsable uuid
stderr: 2019-04-12 10:39:27.213185 7eff461b6e00 -1 bdev(0x55824c273680 /var/lib/ceph/osd/ceph-0//block.wal) open open got: (22) Invalid argument
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) Invalid argument
stderr: 2019-04-12 10:39:27.999039 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) mkfs failed, (22) Invalid argument
stderr: 2019-04-12 10:39:27.999057 7eff461b6e00 -1 OSD::mkfs: ObjectStore::mkfs failed with error (22) Invalid argument
stderr: 2019-04-12 10:39:27.999141 7eff461b6e00 -1 ** ERROR: error creating empty object store in /var/lib/ceph/osd/ceph-0/: (22) Invalid argumentSe si crea un'altra partizione per WAL nello stesso mirror (o altrove, a scelta) e la si specifica durante la creazione di OSD, tutto procederà senza problemi (eccetto per l'emergere di un WAL separato, che forse non desideravi).
Tuttavia, dato che si prevedeva di spostare il WAL su NVMe, questa pratica si è rivelata utile.
root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sdf --block.wal /dev/md0p2 --block.db /dev/md1p2Abbiamo creato monitor, manager e OSD. Ora vogliamo raggrupparli in modo diverso, poiché l'intenzione è avere dischi di diversi tipi: pool veloci su SSD e grandi, ma lenti su dischi SAS.
Consideriamo che sui server ci siano 20 dischi, i primi dieci sono un tipo, i secondi un altro.
La mappa iniziale, predefinita, appare così:
ceph osd tree
root@ceph01-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 up 1.00000 1.00000
9 hdd 0.27299 osd.9 up 1.00000 1.00000
10 hdd 0.27299 osd.10 up 1.00000 1.00000
11 hdd 0.27299 osd.11 up 1.00000 1.00000
12 hdd 0.27299 osd.12 up 1.00000 1.00000
13 hdd 0.27299 osd.13 up 1.00000 1.00000
14 hdd 0.27299 osd.14 up 1.00000 1.00000
15 hdd 0.27299 osd.15 up 1.00000 1.00000
16 hdd 0.27299 osd.16 up 1.00000 1.00000
17 hdd 0.27299 osd.17 up 1.00000 1.00000
18 hdd 0.27299 osd.18 up 1.00000 1.00000
19 hdd 0.27299 osd.19 up 1.00000 1.00000
-5 5.45599 host ceph02-q
20 ssd 0.27299 osd.20 up 1.00000 1.00000
21 ssd 0.27299 osd.21 up 1.00000 1.00000
22 ssd 0.27299 osd.22 up 1.00000 1.00000
23 ssd 0.27299 osd.23 up 1.00000 1.00000
24 hdd 0.27299 osd.24 up 1.00000 1.00000
25 hdd 0.27299 osd.25 up 1.00000 1.00000
26 hdd 0.27299 osd.26 up 1.00000 1.00000
27 hdd 0.27299 osd.27 up 1.00000 1.00000
28 hdd 0.27299 osd.28 up 1.00000 1.00000
29 hdd 0.27299 osd.29 up 1.00000 1.00000
30 hdd 0.27299 osd.30 up 1.00000 1.00000
31 hdd 0.27299 osd.31 up 1.00000 1.00000
32 hdd 0.27299 osd.32 up 1.00000 1.00000
33 hdd 0.27299 osd.33 up 1.00000 1.00000
34 hdd 0.27299 osd.34 up 1.00000 1.00000
35 hdd 0.27299 osd.35 up 1.00000 1.00000
36 hdd 0.27299 osd.36 up 1.00000 1.00000
37 hdd 0.27299 osd.37 up 1.00000 1.00000
38 hdd 0.27299 osd.38 up 1.00000 1.00000
39 hdd 0.27299 osd.39 attivo 1.00000 1.00000
-7 6.08690 host ceph03-q
40 ssd 0.27299 osd.40 attivo 1.00000 1.00000
41 ssd 0.27299 osd.41 attivo 1.00000 1.00000
42 ssd 0.27299 osd.42 attivo 1.00000 1.00000
43 ssd 0.27299 osd.43 attivo 1.00000 1.00000
44 hdd 0.27299 osd.44 attivo 1.00000 1.00000
45 hdd 0.27299 osd.45 attivo 1.00000 1.00000
46 hdd 0.27299 osd.46 attivo 1.00000 1.00000
47 hdd 0.27299 osd.47 attivo 1.00000 1.00000
48 hdd 0.27299 osd.48 attivo 1.00000 1.00000
49 hdd 0.27299 osd.49 attivo 1.00000 1.00000
50 hdd 0.27299 osd.50 attivo 1.00000 1.00000
51 hdd 0.27299 osd.51 attivo 1.00000 1.00000
52 hdd 0.27299 osd.52 attivo 1.00000 1.00000
53 hdd 0.27299 osd.53 attivo 1.00000 1.00000
54 hdd 0.27299 osd.54 attivo 1.00000 1.00000
55 hdd 0.27299 osd.55 attivo 1.00000 1.00000
56 hdd 0.27299 osd.56 attivo 1.00000 1.00000
57 hdd 0.27299 osd.57 attivo 1.00000 1.00000
58 hdd 0.27299 osd.58 attivo 1.00000 1.00000
59 hdd 0.89999 osd.59 attivo 1.00000 1.00000
Creiamo i nostri rack e server virtuali con blackjack e tutto il resto:
root@ceph01-q:~#ceph osd crush add-bucket rack01 root #creato un nuovo root
root@ceph01-q:~#ceph osd crush add-bucket ceph01-q host #creato un nuovo host
root@ceph01-q:~#ceph osd crush move ceph01-q root=rack01 #spostato il server in un altro rack
root@ceph01-q:~#osd crush add 28 1.0 host=ceph02-q # Aggiunto OSD al server
# Se creato in modo errato, è possibile eliminare
root@ceph01-q:~# ceph osd crush remove osd.4
root@ceph01-q:~# ceph osd crush remove rack01Problemi che abbiamo incontrato in campo cluster, nel tentativo di creare un nuovo host e spostarlo in un rack esistente — comando ceph osd crush move ceph01-host root=rack01 si bloccava e i monitor cominciavano a cadere uno dopo l'altro. Interrompere il comando con CTRL+C riportava il cluster nel mondo dei vivi.
La ricerca ha rivelato un problema del genere:
La soluzione è stata fare il dump della crushmap e rimuovere quella sezione rule replicated_ruleset
root@ceph01-prod:~#ceph osd getcrushmap -o crushmap.row #Dump della mappa in formato grezzo
root@ceph01-prod:~#crushtool -d crushmap.row -o crushmap.txt #Convertire in leggibile
root@ceph01-prod:~#vim crushmap.txt #Modificare, rimuovendo la rule replicated_ruleset
root@ceph01-prod:~#crushtool -c crushmap.txt -o new_crushmap.row #Compilare di nuovo
root@ceph01-prod:~#ceph osd setcrushmap -i new_crushmap.row #Caricare nel clusterAttenzione: questa operazione potrebbe causare un bilanciamento del placement group tra OSD. È successo anche a noi, ma in modo molto ridotto.
La stranezza con cui ci siamo imbattuti nel cluster di test è che, dopo il riavvio del server, gli OSD dimenticavano di essere stati spostati in nuovi server e rack, e tornavano nel root default.
Alla fine, avendo creato uno schema finale in cui abbiamo creato un root separato per gli SSD e uno separato per quelli a disco, abbiamo distribuito tutti gli OSD tra i rack e semplicemente eliminato il root predefinito. Dopo il riavvio, gli OSD sono rimasti nei loro posti.
Dopo aver esaminato la documentazione, abbiamo trovato il parametro che controlla questo comportamento. Di questo si parla nella seconda parte.
Come abbiamo creato vari gruppi in base ai tipi di dischi.
Per iniziare, abbiamo creato due root: uno per SSD e uno per HDD.
root@ceph01-q:~#ceph osd crush add-bucket ssd-root root
root@ceph01-q:~#ceph osd crush add-bucket hdd-root rootPoiché fisicamente i server sono disposti in rack diversi, per comodità abbiamo creato i rack e in essi abbiamo collocato i server.
# Стойки:
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 hoste abbiamo distribuito i dischi per tipo su diversi server.
root@ceph01-q:~# I dischi da 0 a 3 sono SSD, si trovano in ceph01-q, li installiamo nel server
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:~# analogamente con gli altri serverDistribuendo i dischi tra i root ssd-root e hdd-root, abbiamo lasciato vuoto root-default, quindi possiamo eliminarlo.
root-ceph01-q:~#ceph osd crush remove defaultSuccessivamente, è necessario creare regole di distribuzione che verranno collegate ai pool creati. Nelle regole indicheremo in quali root è possibile posizionare i dati del nostro pool e il livello di unicità delle repliche, ad esempio, le repliche devono necessariamente trovarsi su server diversi o in rack diversi (si può anche utilizzare root diversi, se abbiamo tale distribuzione).
Prima di scegliere il tipo, è meglio leggere la documentazione:
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:~# Abbiamo definito due regole in cui i dati vengono replicati
root-ceph01-q:~# tra host, ovvero la replica deve trovarsi su un altro host,
root-ceph01-q:~# anche se si trovano nello stesso rack.
root-ceph01-q:~# In produzione, se possibile, è meglio distribuire gli host
root-ceph01-q:~# tra i rack e specificare di distribuire le repliche tra i rack:
root-ceph01-q:~# ##ceph osd crush rule create-simple rule-ssd ssd-root rack firstnE ora creiamo i pool in cui desideriamo conservare in futuro le immagini dei dischi della nostra virtualizzazione — 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 1024E diciamo a questi pool quali regole di posizionamento utilizzare.
root-ceph01-q:~#ceph osd crush rule ls # visualizziamo l'elenco delle regole
root-ceph01-q:~#ceph osd crush rule dump rule-ssd | grep rule_id #selezioniamo l'ID necessario
root-ceph01-q:~#ceph osd pool set ssd_pool crush_rule 2
La scelta del numero di gruppi di placement deve essere effettuata con una visione chiara del proprio cluster: quanti OSD ci saranno, quale percentuale di dati (rispetto al volume totale) sarà nel pool e quale sarà la quantità totale di dati.
In totale, è preferibile non avere più di 300 gruppi di placement per disco, ed è più semplice bilanciare con gruppi di placement più piccoli; ad esempio, se l'intero pool occupa 10 Tb e contiene 10 PG, sarà problematico bilanciare spostando mattoni da un terabyte (pg) — è più semplice e uniforme trasferire sabbia con granelli piccoli con secchi.
Ma bisogna ricordare che più aumentano i PG, più risorse vengono spese per calcolare la loro posizione — si inizia a utilizzare memoria e CPU.
Una comprensione generale può , messo a disposizione dagli sviluppatori della documentazione CEPH.
Elenco dei materiali:
Fonte: habr.com
