Seph — da "fatto a mano" a "produzione"

Scelta di CEPH. Parte 1

Avevamo cinque rack, dieci switch ottici, BGP configurato, un paio di decine di SSD e una marea di dischi SAS di tutti i colori e dimensioni, oltre a proxmox e il desiderio di mettere tutta la staticità nel nostro archivio S3. Non che tutto ciò fosse necessario per la virtualizzazione, ma poiché ho iniziato a utilizzare opensource, devo portare avanti la mia passione. L'unica cosa che mi preoccupava era il BGP. Nel mondo non c'è nessuno più impotente, irresponsabile e immorale della instradazione interna tramite BGP. E sapevo che abbastanza presto ci saremmo immersi in questo.

Seph — da "fatto a mano" a "produzione"

Il compito era banale: avevamo CEPH, che non funzionava molto bene. Doveva essere fatto "bene".
Il cluster che mi è stato assegnato era eterogeneo, configurato in fretta 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 equipaggiati con quattro tipi di dischi: due tipi di SSD, raggruppati in due regole di posizionamento separate e due tipi di HDD di diverse dimensioni, raggruppati in un terzo gruppo. 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 global 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 influenzava sia la scrittura che il bilanciamento. Durante la scrittura, perché il client non riceverà una risposta riguardo la scrittura riuscita finché le repliche dei dati in altri gruppi di posizionamento non confermeranno il successo. Poiché le regole di distribuzione delle repliche nella mappa CRUSH prevedevano una replica per host, la rete veniva sempre utilizzata.

Quindi, per prima cosa, ho deciso di configurare leggermente la rete attuale, cercando nel contempo di convincere a passare a reti separate.

Per cominciare, ho modificato le impostazioni delle schede di rete. Ho iniziato configurando le code:

cosa avevo:

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

È evidente 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 63

Guidato da un ottimo articolo

https://blog.packagecloud.io/eng/2017/02/06/monitoring-tuning-linux-networking-stack-sending-data/

ho aumentato la lunghezza della coda di invio. txqueuelen da 1000 a 10 000

root@ceph01:~#ip link set ens1f0  txqueuelen 10000

E seguendo la documentazione di ceph

https://ceph.com/geen-categorie/ceph-loves-jumbo-frames/

ho aumentato MTU fino a 9000.

root@ceph01:~#ip link set dev ens1f0  mtu 9000

Ho aggiunto a /etc/network/interfaces, per far caricare tutto quanto elencato 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 9000

Dopo 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, è stato creato un certo file di configurazione per sysctl

cat /etc/sysctl.d/50-ceph.conf

net.core.rmem_max = 56623104  
#Dimensione massima del buffer di ricezione dei dati per tutte le connessioni 54M

net.core.wmem_max = 56623104
#Dimensione massima del buffer di trasmissione dei dati per tutte le connessioni 54M

net.core.rmem_default = 56623104
#Dimensione predefinita del buffer di ricezione dei dati per tutte le connessioni 54M

net.core.wmem_default = 56623104
#Dimensione predefinita del buffer di trasmissione dei 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 le dimensioni del buffer di ricezione dei socket TCP.
#Minimo: ogni socket TCP ha il diritto di utilizzare questa memoria al 
#momento della sua creazione. È garantita la possibilità di utilizzare tale buffer 
#anche quando si raggiunge la soglia di limitazione (moderate memory pressure).
#La dimensione minima del buffer predefinita è di 8 KB (8192).
#Il valore predefinito: quantità di memoria consentita per il buffer 
#di trasmissione del socket TCP predefinito. Questo valore viene applicato in sostituzione
#del parametro /proc/sys/net/core/rmem_default, utilizzato da altri protocolli.
#Il valore del buffer normalmente utilizzato (di default) 
#è 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 a quello definito dal valore predefinito tcp_app_win.
#Massimo: dimensione massima del buffer che può essere automaticamente
#assegnata per la ricezione al socket TCP. Questo valore non invalida il massimo,
#definito nel file /proc/sys/net/core/rmem_max. Durante un'allocazione "statica"
#di memoria usando SO_RCVBUF questo parametro non ha effetto.

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
#Consente l'uso di timestamp, secondo RFC 1323.

