
Nazywam się Jurij, jestem kierownikiem grupy administracji systemów w Sitimobil. Dziś podzielę się doświadczeniem pracy z technologią thin provisioning systemów plików Linux i opowiem, jak można ją zastosować w technologicznych procesach CI/CD w firmie. Rozważymy sytuację, w której podczas automatycznego testowania kodu przy wdrażaniu go do produkcji potrzebne nam są jak najszybsze kopie bazy danych MySQL, maksymalnie zbliżone do wersji "produkcyjnej", dostępne do odczytu i zapisu.
Wprowadzenie: dlaczego dawać szkodliwe rady?
Logiczne pytanie, bo są opracowane mechanizmy migracji schematów baz danych do środowisk testowych. Po co w ogóle doprowadzać główną, niepodzieloną bazę danych do takich rozmiarów? A poza tym, do testowania potrzebne są nie wszystkie dane. Postaram się wyjaśnić.
Około rok temu, w obliczu aktywnego wzrostu naszego agregatora taksówek (w 2018 roku liczba zakończonych kursów wzrosła około 15 razy), zwiększyły się ilości danych, obciążenie serwerów i częstotliwość wdrożeń. Znaleźliśmy się w następującej sytuacji:
- Główna baza danych MySQL wzrosła do około 1000 tabel o łącznej pojemności 2,5 TB i nadal rosła.
- Nie było możliwości szybkiego podziału bazy danych na shard'y. Stary sposób "piszę do bazy, co chcę i jak chcę", mnóstwo JOIN'ów i wewnętrznych zależności tabel utrudniał to.
- Nie było mechanizmu migracji schematu bazy danych do testowych środowisk.
- Nie było automatycznego testowania kodu przy wdrożeniu do użytku.
Ostatni problem chciałem rozwiązać jak najszybciej. Zostały już napisane testy Postman do sprawdzania głównego monolitu PHP, ale brakowało aktualnej bazy danych. Nie mogliśmy tworzyć replik w nocy, przekształcać ich w główną i oddawać na dyspozycję w ciągu dnia: bardzo duża liczba wdrożeń i zmian, zarówno w danych, jak i w schemacie bazy danych, uczyniłaby środowisko niezdolnym do użytku już w połowie dnia. Ograniczanie wdrożeń tylko do dni roboczych byłoby również nieefektywne.
Mimo to, zadanie zostało wykonane: pierwszy działający stand mieliśmy już po dwóch tygodniach. W minionym roku przeszedł wiele zmian i nadal jest używany.
Następnie szczegółowo opiszę wszystkie kroki i etapy rozwoju naszego rozwiązania. Upewnicie się, że ta metoda zasługuje na prawo do istnienia.
Czym jest „thin provisioning”?
To jest technologia sprzętowa lub programowa (znana również jako wolumeny sparse), która pozwala przypisywać większą ilość wymaganego zasobu, niż jest dostępne. Przypisany wolumen musi spełniać kryteria just-enough (tyle, ile potrzeba) i just-in-time (w odpowiednim czasie). W głównej mierze cienkie rezerwowanie stosowane jest w różnych macierzach dyskowych, aby zapewnić przestrzeń dyskową w wymaganych ilościach, przekraczających rzeczywiście dostępne. Technologia ta jest wspierana przez różne systemy plików, takie jak LVM2, ZFS, BTRFS. Jest szeroko stosowana w hipernadzorcach wirtualizacji. Dzięki cienkiemu rezerwowaniu mogliśmy szybko utworzyć z migawkach głównej partycji z danymi tyle kopii tej partycji, ile potrzebowaliśmy (katalog data systemu baz danych MySQL).
Pierwsze stanowisko, technologia Thin LVM
Rozdział ten można również nazwać "Jak stworzyć maksymalnie szybkie migawki dużej ilości danych za pomocą , zmniejszając stabilność systemu plików i bazy danych MySQL do niedopuszczalnych poziomów."
Ponieważ już wykorzystaliśmy LVM do budowy głównych partycji systemu operacyjnego, postanowiliśmy zacząć od niej. Na początek potrzebowaliśmy oddzielnej fizycznej maszyny — repliki naszej głównej bazy MySQL, na której moglibyśmy na żądanie tworzyć migawkę repliki i uruchamiać ją obok jako oddzielny egzemplarz MySQL. W czasie testów pozwoliliśmy na stosowanie operacji modyfikujących, a po zakończeniu testów ją usuwaliśmy. Konfiguracja serwera była następująca:
- 2 x Intel Silver 4114 (10×2,2 GHz HT)
- 8 x 32 GB DDR4
- 8 x 1920 GB Intel SSD w kontrolerze RAID Adaptec w RAID-10
Temat wyboru między kontrolerem RAID a programowym RAID MD mógłby być osobnym artykułem. Powiem tylko, że na nasz wybór wpłynęły dwa czynniki:
- W czasach stawiania zadania wszystkie bazy danych instalowaliśmy na kontrolerach RAID, więc można powiedzieć, że tak się to ułożyło historycznie.
- Różnica w wydajności w syntetycznych testach systemu plików oraz testach z różnymi operacjami w MySQL była minimalna.
Podzieliliśmy uzyskany RAID-10: utworzyliśmy jedną Volume Group (VG) na cały wolumen (z narzutami wynoszącymi około 6,7 GB) i utworzyliśmy logiczny wolumen (Logical Volume, LV) pod system o pojemności 50 GB. Zwykle całą pozostałą przestrzeń przeznaczamy na partycję z MySQL. Ale potrzebowaliśmy cienkiego kopiowania, więc najpierw stworzyliśmy tak zwany pool, wewnątrz którego utworzyliśmy partycję pod /var/lib/mysql o pojemności 3,5 TB (na podstawie przewidywanych rozmiarów baz danych):
lvcreate -l 100%FREE -T vga/thin
lvcreate -V 3.5T -T vga/thin -n mysqlSformatowaliśmy partycję w ext4, zamontowaliśmy ją, zapisaliśmy replikę i otrzymaliśmy początkowy zestaw. Następnie stworzyliśmy API, które powinno tworzyć migawki, uruchamiać instancję Bazy Danych MySQL na zadanym porcie i usuwać stworzoną instancję. Ponieważ wykorzystujemy wyłącznie wywołania systemowe, jako język do pisania skryptów wybraliśmy zwykły bash, a jako powiązanie API HTTP → bash wykorzystaliśmy rozwiązanie open source. , napisane w Go.
Kiedyś opublikujemy nasze skrypty bash w open source, a na razie po prostu opiszę główny algorytm:
Tworzenie głównej migawki snapmain:
- Zatrzymujemy główną replikę.
- Nakładamy blokadę na operacje ze snapshotem snapmain.
- Tworzymy nową migawkę snapmain.
- Uruchamiamy MySQL i usuwamy blokadę.
Tworzenie Bazy Danych na dowolnym porcie z snapmain:
- Nakładamy blokadę na konkretną instancję Bazy Danych (port).
- Sprawdzamy, czy istnieje blokada tworzenia głównej migawki. Jeśli tak, czekamy i ponownie sprawdzamy co 5 sekund.
- Sprawdzamy, czy istnieje stara partycja LV instancji.
3.1 Jeśli tak, to zatrzymujemy instancję MySQL za pomocą kill -9 i usuwamy partycję LV. - Tworzymy nową instancję z snapmain.
- Przygotowujemy i montujemy katalogi dla tej instancji.
- Usuwamy znaki sługi (pliki) i uruchamiamy instancję MySQL.
- Uczyń ją główną.
- Usuwamy blokadę.
Usuwanie Bazy Danych na dowolnym porcie:
- Nakładamy blokadę na konkretną instancję Bazy Danych (port).
- Zabijamy instancję MySQL za pomocą kill -9.
- Odłączamy katalogi.
- Usuwamy partycję LV i znosimy blokadę.
Przykład poleceń do klonowania partycji nowej instancji Bazy Danych:
lvcreate -n stage_3307 -s vga/snapmain
lvchange -ay -K vga/stage_3307
mount -o noatime,nodiratime,data=writeback /dev/mapper/vga-stage_3307 /mnt/stage_3307Teraz opowiem o głównym problemie, z którym się zmierzyliśmy podczas korzystania z cienkiego rezerwowania. Napotkaliśmy na wydajność dysków SSD. To zdarzyło się z powodu cech Thin LVM: operuje ona na poziomie urządzenia, używając niskopoziomowych bloków danych o domyślnym rozmiarze 4 MB. Jak to wyglądało:
- Tworzymy zrzut z głównej partycji /var/lib/mysql.
- Uruchamiamy replikację, aby nadrobić zaległości względem mastera.
- Jakiekolwiek zmiany w tabelach repliki powodują zachowanie starych, niezmienionych bloków danych w sekcji zrzutu.
- Jakiekolwiek zmiany w uruchomionym testowym egzemplarzu powodują zachowanie starych, niezmienionych bloków danych w sekcji sklonowanego zrzutu dla tego egzemplarza.
- Zyskujemy obciążenie operacji we/wy na poziomie 100% na urządzeniu, spowalniając wszelkie operacje oraz stopniowo opóźniając replikację.
- Pod koniec dnia roboczego otrzymujemy ustawkę opóźnioną o kilka godzin.
Jak sobie z tym poradziliśmy, aby uzyskać bardziej rozsądny wynik (główne punkty):
Kontroler RAID:
- Wyłączyliśmy wszystkie rodzaje buforowania domyślnie.
- Ustawiliśmy writeback (gdy dane trafiają do bufora, zapis kończy się, zanim rzeczywiste zapisanie na dysk zostanie ukończone).
System plików:
- W punkcie montowania /var/lib/mysql zapisaliśmy noatime,nodiratime,data=writeback
- Wyłączyliśmy dziennikowanie ext4 za pomocą tune2fs.
MySQL:
- Zapisaliśmy innodb_flush_method = O_DSYNC (zwiększyliśmy prędkość zapisu, obniżając tym samym niezawodność).
- Wyłączyliśmy dziennikowanie, logi nie są nam potrzebne.
- Zapisaliśmy innodb_buffer_pool_size = 4G (im mniejszy rozmiar puli InnoDB, tym szybciej MySQL zakończy działanie przy zatrzymaniu, a tym samym szybciej stworzymy zrzut).
To nie jest pełna lista, zwłaszcza jeśli chodzi o MySQL. Niemniej jednak, pozostałe zmiany są drobne i często nie są stosowane lub nie są precyzyjne. Na przykład w próbie odciążenia dysków przenieśliśmy nawet innodb_parallel_doublewrite_path do /dev/shm, co w niektórych przypadkach przy uruchomieniu nieprawidłowo zakończonego egzemplarza oszczędzało nam do 5 sekund.
Dlaczego zatrzymujemy MySQL przed zrobieniem zrzutu? Przecież możemy go wykonać z działającej repliki. Tak, to prawda, tylko nowy egzemplarz bazy danych na tym zrzucie będzie domyślnie uważany za uszkodzony i będzie wymagał pełnego skanowania podczas uruchamiania. Zatrzymanie repliki jest zdecydowanie szybsze, chociaż jest to ostatecznie najdłuższa operacja w całym procesie.
W rezultacie uzyskaliśmy bardziej akceptowalne czasy i gotowy do pracy system. Chociaż, jak pokazuje najbardziej wymowny wykres opóźnienia replikacji głównej repliki, sytuacja wciąż jest daleka od ideału:

