Ceph — od "na koleně" po "produkci"

Výběr CEPH. Část 1

Měli jsme pět racků, deset optických switchů, nastavený BGP, pár desítek SSD a spoustu SAS disků všech barev a velikostí, a také proxmox a přání umístit veškerou statiku do vlastního S3 úložiště. Není to tak, že by to všechno bylo potřeba pro virtualizaci, ale pokud už používáš opensource — jdi ve svém nadšení až do konce. Jediné, co mě znepokojovalo — bylo BGP. Na světě neexistuje nikdo bezmocnější, nezodpovědnější a nemorálnější než vnitřní směrování přes BGP. A věděl jsem, že se do toho docela brzy ponoříme.

Ceph — od "na koleně" po "produkci"

Úkol byl prostý — měl jsem CEPH, který nefungoval příliš dobře. Bylo potřeba to udělat "dobře".
Kluster, který mi přišel do rukou, byl heterogenní, nastavený narychlo a prakticky netuněný. Skládal se ze dvou skupin různých uzlů, s jednou společnou sítí sloužící jako cluster i veřejná síť. Uzly byly osazeny čtyřmi typy disků — dvěma typy SSD, seskupenými do dvou různých pravidel umístění, a dvěma typy HDD různé velikosti, seskupenými do třetí skupiny. Problém s různými velikostmi byl vyřešen různými váhami OSD.

Samotné nastavení jsme rozdělili na dvě části — ladění operačního systému a ladění samotného CEPHu a jeho nastavení.

Vylepšení OS

Síť

Vysoké latence se projevovalo jak při zápisu, tak při vyvažování. Při zápisu — protože klient nedostane odpověď o úspěšném zápisu, dokud repliky dat v jiných skupinách umístění nepotvrdí úspěch. Protože pravidla distribuování replik v CRUSH mapě byla taková, že máme jednu repliku na hostitele, tak se síť vždy používala.

Proto jsem se jako první rozhodl mírně upravit aktuální síť, zároveň jsem se snažil přesvědčit k přechodu na oddělené sítě.

Nejprve jsem se podíval na nastavení síťových karet. Začal jsem s nastavením front:

co bylo:

ethtool -l ens1f1

root@ceph01:~# ethtool -l ens1f1
Parametry kanálu pro ens1f1:
Přednastavené maximum:
RX:     0
TX:     0
Jiné:      1
Slučovací:   63
Aktuální hardwarové nastavení:
RX:     0
TX:     0
Jiné:      1
Slučovací:   1
root@ceph01:~# ethtool -g ens1f1
Parametry fronty pro ens1f1:
Přednastavené maximum:
RX:     4096
RX Mini:    0
RX Jumbo:   0
TX:     4096
Aktuální hardwarové nastavení:
RX:     256
RX Mini:    0
RX Jumbo:   0
TX:     256
root@ceph01:~# ethtool -l ens1f1
Parametry kanálu pro ens1f1:
Přednastavené maximum:
RX:     0
TX:     0
Jiné:      1
Slučovací:   63
Aktuální hardwarové nastavení:
RX:     0
TX:     0
Jiné:      1
Slučovací:   1

Je vidět, že aktuální parametry jsou daleko od maximálních. Zvýšil jsem:

root@ceph01:~#ethtool -G ens1f0 rx 4096
root@ceph01:~#ethtool -G ens1f0 tx 4096
root@ceph01:~#ethtool -L ens1f0 combined 63

Podle skvélého článku

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

zvýšil délku fronty odeslání txqueuelen z 1000 na 10 000

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

A podle dokumentace Cephu

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

zvýšil MTU na 9000.

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

Přidal do /etc/network/interfaces, aby se všechna výše uvedená nastavení načítala při startu

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

Po čemž, podle tohoto článku, jsem začal postupně upravovat jádro 4.15. Vzhledem k tomu, že uzly mají 128 GB RAM, vznikl jakýsi konfigurační soubor pro sysctl

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

net.core.rmem_max = 56623104  
#Maximální velikost příjmového datového bufferu pro všechna připojení  54M

net.core.wmem_max = 56623104
#Maximální velikost výstupního datového bufferu pro všechna připojení 54M

net.core.rmem_default = 56623104
#Výchozí velikost příjmového datového bufferu pro všechna připojení. 54M

net.core.wmem_default = 56623104
#Výchozí velikost výstupního datového bufferu pro všechna připojení 54M  
# na každý socket