net.ipv4.tcp_sack=1
#Consenti conferme selettive per il protocollo TCP

net.core.netdev_max_backlog=5000 (default 1000)
# numero massimo di pacchetti in coda per l'elaborazione, se 
# l'interfaccia riceve pacchetti più rapidamente di quanto il nucleo possa elaborare.

net.ipv4.tcp_max_tw_buckets=262144
# Numero massimo di socket in stato TIME-WAIT simultaneamente.
# Se viene superata questa soglia, un socket "in più" viene distrutto e
# viene scritto un messaggio nel registro di sistema.

net.ipv4.tcp_tw_reuse=1
#Consenti il riutilizzo dei socket TIME-WAIT nei casi,
#in cui il protocollo lo ritenga sicuro.

net.core.optmem_max=4194304
#Aumentare il massimo buffer generale allocabile
#misurato in unità di pagina (4096 byte)

net.ipv4.tcp_low_latency=1
#Consente allo stack TCP/IP di dare priorità a bassa latenza
#rispetto a una maggiore larghezza di banda.

net.ipv4.tcp_adv_win_scale=1
# Questa variabile influisce sul calcolo della dimensione della memoria nel buffer del socket,
# riservata alla dimensione della finestra TCP e al buffer dell'applicazione.
# Se il valore tcp_adv_win_scale è negativo, per il calcolo della dimensione
# si utilizza la seguente espressione:
# Bytes - bytes2 elevato a -tcp_adv_win_scale
# Dove bytes è la dimensione della finestra in byte. Se il valore tcp_adv_win_scale
# è positivo, per determinare la dimensione viene utilizzata la seguente espressione:
# Bytes - bytes2 elevato a tcp_adv_win_scale
# La variabile assume un valore intero. Il valore predefinito è 2, 
# cioè per il buffer dell'applicazione viene riservato ¼ della dimensione totale definita dalla variabile
# tcp_rmem.

net.ipv4.tcp_slow_start_after_idle=0
# meccanismo di riavvio del lento avvio, che reimposta il valore della finestra 
# di sovraccarico, se la connessione non viene utilizzata per un periodo di tempo specificato.
# È meglio disattivare SSR sui server per migliorare le prestazioni 
# delle connessioni a lungo termine.

net.ipv4.tcp_no_metrics_save=1
#Non salvare i risultati delle misurazioni della connessione TCP nella cache al momento della sua chiusura.

net.ipv4.tcp_syncookies=0
#Disattivare il meccanismo di invio di syncookie

net.ipv4.tcp_ecn=0
#Explicit Congestion Notification (Notifica Esplicita di Congestione) nelle 
#connessioni TCP. Utilizzato per notificare l'insorgenza di un "ingorgo" 
#sul percorso verso un determinato host o rete. Può essere utilizzato per avvisare
#l'host mittente della necessità di ridurre la velocità di trasmissione dei pacchetti attraverso
#un particolare 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
#Disabilita quindi il forwarding. Non siamo un gateway, Docker non è attivo sulle macchine,
#non abbiamo bisogno di questo.

net.ipv4.icmp_echo_ignore_broadcasts=1
#Non rispondiamo a richieste ICMP ECHO inviate tramite pacchetti broadcast

net.ipv4.tcp_fin_timeout=10
#determina il tempo di mantenimento del socket nello stato FIN-WAIT-2 dopo la sua
#chiusura da parte della parte locale. Default 60

net.core.netdev_budget=600 # (predefinito 300)
# Se l'esecuzione delle interruzioni software non dura abbastanza a lungo,
# il tasso di crescita dei dati in entrata potrebbe superare la capacità del nucleo 
# di svuotare il buffer. Di conseguenza, i buffer NIC si riempiranno e il traffico andrà perso.
# A volte è necessario aumentare la durata del lavoro degli SoftIRQ
# (interruzioni software) sul CPU. Questo è gestito da netdev_budget. 
# Il valore predefinito è 300. Il parametro costringerà il processo SoftIRQ a elaborare
# 300 pacchetti dal NIC prima di rilasciare il CPU

