Przejdź do treści
Administracja·~30 min czytania

Multipath w Linuksie: jeden dysk, wiele dróg

Dlaczego macierz podłączona dwoma kablami pokazuje się jako dwa dyski, jak sprawić, żeby znów był jeden, i co naprawdę dzieje się z zapisem, gdy ścieżka padnie.

Autor: Zespół LinuxLab

Wszędzie tam, gdzie dane leżą na macierzy, a nie w serwerze, pojawia się to samo pytanie: co się stanie, gdy padnie kabel, przełącznik albo kontroler. Odpowiedzią jest podłączenie macierzy więcej niż jedną drogą - i mechanizm, który potrafi te drogi ze sobą powiązać. W Linuksie odpowiada za to multipath.

W tym artykule budujemy działające stanowisko od zera, razem ze stroną macierzy: jeden dysk udostępniony dwiema drogami, klient, który widzi go najpierw jako dwa osobne urządzenia, i multipath, który scala je z powrotem w jedno. Potem odcinamy ścieżkę przy trwającym zapisie i sprawdzamy, co się dzieje - a na koniec odcinamy wszystkie. Każdy wydruk w artykule pochodzi z tego stanowiska.

Spis treści

  1. Czym jest multipath
  2. Co zmierzyliśmy
  3. Budowa stanowiska: macierz
  4. Budowa stanowiska: klient
  5. Na jakich portach to działa
  6. Jeden dysk, dwie nazwy
  7. Skąd się biorą sda i sdb
  8. Po czym poznać, że to ten sam dysk
  9. Uruchomienie krok po kroku
  10. Jak czytać multipath -ll
  11. Test: awaria ścieżki
  12. Powrót ścieżki
  13. Gdy padną wszystkie
  14. Wybór ścieżki i polityki
  15. Czego multipath nie robi
  16. Checklista
  17. Podsumowanie

Czym jest multipath

Wyobraź sobie serwer i macierz dyskową połączone dwoma niezależnymi drogami - dwoma kartami, dwoma przełącznikami, dwoma kontrolerami po stronie macierzy. Robi się tak z jednego powodu: żeby awaria dowolnego elementu po drodze nie odcięła serwera od danych.

Problem w tym, że system operacyjny nie wie o zamyśle. Każdą drogę wykrywa osobno i dla każdej tworzy osobne urządzenie blokowe. Dwa kable do jednego dysku dają dwa dyski w systemie - i gdyby zamontować oba naraz, system plików zostałby uszkodzony, bo dwa niezależne wejścia zapisywałyby te same bloki.

Multipath rozwiązuje dokładnie ten problem. Rozpoznaje, że kilka urządzeń to w rzeczywistości jeden dysk, i tworzy nad nimi jedno urządzenie zbiorcze. To urządzenie zbiorcze wraz z listą prowadzących do niego dróg nazywa się mapą wielościeżkową - i tym słowem, w skrócie "mapa", będziemy się dalej posługiwać, bo tak nazywają je same narzędzia. Od tej chwili system operacyjny widzi jeden dysk, a multipath decyduje, którą drogą przesłać każdą operację - i przełącza się na sprawną, gdy jedna przestaje odpowiadać.

Mechanizm siedzi w device-mapperze, czyli tej samej warstwie jądra, która obsługuje szyfrowanie dysków i menedżera woluminów. Obsługują go dwa elementy: usługa multipathd, która na bieżąco pilnuje stanu ścieżek, oraz polecenie multipath do oglądania i zmiany konfiguracji.

Multipath to nie jest kopia danych. Dane leżą w jednym miejscu - na macierzy. Zwielokrotnione są wyłącznie drogi do nich. Jeżeli awarii ulegnie sama macierz albo dysk w niej, multipath nie pomoże w niczym; od tego są kopie zapasowe i mechanizmy nadmiarowości po stronie macierzy. Multipath chroni przed awarią połączenia, nie przed utratą danych.

Co zmierzyliśmy

Stanowisko składa się z dwóch maszyn wirtualnych w jednej sieci. Pierwsza udaje macierz: udostępnia dysk przez iSCSI (od angielskiego Internet Small Computer System Interface), czyli protokół podający urządzenie blokowe po sieci. Druga jest klientem. Żeby powstały dwie drogi, macierz nasłuchuje na dwóch adresach - to uproszczony odpowiednik dwóch kontrolerów.

ElementStan zmierzony 11 września 2026
MacierzDebian 13, targetcli-fb 1:2.1.53-1.3, adresy 192.168.123.226 i 192.168.123.240
KlientDebian 12, open-iscsi 2.1.8-1, multipath-tools 0.9.4-3+deb12u2, adres 192.168.123.220
Woluminplik /srv/iscsi/lun0.img o wielkości 256 MB
Drogidwa wejścia do tego samego magazynu, pod dwoma adresami
Skutek na klienciedwa urządzenia sda i sdb, oba 256 MB, ten sam producent i model
Identyfikatoridentyczny na obu: 360014058dd8a6a3ef4943f4876db1b69
Domyślne zachowaniemultipath nie utworzył mapy - ustawienie find_multipaths strict
Po konfiguracjijedno urządzenie mpatha (dm-0), dwie ścieżki active ready
Awaria jednej ścieżkiodczyt, zapis i sync działały bez przerwy; ścieżka oznaczona failed faulty
Powrót ścieżkiautomatyczny, 8-10 sekund od przywrócenia łączności
Awaria wszystkich ścieżekzapis zablokowany, nie przerwany błędem; po powrocie dokończył się

Czego nie sprawdzaliśmy: prawdziwego sprzętu z dwoma kontrolerami, zachowania przy więcej niż dwóch ścieżkach, ani pracy z macierzami wymagającymi własnych ustawień producenta. Mechanizm jest ten sam, ale liczby i czasy na prawdziwym sprzęcie będą inne.

Budowa stanowiska: strona macierzy

Zanim na kliencie pojawią się sda i sdb, ktoś musi je udostępnić. Tę rolę pełni tu Debian 13 z pakietem targetcli-fb - narzędziem do konfigurowania wbudowanego w jądro Linuksa celu iSCSI (w terminologii protokołu: target).

W prawdziwym środowisku w tym miejscu stoi macierz dyskowa i tych poleceń się nie wykonuje - konfiguruje się ją własnym interfejsem producenta. Ta sekcja istnieje po to, żeby całość dało się odtworzyć bez macierzy, na dwóch maszynach wirtualnych.

Zanim przejdziemy do poleceń, pięć słów, które będą się przewijać przez cały artykuł. Warto je znać, bo narzędzia posługują się nimi bez tłumaczenia:

  • Cel (w narzędziach: target) - strona, która udostępnia dysk. U nas Debian 13; w firmie byłaby to macierz.
  • Inicjator (initiator) - strona, która się podłącza i korzysta z dysku, czyli klient. U nas Debian 12.
  • Portal - jeden adres wraz z numerem portu, pod którym cel jest osiągalny. To jest właśnie "droga" z tytułu artykułu: jeden portal to jedno wejście do magazynu. Macierz z dwoma kontrolerami wystawia dwa portale i dlatego prowadzą do niej dwie drogi.
  • LUN (od angielskiego Logical Unit Number) - numer, pod którym cel wystawia konkretny wolumin. Jeden cel może udostępniać wiele woluminów, każdy pod swoim numerem.
  • Backstore - to, co po stronie celu faktycznie przechowuje dane: prawdziwy dysk, wolumin logiczny albo, jak u nas, zwykły plik.