net.ipv4.tcp_rmem = 4096 87380 56623104
#Vektor (minimum, výchozí, maximum) proměnná v souboru tcp_rmem
# obsahuje 3 celá čísla definující velikost příjmového bufferu TCP socketů.
# Minimum: každý TCP socket má nárok používat tuto paměť od 
# okamžiku svého vytvoření. Možnost použití takového bufferu 
# je zaručena i při dosažení prahu omezení (moderate memory pressure).
# Velikost minimálního bufferu je defaultně 8 Kbyte (8192).
#Výchozí hodnota: množství paměti povolené pro buffer 
# výstupu socketu TCP výchozí. Tato hodnota se používá jako
# náhrada za parametr /proc/sys/net/core/rmem_default, používaný jinými protokoly.
# Výchozí velikost bufferu používaného obvykle
# (defaultně) činí 87830 bajtů. To určuje velikost okna 65535 s 
# daným výchozím nastavením tcp_adv_win_scale a tcp_app_win = 0, 
# poněkud menší, než určuje běžně používané výchozí nastavení tcp_app_win.
# Maximum: maximální velikost bufferu, která může být automaticky
# přidělena pro příjem TCP socketu. Tato hodnota nezrušuje maximum,
# které je zadáno v souboru /proc/sys/net/core/rmem_max. Při "statickém"
# přidělení paměti pomocí SO_RCVBUF tento parametr nemá význam.

net.ipv4.tcp_wmem = 4096 65536 56623104
net.core.somaxconn = 5000    
# Maximální počet otevřených socketů čekajících na připojení.

net.ipv4.tcp_timestamps=1
# Povolit používání časových značek (timestamps) v souladu s RFC 1323.

net.ipv4.tcp_sack=1
# Povolit selektivní potvrzení TCP protokolu

net.core.netdev_max_backlog=5000 (výchozí 1000)
# maximální počet paketů ve frontě na zpracování, pokud 
# rozhraní přijímá pakety rychleji, než je jádro dokáže zpracovat.

net.ipv4.tcp_max_tw_buckets=262144
# Maximální počet socketů v stavu TIME-WAIT současně.
# Při překročení této hranice je "navíc" socket zničen a
# je zapsána zpráva do systémového logu.

net.ipv4.tcp_tw_reuse=1
#Povolujeme opětovné použití TIME-WAIT socketů v případech,
# kdy protokol považuje toto za bezpečné.

net.core.optmem_max=4194304
#Zvýšit maximální celkovou velikost bufferu ALLOCATABLE
# měřeno ve stránkových jednotkách (4096 bajtů)

net.ipv4.tcp_low_latency=1
#Povolit TCP/IP zásobníku upřednostnit nízkou latenci
# před vyšší propustností.

net.ipv4.tcp_adv_win_scale=1
# Tato proměnná ovlivňuje výpočet objemu paměti v bufferu socketu,
# vyčleněnou na velikost TCP okna a pro buffer aplikace.
# Pokud je hodnota tcp_adv_win_scale záporná, pak pro výpočet velikosti
# se používá následující výraz:
# Bytes - bytes^(-tcp_adv_win_scale)
# Kde bytes – to je velikost okna v bajtech. Pokud je hodnota tcp_adv_win_scale
# kladná, pak pro určení velikosti se používá následující výraz:
# Bytes - bytes^tcp_adv_win_scale
# Proměnná přijímá celé číslo. Výchozí hodnota je 2, 
# tzn. pro buffer aplikace je vyčleněna ¼ objemu, definovaného proměnnou
# tcp_rmem.

net.ipv4.tcp_slow_start_after_idle=0
# mechanismus restartu pomalého startu, který resetuje hodnotu okna 
# přetížení, pokud spojení nebylo používáno po stanovenou dobu.
# Lepší je vypnout SSR na serveru pro zlepšení výkonnosti 
# dlouhotrvajících spojení.

net.ipv4.tcp_no_metrics_save=1
#Nepamatovat si výsledky měření TCP spojení v cache při jeho uzavření.

net.ipv4.tcp_syncookies=0
#Vypnout mechanismus zasílání syncookie