net.ipv4.tcp_fastopen=3
# TFO TCP Fast Open
# se sia il client che il server supportano il TFO, di cui comunicano la presenza attraverso
# un flag speciale nel pacchetto TCP. Nel nostro caso è un placebo, semplicemente
# ha un bell'aspetto.

Crete di luster è stata isolata su interfacce di rete da 10 Gbps su una rete piatta separata. Su ogni macchina sono state installate schede di rete a doppia porta mellanox 10/25 Gbps, collegate a due switch da 10 Gbps separati. L'aggregazione è stata realizzata utilizzando OSPF, poiché il bonding con lacp per qualche motivo ha mostrato una larghezza di banda totale massima di 16 Gbps, mentre ospf ha sfruttato con successo entrambe le decine su ciascuna macchina. Nei piani futuri c'era l'intenzione di utilizzare ROCE su questi mellanox, per ridurre la latenza. Come abbiamo configurato questa parte della rete:

  1. Poiché le macchine stesse hanno indirizzi IP esterni su BGP, abbiamo bisogno del software — (e più precisamente al momento della scrittura dell'articolo era frr=6.0-1 ) già installato.
  2. In totale sulle macchine c'erano due schede di rete con due interfacce — in totale 4 porte. Una scheda di rete con due porte guardava verso il fabric e su di essa era configurato BGP, l'altra — con due porte guardava verso due switch diversi e su di essa era configurato OSPF

Ulteriori dettagli sulla configurazione di OSPF: L'obiettivo principale è aggregare due link e garantire tolleranza ai guasti.
due interfacce di rete sono configurate in due semplici reti piatte — 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 ambito globale ens1f0

2: ens1f1:  mtu 9000 qdisc mq stato UP gruppo default qlen 1000
    inet 10.10.20.2/24 brd 10.10.20.255 ambito globale ens1f1

attraverso i quali le macchine si vedono a vicenda.

DISCO

Il passo successivo è stato ottimizzare il funzionamento dei dischi. Per gli SSD ho cambiato il pianificatore in noop, per gli HDD — deadline. In parole semplici, NOOP funziona secondo il principio "chi prima arriva, prima viene servito", che in inglese suona come "FIFO (First In, First Out)". Le richieste si mettono in coda all'arrivo. DEADLINE è più orientato alla lettura, inoltre il processo in coda ottiene praticamente accesso monopolistico al disco durante l'operazione. Per il nostro sistema è perfetto — poiché ogni disco è gestito solo da un processo — OSD daemon.
(Coloro che desiderano approfondire il pianificatore di input/output possono leggerne qui:
http://www.admin-magazine.com/HPC/Articles/Linux-I-O-Schedulers

Coloro che preferiscono leggere in russo: https://www.opennet.ru/base/sys/linux_shedulers.txt.html)

Nelle raccomandazioni per la regolazione di Linux è consigliato anche aumentare nr_request

nr_requests
Il valore di nr_requests determina la quantità di richieste I/O che vengono memorizzate in buffer prima che lo scheduler I/O invii/riceva dati al dispositivo di blocco. Se stai utilizzando un'unità RAID/Device di Blocco che può gestire una coda più grande rispetto a quella impostata dallo scheduler I/O, aumentare il valore di nr_requests può aiutare a migliorare il throughput e ridurre il carico del server quando si verificano grandi quantità di I/O. Se stai usando Deadline o CFQ come scheduler, è suggerito impostare il valore di nr_request a 2 volte il valore della profondità della coda.

MA! Gli stessi sviluppatori cittadini di CEPH ci convincono che il loro sistema di priorità funzioni meglio

Seph — da "fatto a mano" a "produzione"

WBThrottle e/o nr_requests

WBThrottle e/o nr_requests
Lo storage dei file utilizza operazioni di input/output memorizzate in buffer per la scrittura; questo comporta una serie di vantaggi se il journal dello storage dei file si trova su un supporto più veloce. Le richieste dei clienti ricevono notifiche non appena i dati vengono scritti nel journal e poi vengono scaricati sul disco dati vero e proprio in un secondo momento, utilizzando la funzionalità standard di Linux. Questo consente agli OSD degli hard disk di fornire una latenza di scrittura simile a quella degli SSD durante scritture a pacchetti piccoli. Tale scrittura ritardata consente anche al kernel stesso di riorganizzare le richieste di operazioni di I/O al disco, sperando di unirle o permettere alle teste esistenti di seguire un percorso più ottimale sulle proprie piastre. L'effetto finale è che si riesce a spremere leggermente più operazioni di I/O da ogni disco di quanto sarebbe possibile con operazioni di I/O dirette o sincrone.

Tuttavia, sorge un problema se il volume delle scritture in arrivo in questo cluster Ceph supera tutte le capacità dei dischi sottostanti. In tale scenario, il numero complessivo di operazioni di I/O in attesa di scrittura sul disco può crescere in modo incontrollato, risultando in code di operazioni di I/O che riempiono l'intero disco e le code Ceph. Le richieste di lettura ne risentono particolarmente, poiché si bloccano tra le richieste di scrittura, che possono richiedere diversi secondi per essere scaricate sul disco principale.

Per affrontare questo problema, Ceph dispone di un meccanismo di throttling della scrittura in modo posticipato (writeback) chiamato WBThrottle. È progettato per limitare il numero totale di operazioni di input/output in attesa di scrittura che possono accumularsi in coda e iniziare il loro processo di scrittura prima di quanto accadrebbe naturalmente a causa dell'intervento stesso del kernel. Sfortunatamente, i test dimostrano che i valori predefiniti potrebbero comunque non ridurre il comportamento esistente a un livello che possa mitigare questo impatto sulla latenza delle operazioni di lettura. La regolazione può modificare questo comportamento e ridurre le dimensioni complessive delle code di scrittura, rendendo possibile una minore influenza. Tuttavia, esiste un certo compromesso: riducendo il numero massimo totale di scritture consentite in coda, si potrebbe limitare la capacità stessa del kernel di massimizzare l'efficienza nel gestire le richieste in arrivo. È importante riflettere su ciò che è più necessario per il proprio caso d'uso specifico e le proprie esigenze di carico di lavoro, e adattarsi di conseguenza.

Per gestire la profondità di tale coda di scrittura in modo posticipato, è possibile ridurre il numero massimo complessivo di operazioni di input/output non completate utilizzando le impostazioni di WBThrottle, oppure diminuire il valore massimo per le operazioni non completate a livello di blocco nel proprio kernel. Entrambi possono gestire efficacemente lo stesso comportamento e le vostre preferenze saranno alla base dell'implementazione di questa configurazione.
È importante notare che il sistema attuale di priorità delle operazioni in Ceph è più efficace per richieste brevi a livello di disco. Riducendo la coda totale per questo disco, la posizione principale nella coda viene spostata in Ceph, dove ha un maggiore controllo sulla priorità delle operazioni di input/output. Consideriamo il seguente esempio:

echo 8 > /sys/block/sda/queue/nr_requests

http://onreader.mdl.ru/MasteringCeph/content/Ch09.html#030202

COMUNE

E altre impostazioni del kernel che ti permetteranno di rendere la tua macchina più fluida e levigata, estraendo ulteriore performance dall'hardware

cat /etc/sysctl.d/60-ceph2.conf

 kernel.pid_max = 4194303
#Dischi in ogni macchina 25, quindi abbiamo calcolato che ci sarebbero stati molti processi
kernel.threads-max=2097152
# Thread, ovviamente, anche.
vm.max_map_count=524288
# Aumentato il numero di aree di memoria del processo. 
# Come indicato dalla documentazione sulle variabili del kernel 
# Le aree di mappatura della memoria sono utilizzate come effetto collaterale della chiamata
# malloc, direttamente tramite mmap, mprotect e madvise, così come durante il caricamento
# delle librerie comuni.
fs.aio-max-nr=50000000
# Ottimizziamo i parametri di input-output
# Il kernel Linux fornisce una funzione di input-output asincrono non bloccante (AIO),
# che consente ai processi di iniziare più operazioni di input-output
# contemporaneamente, senza attendere il completamento di nessuna di esse. 
# Questo aiuta a migliorare le prestazioni delle applicazioni, 
# che possono sovrapporre l'elaborazione e l'input-output.
# Il parametro aio-max-nr definisce il numero massimo di richieste 
# simultanee consentite.
vm.min_free_kbytes=1048576
# dimensione minima della memoria libera da mantenere.
# Impostato a 1Gb, che è più che sufficiente per il funzionamento del sistema operativo, 
# e consente di evitare l'OM Killer per i processi OSD. Anche se la memoria è
# come le caramelle per un bambino, ma una riserva non guasta
vm.swappiness=10
# Diciamo di utilizzare lo swap se il 10% della memoria è libero.
# Su macchine con 128G di RAM, il 10% sono 12 Gigabyte. Più che sufficiente per il lavoro.
# Il parametro standard al 60% costringeva il sistema a rallentare, accedendo allo swap,
# quando c'è ancora molta memoria libera
vm.vfs_cache_pressure=1000
# Aumentiamo dai 100 standard. Costringiamo il kernel a scaricare più attivamente
# le pagine di memoria non utilizzate dalla cache.
vm.zone_reclaim_mode=0
# Permette di stabilire approcci più o meno aggressivi al
# recupero della memoria, quando nella zona termina la memoria. 
# Se impostato a zero, non avviene nessun recupero della zona.
# Per i file server o i carichi di lavoro
# è vantaggioso se i loro dati sono memorizzati nella cache, il zone_reclaim_mode
# dovrebbe rimanere disattivato, poiché l'effetto di caching, 
# sarà probabilmente più importante della posizione dei dati.
vm.dirty_ratio=20
# Percentuale di memoria RAM che può essere utilizzata per "pagine sporche"
# Calcolato da una stima approssimativa: 
# Il sistema ha 128 giga di memoria.
# Circa 20 dischi SSD, ai quali nelle impostazioni CEPH è specificato 
# di riservare 3G di RAM per la cache.
# Circa 40 dischi HDD, per i quali questo parametro è pari a 1G
# Il 20% di 128 sono 25.6 gigabyte. In totale, nel caso di massima u tilizzazione della memoria,
# per il sistema rimarranno 2.4G di memoria. Dovrebbe bastare per sopravvivere e aspettare
# il galoppo della cavalleria - cioè l'arrivo di un DevOps che riparerà tutto.
vm.dirty_background_ratio=3
# percentuale di memoria di sistema che può essere occupata da pagine sporche fino a quando,
# i processi in background pdflush/flush/kdmflush le scriveranno su disco
fs.file-max=524288
# E anche i file aperti saranno, probabilmente, molti di più di quanto indicato per impostazione predefinita. 

Immersione in 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 # Estendiamo leggermente la dimensione massima
                                        # dell'operazione di scrittura 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 tentato di ottenere supporto
                                        # per questo
    bluestore_block_db_create: true     # Quindi, 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' 
                                            # Grande quantità di RAM consente di 
                                            # memorizzare volumi abbastanza grandi
    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 per il demone su 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 su un 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à. Lo spazio è finito molto rapidamente
    osd_pool_default_size: 2         # a causa del fatto che abbiamo deciso
                                     # di ridurre il numero di 
                                     # 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            # parametro che regoliamo secondo necessità
    osd_recovery_sleep: 2
    osd_scrub_chunk_max: 4

Alcuni parametri testati su QA nella versione 12.2.12 non sono presenti nella versione ceph 12.2.2, per esempio osd_recovery_threads. Pertanto, è stato previsto un aggiornamento in produzione a 12.2.12. La pratica ha dimostrato la compatibilità in un cluster delle versioni 12.2.2 e 12.2.12, il che consente un aggiornamento rolling.

Cluster di test

Naturalmente, per i test era necessario avere la stessa versione che si stava utilizzando in produzione, ma al momento dell'inizio del mio lavoro con il cluster, nel repository c'era solo una versione più recente. Dopo aver notato che le differenze nella versione minore non erano così significative (1393 righe nei file di configurazione rispetto 1436 alla nuova versione), abbiamo deciso di iniziare a testare la nuova (comunque dovevamo aggiornare, quindi non aveva senso restare su materiale obsoleto)

L'unica cosa che abbiamo cercato di mantenere della vecchia versione è stato il pacchetto ceph-deploy, poiché alcune utilità (e parte del personale) erano ottimizzate per la sua sintassi. La nuova versione differiva abbastanza, ma non influiva sul funzionamento del cluster stesso, quindi abbiamo mantenuto la vecchia versione. 1.5.39

Poiché il comando ceph-disk indica chiaramente che è deprecato e suggerisce di utilizzare il comando ceph-volume — abbiamo iniziato a creare OSD proprio con questo comando, senza perdere tempo con qualcosa di obsoleto.

Il piano era il seguente: creare un mirror di due dischi SSD, su cui posizionare i log OSD, i quali, a loro volta, sono ospitati su dischi SAS. In questo modo ci siamo messi al riparo da problemi con i 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 preparato 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, naturalmente ))
rgw_dns_name = s3-qa.mycompany.ru # e anche questo indirizzo è cambiato
rgw_host = s3-qa.mycompany.ru # e anche questo
[mon]
mon allow pool delete = true
mon_max_pg_per_osd = 300 # più di trecento gruppi di posizionamento
                          # non ci siamo avventurati su disco
                     # anche se il parametro, naturalmente, dipende dal numero di pool,
                     # dalle loro dimensioni e dal numero di OSD. Avere pochi ma sani PG
                        # non è nemmeno la scelta migliore - ne risente l'equilibrio