$ sudo apt-get install -y targetcli-fb

Krok 1: plik, który udaje dysk. Nie jest potrzebny osobny wolumin - wystarczy zwykły plik. Polecenie truncate tworzy go natychmiast, bez zapisywania 256 MB zer:

$ sudo mkdir -p /srv/iscsi
$ sudo truncate -s 256M /srv/iscsi/lun0.img

Krok 2: backstore - wskazujemy celowi, że to właśnie ten plik ma odtąd udawać dysk i przechowywać dane:

$ sudo targetcli /backstores/fileio create name=dysk0 file_or_dev=/srv/iscsi/lun0.img size=256M
/srv/iscsi/lun0.img exists, using its size (268435456 bytes) instead
Created fileio dysk0 with size 268435456

Krok 3: cel (target) i jego nazwa. Nazwa w protokole iSCSI ma ustalony format zwany IQN (od angielskiego iSCSI Qualified Name): człon iqn., rok i miesiąc, odwrócona nazwa domeny, dwukropek i dowolna nazwa własna. Domena ma być taka, którą naprawdę się włada - to jedyny sposób, żeby nazwy były niepowtarzalne na świecie:

$ sudo targetcli /iscsi create iqn.2026-09.pl.linuxlab.lab:magazyn0
Created target iqn.2026-09.pl.linuxlab.lab:magazyn0.
Created TPG 1.
Global pref auto_add_default_portal=true
Created default portal listening on all IPs (0.0.0.0), port 3260.

W ścieżkach poleceń zaraz pojawi się człon tpg1. To skrót od angielskiego Target Portal Group, czyli grupy wejść obsługujących ten sam cel. Przy zwykłej konfiguracji grupa jest jedna i nosi numer 1 - wystarczy wiedzieć, że to stały element ścieżki, a nie coś, co trzeba osobno tworzyć.

Krok 4: LUN - podpięcie backstore'u pod cel, czyli nadanie woluminowi numeru, pod którym zobaczy go klient:

$ sudo targetcli /iscsi/iqn.2026-09.pl.linuxlab.lab:magazyn0/tpg1/luns create /backstores/fileio/dysk0
Created LUN 0.

Krok 5: dwie drogi. Tu powstaje to, o co w całym artykule chodzi. Najpierw maszyna dostaje drugi adres na tej samej karcie - w prawdziwej macierzy odpowiada to drugiemu kontrolerowi:

$ sudo ip addr add 192.168.123.240/24 dev enp1s0

Adres dodany poleceniem ip addr add nie przetrwa restartu. Na stanowisku testowym to zaleta - wystarczy ponowne uruchomienie, żeby wrócić do stanu wyjściowego. Do trwałej konfiguracji służy plik /etc/network/interfaces albo systemd-networkd, zależnie od tego, co zarządza siecią w danym systemie.

Teraz najważniejsze. W kroku 3 cel sam utworzył sobie jedno wejście nasłuchujące na wszystkich adresach maszyny naraz - to znaczy zapis 0.0.0.0. Dla systemu operacyjnego klienta jest to jedna droga, choć adresy są dwa. Żeby powstały dwie osobne drogi, trzeba to wejście skasować i utworzyć dwa, każde przypisane do konkretnego adresu:

$ T=iqn.2026-09.pl.linuxlab.lab:magazyn0
$ sudo targetcli /iscsi/$T/tpg1/portals delete 0.0.0.0 3260
Deleted network portal 0.0.0.0:3260

$ sudo targetcli /iscsi/$T/tpg1/portals create 192.168.123.226 3260
Created network portal 192.168.123.226:3260.

$ sudo targetcli /iscsi/$T/tpg1/portals create 192.168.123.240 3260
Created network portal 192.168.123.240:3260.

Krok 6: kto ma prawo się podłączyć. Cel nie wpuszcza kogokolwiek - listę uprawnionych tworzą wpisy ACL (od angielskiego Access Control List), a klienta identyfikuje jego własny IQN. Skąd go wziąć, pokazujemy w następnej sekcji; tutaj jest już podstawiony:

$ sudo targetcli /iscsi/$T/tpg1/acls create iqn.1993-08.org.debian:01:51cf2a72f4a
Created Node ACL for iqn.1993-08.org.debian:01:51cf2a72f4a
Created mapped LUN 0.

Zwróćmy uwagę na drugą linię: wraz z wpisem ACL powstało przypisanie LUN-u do tego konkretnego klienta. Bez tego klient owszem nawiązałby sesję z celem, ale nie zobaczyłby żadnego dysku - to częsta przyczyna pustego lsblk mimo komunikatu o powodzeniu.

Krok 7: zapis konfiguracji. Bez tego wszystko powyżej zniknie po restarcie, bo konfiguracja celu żyje w jądrze:

$ sudo targetcli saveconfig
Last 10 configs saved in /etc/rtslib-fb-target/backup/.
Configuration saved to /etc/rtslib-fb-target/saveconfig.json

Całość można teraz obejrzeć jednym poleceniem. Ten wydruk warto przeczytać uważnie, bo widać na nim wszystkie elementy naraz:

$ sudo targetcli ls
o- / ......................................................... [...]
  o- backstores .............................................. [...]
  | o- fileio ................................. [Storage Objects: 1]
  | | o- dysk0 ...... [/srv/iscsi/lun0.img (256.0MiB) write-back activated]
  | |   o- alua ................................... [ALUA Groups: 1]
  | |     o- default_tg_pt_gp ....... [ALUA state: Active/optimized]
  o- iscsi ............................................ [Targets: 1]
  | o- iqn.2026-09.pl.linuxlab.lab:magazyn0 .............. [TPGs: 1]
  |   o- tpg1 ............................... [no-gen-acls, no-auth]
  |     o- acls .......................................... [ACLs: 1]
  |     | o- iqn.1993-08.org.debian:01:51cf2a72f4a . [Mapped LUNs: 1]
  |     |   o- mapped_lun0 ............ [lun0 fileio/dysk0 (rw)]
  |     o- luns .......................................... [LUNs: 1]
  |     | o- lun0 ... [fileio/dysk0 (/srv/iscsi/lun0.img) (default_tg_pt_gp)]
  |     o- portals .................................... [Portals: 2]
  |       o- 192.168.123.226:3260 ......................... [OK]
  |       o- 192.168.123.240:3260 ......................... [OK]