net.ipv4.tcp_ecn=0
#Explicitní oznámení o přetížení (Explicit Congestion Notification) v 
# TCP připojeních. Používá se k upozornění na vznik "zácpy" 
# na cestě k určenému hostiteli nebo síti. Může být použito pro informování
# odesílajícího hostitele o potřebě snížit rychlost přenosu paketů přes
# konkrétní směrovač nebo firewall.

net.ipv4.conf.all.send_redirects=0
# vypíná vydávání ICMP Redirect … dalším hostům. Tato volba musí být
# povinně zapnuta, pokud hostitel vystupuje jako směrovač jakéhokoli druhu.
# Nemáme žádnou routování.

net.ipv4.ip_forward=0
#V podstatě vypíná forwarding. Nejsme brána, Docker na strojích není zvednut,
# to nepotřebujeme.

net.ipv4.icmp_echo_ignore_broadcasts=1
#Neodpovídáme na ICMP ECHO požadavky předávané širokopásmovými pakety

net.ipv4.tcp_fin_timeout=10
#určuje dobu udržování socketu ve stavu FIN-WAIT-2 po jeho
# uzavření místní stranou. Výchozí 60

net.core.netdev_budget=600 # (výchozí 300)
# Pokud se zpracování softwarových přerušení neprovádí dostatečně dlouho,
# pak může tempo růstu příchozích dat překročit schopnost jádra 
# vyprázdnit buffer. V důsledku toho se vyplní buffery NIC a dojde k ztrátě provozu.
# Někdy je nutné zvýšit dobu zpracování SoftIRQs
# (softwarová přerušení) s CPU. O to se stará netdev_budget. 
# Výchozí hodnota 300. Tento parametr přinutí proces SoftIRQ zpracovat
# 300 paketů od NIC před tím, než uvolní CPU

net.ipv4.tcp_fastopen=3
# TFO TCP Fast Open
# pokud jak klient, tak server mají podporu TFO, o které informují díky
# speciální vlajce v TCP paketu. V našem případě je to placebo, prostě
# to vypadá hezky)

Sluster network byla vyhrazena na samostatných 10Gbps síťových rozhraních v samostatné ploché síti. Na každém stroji byly nainstalovány síťové dvoupenné karty mellanox 10/25 Gbps, zapojené do dvou samostatných 10Gbps switchů. Agregace byla realizována pomocí OSPF, protože bonding s lacp z nějakého důvodu prokázal maximální celkovou šířku pásma 16 Gbps, zatímco ospf úspěšně využíval plně obě desítky na každém stroji. V budoucnu bylo v plánu využít ROCE na těchto melanochs, aby se snížila latence. Jak jsme nastavili tuto část sítě:

  1. Jelikož samotné stroje mají externí IP adresy na BGP, potřebujeme software - (přesněji v době psaní článku to bylo frr=6.0-1 ) už nainstalované.
  2. Celkem bylo na strojích dvě síťové karty se dvěma rozhraními — celkem 4 porty. Jeden síťový adaptér se dvěma porty byl připojen na fabrik, kde byl nastaven BGP, druhý — se dvěma porty se připojil do dvou různých switchů a na něj byl spuštěn OSPF

Podrobnosti k nastavení OSPF: Hlavním cílem je agregovat dva linky a mít fault tolerance.
dva síťové rozhraní byly nastaveny do dvou jednoduchých plochých sítí — 10.10.10.0/24 a 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 ens1f1

podle kterých se stroje vzájemně vidí.

DISK