mon_osd_backfillfull_ratio = 0.9
mon_osd_down_out_interval = 5
mon_osd_full_ratio = 0.95 # per ora per i dischi SSD lo spazio per il loro
                          # registro è lo stesso dispositivo per l'OSD
                          # abbiamo pensato che il 5% del disco (che ha una capacità di 1.2Tb)
                          # dovrebbe essere sufficiente e si correla con il parametro
                          # bluestore_block_db_size più la variabilità per gruppi di posizionamento più ampi
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-q

La prima cosa su cui mi sono imbattuto nel lavoro di questa versione di ceph-deploy con un cluster versione 12.2.12 è stato 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/md1

In effetti, blkid non mostra il PARTUUID, quindi 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 l'abbiamo indicata nel config sopra = bluestore_block_db_size: '5368709120 #5G'
# Ho 20 dischi per OSD, è noioso crearle manualmente
# quindi ho fatto un ciclo
root@ceph01-qa:~#for i in {1..20}; do echo -e "nnnn+5Gnw" | fdisk /dev/md0; done

Sembrava tutto pronto, proviamo a creare di nuovo un OSD e otteniamo il seguente errore (che, tra l'altro, non si riproduce in produzione)

durante la creazione di un OSD di tipo bluestore senza specificare il percorso per WAL, ma con l'indicazione del 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 uuid non interpretabile
 stderr: 2019-04-12 10:39:27.213185 7eff461b6e00 -1 bdev(0x55824c273680 /var/lib/ceph/osd/ceph-0//block.wal) apertura aperta è: (22) Argomento non valido
 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) restituito: (22) Argomento non valido
 stderr: 2019-04-12 10:39:27.999039 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) mkfs fallito, (22) Argomento non valido
 stderr: 2019-04-12 10:39:27.999057 7eff461b6e00 -1 OSD::mkfs: ObjectStore::mkfs fallito con errore (22) Argomento non valido
 stderr: 2019-04-12 10:39:27.999141 7eff461b6e00 -1  ** ERRORE: errore durante la creazione di un oggetto store vuoto in /var/lib/ceph/osd/ceph-0/: (22) Argomento non valido