Dwa wiersze są tu ważne dla dalszej części artykułu. Portals: 2 to są nasze dwie drogi. A wpis ALUA state: Active/optimized dotyczy mechanizmu ALUA (od angielskiego Asymmetric Logical Unit Access) - to ustalony sposób, w jaki macierz sama informuje klienta, które drogi do danego woluminu są dla niej preferowane, a które zapasowe. Cel ogłasza tę informację z własnej inicjatywy, a multipath ją podchwytuje; zobaczymy ją potem na kliencie jako hwhandler='1 alua'.

Budowa stanowiska: strona klienta

Na kliencie potrzebne są dwa pakiety: open-iscsi do samego protokołu i multipath-tools do scalania ścieżek.

$ sudo apt-get install -y open-iscsi multipath-tools

Instalacja open-iscsi nadaje maszynie własny IQN - to ten identyfikator, który wpisaliśmy na macierzy w kroku 6:

$ cat /etc/iscsi/initiatorname.iscsi
## DO NOT EDIT OR REMOVE THIS FILE!
## If you remove this file, the iSCSI daemon will not start.
InitiatorName=iqn.1993-08.org.debian:01:51cf2a72f4a

Teraz wykrywanie celów. I tu pierwsza rzecz warta zapamiętania: wystarczy zapytać jeden adres, bo cel sam wymienia wszystkie adresy, pod którymi jest osiągalny:

$ sudo iscsiadm -m discovery -t sendtargets -p 192.168.123.226
192.168.123.226:3260,1 iqn.2026-09.pl.linuxlab.lab:magazyn0
192.168.123.240:3260,1 iqn.2026-09.pl.linuxlab.lab:magazyn0

Jedno pytanie, dwie odpowiedzi - i to jest pierwszy moment, w którym widać, że drogi są dwie. Zapytanie drugiego adresu daje dokładnie ten sam wynik. Gdyby lista zawierała tylko jeden wiersz, nie byłoby sensu iść dalej: multipath nie miałby czego scalać, a przyczyny należałoby szukać w konfiguracji portali na macierzy.

Kolejny krok narzędzia nazywają logowaniem - i to jest określenie mylące, więc warto je od razu wyjaśnić. Nie chodzi o podanie nazwy użytkownika ani hasła. W protokole iSCSI "logowanie" to nazwa etapu, na którym klient nawiązuje sesję: otwiera połączenie do wskazanego adresu i uzgadnia z celem parametry dalszej pracy. Dopiero po tym cel pokazuje mu swoje woluminy.

Hasło może się na tym etapie pojawić - służy do tego mechanizm CHAP (od angielskiego Challenge-Handshake Authentication Protocol) - ale domyślnie go nie ma i my go nie włączaliśmy. Widać to w wydruku targetcli ls z poprzedniej sekcji: przy tpg1 stoi no-auth, czyli uwierzytelnianie wyłączone. Dostępu pilnuje u nas wyłącznie lista ACL z kroku 6, czyli sprawdzenie, czy klient przedstawia się znanym IQN.

Polecenie wywołane bez dodatkowych argumentów nawiązuje sesje ze wszystkimi wykrytymi adresami naraz:

$ sudo iscsiadm -m node --login
Logging in to [iface: default, target: iqn.2026-09...:magazyn0, portal: 192.168.123.240,3260]
Logging in to [iface: default, target: iqn.2026-09...:magazyn0, portal: 192.168.123.226,3260]
Login to [iface: default, target: iqn.2026-09...:magazyn0, portal: 192.168.123.240,3260] successful.
Login to [iface: default, target: iqn.2026-09...:magazyn0, portal: 192.168.123.226,3260] successful.

Powstały dwie sesje - po jednej na drogę:

$ sudo iscsiadm -m session
tcp: [3] 192.168.123.240:3260,1 iqn.2026-09.pl.linuxlab.lab:magazyn0 (non-flash)
tcp: [4] 192.168.123.226:3260,1 iqn.2026-09.pl.linuxlab.lab:magazyn0 (non-flash)

Po restarcie sesje nie wrócą same. Domyślnie węzeł ma node.startup = manual, co można sprawdzić poleceniem iscsiadm -m node -T <IQN> -p <adres> -o show. Żeby klient logował się przy starcie systemu, trzeba to zmienić na automatic - i dopiero wtedy ma sens wpisywanie woluminu do /etc/fstab.

Na jakich portach to działa

Warto wiedzieć dokładnie, co się z czym łączy - choćby po to, żeby ustawić zaporę i nie szukać potem godzinami, dlaczego jedna ścieżka nie wstaje.

iSCSI używa portu TCP 3260. Ma on nawet własną nazwę w systemowym spisie usług:

$ grep '^iscsi' /etc/services
iscsi-target	3260/tcp

Po stronie macierzy każde wejście to osobny nasłuch na swoim adresie. Dwie drogi widać tu wprost, jako dwa wiersze:

$ sudo ss -ltnp | grep 3260
LISTEN 0 256  192.168.123.226:3260  0.0.0.0:*
LISTEN 0 256  192.168.123.240:3260  0.0.0.0:*

Po stronie klienta jest odwrotnie - nic nie nasłuchuje. Usługa iscsid wyłącznie nawiązuje połączenia wychodzące, po jednym na ścieżkę:

$ sudo ss -tnp | grep 3260
ESTAB 0 0  192.168.123.220:45796  192.168.123.226:3260  users:(("iscsid",pid=216704,fd=7))
ESTAB 0 0  192.168.123.220:53854  192.168.123.240:3260  users:(("iscsid",pid=216704,fd=8))

Te dwa połączenia to najbardziej dosłowny obraz tego, czym są "dwie ścieżki": dwie niezależne sesje TCP do tego samego woluminu. Kiedy w dalszej części artykułu odetniemy jedną z nich, zniknie dokładnie jeden z tych wierszy.

A sam multipath? Nie zajmuje żadnego portu sieciowego. Działa wyłącznie lokalnie, a z narzędziami wiersza poleceń rozmawia przez gniazdo domenowe systemu Unix:

$ sudo ss -lxp | grep multipath
u_str LISTEN 0 10  @/org/kernel/linux/storage/multipathd  users:(("multipathd",pid=216864,fd=5))

Wynika z tego prosta reguła dla zapory:

  • Na macierzy otworzyć port 3260/tcp - i to najlepiej wyłącznie dla adresów klientów, którzy mają prawo się łączyć.
  • Na kliencie nie trzeba otwierać niczego; wystarczy, żeby zapora przepuszczała ruch wychodzący i odpowiedzi na nawiązane połączenia.
  • Dla multipath nie trzeba otwierać niczego w ogóle - to mechanizm lokalny.

Warto też pamiętać, że sam protokół iSCSI domyślnie nie szyfruje przesyłanych danych. W sieci, której się nie ufa, stosuje się do tego wspomniane wyżej uwierzytelnianie CHAP albo tunel IPsec - to jednak wykracza poza zakres tego artykułu i nie było na stanowisku sprawdzane.

Jeden dysk, dwie nazwy

Wracamy do klienta. Zanim nawiązał sesje z macierzą, widać było tylko jego własne dyski wirtualne:

$ lsblk -d -o NAME,SIZE,TYPE,VENDOR,MODEL
NAME  SIZE TYPE VENDOR   MODEL
vda    10G disk 0x1af4
vdb   366K disk 0x1af4

Po nawiązaniu sesji z obydwoma adresami pojawia się problem, dla którego istnieje multipath:

$ lsblk -d -o NAME,SIZE,TYPE,VENDOR,MODEL
NAME  SIZE TYPE VENDOR   MODEL
sda   256M disk LIO-ORG  dysk0
sdb   256M disk LIO-ORG  dysk0
vda    10G disk 0x1af4
vdb   366K disk 0x1af4

Dwa dyski po 256 MB, ten sam producent, ten sam model. To nie są dwa magazyny - to jeden, pokazany dwa razy, bo prowadzą do niego dwie sesje. Gdyby ktoś teraz utworzył system plików na sda, a potem zamontował sdb i też zaczął pisać, dane zostałyby uszkodzone, bo obie nazwy prowadzą do tych samych bloków.

Warto zauważyć, że LIO-ORG w kolumnie producenta to nie jest żadna firma. Tak przedstawia się implementacja celu iSCSI wbudowana w jądro Linuksa, nazywana LIO - czyli u nas po prostu druga maszyna wirtualna. Prawdziwa macierz pokaże w tym miejscu swojego producenta i model, ale mechanizm jest identyczny.

Skąd się biorą urządzenia sda i sdb

To jest pytanie, które warto zadać, bo odpowiedź tłumaczy całą resztę. Żadne polecenie ich nie tworzy. Nie ma kroku, w którym administrator wywołuje mknod albo cokolwiek innego - urządzenia pojawiają się same, jako skutek zalogowania do portalu, i znikają, gdy się wylogować.

Zacznijmy od stanu wyjściowego. Klient nie ma ani jednego urządzenia SCSI - to skrót od angielskiego Small Computer System Interface, standardu, którym system porozumiewa się z dyskami; iSCSI to po prostu ten sam zestaw poleceń przesyłany po sieci:

$ ls /sys/class/scsi_host/
(pusto)

$ ls /dev/sd*
ls: cannot access '/dev/sd*': No such file or directory

Teraz nawiązujemy sesję z jednym tylko adresem macierzy - czyli otwieramy jedną z dwóch dróg. Służy do tego to samo polecenie co wyżej, ale ze wskazaniem konkretnego celu i adresu:

$ sudo iscsiadm -m node -T iqn.2026-09.pl.linuxlab.lab:magazyn0 -p 192.168.123.226 --login
Login to [iface: default, target: iqn.2026-09...:magazyn0, portal: 192.168.123.226,3260] successful.

$ ls /sys/class/scsi_host/
host0

$ ls /dev/sd*
/dev/sda

Jedna sesja - i pojawił się jeden host SCSI oraz jedno urządzenie. "Host SCSI" to w nazewnictwie jądra tyle, co jeden kanał, przez który system rozmawia z dyskami - odpowiednik pojedynczego kontrolera dyskowego. Przy dyskach wpinanych w płytę główną host odpowiada fizycznemu kontrolerowi; przy iSCSI powstaje jeden host na każdą sesję sieciową.

Otwieramy teraz drugą drogę, czyli sesję z drugim adresem:

$ sudo iscsiadm -m node -T iqn.2026-09.pl.linuxlab.lab:magazyn0 -p 192.168.123.240 --login
Login to [iface: default, target: iqn.2026-09...:magazyn0, portal: 192.168.123.240,3260] successful.

$ ls /sys/class/scsi_host/
host0
host1

$ ls /dev/sd*
/dev/sda
/dev/sdb

Druga sesja, drugi host, drugie urządzenie. Zależność jest dokładnie taka: jedna sesja iSCSI = jeden host SCSI = jedno urządzenie blokowe na każdy udostępniony wolumin. Potwierdza to szczegółowy widok sesji, gdzie widać całe powiązanie naraz:

$ sudo iscsiadm -m session -P 3 | grep -E 'Current Portal:|Host Number:|Attached scsi disk'
Current Portal: 192.168.123.226:3260,1
	Host Number: 0	State: running
	Attached scsi disk sda		State: running
Current Portal: 192.168.123.240:3260,1
	Host Number: 1	State: running
	Attached scsi disk sdb		State: running

Portal, numer hosta, nazwa urządzenia - jeden wiersz na drogę. Stąd biorą się też adresy SCSI widoczne w multipath -ll. Zapis 0:0:0:0 i 1:0:0:0 to cztery liczby: host, kanał, cel i numer LUN. Różni się tylko pierwsza, bo hosty są dwa; wolumin jest ten sam, więc LUN w obu przypadkach wynosi 0.

Najczytelniej widać to w dowiązaniach tworzonych przez udev - usługę, która pilnuje zawartości katalogu /dev i dla każdego wykrytego urządzenia zakłada dodatkowe nazwy opisowe. W samej nazwie jest adres, pod którym poszło połączenie, nazwa celu i numer LUN-u:

$ ls -l /dev/disk/by-path/ | grep iscsi
ip-192.168.123.226:3260-iscsi-iqn.2026-09.pl.linuxlab.lab:magazyn0-lun-0 -> ../../sda
ip-192.168.123.240:3260-iscsi-iqn.2026-09.pl.linuxlab.lab:magazyn0-lun-0 -> ../../sdb

Ten sam cel, ten sam lun-0, dwa różne adresy - i dwa urządzenia. Cała dwuznaczność, którą naprawia multipath, mieści się w tej jednej różnicy.

Działa to symetrycznie. Zamknięcie jednej sesji usuwa jej host i jej urządzenie:

$ sudo iscsiadm -m node -T iqn...:magazyn0 -p 192.168.123.240 --logout
$ ls /sys/class/scsi_host/
host0
$ ls /dev/sd*
/dev/sda

Kto właściwie tworzy plik w /dev

Skoro nie administrator, to kto? Katalog /dev nie jest zwykłym katalogiem na dysku - to system plików zarządzany przez jądro:

$ mount | grep ' /dev type'
udev on /dev type devtmpfs (rw,nosuid,relatime,size=979748k,...)

Kolejność zdarzeń jest więc taka: sesja iSCSI rejestruje w jądrze host SCSI; jądro pyta cel o listę LUN-ów; dla każdego znalezionego woluminu powstaje urządzenie SCSI; przejmuje je sterownik sd (od SCSI disk) i nadaje mu pierwszą wolną nazwę z serii sda, sdb, sdc; na końcu devtmpfs tworzy plik w /dev, a udev dokłada dowiązania w /dev/disk/. Sterownik można sprawdzić wprost:

$ basename $(readlink -f /sys/block/sda/device/driver)
sd

Z tego bierze się też para liczb, którą zobaczymy później w wyniku multipath -ll jako sda 8:0 i sdb 8:16. Pierwsza z nich, numer główny, wskazuje jądru sterownik obsługujący urządzenie - i numer 8 należy właśnie do sterownika sd:

$ grep -E '^ *8 ' /proc/devices
  8 sd

Dlaczego mknod tu nie pomoże. Polecenie mknod tworzy wpis wskazujący na parę numerów, a nie urządzenie. Jeżeli za tymi numerami nic nie stoi, wpis jest pusty w środku:

$ sudo mknod /tmp/niby-sdz b 8 240
$ ls -l /tmp/niby-sdz
brw-r--r-- 1 root root 8, 240 /tmp/niby-sdz

$ sudo dd if=/tmp/niby-sdz of=/dev/null bs=512 count=1
dd: failed to open '/tmp/niby-sdz': No such device or address

Plik jest, prawa się zgadzają, numery też - a odczytu nie ma, bo nie ma urządzenia. Dysk tworzy sterownik w jądrze, nie wpis w katalogu.

Z tego wynika jeszcze jedna rzecz, ważna dla całego artykułu. Nazwy sda i sdb to kolejność przydzielania, a nie tożsamość. Przy następnym uruchomieniu sesje mogą wstać w innej kolejności i ten sam wolumin dostanie inną literę. Dlatego multipath nie może opierać się na nazwach - i dlatego w następnej sekcji szukamy czegoś, co należy do samego woluminu.

Po czym poznać, że to ten sam dysk

Nie trzeba tego zgadywać z rozmiaru i modelu. Każde urządzenie blokowe ma identyfikator nadany przez macierz - w świecie multipath nazywany WWID, od angielskiego World Wide Identifier. Jest unikalny dla konkretnego woluminu i taki sam niezależnie od drogi, którą się do niego dotrze:

$ sudo /lib/udev/scsi_id -g -u -d /dev/sda
360014058dd8a6a3ef4943f4876db1b69

$ sudo /lib/udev/scsi_id -g -u -d /dev/sdb
360014058dd8a6a3ef4943f4876db1b69

Identyczny ciąg znaków na obu urządzeniach to dowód, że mamy do czynienia z jednym woluminem. To jest cała podstawa działania multipath - mechanizm nie zgaduje po nazwach ani rozmiarach, tylko grupuje urządzenia według tego identyfikatora.

Identyfikator jest przypisany do woluminu, nie do maszyny. Gdyby na macierzy usunąć backstore i utworzyć go od nowa, powstałby nowy wolumin z nowym identyfikatorem - a wcześniejszy wpis w konfiguracji multipath przestałby do czegokolwiek pasować.

Praktyczna konsekwencja: jeżeli macierz z jakiegoś powodu podaje różne identyfikatory różnymi drogami, multipath nie połączy ścieżek. Dlatego przy problemach to jest pierwsza rzecz do sprawdzenia - zanim zacznie się grzebać w konfiguracji.

Uruchomienie krok po kroku

Tu czeka niespodzianka, która potrafi zabrać sporo czasu. Po instalacji pakietu usługa multipathd działa, urządzenia są widoczne, identyfikatory się zgadzają - a mapy nie ma:

$ systemctl is-active multipathd
active

$ sudo multipath -ll
(nic)

Przyczyna siedzi w domyślnym ustawieniu:

$ sudo multipathd show config | grep find_multipaths
	find_multipaths "strict"

W trybie strict multipath obsługuje wyłącznie urządzenia wpisane wcześniej na listę. Nowego dysku nie doda z własnej inicjatywy, nawet gdy widzi do niego dwie drogi. Jest to zachowanie celowe - chroni przed przypadkowym przejęciem dysku systemowego - ale dla kogoś, kto uruchamia multipath pierwszy raz, wygląda jak awaria.

Uruchomienie sprowadza się do trzech kroków. Najpierw plik konfiguracyjny, którego po instalacji w ogóle nie ma:

# /etc/multipath.conf
defaults {
    user_friendly_names     yes
    find_multipaths         yes
}

blacklist {
    devnode "^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
    devnode "^(vd|xvd)[a-z]"
}

Ustawienie user_friendly_names sprawia, że urządzenie dostanie czytelną nazwę mpatha zamiast długiego identyfikatora. Lista wykluczeń jest równie ważna: wpisujemy tam urządzenia, których multipath nie ma ruszać - w naszym przypadku dyski wirtualne maszyny, czyli te, na których stoi system.

Drugi krok to wskazanie konkretnego dysku po jego identyfikatorze:

$ sudo multipath -a 360014058dd8a6a3ef4943f4876db1b69
wwid '360014058dd8a6a3ef4943f4876db1b69' added

I trzeci - przeładowanie:

$ sudo systemctl restart multipathd
$ sudo multipath -r

Od tej chwili mapa istnieje. Potwierdza ją multipath -ll, którego wynik rozbieramy na czynniki w następnej sekcji, oraz nowe urządzenie w katalogu /dev/mapper/:

$ ls -1 /dev/mapper/
control
mpatha

Oba urządzenia sda i sdb są teraz podporządkowane jednemu urządzeniu mpatha. Od tej pory pracujemy wyłącznie na /dev/mapper/mpatha - a sda i sdb zostawiamy w spokoju. Dobrym odruchem jest sprawdzenie tego w lsblk: obie ścieżki powinny być tam pokazane jako gałęzie pod wspólnym wpisem typu mpath.

Kolejność ma znaczenie. System plików zakładamy dopiero na /dev/mapper/mpatha, nigdy wcześniej na sda. Jeżeli ktoś sformatuje pojedynczą ścieżkę, a potem doda ją do mapy, multipath owszem zadziała - ale przez chwilę istniały dwie niezależne drogi zapisu do tych samych bloków, i to jest dokładnie ta sytuacja, przed którą multipath ma chronić.

Jak czytać wynik multipath -ll

To jest polecenie, które będziemy wpisywać najczęściej. Wygląda gęsto, ale każda linia znaczy coś konkretnego:

$ sudo multipath -ll
mpatha (360014058dd8a6a3ef4943f4876db1b69) dm-0 LIO-ORG,dysk0
size=256M features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
`-+- policy='service-time 0' prio=50 status=active
  |- 0:0:0:0 sda 8:0  active ready running
  `- 1:0:0:0 sdb 8:16 active ready running