Dalším krokem jsem se rozhodl optimalizovat práci disků. Pro SSD jsem změnil plánovač na noop, pro HDD — deadline. Zjednodušeně řečeno — NOOP funguje na principu „kdo první vstane — toho také tapky“, což se anglicky nazývá „FIFO (First In, First Out)“. Požadavky se zařazují do fronty podle toho, jak přicházejí. DEADLINE je více zaměřen na čtení, navíc proces z fronty získává prakticky monopolní přístup k disku v okamžiku operace. Pro naše systémy to výborně vyhovuje — vždyť s každým diskem pracuje pouze jeden proces — OSD daemon.
(Ti, kteří chtějí proniknout do plánovače I/O, si o něm mohou přečíst zde:
http://www.admin-magazine.com/HPC/Articles/Linux-I-O-Schedulers

Upřednostňující číst v ruštině: https://www.opennet.ru/base/sys/linux_shedulers.txt.html)

V doporučeních pro ladění Linuxu se také doporučuje zvýšit nr_request

nr_requests
Hodnota nr_requests určuje množství I/O požadavků, které se vyrovnávají, než I/O plánovač odešle/přijme data do zařízení pro blokové ukládání. Pokud používáte RAID kartu/název zařízení, které dokáže zpracovat větší frontu, než je nastavena na I/O plánovači, zvýšení hodnoty nr_requests může přispět ke zlepšení propustnosti a snížení zatížení serveru při velkém množství operací I/O na serveru. Pokud používáte Deadline nebo CFQ jako plánovač, doporučuje se nastavit hodnotu nr_requests na dvojnásobek hodnoty hloubky fronty.

ALE! Samotní občané vývojáři CEPH nás přesvědčují, že jejich prioritní systém funguje lépe

Ceph — od "na koleně" po "produkci"

WBThrottle a/nebo nr_requests

WBThrottle a/nebo nr_requests
Souborové úložiště využívá vyrovnané operace I/O pro zápis; to přináší celou řadu výhod, pokud je souborový žurnál na rychlejším médiu. Požadavky klientů dostávají oznámení, jakmile jsou data zapsána do žurnálu, a poté jsou v pozdější době vyprázdněna na hlavní datový disk pomocí standardní funkcionality Linuxu. To umožňuje OSD točivým diskům poskytovat latenci zápisu srovnatelnou s SSD při zápisech malými balíčky. Tato zpožděná zápis v režimu odloženého zápisu také umožňuje samotnému jádru reorganizovat požadavky operací I/O na disk s nadějí, že je buď sloučí, nebo umožní stávajícím hlavám disku zvolit optimálnější cestu přes své disky. Konečným efektem je, že můžete vyždímat o něco více operací I/O z každého disku, než by to bylo možné při přímých nebo synchronních operacích I/O.

Nicméně se vyskytuje určitý problém, pokud objem příchozích záznamů do tohoto Ceph clusteru překročí všechny možnosti ležících disků. V tomto scénáři může celkový počet zvažovaných operací I/O čekajících na zápis na disk nekontrolovaně růst a mít za následek fronty operací I/O, které zaplní celý disk a fronty Ceph. Požadavky na čtení mají obzvláště špatný vliv, protože uvíznou mezi zápisovými požadavky, které mohou vyžadovat několik sekund k vyprázdnění na hlavní disk.

Aby překonali tento problém, má Ceph vestavěný mechanismus zpožděného zápisu s názvem WBThrottle, který je součástí souborového úložiště. Tento mechanismus je navržen tak, aby omezil celkový objem zpožděných operací vstupu/výstupu, které mohou být zařazeny do fronty a začít svůj proces vyprázdnění dříve, než by se to stalo přirozeným způsobem samo jádro. Bohužel testování ukazuje, že výchozí hodnoty stále nemusí omezit stávající chování na úroveň, která by mohla snížit dopad na latentnost čtecích operací. Úprava může toto chování změnit a snížit celkové délky zápisových front, což může omezit tento dopad. Nicméně existuje určitý kompromis: snížením celkového maximálního počtu povolených zápisů ve frontě můžete snížit schopnost samotného jádra maximálně optimalizovat svoji efektivitu při zařazování příchozích požadavků. Je dobré se zamyslet, co je pro vás důležitější pro váš konkrétní případ použití, pracovní zátěže a přizpůsobit se jim.

Pro řízení hloubky této fronty zpožděného zápisu můžete buď snížit celkový maximální počet neprovedených operací vstupu/výstupu použitím nastavení WBThrottle, nebo snížit maximální hodnotu pro neprovedené operace na úrovni bloku samotného jádra. Obě metody mohou efektivně řídit stejné chování a vaším preferencím bude odpovídat implementace tohoto nastavení.
Je také důležité poznamenat, že stávající systém priorit operací v Ceph je efektivnější pro kratší požadavky na diskové úrovni. Snížením celkové fronty pro tento disk se hlavní pozice v čekání přesune na Ceph, kde má větší kontrolu nad tím, jakou prioritu má operace vstupu/výstupu. Zvažme následující příklad:

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

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

OBECNÉ

A několik dalších nastavení jádra, které umožní vaší mašině být hladkou a jemnou a vytáhnout ještě trochu výkonu z hardwaru.

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

 kernel.pid_max = 4194303
# V každém stroji je 25 disků, takže jsme počítali, že procesů bude hodně
kernel.threads-max=2097152
# Samozřejmě také vlákna.
vm.max_map_count=524288
# Zvýšili jsme počet oblastí mapy paměti procesu.
# Jak vyplývá z dokumentace o jádrových proměnných
# Oblasti mapy paměti se používají jako vedlejší efekt volání
# malloc, přímo pomocí mmap, mprotect a madvise, a také při načítání
# sdílených knihoven.
fs.aio-max-nr=50000000
# Optimalizujeme parametry pro vstup-výstup
# Jádro Linuxu poskytuje funkci asynchronního neblokujícího vstupu-výstupu (AIO),
# která umožňuje procesu iniciovat několik operací vstupu-výstupu
# současně, aniž by čekal na dokončení některého z nich.
# To pomáhá zvyšovat výkon aplikací,
# které mohou překrývat zpracování a vstup-výstup.
# Parametr aio-max-nr určuje maximální počet povolených
# současných požadavků.
vm.min_free_kbytes=1048576
# minimální velikost volné paměti, kterou je třeba udržovat.
# Nastaveno na 1Gb, což je dostatečné pro chod operačního systému,
# a umožňuje se vyhnout OOM Killer pro procesy OSD. I když je paměti
# dost, vždycky je lepší mít rezervu.
vm.swappiness=10
# Říkáme, že swap bude použit, pokud zůstane 10 % paměti volné.
# Na strojích s 128G operační paměti je 10 % to 12 GB. Více než dostatečné.
# Výchozí parametr 60 % zpomaloval systém, přičemž se dostával na swap,
# když bylo ještě spousta volné paměti.
vm.vfs_cache_pressure=1000
# Zvyšujeme z výchozích 100. Nutíme jádro aktivněji vykládat
# nepoužívané stránky paměti z cache.
vm.zone_reclaim_mode=0
# Umožňuje nastavovat více či méně agresivní přístupy k
# obnovování paměti, když v zóně dochází paměť.
# Pokud je nastaven na nulu, neprobíhá obnova zóny.
# Pro souborové servery nebo pracovní zatížení
# je výhodné, pokud jsou data cachována, zone_reclaim_mode
# nechat vypnuté, protože efekt cachování
# pravděpodobně bude důležitější než umístění dat.
vm.dirty_ratio=20
# Procento operační paměti, které lze vyčlenit na "špinavé" stránky
# Vypočítáno z orientačního výpočtu:
# V systému 128 gigabajtů paměti.
# Přibližně 20 SSD disků, které mají v nastavení CEPH uvedeno
# vyčlenit na cachování po 3G operační paměti.
# Přibližně 40 HDD disků, pro které je tento parametr roven 1G
# 20 % z 128 je 25.6 gigabajtů. Celkově, v případě maximálního využití paměti,
# by mělo pro systém zůstat 2.4G paměti. Což by mělo stačit, aby přežil a vyčkal
# na příjezd kavalérie - tedy příchod DevOps, který vše opraví.
vm.dirty_background_ratio=3
# procento systémové paměti, které lze zaplnit "špinavými" stránkami, než
# pozadí procesy pdflush/flush/kdmflush je zapíší na disk
fs.file-max=524288
# No a otevřených souborů u nás pravděpodobně bude mnohem více, než je uvedeno jako výchozí. 

Ponoření do CEPH

Nastavení, na kterých bychom se chtěli podrobněji zastavit:

cat /etc/ceph/ceph.conf

osd:
    journal_aio: true               # Tři parametry, které zahrnují 
    journal_block_align: true       # přímý i/o
    journal_dio: true               # do deníku
    journal_max_write_bytes: 1073714824 # Trochu zvětšíme maximální velikost
                                        # jednorázové operace zapisování do deníku
    journal_max_write_entries: 10000    # A počet současných záznamů
    journal_queue_max_bytes: 10485760000 
    journal_queue_max_ops: 50000
    rocksdb_separate_wal_dir: true      # Rozhodli jsme se vytvořit samostatný wal
                                        # Dokonce jsme se pokusili vyčlenit místo                                                                                                                                                                                     
                                        # pro NVMe
    bluestore_block_db_create: true     # A pro deník samostatné zařízení
    bluestore_block_db_size: '5368709120 #5G'
    bluestore_block_wal_create: true
    bluestore_block_wal_size: '1073741824   #1G' 
    bluestore_cache_size_hdd: '3221225472   # 3G' 
                                            # velký objem RAM umožňuje 
                                            # uchovávat dostatečně velké objemy
    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 # počet threadů u démona na jeden disk
    osd_failsafe_full_ratio: 0.95
    osd_heartbeat_grace: 5
    osd_heartbeat_interval: 3
    osd_map_dedup: true
    osd_max_backfills: 2 # počet současných operací plnění na jedno 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     # Vlastnosti žravosti. Velmi rychle začne
    osd_pool_default_size: 2         # docházet místo, jelikož dočasné 
                                     # řešení spočívalo v snížení počtu 
                                     # replik dat
    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            # parametr upravujeme podle potřeby
    osd_recovery_sleep: 2
    osd_scrub_chunk_max: 4

Některé parametry, které byly testovány na QA na verzi 12.2.12, chybí ve verzi ceph 12.2.2, například osd_recovery_threads. Proto bylo zahrnuto v plánech upgrade na prod na 12.2.12. Praxe ukázala kompatibilitu v jednom clusteru mezi verzemi 12.2.2 a 12.2.12, což umožňuje provést rolling update.

Testovací cluster

Samozřejmě, pro testování bylo nutné mít stejnou verzi jako na produkci, ale v době, kdy jsem začal pracovat s clusterem, byla v repozitáři pouze novější verze. Po zhodnocení, že rozdíly v minoritní verzi nejsou příliš velké,1393 řádky v konfiguračních souborech proti 1436 (nová verze), jsme se rozhodli začít testovat novou verzi (tak či onak je třeba aktualizovat, proč se trápit se starým harampádím)

Jediné, co jsme se snažili zachovat ze staré verze, byl balíček ceph-deploy, protože část utilit (a část zaměstnanců) byla přizpůsobena jejímu syntaktis. Nová verze se výrazně lišila, ale neměla žádný vliv na funkčnost samotného clusteru, takže ji zachovali ve verzi 1.5.39

Jelikož nástroj ceph-disk jasně uvádí, že je zastaralý a doporučuje používat nástroj ceph-volume – začali jsme vytvářet OSD právě tímto nástrojem, aniž bychom ztráceli čas se zastaralým.

Plán byl následující – vytvořit zrcadlo ze dvou SSD disků, na kterých umístíme deníky OSD, které se samotné nacházejí na rotačních SAS discích. Tak se pojistíme proti problémům s daty v případě selhání disku s deníkem.

Začali jsme vytvářet cluster podle dokumentace

cat /etc/ceph/ceph.conf

root@ceph01-qa:~# cat /etc/ceph/ceph.conf # předem připravená konfigurace
[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 byla změněna, pochopitelně ))
rgw_dns_name = s3-qa.mycompany.ru # a tato adresa byla změněna
rgw_host = s3-qa.mycompany.ru # a tato také
[mon]
mon allow pool delete = true
mon_max_pg_per_osd = 300 # více než tři sta umístění skupin
                          # na disk nepřicházelo v úvahu
                     # i když parametr závisí na počtu bazénů,
                     # jejich velikosti a počtu OSD. Mít málo, ale zdravé PG
                        # také není nejlepší volba - trpí přesnost vyvážení