Tuttavia, se su quello stesso mirror (o in un'altra posizione a scelta) creo un'altra partizione per WAL e la specifico durante la creazione dell'OSD, allora tutto andrà liscio (eccetto l'apparizione di un WAL separato, che forse non volevate).

Ma, poiché comunque nei piani futuri c'era l'intenzione di spostare WAL su NVMe, la pratica non si è rivelata superflua.

root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sdf --block.wal /dev/md0p2 --block.db /dev/md1p2

Abbiamo creato monitor, manager e OSD. Ora vogliamo raggrupparli in modo diverso, poiché nei piani ci sono dischi di diversi tipi: pool veloci su SSD e grandi, ma lenti su dischi SAS.

Consideriamo che sui server ci siano 20 dischi, la prima decina è un tipo, la seconda è un altro.
La mappa iniziale, predefinita, appare così:

ceph osd tree

root@ceph01-q:~# ceph osd tree
ID CLASSE PESO TIPO NOME STATO RIVISITAZIONE 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 attivo 1.00000 1.00000
9 hdd 0.27299 osd.9 attivo 1.00000 1.00000
10 hdd 0.27299 osd.10 attivo 1.00000 1.00000
11 hdd 0.27299 osd.11 attivo 1.00000 1.00000
12 hdd 0.27299 osd.12 attivo 1.00000 1.00000
13 hdd 0.27299 osd.13 attivo 1.00000 1.00000
14 hdd 0.27299 osd.14 attivo 1.00000 1.00000
15 hdd 0.27299 osd.15 attivo 1.00000 1.00000
16 hdd 0.27299 osd.16 attivo 1.00000 1.00000
17 hdd 0.27299 osd.17 attivo 1.00000 1.00000
18 hdd 0.27299 osd.18 attivo 1.00000 1.00000
19 hdd 0.27299 osd.19 attivo 1.00000 1.00000
-5 5.45599 host ceph02-q
20 ssd 0.27299 osd.20 attivo 1.00000 1.00000
21 ssd 0.27299 osd.21 attivo 1.00000 1.00000
22 ssd 0.27299 osd.22 attivo 1.00000 1.00000
23 ssd 0.27299 osd.23 attivo 1.00000 1.00000
24 hdd 0.27299 osd.24 attivo 1.00000 1.00000
25 hdd 0.27299 osd.25 attivo 1.00000 1.00000
26 hdd 0.27299 osd.26 attivo 1.00000 1.00000
27 hdd 0.27299 osd.27 attivo 1.00000 1.00000
28 hdd 0.27299 osd.28 attivo 1.00000 1.00000
29 hdd 0.27299 osd.29 attivo 1.00000 1.00000
30 hdd 0.27299 osd.30 attivo 1.00000 1.00000
31 hdd 0.27299 osd.31 attivo 1.00000 1.00000
32 hdd 0.27299 osd.32 attivo 1.00000 1.00000
33 hdd 0.27299 osd.33 attivo 1.00000 1.00000
34 hdd 0.27299 osd.34 attivo 1.00000 1.00000
35 hdd 0.27299 osd.35 attivo 1.00000 1.00000
36 hdd 0.27299 osd.36 attivo 1.00000 1.00000
37 hdd 0.27299 osd.37 attivo 1.00000 1.00000
38 hdd 0.27299 osd.38 attivo 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