Z innych wad należy wymienić praktyczną niemożność monitorowania puli Thin LVM: poza systemowymi standardowymi funkcjami iostat, nie można na przykład zrozumieć, który element puli w danym momencie generuje największe obciążenie dla systemu plików.
Osobno należy zwrócić uwagę na jeden poważny defekt związany z opisaną powyżej optymalizacją: otrzymaliśmy system YOLO. Około raz na jeden-dwa miesiące ext4 nie wytrzymywał podobnych nadużyć i bezpowrotnie się psuł, wymagając sformatowania i ponownego załadowania repliki. Zyskawszy na prędkości, beznadziejnie zrujnowaliśmy stabilność.
Jakie metryki warto monitorować podczas eksploatacji Thin LVM:
- Procent danych w puli thin
- Procent metadanych w puli thin
Jeśli nasz system przetrwa brak miejsca na dane (wystarczy posprzątać dyski), to brak miejsca na metadane doprowadzi do całkowitego załamania puli i konieczności jej ponownego utworzenia od nowa.
System plików w obrębie puli z czasem bardzo mocno się fragmentuje. Zalecam codzienne uruchamianie poprzez cron polecenia fstrim -v /var/lib/mysql.
Podsumowanie:
- Technologia jest łatwo stosowalna, podobnie jak sam LVM, i nie wymaga szczególnych kwalifikacji inżyniera.
- Dobrze nadaje się do baz danych małego rozmiaru i niezbyt obciążonych. Im mniejsza baza danych, tym mniej kawałków przemieszcza się po systemie plików w obrębie puli, a obciążenie dysków jest niższe.
- W przypadku naszego zadania zaczęliśmy szukać innych rozwiązań, o czym będzie mowa w następnej sekcji.
Drugi system, technologia ZFS
Dawno temu miałem do czynienia z systemem plików ZFS, ale wtedy ZFS działał naprawdę dobrze na swojej macierzystej rodzinie systemów operacyjnych Solaris. Istniała portowana wersja na FreeBSD z całkiem dobrym poziomem implementacji. Był także niedokończony port na Linux, który niewiele osób używało. Z powodu struktury przechowywania danych B-tree (zresztą taka sama struktura jak w InnoDB MySQL), ZFS słabo sprawdzał się w instalacjach z bardzo dużą liczbą plików. Wszystko to, w połączeniu z koniecznością poznania teorii przed użyciem, na długo wykreśliło ten system plików z mojej praktyki. Pojawiły się ext4 i xfs, które stały się standardem. Jednak biorąc pod uwagę, że ZFS bardziej niż pasuje do naszego zadania, a wersja na Linux, sądząc po opiniach, rozwinęła się w całkiem sensowny produkt (choć nie w pełni wspierany, przez co postawienie systemu na ZFS od podstaw można zrobić tylko przy użyciu różnych sztuczek), postanowiliśmy go wypróbować.
Z oczywistych powodów wybraliśmy stanowisko o podobnej konfiguracji (z wyjątkiem kontrolera RAID). Zainstalowaliśmy osiem dysków SSD o pojemności 1920 GB. Nie mieliśmy ochoty pisać swojego obrazu systemu do wgrania serwera na czystym ZFS, więc od każdego dysku odjęliśmy 50 GB i utworzyliśmy na nich MD RAID-10 pod system. Pozostałe 1950 GB na każdym dysku połączyliśmy w ZFS-analog RAID-10:
zpool create zpool mirror /dev/sda2 /dev/sdb2 mirror /dev/sdc2 /dev/sdd2 mirror /dev/sde2 /dev/sdf2 mirror /dev/sdg2 /dev/sdh2Utworzyliśmy partycje dla MySQL:
zfs create zpool/mysql
zfs set compression=gzip zpool/mysql
zfs set recordsize=128k zpool/mysql
zfs set atime=off zpool/mysql
zfs create zpool/mysql/data
zfs set recordsize=16k zpool/mysql/data
zfs set primarycache=metadata zpool/mysql/data
zfs set mountpoint=/var/lib/mysql zpool/mysql/dataZwróć uwagę, że włączyliśmy standardowe kompresowanie danych gzip. Na serwerze mamy dużo zasobów procesorowych, które nie są w pełni wykorzystywane. W efekcie 3 TB naszej bazy danych zmieniło się w 1,6 TB, a ponieważ najsłabszym ogniwem, jak w poprzednim przypadku, jest maksymalna wydajność dysków, im mniej danych — tym lepiej. Od samego początku zyskujemy doskonały bonus od ZFS! W godzinach szczytu, przy pełnym obciążeniu, na utrzymanie działania gzip zużywa się do 4 rdzeni, ale nie ma na co narzekać.
Następnie wdrożenie przebiegało szybciej. Bez zmian przeniesiono ustawienia replikacji MySQL z standu LVM. Musieliśmy poświęcić trochę czasu na przerobienie skryptów na polecenia ZFS, ale ogólnie algorytmy pozostały takie same. Przykład tworzenia migawki:
zfs set snapdir=visible zpool/mysql/data
zfs create zpool/stage_3307
zfs clone zpool/mysql/data@snapmain zpool/stage_3307/data
zfs set mountpoint=/mnt/stage_3307 zpool/stage_3307/dataZ dodatkowego tuningu: umieściliśmy w pamięci sekcje ZFS z metadanymi i logami l2arc i zil. Okazało się, że dla naszego zadania było to zbyteczne, ale na razie pozostawiliśmy tę optymalizację, zmiana przy okazji nie jest trudna. Negatywne efekty — po restarcie serwera trzeba ponownie tworzyć odpowiednie obszary pamięci. Fragment z zpool status:
logs
/dev/shm/zil_slog.img ONLINE 0 0 0
cache
/dev/shm/l2arc.img ONLINE 0 0 0W takiej konfiguracji zaczęliśmy testować stanowisko i uzyskaliśmy znakomite wyniki: przy dwóch jednocześnie działających egzemplarzach bazy danych (i aktywnej głównej replikacji) z migawkami uzyskaliśmy obciążenie dysków na poziomie 50-60%.
Pozbyliśmy się naszego głównego problemu, co widać na wykresie opóźnienia replikacji (porównaj z poprzednim wykresem w sekcji Thin LVM):

Oprócz tego i dzięki temu znacznie przyspieszyliśmy we wszystkich operacjach: pełne tworzenie migawki z zatrzymaniem i uruchomieniem replikacji zajmuje do 40 sekund, wdrożenie nowego egzemplarza MySQL z migawki zajmuje do 20 sekund. Co więcej niż zadowala zarówno nas, jak i nasze testy kodu.
Podsumowanie:
- Wyniki w pełni spełniły nasze potrzeby w zakresie uzyskania kopii działającej bazy danych do testowania kodu.
- Technologia wymaga wprowadzenia: trzeba zrozumieć, czym jest ZFS i jak z nią pracować.
- Nie sprawdzaliśmy bieżącego statusu pracy ZFS z dużą ilością (powyżej 1 mln) małych plików. Ale zakładamy, że problem pozostaje, więc nie polecałbym tego systemu plików do jakichkolwiek magazynów plików.
Co dalej?
W ramach stoiska nie robimy nic więcej, wynik nas satysfakcjonuje. Może w przyszłości dodamy do ustawień replikacji stoiska wyłączenia tabel, które nie są potrzebne do testowania, co jeszcze bardziej zmniejszy objętość bazy danych. Nie testowaliśmy systemu BTRFS i jego realizacji technologii cienkiego tworzenia kopii zapasowych. Niemniej jednak, takie zadanie już nie istnieje, ponieważ główny cel został osiągnięty. Ogólnie rzecz biorąc, oczywiście, chcemy odejść od opisanego powyżej podejścia — wdrożyć operacyjne migracje bazy danych w środowisku testowym, stworzyć osobną testową pętlę bazy danych, zająć się shardowaniem głównej bazy. Wiele z tego już realizujemy, o czym z pewnością opowiemy w przyszłych artykułach.
Podsumowanie
Pierwotne zadanie zostało rozwiązane, choć w nietypowy sposób. W pośrednich wnioskach opisano zalety i wady każdej z zastosowanych technologii, dlatego zdecydujmy, którą technologię i kiedy można zastosować:
- Thin LVM — przy małych bazach danych i gdy nie chce się lub nie ma czasu na naukę ZFS.
- ZFS — jeśli masz doświadczenie w pracy z nią lub możliwość poświęcenia czasu na naukę w każdej sytuacji.
Na wyższym poziomie przedstawienia ten artykuł to nie tylko porównanie technologii dwóch systemów plików. Główna idea, którą chciałbym przekazać i utrwalić, polega na tym, że nie warto bać się myśleć nieszablonowo w sytuacjach krytycznych dla biznesu i korzystać tylko z gotowych przepisów. Kiedyś moglibyśmy wszyscy w departamencie technicznym pokiwać głowami i powiedzieć, że zadanie stworzenia trójterabajtowych kopii bazy danych w mniej niż minutę jest niemożliwe i nie potrzebujemy ryzykownych technologii, zróbmy to jak należy. Byłoby to możliwe, ale stracilibyśmy około pół roku do roku i wiele podróży klientów (podróże to nasz główny wskaźnik biznesowy) bez testów i w trakcie wdrożenia. Podejmując nietypowe kroki, straciliśmy niewiele czasu na wdrożenie, zdobyliśmy doświadczenie w nowych i zapomnianych starych technologiach, i przeprowadziliśmy testy dokładnie w momencie, gdy były nam bardzo potrzebne. Niewątpliwie pozytywnie wpłynęło to na wszystkie nasze wskaźniki. Wybór zawsze należy do was, a my z naszej strony będziemy nadal opowiadać na naszym blogu o interesujących bieżących i przyszłych osiągnięciach.
Źródło: habr.com