mon_osd_backfillfull_ratio = 0.9
mon_osd_down_out_interval = 5
mon_osd_full_ratio = 0.95 # pro SSD disky je prostor pro jejich
                          # žurnál stejná jednotka jako pro OSD
                          # rozhodli jsme, že 5% z disku (který má velikost 1.2Tb)
                          # by mělo být dostatečné, a souvisí s parametrem
                          # bluestore_block_db_size plus variabilita pro větší
                          # umístění skupiny
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

První, o co jsem se v této verzi ceph-deploy s clusterem verze 12.2.12 zakopnul, byla chyba při pokusu o vytvoření OSD s db na softwarovém RAIDu —

root@ceph01-qa:~#ceph-volume lvm create --bluestore --data /dev/sde --block.db /dev/md0
blkid nemohl detekovat PARTUUID pro zařízení: /dev/md1

Opravdu, blkid neukazuje PARTUUID, musel jsem vytvořit oddíly ručně:

root@ceph01-qa:~#parted /dev/md0 mklabel GPT 
# oddílů bude hodně, 
# bez GPT je nelze vytvořit
# velikost oddílu jsme uvedli v konfiguraci výše = bluestore_block_db_size: '5368709120 #5G'
# Mám 20 disků pro OSD, ručně vytvářet oddíly se mi nechtělo
# proto jsem udělal cyklus
root@ceph01-qa:~#for i in {1..20}; do echo -e "nnnn+5Gnw" | fdisk /dev/md0; done