Creeremo i nostri rack virtuali e server con blackjack e altro:

root@ceph01-q:~#ceph osd crush add-bucket rack01 root #creato nuovo root
root@ceph01-q:~#ceph osd crush add-bucket ceph01-q host #creato nuovo host
root@ceph01-q:~#ceph osd crush move ceph01-q root=rack01 #spostato server in 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 rack01

Problemi che abbiamo affrontato in produzione cluster, mentre tentavamo di creare un nuovo host e spostarlo in un rack esistente — il comando ceph osd crush move ceph01-host root=rack01 si bloccava, e i monitor cominciavano a cadere uno dopo l'altro. L'interruzione del comando con CTRL+C restituiva il cluster al mondo dei vivi.

La ricerca ha mostrato un problema del genere: https://tracker.ceph.com/issues/23386

La soluzione è stata quella di estrarre il crushmap e rimuovere da esso la sezione rule replicated_ruleset

root@ceph01-prod:~#ceph osd getcrushmap -o crushmap.row #dump della mappa in forma grezza
root@ceph01-prod:~#crushtool -d crushmap.row -o crushmap.txt #convertire in leggibile
root@ceph01-prod:~#vim crushmap.txt #modificare, rimuovendo la regra replicated_ruleset
root@ceph01-prod:~#crushtool -c crushmap.txt -o new_crushmap.row #ricompilare
root@ceph01-prod:~#ceph osd setcrushmap -i new_crushmap.row #caricare nel cluster

Attenzione: questa operazione può causare un riequilibrio del placement group tra gli OSD. È successo anche a noi, ma in modo molto limitato.

La stranezza che abbiamo riscontrato nel cluster di test è che, dopo il riavvio del server, gli OSD si dimenticavano di essere stati spostati nei nuovi server e rack, tornando al root di default.
Alla fine, creando uno schema finale in cui abbiamo creato un root separato per i dischi SSD e uno per gli HDD, abbiamo distribuito tutti gli OSD nei rack e abbiamo semplicemente eliminato il root di default. Dopo il riavvio, gli OSD rimanevano al loro posto.
Successivamente, esplorando la documentazione, abbiamo trovato un parametro che controlla questo comportamento. Ne parleremo nella seconda parte.

Come abbiamo creato diversi gruppi in base ai tipi di dischi.

Per iniziare, abbiamo creato due root: uno per gli SSD e uno per gli HDD.

root@ceph01-q:~#ceph osd crush add-bucket ssd-root root
root@ceph01-q:~#ceph osd crush add-bucket hdd-root root

Poiché i server si trovano fisicamente in rack diversi, per comodità abbiamo creato i rack e all'interno di essi 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 host

e abbiamo distribuito i dischi in base ai loro tipi nei vari server.

root@ceph01-q:~# I dischi da 0 a 3 sono SSD, si trovano in ceph01-q, li mettiamo 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 altri server