Linia po linii:

  • Linia pierwsza - nazwa mapy (mpatha), w nawiasie identyfikator woluminu, dalej urządzenie device-mappera (dm-0), producent i model.
  • size=256M - rozmiar woluminu. Tyle, ile ma pojedyncza ścieżka, nie suma - bo to jeden dysk.
  • features='1 queue_if_no_path' - najważniejsze ustawienie w całym wyniku. Mówi, co się stanie, gdy padną wszystkie ścieżki. Opisujemy to w osobnej sekcji niżej.
  • hwhandler='1 alua' - moduł obsługi sprzętu. ALUA (od angielskiego Asymmetric Logical Unit Access) to standardowy sposób, w jaki macierz informuje system, które ścieżki są dla danego woluminu preferowane.
  • wp=rw - wolumin jest do odczytu i zapisu.
  • policy='service-time 0' - reguła wyboru ścieżki dla kolejnej operacji.
  • prio=50 status=active - priorytet i stan grupy ścieżek. Multipath nie traktuje ścieżek pojedynczo, tylko dzieli je na grupy i wybiera jedną grupę do pracy; u nas grupa jest jedna i obejmuje obie ścieżki. Do czego to służy przy prawdziwych macierzach, wracamy w sekcji o politykach.
  • Dwie ostatnie linie to ścieżki. Każda ma adres SCSI (0:0:0:0), nazwę urządzenia, parę liczb w rodzaju 8:0, a na końcu trzy słowa stanu. Te dwie liczby to numer główny i poboczny, omówione wcześniej przy powstawaniu urządzeń: pierwszy mówi jądru, który sterownik obsługuje urządzenie (8 to sd), drugi które to urządzenie spośród obsługiwanych przez ten sterownik.

Te trzy słowa to jest to, co się czyta przy awarii. Znaczenie po kolei:

PozycjaWartośćZnaczenie
1 - stan w mapieactivemultipath kieruje tędy operacje
failedmultipath tędy nie kieruje niczego
2 - stan sprawdzeniareadyostatnie sprawdzenie ścieżki się powiodło
faultyostatnie sprawdzenie się nie powiodło
3 - stan urządzeniarunningurządzenie SCSI odpowiada

Warto zapamiętać, że dwa pierwsze słowa zmieniają się niezależnie i w innym tempie. Ścieżka może być już failed, ale jeszcze ready - to znaczy, że łączność wróciła, a multipath jeszcze jej nie przywrócił do użycia. Zobaczymy ten stan za chwilę, w teście powrotu.

Test: co się dzieje przy awarii ścieżki

Teraz najciekawsza część. Zakładamy system plików i montujemy - wyłącznie przez mapę:

$ sudo mkfs.ext4 -q -L MPTEST /dev/mapper/mpatha
$ sudo mkdir -p /mnt/mp
$ sudo mount /dev/mapper/mpatha /mnt/mp
$ df -h /mnt/mp
Filesystem          Size  Used Avail Use% Mounted on
/dev/mapper/mpatha  230M   14K  214M   1% /mnt/mp

Zapisujemy plik kontrolny i liczymy jego sumę kontrolną, żeby później mieć co porównać. Potem w trakcie pracy zabieramy macierzy jeden z adresów - to odpowiednik wyciągnięcia kabla albo awarii kontrolera.

Wynik, zmierzony na działającym stanowisku:

Operacja w czasie awariiWynik
odczyt pliku zapisanego wcześniejpowiódł się, suma kontrolna zgodna
zapis nowego plikupowiódł się
sync (wymuszenie zapisu na dysk)powiódł się
system plikówpozostał zamontowany, bez błędów

Aplikacja nie zauważyła awarii. To jest cały sens multipath i właśnie to chcieliśmy potwierdzić pomiarem, a nie założyć.

W tym samym czasie mapa wyglądała tak:

mpatha (360014058dd8a6a3ef4943f4876db1b69) dm-0 LIO-ORG,dysk0
size=256M features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
`-+- policy='service-time 0' prio=50 status=active
  |- 0:0:0:0 sda 8:0  failed faulty running
  `- 1:0:0:0 sdb 8:16 active ready  running

Ścieżka sda ma failed faulty - multipath nie kieruje już tędy niczego, bo sprawdzenie się nie powiodło. Druga przejęła cały ruch. W dzienniku systemowym widać to wprost:

$ journalctl -u multipathd -n 20 --no-pager
multipathd[..]: sda: mark as failed
multipathd[..]: mpatha: remaining active paths: 1

Linia remaining active paths: 1 to jest dokładnie ta informacja, której szuka się w dzienniku przy zgłoszeniu "coś jest nie tak z macierzą". Jeżeli spadła do 1, system działa, ale utracił nadmiarowość - kolejna awaria zatrzyma pracę. To jest moment na reakcję, a nie moment na spokój.

Stąd wniosek dla monitoringu: samo sprawdzanie, czy system plików jest zamontowany i czy działa zapis, nie wykryje utraty ścieżki - bo zapis działa dalej. Monitorować trzeba liczbę czynnych ścieżek. Najprościej: multipath -ll i policzenie linii ze słowem active, albo obserwacja komunikatów multipathd w dzienniku.

Powrót ścieżki

Przywracamy macierzy zabrany adres i nie robimy nic więcej - żadnego ponownego logowania, żadnego przeładowania usługi.

Ścieżka wróciła sama - w dwóch przebiegach zajęło to 8 i 10 sekund. Po drodze mapa przeszła przez stan pośredni, o którym była mowa wyżej:

  |- 0:0:0:0 sda 8:0  failed ready running

Czyli: sprawdzenie już się powiodło (ready), ale multipath jeszcze nie wznowił kierowania tędy operacji (failed). Chwilę później:

  |- 0:0:0:0 sda 8:0  active ready running
  `- 1:0:0:0 sdb 8:16 active ready running

Obie ścieżki czynne, nadmiarowość odzyskana. Bez ingerencji administratora - to zasługa usługi multipathd, która w tle regularnie odpytuje każdą ścieżkę i sama decyduje o przywróceniu.

Widząc stan pośredni failed ready nie należy się niecierpliwić ani nic naprawiać ręcznie. Odczekanie kilkunastu sekund i ponowne multipath -ll zwykle wystarcza.

Gdy padną wszystkie ścieżki

Awaria jednej ścieżki to sytuacja, którą multipath obsługuje niewidocznie. Znacznie ciekawsze jest pytanie, co się dzieje, gdy przestaną odpowiadać wszystkie. Odpowiedź zależy od ustawienia, które widzieliśmy w pierwszym wierszu wyniku: features='1 queue_if_no_path'.

Nazwa mówi dosłownie: "kolejkuj, gdy nie ma ścieżki". Zamiast zwrócić aplikacji błąd wejścia-wyjścia, multipath wstrzymuje operacje i czeka, aż jakakolwiek ścieżka wróci.

Sprawdziliśmy to, odcinając regułą zapory port 3260 na macierzy - czyli obie drogi naraz. Mapa wygląda wtedy tak:

$ sudo multipath -ll
mpatha (360014058dd8a6a3ef4943f4876db1b69) dm-0 LIO-ORG,dysk0
size=256M features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
`-+- policy='service-time 0' prio=0 status=active
  |- 0:0:0:0 sda 8:0  failed faulty running
  `- 1:0:0:0 sdb 8:16 failed faulty running

Obie ścieżki failed faulty, a priorytet grupy spadł do prio=0. Wyniki operacji:

Operacja przy wszystkich ścieżkach niedostępnychWynik
odczyt pliku, który był już w pamięci podręcznejpowiódł się natychmiast - dane nie musiały iść po sieci
zapis z pominięciem pamięci podręcznej (dd oflag=direct)zawisł - nie zakończył się błędem, tylko czekał
po przywróceniu łącznościzawieszony zapis dokończył się sam, bez błędu i bez powtarzania
pliki kontrolne po całym teścieobie sumy kontrolne zgodne, dane nienaruszone

To jest zachowanie, które trzeba rozumieć zanim wystąpi, bo objawia się inaczej, niż podpowiada intuicja. Serwer nie zgłasza błędu dysku. Nie ma wpisu "input/output error" w dzienniku aplikacji. Zamiast tego procesy, które chcą pisać, po prostu przestają się kończyć - zawieszają się w stanie nieprzerywalnego oczekiwania (w ps kolumna stanu pokazuje D). Takiego procesu nie da się zabić nawet sygnałem KILL, bo czeka w jądrze. Tak to wygląda zmierzone:

$ ps -o pid,stat,wchan:20,cmd -C dd --no-headers
 217251 D+   iomap_dio_rw         dd if=/dev/urandom of=/mnt/mp/blokada.bin bs=1M count=8 oflag=direct

Kolumna STAT pokazuje D, a kolumna WCHAN mówi, w którym miejscu jądra proces czeka. Po przywróceniu łączności zapis dokończył się sam, bez żadnej ingerencji:

8+0 records in
8+0 records out
8388608 bytes (8,4 MB, 8,0 MiB) copied, 53,9249 s, 156 kB/s

Warto zwrócić uwagę na te liczby, bo łatwo je źle odczytać. 156 kB/s to nie jest wydajność dysku - to średnia z całego czasu, w którym proces przez większość sekund nie robił nic, tylko czekał w kolejce. Sam zapis 8 MB zajął ułamek sekundy; pozostałe pięćdziesiąt kilka to czas odcięcia. Dokładnie tak samo zachowa się aplikacja: nie zobaczy błędu, tylko operację, która trwała absurdalnie długo.

Objaw z życia: baza danych przestaje odpowiadać, obciążenie procesora spada niemal do zera, a średnie obciążenie systemu rośnie do kilkudziesięciu. To nie jest przeciążenie - to kolejka procesów czekających na dysk, którego nie ma. Pierwszym poleceniem w takiej sytuacji jest multipath -ll, a nie restart usługi.

Czy to zachowanie jest dobre? To zależy od zastosowania i jest to świadoma decyzja do podjęcia, a nie ustawienie do zostawienia domyślnie:

  • Kolejkowanie ma sens, gdy przerwa jest krótka i przewidywalna - przełączenie kontrolera macierzy, restart przełącznika, aktualizacja oprogramowania macierzy. Aplikacja przeczeka i będzie pracować dalej, bez utraty danych i bez ręcznego wznawiania.
  • Kolejkowanie szkodzi, gdy macierz nie wróci szybko. System zawiesza się w sposób trudny do naprawienia - nie da się nawet czysto odmontować systemu plików, a wyłączenie serwera potrafi utknąć.

Alternatywą jest ustawienie no_path_retry na liczbę prób zamiast na kolejkowanie bez końca. Po wyczerpaniu prób multipath zwraca aplikacji błąd - system plików przejdzie w tryb tylko do odczytu, baza zgłosi awarię dysku, ale serwer pozostanie sterowalny. Szczegóły obu ustawień opisuje strona podręcznika multipath.conf(5).

Wybór ścieżki: polityki

Gdy działają obie ścieżki, multipath musi zdecydować, którą wysłać kolejną operację. Reguła nazywa się polityką wyboru ścieżki i w naszym pomiarze była to service-time 0.

Jądro Debiana 12 udostępnia trzy takie reguły - sprawdzone na liście modułów jądra:

NazwaModuł jądraJak wybiera
round-robindm-round-robinpo kolei, na zmianę. Najprostsza; nie patrzy na to, czy ścieżka jest obciążona.
queue-lengthdm-queue-lengthtę, która ma w tej chwili mniej operacji w kolejce.
service-timedm-service-timetę, która według szacunku obsłuży operację najszybciej - bierze pod uwagę wielkość kolejki i przepustowość ścieżki.

W praktyce domyślnej wartości nie zmienia się bez powodu. service-time radzi sobie dobrze także wtedy, gdy ścieżki nie są równorzędne - na przykład jedna prowadzi przez wolniejsze łącze. Zmiana polityki to sensowny krok dopiero wtedy, gdy producent macierzy zaleca konkretną wartość albo gdy pomiar pokazuje nierówne wykorzystanie ścieżek.

Warto natomiast wiedzieć, że polityka działa wewnątrz grupy ścieżek. Macierze z ALUA dzielą ścieżki na grupy o różnym priorytecie (widoczne jako prio=) i multipath najpierw wybiera grupę o najwyższym priorytecie, a dopiero w jej obrębie stosuje politykę. Dlatego na prawdziwej macierzy można zobaczyć ścieżki w stanie enabled zamiast active - to grupa zapasowa, gotowa, ale nieużywana, dopóki działa grupa preferowana.

Czego multipath nie robi

Zebrane w jednym miejscu, bo każde z tych nieporozumień prowadzi do realnych kłopotów:

  • Nie jest kopią zapasową ani nadmiarowością danych. Dane są w jednym miejscu - na macierzy. Kasowanie pliku, uszkodzenie systemu plików, awaria macierzy: multipath nie pomaga w żadnym z tych przypadków.
  • Nie zwiększa pojemności. Dwie ścieżki do woluminu 256 MB dają wolumin 256 MB, a nie 512 MB. Od łączenia dysków w większą całość jest menedżer woluminów logicznych (LVM, od angielskiego Logical Volume Manager) albo RAID.
  • Nie zawsze zwiększa wydajność. Owszem, operacje rozkładają się na dwie drogi, ale wąskim gardłem najczęściej jest macierz albo dysk w niej, a nie łącze. Multipath buduje się dla dostępności; wzrost przepustowości to skutek uboczny, na który nie należy liczyć z góry.
  • Nie chroni przed własnym błędem konfiguracji. Jeżeli ktoś zamontuje sda obok /dev/mapper/mpatha, multipath tego nie zablokuje - a system plików zostanie uszkodzony. Stąd lista wykluczeń w konfiguracji i zasada, żeby pracować wyłącznie przez /dev/mapper/.
  • Nie uruchomi się sam na nowym dysku przy domyślnym ustawieniu find_multipaths strict w Debianie. To nie jest awaria, tylko celowe zachowanie - opisane wyżej.

Osobna sprawa, o której łatwo zapomnieć: nazwy typu mpatha nie są gwarantowane na stałe przy zmianach sprzętu. W plikach /etc/fstab i w konfiguracji menedżera woluminów bezpieczniej jest używać etykiety systemu plików albo jego identyfikatora UUID (od angielskiego Universally Unique Identifier) niż nazwy urządzenia. Alternatywnie można w /etc/multipath.conf przypisać woluminowi własną, opisową nazwę na podstawie jego identyfikatora - i wtedy nazwa jest stabilna, bo wynika z konfiguracji, a nie z kolejności wykrywania.

Checklista: uruchomienie i diagnostyka

Kolejność, która wynika z powyższego przebiegu.

Strona macierzy - tylko dla stanowiska testowego; prawdziwą macierz konfiguruje się jej własnym interfejsem:

# Wolumin udajacy dysk
sudo truncate -s 256M /srv/iscsi/lun0.img

# Backstore, cel, LUN
sudo targetcli /backstores/fileio create name=dysk0 file_or_dev=/srv/iscsi/lun0.img
sudo targetcli /iscsi create iqn.ROK-MC.odwrocona.domena:nazwa
sudo targetcli /iscsi/IQN/tpg1/luns create /backstores/fileio/dysk0

# Dwie drogi: zamiast domyslnego portalu 0.0.0.0 dwa jawne adresy
sudo targetcli /iscsi/IQN/tpg1/portals delete 0.0.0.0 3260
sudo targetcli /iscsi/IQN/tpg1/portals create ADRES_1 3260
sudo targetcli /iscsi/IQN/tpg1/portals create ADRES_2 3260

# Kto ma prawo sie podlaczyc (IQN klienta) + zapis konfiguracji
sudo targetcli /iscsi/IQN/tpg1/acls create IQN_KLIENTA
sudo targetcli saveconfig

# Kontrola: maja byc dwa nasluchy
sudo ss -ltnp | grep 3260

Strona klienta - uruchomienie:

# 0. Podlaczenie do macierzy (jesli to iSCSI): jedno pytanie
#    powinno zwrocic TYLE wierszy, ile jest drog
sudo iscsiadm -m discovery -t sendtargets -p ADRES_MACIERZY
sudo iscsiadm -m node --login
sudo iscsiadm -m session          # po jednej sesji na droge

# 1. Czy widać wiele urządzeń o tym samym rozmiarze i modelu?
lsblk -d -o NAME,SIZE,TYPE,VENDOR,MODEL

# 2. Czy identyfikator woluminu jest ten sam? To warunek konieczny.
sudo /lib/udev/scsi_id -g -u -d /dev/sda
sudo /lib/udev/scsi_id -g -u -d /dev/sdb

# 3. Plik konfiguracyjny: nazwy czytelne + wykluczenie dyskow systemowych
sudoedit /etc/multipath.conf

# 4. Dopisanie woluminu po identyfikatorze (konieczne przy find_multipaths strict)
sudo multipath -a <identyfikator>

# 5. Przeladowanie i sprawdzenie
sudo systemctl restart multipathd
sudo multipath -r
sudo multipath -ll

# 6. System plikow TYLKO na mapie, nigdy na pojedynczej sciezce
sudo mkfs.ext4 -L DANE /dev/mapper/mpatha
sudo mount /dev/mapper/mpatha /mnt/dane

Diagnostyka, gdy coś nie działa:

# Mapa w ogole nie powstala - sprawdz tryb find_multipaths
sudo multipathd show config | grep find_multipaths

# Pelna konfiguracja obowiazujaca (domyslna + z pliku)
sudo multipathd show config

# Dlaczego urzadzenie zostalo pominiete - tryb szczegolowy
sudo multipath -v3 -d          # -d = tylko pokaz, nic nie zmieniaj

# Stan sciezek i historia decyzji multipathd
sudo multipath -ll
journalctl -u multipathd -n 50 --no-pager

# Ile sciezek zostalo czynnych - to monitorowac, nie sam montaz
sudo multipath -ll | grep -c 'active ready running'

Uwaga do dwóch poleceń wyżej: multipath -v3 -d jest bezpieczne, bo -d oznacza wyłącznie wypisanie tego, co narzędzie by zrobiło, bez wprowadzania zmian. To najszybszy sposób, żeby zobaczyć, dlaczego konkretne urządzenie nie trafiło do mapy - wynik podaje powód dla każdego pominiętego urządzenia osobno.

Polecenie multipathd show config pokazuje z kolei rzeczywiście obowiązującą konfigurację, czyli wartości domyślne wraz z nadpisaniami z pliku. Przy diagnozowaniu to ono rozstrzyga, a nie zawartość /etc/multipath.conf - bo tam widać tylko to, co ktoś dopisał. Pełny opis obu narzędzi znajduje się w multipathd(8).

Podsumowanie

Multipath nie jest skomplikowany, ale ma kilka zachowań, które zaskakują przy pierwszym zetknięciu. Z przeprowadzonego przebiegu wynikają cztery rzeczy warte zapamiętania:

  1. Punktem odniesienia jest identyfikator woluminu, a nie nazwa czy rozmiar urządzenia. Jeżeli identyfikatory się zgadzają, to jest jeden dysk - i to jest pierwsza rzecz do sprawdzenia przy każdym problemie.
  2. W Debianie multipath nie przejmie dysku sam. Domyślne find_multipaths strict wymaga jawnego dopisania woluminu. Brak mapy po instalacji pakietu to zachowanie zgodne z projektem, nie awaria.
  3. Awaria jednej ścieżki jest niewidoczna dla aplikacji - i właśnie dlatego monitoring musi liczyć czynne ścieżki. Sprawdzanie, czy zapis działa, nie wykryje utraty nadmiarowości.
  4. Dwie ścieżki to dwie sesje TCP na porcie 3260, po jednej do każdego portalu macierzy. Sam multipath nie zajmuje żadnego portu sieciowego - na zaporze trzeba zająć się wyłącznie stroną iSCSI.
  5. Awaria wszystkich ścieżek nie daje błędu, tylko zawieszenie - przy domyślnym queue_if_no_path. Czy to zachowanie jest właściwe, zależy od tego, co na tym woluminie stoi. To decyzja do podjęcia z góry, nie do odkrywania w trakcie awarii.

Cały opis powstał na stanowisku zbudowanym w labie: dwie maszyny wirtualne, dysk plikowy o pojemności 256 MB i drugi adres na karcie sieciowej w miejsce drugiego kontrolera. Wystarczyło to, żeby zobaczyć każde z powyższych zachowań na własne oczy. Jak postawić sobie takie środowisko, opisujemy w artykule Lab do testów na Terraform i OpenTofu.

Dokumentacja

  • multipath.conf(5) - format pliku konfiguracyjnego, w tym find_multipaths, no_path_retry i listy wykluczeń
  • multipathd(8) - usługa nadzorująca ścieżki i jej tryb interaktywny
  • multipath(8) - polecenie do oglądania i przebudowy map
  • iscsiadm(8) - wykrywanie celów iSCSI i logowanie do nich
  • targetcli(8) - konfiguracja strony macierzy: backstore, cel, LUN, portale i ACL

Macierz, do której prowadzi jedna droga, to jeden punkt awarii

Projektujemy i utrzymujemy dostęp do magazynu danych tak, żeby awaria kabla, przełącznika czy kontrolera nie zatrzymała pracy - razem z monitoringiem, który zauważy utratę nadmiarowości, zanim zabraknie drugiej ścieżki.