Vypadá to, že je vše připraveno, zkusíme znovu vytvořit OSD a dostáváme tuto chybu (která se mimochodem na produkci neprojevila)

při vytváření OSD typu bluestore bez určení cesty k WAL, ale s určením 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 nepřístupné uuid
 stderr: 2019-04-12 10:39:27.213185 7eff461b6e00 -1 bdev(0x55824c273680 /var/lib/ceph/osd/ceph-0//block.wal) otevřeno: (22) Neplatný argument
 stderr: 2019-04-12 10:39:27.213201 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) _open_db přidat blokové zařízení(/var/lib/ceph/osd/ceph-0//block.wal) vrátilo: (22) Neplatný argument
 stderr: 2019-04-12 10:39:27.999039 7eff461b6e00 -1 bluestore(/var/lib/ceph/osd/ceph-0/) mkfs selhalo, (22) Neplatný argument
 stderr: 2019-04-12 10:39:27.999057 7eff461b6e00 -1 OSD::mkfs: ObjectStore::mkfs selhalo s chybou (22) Neplatný argument
 stderr: 2019-04-12 10:39:27.999141 7eff461b6e00 -1  ** CHYBA: chyba při vytváření prázdného objektového úložiště v /var/lib/ceph/osd/ceph-0/: (22) Neplatný argument

Ale pokud na tomtéž zrcadle (nebo na jiném místě, podle výběru) vytvoříte další oddíl pro WAL a uvedete ho při vytváření OSD — půjde to hladce (s výjimkou vzniku odděleného WAL, který jste možná nechtěli).

Ale protože byly stejně v dalekých plánech přesunout WAL na NVMe, nebyla praxe nadbytečná.

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

Vytvořili jsme monitory, manažery a OSD. Teď bychom je chtěli seskupit různě, protože plánujeme mít disky různých typů — rychlé pooly na SSD a velké, ale pomalé na SAS discích.

Budeme předpokládat, že na serverech je 20 disků, první desetka to je jeden typ, druhá — jiný.
Původní, výchozí, mapa vypadá takto:

ceph osd tree

root@ceph01-q:~# ceph osd tree
ID TŘÍDA HMOTNOST TYP NÁZEV STATUS ZNOVUHMOTNĚNÍ PRI-AFF
-1 14.54799 kořen výchozí
-3 9.09200 host ceph01-q
0 ssd 1.00000 osd.0 nahoru 1.00000 1.00000
1 ssd 1.00000 osd.1 nahoru 1.00000 1.00000
2 ssd 1.00000 osd.2 nahoru 1.00000 1.00000
3 ssd 1.00000 osd.3 nahoru 1.00000 1.00000
4 hdd 1.00000 osd.4 nahoru 1.00000 1.00000
5 hdd 0.27299 osd.5 nahoru 1.00000 1.00000
6 hdd 0.27299 osd.6 nahoru 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 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

Vytvoříme si virtuální stojany a servery s blackjackem a dalšími:

root@ceph01-q:~#ceph osd crush add-bucket rack01 root #vytvořili jsme nový root
root@ceph01-q:~#ceph osd crush add-bucket ceph01-q host #vytvořili jsme nový host
root@ceph01-q:~#ceph osd crush move ceph01-q root=rack01 #přesuňte server do jiného stojanu
root@ceph01-q:~#osd crush add 28 1.0 host=ceph02-q # přidali jsme OSD do serveru

# Pokud jsme to vytvořili špatně, můžeme to odstranit
root@ceph01-q:~# ceph osd crush remove osd.4
root@ceph01-q:~# ceph osd crush remove rack01

Problémy, se kterými jsme se setkali v produkčním klastru, při pokusu o vytvoření nového hostitele a jeho přesunutí do existujícího stojanu — příkaz ceph osd crush move ceph01-host root=rack01 se zasekával a monitory začaly padat po jednom. Přerušení příkazu jednoduchým CTRL+C vrátilo klastr zpět do stavu živých.

Hledání ukázalo tento problém: https://tracker.ceph.com/issues/23386

Řešení spočívalo v dumpování crushmapy a odstranění sekce rule replicated_ruleset

root@ceph01-prod:~#ceph osd getcrushmap -o crushmap.row #Vytáhnout mapu v surovém formátu
root@ceph01-prod:~#crushtool -d crushmap.row -o crushmap.txt #převést do čitelného formátu
root@ceph01-prod:~#vim  crushmap.txt #upravujeme, odstraníme pravidlo replicated_ruleset
root@ceph01-prod:~#crushtool -c crushmap.txt  -o new_crushmap.row #kompilovat zpět
root@ceph01-prod:~#ceph osd setcrushmap -i  new_crushmap.row #načíst do clusteru

Pozor: tato operace může způsobit přerozdělení placement group mezi OSD. U nás to způsobilo, ale jen velmi málo.

Podivnost, se kterou jsme se setkali v testovacím clusteru, je, že po restartu serveru OSD zapomněli, že byli přesunuti na nové servery a racky, a vrátili se zpět do root default.
Nakonec jsme vytvořili konečnou schéma, kde jsme vytvořili zvlášť root pro SSD disky a zvlášť pro plotnové, veškeré OSD jsme distribuovali po rackech a prostě odstranili default root. Po restartu OSD zůstali na svých místech.
Po dalším prozkoumání dokumentace jsme našli parametr, který odpovídá za toto chování. O něm ve druhé části

Jak jsme dělali různé skupiny podle typů disků.

Nejprve jsme vytvořili dva rooty — pro SSD a pro HDD

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

Protože servery jsou fyzicky umístěny v různých rackách — pro pohodlí jsme vytvořili racky a do nich jsme umístili servery

# Стойки:
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

a rozdělili disky podle jejich typů na různé servery

root@ceph01-q:~# Disky od 0 do 3 jsou SSD, nacházejí se v ceph01-q, umístíme je na 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:~# podobně s dalšími servery

Po rozložení disků podle rootů ssd-root a hdd-root jsme nechali root-default prázdný, takže ho můžeme odstranit

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

Dále je třeba vytvořit pravidla distribuce, která budeme přiřazovat k vytvářeným poolům — v pravidlech určíme, do jakých rootů lze umístit data našeho poolu a úroveň unikátnosti repliky — například repliky musí být nutně na různých serverech, nebo v různých rackech (mohou být dokonce v různých rootech, pokud máme takové rozložení)

Před tím, než zvolíte typ, je lepší si přečíst dokumentaci:
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:~# Uvedli jsme dvě pravidla, ve kterých se data replikují
root-ceph01-q:~# mezi hostiteli - to znamená, že replikát by měl být na jiném hostiteli,
root-ceph01-q:~# i když jsou v jedné skříni.
root-ceph01-q:~# V produkci, pokud je to možné, je lepší rozložit hostitele
root-ceph01-q:~# podle skříní a uvést, že repliky by měly být rozloženy po skříních:
root-ceph01-q:~# ##ceph osd crush rule create-simple rule-ssd ssd-root rack firstn

A vytváříme pooly, ve kterých chceme v budoucnu uchovávat obrazy disků naší virtualizace — 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

A říkáme těmto poolům, jakými pravidly umístění se mají řídit.

 root-ceph01-q:~#ceph osd crush rule ls # zobrazení seznamu pravidel
    root-ceph01-q:~#ceph osd crush rule dump rule-ssd | grep rule_id #vybíráme potřebné ID
    root-ceph01-q:~#ceph osd pool set ssd_pool crush_rule 2

K volbě počtu umístění skupin je nutné přistupovat s předem vytvořenou vizí o svém clustru — kolik přibližně OSD tam bude, jaké množství dat (v procentech od celkového objemu) bude v poolu, kolik dat bude celkem.

Celkově je žádoucí mít maximálně 300 umístění skupin na disk, a jednodušší bude vyvážení malými umístěními skupinami — to znamená, že pokud celý váš pool zabírá 10 Tb a má 10 PG — vyvážení přenášením terabytových cihel (pg) bude problematické — přesouvání písku s malými zrny písku do kyblíků je jednodušší a rovnoměrnější).

Ale je nutné mít na paměti, že čím více PG — tím více zdrojů se vynakládá na výpočet jejich umístění — začíná se využívat paměť a CPU.

Přibližné pochopení může poskytnout kalkulačka, kterou poskytli vývojáři dokumentace CEPH.

Seznam materiálů:

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/

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster