Alegerea CEPH. Partea 1
Aveam cinci rack-uri, zece switch-uri optice, BGP configurat, câteva zeci de SSD-uri și o mulțime de hard disk-uri SAS de toate culorile și dimensiunile, precum și proxmox și dorința de a stoca toată statică în propriul depozit S3. Nu că toate acestea ar fi fost necesare pentru virtualizare, dar dacă am început să folosesc open source — atunci trebuie să continui cu pasiunea ta. Singurul lucru care mă îngrijora era BGP-ul. Nu există pe lume nimeni mai neajutorat, iresponsabil și nemoral decât rutarea internă prin BGP. Știam că în curând ne vom adânci în asta.

Sarcina era banală — exista un CEPH, care nu funcționa foarte bine. Trebuia să facem ca acesta să funcționeze "bine".
Clusterul pe care l-am primit era heterogen, configurat pe fugă și practic netuneat. Era format din două grupuri diferite de noduri, cu o rețea comună care îndeplinea rolul atât de cluster, cât și de rețea publică. Nodurile erau dotate cu patru tipuri de diskuri — două tipuri de SSD-uri, organizate în două reguli de plasare separate și două tipuri de HDD-uri de dimensiuni diferite, organizate într-un al treilea grup. Problema dimensiunilor diferite a fost rezolvată prin greutăți diferite pentru OSD.
Configurarea a fost împărțită în două părți — tuning-ul sistemului de operare și tuning-ul CEPH-ului în sine și a setărilor sale.
Optimizarea OS-ului
Rețea
Latencile mari au afectat atât scrierea, cât și balansarea. La scriere — pentru că clientul nu va primi confirmarea unei scrieri reușite până când replicile de date din celelalte grupuri de plasare nu confirmă succesul. Deoarece regulile de distribuire a replicilor din mapa CRUSH erau configurate pentru o replică pe gazdă, rețeaua era utilizată întotdeauna.
Prin urmare, primul lucru pe care l-am decis a fost să configurez ușor rețeaua existentă, încercând în același timp să conving de a trece la rețele separate.
Am început prin a ajusta setările plăcilor de rețea. Am început cu setările canalelor:
ce aveam:
ethtool -l ens1f1
root@ceph01:~# ethtool -l ens1f1
Parametrii canalelor pentru ens1f1:
Maxime pre-setate:
RX: 0
TX: 0
Altele: 1
Combinat: 63
Setări hardware curente:
RX: 0
TX: 0
Altele: 1
Combinat: 1
root@ceph01:~# ethtool -g ens1f1
Parametrii ring-ului pentru ens1f1:
Maxime pre-setate:
RX: 4096
RX Mini: 0
RX Jumbo: 0
TX: 4096
Setări hardware curente:
RX: 256
RX Mini: 0
RX Jumbo: 0
TX: 256
root@ceph01:~# ethtool -l ens1f1
Parametrii canalelor pentru ens1f1:
Maxime pre-setate:
RX: 0
TX: 0
Altele: 1
Combinat: 63
Setări hardware curente:
RX: 0
TX: 0
Altele: 1
Combinat: 1Se observă că parametrii actuali sunt departe de maxime. Am crescut:
root@ceph01:~#ethtool -G ens1f0 rx 4096
root@ceph01:~#ethtool -G ens1f0 tx 4096
root@ceph01:~#ethtool -L ens1f0 combined 63Ghidat de un articol excelent
am crescut lungimea cozii de trimitere txqueuelen de la 1000 la 10 000
root@ceph01:~#ip link set ens1f0 txqueuelen 10000Și urmărind documentația lui ceph
am crescut MTU până la 9000.
root@ceph01:~#ip link set dev ens1f0 mtu 9000Am adăugat în /etc/network/interfaces, pentru ca tot ce este menționat mai sus să se încarce la pornire
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 9000După care, urmând același articol, am început să ajustez cu grijă parametrii kernelului 4.15. Având în vedere că nodurile au 128G RAM, am obținut un fel de fișier de configurare pentru sysctl
cat /etc/sysctl.d/50-ceph.conf
net.core.rmem_max = 56623104
#Dimensiunea maximă a buffer-ului de primire a datelor pentru toate conexiunile 54M
net.core.wmem_max = 56623104
#Dimensiunea maximă a buffer-ului de transmisie a datelor pentru toate conexiunile 54M
net.core.rmem_default = 56623104
#Dimensiunea buffer-ului de primire a datelor implicit pentru toate conexiunile. 54M
net.core.wmem_default = 56623104
#Dimensiunea buffer-ului de transmisie a datelor implicit pentru toate conexiunile 54M
# pe fiecare socket
net.ipv4.tcp_rmem = 4096 87380 56623104
#Variabilă vectorială (minim, implicit, maxim) în fișierul tcp_rmem
# conține 3 numere întregi care definesc dimensiunea buffer-ului de primire a socket-urilor TCP.
# Minim: fiecare socket TCP are dreptul de a utiliza această memorie la
# momentul creării sale. Posibilitatea utilizării acestui buffer
# este garantată chiar și în cazul atingerii limitelor (presiune moderată asupra memoriei).
# Dimensiunea buffer-ului minim implicit este de 8 Kb (8192).
#Valoarea implicită: cantitatea de memorie permisă pentru buffer-ul
# transmisiei socket-ului TCP implicit. Această valoare se aplică în locul
# parametrului /proc/sys/net/core/rmem_default, utilizat de alte protocoale.
# Dimensiunea buffer-ului utilizat implicit este de obicei (implicit)
# 87830 octeți. Aceasta definește dimensiunea feronului 65535 cu
# valoarea implicită a tcp_adv_win_scale și tcp_app_win = 0,
# ceva mai mică decât valoarea implicită a tcp_app_win.
# Maxim: dimensiunea maximă a buffer-ului care poate fi alocată automat
# pentru primirea socket-ului TCP. Această valoare nu anulează maximul,
# specificat în fișierul /proc/sys/net/core/rmem_max. În cazul alocării „statice”
# de memorie prin SO_RCVBUF acest parametru nu are relevanță.
net.ipv4.tcp_wmem = 4096 65536 56623104
net.core.somaxconn = 5000
# Numărul maxim de socket-uri deschise care așteaptă conexiuni.
net.ipv4.tcp_timestamps=1
# Permite utilizarea timpurilor (timestamps), conform RFC 1323.
net.ipv4.tcp_sack=1
# Permite confirmarea selectivă a protocolului TCP
net.core.netdev_max_backlog=5000 (implicit 1000)
# numărul maxim de pachete în așteptare pentru procesare, dacă
# interfața primește pachete mai repede decât kernelul le poate procesa.
net.ipv4.tcp_max_tw_buckets=262144
# Numărul maxim de socket-uri aflate în stare de TIME-WAIT simultan.
# La depășirea acestui prag – socket-ul „extras” este distrus și un
# mesaj este scris în jurnalul de sistem.
net.ipv4.tcp_tw_reuse=1
#Permitem reutilizarea socket-urilor TIME-WAIT în cazurile,
# dacă protocolul consideră că este sigur.
net.core.optmem_max=4194304
#Crește dimensiunea maximă a buffer-ului total ALLOCATABLE
# măsurată în unități de pagină (4096 octeți)
net.ipv4.tcp_low_latency=1
#Permite stivei TCP/IP să acorde prioritate latenței scăzute
# în detrimentul unei capacități mai mari de transmisie.
net.ipv4.tcp_adv_win_scale=1
# Această variabilă influențează calculul volumului de memorie în bufferul socket-ului,
# dedicat dimensiunii feronului TCP și buffer-ului aplicației.
# Dacă valoarea tcp_adv_win_scale este negativă, atunci pentru calcularea dimensiunii
# se utilizează următoarea expresie:
# Bytes - bytes2 la puterea -tcp_adv_win_scale
# Unde bytes – este dimensiunea feronului în octeți. Dacă valoarea tcp_adv_win_scale
# este pozitivă, atunci pentru determinarea dimensiunii se folosește următoarea expresie:
# Bytes - bytes2 la puterea tcp_adv_win_scale
# Variabila poate lua o valoare întreagă. Valoarea implicită este 2,
# adică pentru buffer-ul aplicației se alocă ¼ din volumul definit de variabila
# tcp_rmem.
net.ipv4.tcp_slow_start_after_idle=0
# mecanism de reincepere lentă a startului, care resetează valoarea feronului
# de suprasarcină, dacă conexiunea nu a fost utilizată pentru o perioadă dată de timp.
# Mai bine să dezactivăm SSR pe server pentru a îmbunătăți performanța
# conexiunilor pe termen lung.
net.ipv4.tcp_no_metrics_save=1
#Nu salva rezultatele măsurărilor TCP în cache la închiderea acestuia.
net.ipv4.tcp_syncookies=0
#Dezactivează mecanismul de trimitere syncookie
net.ipv4.tcp_ecn=0
#Notificarea Explicită a Congestiei (ECN) în
# conexiunile TCP. Este utilizată pentru a anunța apariția unui „blocat”
# pe traseul către un anumit gazdă sau rețea. Poate fi folosită pentru a notifica
# gazda-expeditor despre necesitatea reducerii vitezei de transmisie a pachetelor prin
# anumite routere sau firewall-uri.
net.ipv4.conf.all.send_redirects=0
# dezactivează emiterea ICMP Redirect … către alte gazde. Această opțiune trebuie
# să fie activată dacă gazda acționează ca un router de orice fel.
# Nu avem rutare.
net.ipv4.ip_forward=0
#De fapt, dezactivarea rutării. Nu suntem un gateway, Docker pe mașini nu este pornit,
# nu avem nevoie de aceasta.
net.ipv4.icmp_echo_ignore_broadcasts=1
#Nu răspundem la cererile ICMP ECHO transmise de pachetele de difuzare
net.ipv4.tcp_fin_timeout=10
#definește timpul de păstrare a socket-ului în stare FIN-WAIT-2 după închiderea acestuia
# de către partea locală. Implicit 60
net.core.netdev_budget=600 # (implicit 300)
# Dacă execuția întreruperilor software nu durează suficient de mult,
# atunci rata de creștere a datelor de intrare poate depăși capacitatea kernelului
# de a goli buffer-ul. Ca urmare, buffer-urile NIC se vor umple, iar traficul va fi pierdut.
# Uneori, este necesar să se mărească durata de funcționare a SoftIRQs.
# (întreruperi software) de la CPU. Acest lucru este responsabil netdev_budget.
# Valoarea implicită este 300. Parametrul va determina procesul SoftIRQ să proceseze
# 300 de pachete de la NIC înainte de a elibera CPU-ul.
net.ipv4.tcp_fastopen=3
# TFO TCP Fast Open
# dacă atât clientul cât și serverul au suport TFO, de care se raportează printr-un
# bandă specială în pachetul TCP. În cazul nostru acesta este un placebo, pur și simplu
# arată bine)Curețea luster a fost alocată pe interfețe de rețea separate de 10Gbps într-o rețea plată separată. Fiecare mașină a fost echipată cu plăci de rețea cu două porturi mellanox 10/25 Gbps, conectate la două switch-uri separate de 10Gbps. Agregarea a fost realizată cu ajutorul OSPF, deoarece bonding-ul cu lacp dintr-un motiv oarecare a arătat o capacitate maximă totală de 16 Gbps, în timp ce ospf a utilizat cu succes complet cele două 10 pe fiecare mașină. Planurile viitoare includeau utilizarea ROCE pe aceste mellanox-uri, pentru a reduce latența. Cum am configurat această parte a rețelei:
- Deoarece mașinile au adrese IP externe pe BGP, avem nevoie de software-ul — (sau mai bine spus, în momentul scrierii acestui articol, acesta era ) era deja instalat.
- În total, pe mașini erau două interfețe de rețea — în total 4 porturi. O placă de rețea cu două porturi privea către fabrică, iar BGP era configurat pe aceasta, iar cealaltă — cu două porturi privea către două switch-uri diferite și OSPF era direcionat pe aceasta
Detalii despre configurarea OSPF: Sarcina principală — de a agrega două link-uri și a avea toleranță la defecțiuni.
două interfețe de rețea sunt configurate în două rețele plate simple — 10.10.10.0/24 și 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 ens1f1pe care mașinile se văd între ele.
DISK
Următorul pas a fost să optimizez funcționarea discurilor. Pentru SSD-uri, am schimbat planificatorul în noop, pentru HDD — deadline. În termeni simpli — NOOP funcționează pe principiul „cine se ridică primul, acela primește încălțămintea”, ceea ce în engleză se numește „FIFO (First In, First Out)”. Cererile se așează în coadă pe măsură ce apar. DEADLINE este mai bine optimizat pentru citire, plus că procesul din coadă obține practic acces monopol la disc în momentul operației. Pentru sistemul nostru, aceasta se potrivește perfect — deoarece cu fiecare disc lucrează doar un singur proces — daemon-ul OSD.
(Cei care doresc să se adâncească în planificatorul de intrare-ieșire pot citi despre el aici:
Cei care preferă să citească în rusă: )
În recomandările pentru optimizarea Linux-ului se sugerează de asemenea să se mărească nr_request
nr_requests
Valoarea nr_requests determină cantitatea de cereri I/O care sunt tamponate înainte ca programatorul I/O să trimită / primească date către dispozitivul de bloc. Dacă folosești un card RAID / Dispozitiv de Bloc care poate gestiona o coadă mai mare decât cea setată pentru programatorul I/O, creșterea valorii nr_requests poate ajuta la îmbunătățirea performanței și la reducerea încărcării serverului atunci când se desfășoară cantități mari de I/O pe server. Dacă utilizezi Deadline sau CFQ ca programator, se sugerează că ar trebui să stabilești valoarea nr_request la 2 ori valoarea adâncimii cozii.
ÎNSĂ! Dezvoltatorii CEPH ne conving că sistemul lor de priorități funcționează mai bine.

WBThrottle și/sau nr_requests.
WBThrottle și/sau nr_requests.
Stocarea fișierelor folosește operațiuni de intrare/ieșire tamponate pentru scriere; acest lucru aduce o serie de avantaje dacă jurnalul de stocare a fișierelor se află pe un suport mai rapid. Cererile clienților primesc notificări de îndată ce datele sunt scrise în jurnal și apoi sunt scrise pe discul de date mai târziu, utilizând funcționalitatea standard a Linux. Acest lucru face posibil ca OSD-urile cu discuri rotative să ofere latența scrierii similară cu cea a SSD-urilor pentru scrieri în blocuri mici. Această scriere întârziată permite, de asemenea, kernelului să recompileze cererile de operațiuni de intrare/ieșire către disc cu speranța de a le combina sau de a permite capetelor de disc existente să aleagă un drum mai optim pe plăcile lor. Efectul final este că poți obține puțin mai multe operațiuni de intrare/ieșire de la fiecare disc decât ar fi posibil cu operațiuni de intrare/ieșire directe sau sincrone.
Cu toate acestea, apare o problemă dacă volumul de înregistrări despre care se primesc în acest cluster Ceph depășește capacitățile discurilor de bază. În acest scenariu, numărul total de operațiuni de intrare/ieșire aflate în așteptare pentru scriere pe disc poate crește necontrolat, rezultând o coadă de operațiuni de intrare/ieșire care umple întregul disc și coada Ceph. Cererile de citire sunt afectate în special, deoarece acestea rămân blocate între cererile de scriere, care pot necesita câteva secunde pentru a fi scrise pe discul principal.
Pentru a depăși această problemă, Ceph dispune de un mecanism integrat în stocarea fișierelor pentru throttling-ul scrierii întârziante, denumit WBThrottle. Acesta este conceput pentru a restricționa volumul total de operațiuni de intrare/ieșire pentru scrierile întârziante, care pot fi plasate în coadă și pot începe procesul de descărcare mai devreme decât ar face-o în mod natural prin activarea de către nucleu. Din păcate, testările demonstrează că valorile implicite încă pot să nu limiteze comportamentul existent la un nivel care să reducă impactul asupra latenței operațiunilor de citire. Ajustarea poate schimba acest comportament și reduce lungimile totale ale cozilor de scriere, făcând posibil un impact mai redus. Cu toate acestea, există un compromis: reducând numărul maxim total de înregistrări permise în coadă, puteți diminua capacitatea nucleului de a maximiza eficiența în ordonarea cererilor primite. Merită să vă gândiți la ceea ce este mai necesar pentru cazul vostru specific de utilizare și sarcinile de muncă și să reglați pentru a se conforma acestora.
Pentru a gestiona adâncimea acestei cozi de scriere întârziată, puteți fie să reduceți numărul total maxim de operațiuni de intrare/ieșire nefinalizate, aplicând setările WBThrottle, fie să reduceți valoarea maximă pentru operațiunile nefinalizate la nivelul blocului din nucleul vostru. Ambele pot gestiona eficient același comportament, iar preferințele voastre vor sta la baza implementării acestei setări.
De asemenea, trebuie menționat că sistemul de priorități al operațiunilor din Ceph este mai eficient pentru cereri mai scurte la nivelul discului. Atunci când se reduce coada totală pentru acest disc, poziția principală în coadă se mută în Ceph, unde are mai mult control asupra priorității operațiunii de intrare/ieșire. Iată un exemplu:
echo 8 > /sys/block/sda/queue/nr_requestsCOMMON
Și câteva setări suplimentare ale nucleului care permit să faceți hardware-ul vostru mai fluid și mai elegant, extrăgând puțin mai multă performanță din echipament.
cat /etc/sysctl.d/60-ceph2.conf
kernel.pid_max = 4194303
#Discurile din fiecare mașină sunt 25, așa că am estimat că vor fi multe procese
kernel.threads-max=2097152
# Firele, desigur, la fel.
vm.max_map_count=524288
# Am crescut numărul de zone de memorie ale procesului.
# După cum se menționează în documentația variabilelor de kernel
# Zonele de memorie sunt utilizate ca efect secundar al apelurilor
# malloc, direct prin mmap, mprotect și madvise, precum și la încărcarea
# bibliotecilor comune.
fs.aio-max-nr=50000000
# Optimisăm parametrii de input-output
# Kernel-ul Linux oferă funcția de input-output asincron non-blocant (AIO),
# care permite procesului să inițieze mai multe operațiuni de input-output
# simultan, fără a aștepta finalizarea uneia dintre acestea.
# Aceasta ajută la îmbunătățirea performanței aplicațiilor,
# care pot suprapune procesarea și input-output-ul.
# Parametrul aio-max-nr definește numărul maxim de
# solicitări simultane permise.
vm.min_free_kbytes=1048576
# dimensiunea minimă a memoriei libere care trebuie menținută.
# Setat la 1Gb, ceea ce este suficient pentru funcționarea sistemului de operare,
# și permite evitarea OOM Killer pentru procesele OSD. Deși memorie avem din belșug,
# totuși, este bine să avem un rezerva de memorie.
vm.swappiness=10
# spunem să folosim swap dacă rămân 10% din memorie liber.
# Pe mașinile cu 128G RAM, 10% este 12 Giga. Mai mult decât suficient pentru funcționare.
# Parametrul standard de 60% făcea să încetinească sistemul, intrând în swap,
# când mai era încă o mulțime de memorie liberă.
vm.vfs_cache_pressure=1000
# Creștem față de cei 100 standard. Forțăm kernel-ul să descarce
# paginile de memorie nefolosite din cache.
vm.zone_reclaim_mode=0
# Permite stabilirea unor abordări mai agresive sau mai puțin agresive
# pentru recuperarea memoriei, atunci când memoria din zonă se epuizează.
# Dacă este setat pe zero, nu se va realiza recuperarea zonei.
# Pentru serverele de fișiere sau sarcinile de lucru
# este avantajos să aibă datele cache-uite, iar zonele de recuperare
# ar trebui să rămână dezactivate, deoarece efectul de caching
# va fi probabil mai important decât locația datelor.
vm.dirty_ratio=20
# Procentul de memorie RAM care poate fi alocat pentru paginile "murdare"
# Calculat pe baza unei estimări:
# În sistem sunt 128 de giga de memorie.
# Aproximativ 20 de discuri SSD, care în setările CEPH sunt definite
# să aloce 3G de RAM pentru caching.
# Aproximativ 40 de discuri HDD, pentru care acest parametru este 1G.
# 20% din 128 este 25.6 giga. Astfel, în cazul utilizării maxime a memoriei,
# pentru sistem vor rămâne 2.4G de memorie. Ceea ce ar trebui să fie suficient
# pentru a supraviețui și a aștepta
# sosirea cavaleriei - adică venirea DevOps care va repara totul.
vm.dirty_background_ratio=3
# procentul de memorie de sistem care poate fi umplut cu pagini murdare până când,
# procesele de fundal pdflush/flush/kdmflush le scriu pe disc.
fs.file-max=524288
# Și numărul de fișiere deschise va fi, probabil, mult mai mare decât ceea ce este specificat implicit. Imersiune în CEPH
Setările asupra cărora am dori să ne oprim mai în detaliu:
cat /etc/ceph/ceph.conf
osd:
journal_aio: true # Trei parametri care includ
journal_block_align: true # i/o direct
journal_dio: true # pe jurnal
journal_max_write_bytes: 1073714824 # Să extindem puțin dimensiunea maximă
# a operațiunii scrise o singură dată în jurnal
journal_max_write_entries: 10000 # Numărul de înregistrări simultane
journal_queue_max_bytes: 10485760000
journal_queue_max_ops: 50000
rocksdb_separate_wal_dir: true # Decidem să facem un wal separat
# Am încercat chiar să creăm ceva pentru asta
# NVMe
bluestore_block_db_create: true # Și un dispozitiv separat pentru jurnal
bluestore_block_db_size: '5368709120 #5G'
bluestore_block_wal_create: true
bluestore_block_wal_size: '1073741824 #1G'
bluestore_cache_size_hdd: '3221225472 # 3G'
# un volum mare de RAM permite
# stocarea unor cantități considerabile
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 # numărul de fire la demon pentru un singur disc
osd_failsafe_full_ratio: 0.95
osd_heartbeat_grace: 5
osd_heartbeat_interval: 3
osd_map_dedup: true
osd_max_backfills: 2 # numărul de operațiuni simultane de umplere pe 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 # Caracteristici de lăcomie. S-a ajuns foarte repede
osd_pool_default_size: 2 # să lipsească spațiu, deoarece soluția temporară
# a fost să reducem numărul
# de replici de date
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 # parametru reglat după necesitate în timp real
osd_recovery_sleep: 2
osd_scrub_chunk_max: 4O parte din parametrii care au fost testați la QA pe versiunea 12.2.12, lipsesc în versiunea ceph 12.2.2, de exemplu osd_recovery_threads. Prin urmare, s-a planificat actualizarea în producție la 12.2.12. Practica a arătat compatibilitatea într-un cluster între versiunea 12.2.2 și 12.2.12, ceea ce permite realizarea unui rolling update.
Cluster de test
Este naturalmente, pentru testare a fost necesară utilizarea aceleași versiuni ca în producție, dar la începutul lucrului meu cu clusterul, în repo era disponibilă doar o versiune mai nouă. Observând că diferențele în versiunea minoră nu sunt semnificative,1393 liniile din configurații comparativ cu 1436 versiunea nouă), am decis să înceapă testarea versiunii noi (oricum trebuia să facem actualizarea, așa că de ce să ne pierdem timpul cu vechiturile)
Singurul lucru pe care am încercat să-l păstrăm din versiunea veche a fost pachetul ceph-deploy, deoarece o parte din utilitare (și o parte din angajați) erau obținute pentru sintaxa sa. Noua versiune era destul de diferită, dar nu afecta funcționarea clusterului, așa că am păstrat versiunea 1.5.39
Deoarece echipa ceph-disk spune clar că este depreciată și recomandă folosirea comenzii ceph-volume — am început să creăm OSD-uri exact cu această comandă, fără a pierde timp cu cele învechite.
Planul a fost să creăm un mirror din două SSD-uri, pe care să plasăm jurnalele OSD, care, la rândul lor, se află pe SAS-uri tradiționale. Astfel, ne asigurăm împotriva problemelor cu datele în cazul unei căderi a discului cu jurnal.
Clusterul a început să fie creat conform documentației.
cat /etc/ceph/ceph.conf
root@ceph01-qa:~# cat /etc/ceph/ceph.conf # am pus un fișier de configurare pregătit anterior
[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 # adresa a fost schimbată, evident ))
rgw_dns_name = s3-qa.mycompany.ro # și această adresă a fost schimbată
rgw_host = s3-qa.mycompany.ro # și aceasta la fel
[mon]
mon allow pool delete = true
mon_max_pg_per_osd = 300 # nu ne-am decis să avem peste trei sute de grupuri de plasare
# pe disc
# deși parametrul, evident, depinde de numărul de pool-uri,
# dimensiunile și numărul OSD. A avea puține dar sănătoase PG
# nu este nici el cea mai bună alegere - afectează precizia balansării
mon_osd_backfillfull_ratio = 0.9
mon_osd_down_out_interval = 5
mon_osd_full_ratio = 0.95 # pentru SSD-uri, locul pentru jurnalul lor
# este același dispozitiv ca și pentru OSD
# am decis că 5% din disc (care are dimensiunea de 1.2Tb)
# ar trebui să fie suficient, și corelează cu parametrul
# bluestore_block_db_size plus variabilitatea pentru grupurile mari
# de plasare
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-qPrimul lucru peste care m-am împiedicat în utilizarea acestei versiuni de ceph-deploy cu clusterul versiunii 12.2.12 a fost o eroare la încercarea de a crea un OSD cu DB pe un RAID software —
root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sde --block.db /dev/md0
blkid nu a putut detecta un PARTUUID pentru dispozitivul: /dev/md1Adevărat, blkid nu arată PARTUUID, a trebuit să creez partiții manual:
root@ceph01-qa:~#parted /dev/md0 mklabel GPT
# vor fi multe partiții,
# fără GPT nu se pot crea
# dimensiunea partiției am specificat-o în configurația de mai sus = bluestore_block_db_size: '5368709120 #5G'
# Am 20 de discuri pentru OSD, să creez partiții manual este obositor
# așa că am făcut un ciclu
root@ceph01-qa:~#for i in {1..20}; do echo -e "nnnn+5Gnw" | fdisk /dev/md0; doneSe pare că totul este gata, încercăm din nou să creăm OSD și primim următoarea eroare (care, de altfel, nu apare în producție)
la crearea unui OSD de tip bluestore fără specificarea căii către WAL, dar cu specificarea 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 imposibil de parcurs
stderr: 2019-04-12 10:39:27.213185 7eff461b6e00 -1 bdev(0x55824c273680 /var/lib/ceph/osd/ceph-0//block.wal) open got: (22) Argument invalid
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) Argument invalid
stderr: 2019-04-12 10:39:27.999039 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) mkfs a eșuat, (22) Argument invalid
stderr: 2019-04-12 10:39:27.999057 7eff461b6e00 -1 OSD::mkfs: ObjectStore::mkfs a eșuat cu eroarea (22) Argument invalid
stderr: 2019-04-12 10:39:27.999141 7eff461b6e00 -1 ** EROARE: eroare la crearea magazinului de obiecte gol în /var/lib/ceph/osd/ceph-0/: (22) Argument invalidTotuși, dacă pe același raid (sau în alt loc, la alegere) creez o altă partiție pentru WAL și o specific la crearea OSD — atunci totul va decurge fără probleme (cu excepția apariției unui WAL separat, pe care poate nu l-ați dorit).
Dar, deoarece aveam planuri pe termen lung să mut WAL pe NVMe, experiența nu a fost în zadar.
root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sdf --block.wal /dev/md0p2 --block.db /dev/md1p2Am creat monitoare, manageri și OSD. Acum vreau să le grupeze diferit, deoarece am planuri să am discuri de diferite tipuri — pool-uri rapide pe SSD și mari, dar lente pe hard disk-uri SAS.
Să presupunem că pe servere sunt 20 de discuri, prima zecime este un tip, a doua — altul.
Diagrama inițială, implicită, arată așa:
ceph osd tree
root@ceph01-q:~# ceph osd tree
ID CLASĂ GREUTATE TIP NUME STATUS REGREGAȚIE PRI-AFF
-1 14.54799 rădăcină implicită
-3 9.09200 gazdă ceph01-q
0 ssd 1.00000 osd.0 activ 1.00000 1.00000
1 ssd 1.00000 osd.1 activ 1.00000 1.00000
2 ssd 1.00000 osd.2 activ 1.00000 1.00000
3 ssd 1.00000 osd.3 activ 1.00000 1.00000
4 hdd 1.00000 osd.4 activ 1.00000 1.00000
5 hdd 0.27299 osd.5 activ 1.00000 1.00000
6 hdd 0.27299 osd.6 activ 1.00000 1.00000
7 hdd 0.27299 osd.7 activ 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 up 1.00000 1.00000
-7 6.08690 host ceph03-q
40 ssd 0.27299 osd.40 up 1.00000 1.00000
41 ssd 0.27299 osd.41 up 1.00000 1.00000
42 ssd 0.27299 osd.42 up 1.00000 1.00000
43 ssd 0.27299 osd.43 up 1.00000 1.00000
44 hdd 0.27299 osd.44 up 1.00000 1.00000
45 hdd 0.27299 osd.45 up 1.00000 1.00000
46 hdd 0.27299 osd.46 up 1.00000 1.00000
47 hdd 0.27299 osd.47 up 1.00000 1.00000
48 hdd 0.27299 osd.48 up 1.00000 1.00000
49 hdd 0.27299 osd.49 up 1.00000 1.00000
50 hdd 0.27299 osd.50 up 1.00000 1.00000
51 hdd 0.27299 osd.51 up 1.00000 1.00000
52 hdd 0.27299 osd.52 up 1.00000 1.00000
53 hdd 0.27299 osd.53 up 1.00000 1.00000
54 hdd 0.27299 osd.54 up 1.00000 1.00000
55 hdd 0.27299 osd.55 up 1.00000 1.00000
56 hdd 0.27299 osd.56 up 1.00000 1.00000
57 hdd 0.27299 osd.57 up 1.00000 1.00000
58 hdd 0.27299 osd.58 up 1.00000 1.00000
59 hdd 0.89999 osd.59 up 1.00000 1.00000
Vom instala rack-uri și servere virtuale cu blackjack și alte lucruri:
root@ceph01-q:~#ceph osd crush add-bucket rack01 root #am creat un nou root
root@ceph01-q:~#ceph osd crush add-bucket ceph01-q host #am creat un nou host
root@ceph01-q:~#ceph osd crush move ceph01-q root=rack01 #am mutat server-ul într-un alt rack
root@ceph01-q:~#osd crush add 28 1.0 host=ceph02-q # Am adăugat OSD în server
# Dacă a fost creat aiurea, poate fi șters
root@ceph01-q:~# ceph osd crush remove osd.4
root@ceph01-q:~# ceph osd crush remove rack01Problemele cu care ne-am confruntat în clusterele de luptă, când am încercat să creăm un nou host și să-l mutăm într-un rack existent — comanda ceph osd crush move ceph01-host root=rack01 s-a blocat, iar monitorii au început să cadă unul câte unul. Interuperea comenzii cu un simplu CTRL+C a readus cluster-ul în lumea celor vii.
Cercetarea a arătat o problemă de acest tip:
Soluția a fost să facem dump de crushmap și să eliminăm secțiunea rule replicated_ruleset
root@ceph01-prod:~#ceph osd getcrushmap -o crushmap.row #dumpăm harta în forma brută
root@ceph01-prod:~#crushtool -d crushmap.row -o crushmap.txt #transformăm în format citibil
root@ceph01-prod:~#vim crushmap.txt #edităm, ștergând rule replicated_ruleset
root@ceph01-prod:~#crushtool -c crushmap.txt -o new_crushmap.row #recompilăm
root@ceph01-prod:~#ceph osd setcrushmap -i new_crushmap.row #încărcăm în clusterAtenție: această operațiune poate provoca reechilibrarea grupului de plasare între OSD-uri. La noi a provocat, dar într-o măsură foarte mică.
Iar ciudățenia cu care ne-am confruntat în clusterul de testare este că, după repornirea serverului, OSD-urile uitau că au fost mutate pe servere și rafturi noi și reveneau la root default.
În final, având schema finală, în care am creat un root separat pentru SSD-uri și altul pentru HDD-uri, am distribuit toate OSD-urile pe rafturi și pur și simplu am șters root-ul default. După repornire, OSD-urile au rămas la locul lor.
Cercetând ulterior documentația, am găsit parametrul care este responsabil pentru acest comportament. Despre acesta în partea a doua.
Cum am creat diverse grupuri în funcție de tipul discurilor.
Pentru început, am creat două root-uri – pentru SSD și pentru HDD.
root@ceph01-q:~#ceph osd crush add-bucket ssd-root root
root@ceph01-q:~#ceph osd crush add-bucket hdd-root rootÎncărcând fizic serverele în rafturi diferite – pentru comoditate, am creat rafturi și în acestea serverele.
# Стойки:
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și am distribuit discurile în funcție de tipurile lor pe servere diferite.
root@ceph01-q:~# Discurile de la 0 la 3 sunt SSD-uri, se află în ceph01-q, le instalăm pe 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:~# similar cu celelalte servereDistribuind discurile între root-urile ssd-root și hdd-root, am lăsat root-default gol, deci îl putem șterge.
root-ceph01-q:~#ceph osd crush remove defaultApoi, trebuie să creăm reguli de distribuție, pe care le vom lega de piscinele create – în reguli vom specifica în ce root-uri se pot plasa datele piscinei noastre și nivelul de unicitate a replicatilor – de exemplu, replicatul trebuie să fie neapărat pe servere diferite sau în rafturi diferite (se poate chiar în root-uri diferite, dacă avem o astfel de distribuție).
Înainte de a alege tipul, este mai bine să citim documentația:
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:~# Am specificat două reguli, în care datele sunt replicate
root-ceph01-q:~# între gazde – adică replicatul trebuie să fie pe altă gazdă,
root-ceph01-q:~# chiar dacă sunt în același raft
root-ceph01-q:~# În producție, dacă este posibil, este mai bine să distribui gazdele
root-ceph01-q:~# pe rafturi și să specificăm distribuirea replicatelor pe rafturi:
root-ceph01-q:~# ##ceph osd crush rule create-simple rule-ssd ssd-root rack firstnAșadar, creăm pool-uri în care vrem să stocăm imaginile discurilor noastre de virtualizare – 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Și le spunem acestor pool-uri ce reguli de plasare să folosească.
root-ceph01-q:~#ceph osd crush rule ls # vedem lista regulilor
root-ceph01-q:~#ceph osd crush rule dump rule-ssd | grep rule_id # alegem ID-ul necesar
root-ceph01-q:~#ceph osd pool set ssd_pool crush_rule 2
Alegerea numărului de grupuri de plasare trebuie făcută având un plan bine definit pentru clusterul nostru – câte OSD-uri vor fi acolo, ce pondere de date (în procente din volumul total) va fi în pool, cât volum total de date.
Este preferabil să nu avem mai mult de 300 de grupuri de plasare pe disc, iar va fi mai simplu să se echilibreze cu grupuri de plasare mai mici – adică, dacă întregul pool ocupă 10 Tb și are 10 PG – atunci va fi problematic să se echilibreze prin trasferarea de blocuri de un terabyte (pg) – e mai ușor și mai uniform să „împrăștii” nisip cu granule mici prin găleți.
Dar trebuie să ne amintim că, cu cât numărul de PG crește, cu atât mai multe resurse sunt consumate pentru a calcula amplasamentul lor – încep să fie utilizate memoria și CPU-ul.
O înțelegere aproximativă poate , furnizat de dezvoltatorii documentației CEPH.
Lista materialelor:
Sursa: habr.com