Distribuendo i dischi tra i root ssd-root e hdd-root, abbiamo lasciato il root-default vuoto, quindi possiamo eliminarlo.

root-ceph01-q:~#ceph osd crush remove default

In seguito, dobbiamo creare regole di distribuzione che collegheremo ai pool creati — nelle regole indicheremo quali root possono contenere i dati del nostro pool e il livello di unicità della replica — ad esempio, le repliche devono essere necessariamente su server diversi, o in rack diversi (è possibile anche in root diversi, se abbiamo tale distribuzione).

Prima di scegliere il tipo, è meglio leggere la documentazione:
http://docs.ceph.com/docs/jewel/rados/operations/crush-map/#crushmaprules

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 specificato due regole, in cui i dati sono replicati
root-ceph01-q:~# tra i host - cioè la replica deve trovarsi su un host diverso,
root-ceph01-q:~# anche se sono 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 firstn

Creiamo i pool in cui vogliamo 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 1024

E diciamo a questi pool quali regole di distribuzione utilizzare

 root-ceph01-q:~#ceph osd crush rule ls # vediamo l'elenco delle regole
    root-ceph01-q:~#ceph osd crush rule dump rule-ssd | grep rule_id #scegliamo l'ID necessario
    root-ceph01-q:~#ceph osd pool set ssd_pool crush_rule 2

La scelta del numero di placement group deve essere affrontata con una visione precostituita del proprio cluster: quanti OSD ci saranno all'incirca, quale percentuale dei dati (rispetto al volume totale) sarà nel pool e quale quantità di dati ci sarà in totale.

In totale, è preferibile non avere più di 300 placement group per disco, e sarà più semplice bilanciare con placement group di dimensioni ridotte — cioè, se tutto il tuo pool occupa 10 Tb e contiene 10 PG, sarà complicato bilanciare spostando mattoni terabyte (pg) — è più facile e uniforme spostare sabbia con piccole dimensioni di granelli in secchi.

Ma bisogna ricordare che più alto è il numero di PG, maggiore è la quantità di risorse necessarie per calcolarne la posizione — si inizia a utilizzare memoria e CPU.

Una comprensione approssimativa può essere fornita da un calcolatore, fornito dagli sviluppatori della documentazione CEPH.

Elenco dei materiali:

https://blog.packagecloud.io/eng/2017/02/06/monitoring-tuning-linux-networking-stack-sending-data
http://www.admin-magazine.com/HPC/Articles/Linux-I-O-Schedulers
http://onreader.mdl.ru/MasteringCeph/content/Ch09.html#030202
https://tracker.ceph.com/issues/23386
https://ceph.com/pgcalc/

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster