Konfigurace DRBD a řešení situace „Split-Brain“

Pro DRBD vyberte na obou serverech stejně velké diskové oddíly, ideálně nějaké pod RAID1 (např /dev/md3). Na dvou počítačích můžete mít více DRBD mirrorů, takže se nemusíte nějak moc omezovat. Kompletní dokumentaci k DRBD najdete na webu www.drbd.org.

Ukázka mé konfigurace DRBD a postupu zprovoznění, soubor /etc/drbd.conf:

 global { usage-count no; }
 common { syncer { rate 100M; } }

 resource mysql {
       protocol C;
       device    /dev/drbd1;
       disk      /dev/md3;
       meta-disk  internal;
       startup {
              wfc-timeout 20;
              degr-wfc-timeout 120;
              become-primary-on both; # Enable this *after* initial testing
       }
       net {
               cram-hmac-alg sha1;
               shared-secret "pai0eeRo";
               max-buffers 2048;
               ko-count 4;
               allow-two-primaries;
               after-sb-0pri discard-zero-changes;
               after-sb-1pri discard-secondary;
               after-sb-2pri disconnect;
       }
       on clemenza.klenot.eu {
               address   172.16.100.10:7790; #clemenza-clust
       }
       on tessio.klenot.eu {
               address   172.16.100.11:7790; #tessio-clust
       }
       disk {
               on-io-error detach;
               fencing resource-and-stonith;
       }
       handlers {
               outdate-peer "/usr/lib/heartbeat/drbd-peer-outdater -t 5";
       }
 }

Po vytvoření konfiguračního souboru vytvořte na obou počítačích DRBD RAID:

[root@tessio cluster]# drbdadm create-md mysql
v08 Magic number not found
v07 Magic number not found
v07 Magic number not found
v08 Magic number not found
Writing meta data...
initialising activity log
NOT initialized bitmap
New drbd meta data block sucessfully created.

Dále na obou počítačích proveďte start DRBD příkazem:

[root@tessio cluster]# /etc/init.d/drbd start
Starting DRBD resources:    [ d(mysql) s(mysql) n(mysql) ].

Zkontrolujte stav DRBD v souboru /proc/drbd:

[root@tessio cluster]# cat /proc/drbd
version: 8.2.6 (api:88/proto:86-88)
GIT-hash: 3e69822d3bb4920a8c1bfdf7d647169eba7d2eb4 build by buildsvn@c5-x8664-build, 2008-10-03 11:30:32

 1: cs:Connected st:Secondary/Secondary ds:Inconsistent/Inconsistent C r---
    ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 oos:987772

Pokud jste ve stavu, že se nesynchronizuje DRBD mezi počítači, ale vidíte Inconsistent/Inconsistent, můžete DRBD stopnout, spustit níže uvedený příkaz (jen na nově vytvářeném DRBD zařízení, přijdete o data!) a pak znovu DRBD nahodit.

[root@tessio cluster]# drbdadm detach mysql # ručně zastavíme konkrétní DRBD zařízení
[root@tessio cluster]# drbdadm disconnect mysql
[root@tessio cluster]# drbdadm -- 6::::1 set-gi mysql # přinutíme DRBD aby si myslelo, že jsou oba disky UpToDate
previously 0000000000000004:0000000000000000:0000000000000000:0000000000000000:0:0:0:1:0:1
set GI to  0000000000000006:0000000000000000:0000000000000000:0000000000000000:1:0:0:1:0:1

Write new GI to disk?
[need to type 'yes' to confirm] yes

[root@tessio cluster]# /etc/init.d/drbd reload # nahodíme DRBD
Reloading DRBD configuration.
[root@tessio cluster]# drbdadm primary mysql # nahodíme DRBD zařízení do stavu Primary
[root@tessio cluster]# cat /proc/drbd # ověříme stav
version: 8.2.6 (api:88/proto:86-88)
GIT-hash: 3e69822d3bb4920a8c1bfdf7d647169eba7d2eb4 build by buildsvn@c5-x8664-build, 2008-10-03 11:30:32

 1: cs:Connected st:Primary/Primary ds:UpToDate/UpToDate C r---
    ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 oos:987772

Pokud byste se snažili vytvořit drbd zařízení na někde kde už bylo, dostanete podobné hlášení:

[root@clemenza ~]# drbdadm create-md mysql
md_offset 1011544064
al_offset 1011511296
bm_offset 1011478528

Found ext2 filesystem which uses 987772 kB
current configuration leaves usable 987772 kB

 ==> This might destroy existing data! < ==

Do you want to proceed?
[need to type 'yes' to confirm] yes v07 Magic number not found v07 Magic number not found You want me to create a v08 style flexible-size internal meta data block. There apears to be a v08 flexible-size internal meta data block already in place on /dev/md2 at byte offset 1011544064 Do you really want to overwrite the existing v08 meta-data? [need to type 'yes' to confirm] yes Writing meta data... initialising activity log NOT initialized bitmap New drbd meta data block sucessfully created.

Split brain a oprava

Pokud se dostanete do situace kdy se vám rozpadne DRBD a nastane tzv. „split brain“ kdy DRBD neví kde jsou aktuální data, bude rozhodnutí na vás.

Při přesunu serverů a změně síťové konfigurace se mi podařilo na obou serverech nabootovat a spustit virtuální stroj – to znamená, že nakaždém uzlu clusteru běžel jeden stroj v XENu a servery spolu přes DRBD nekomunikovaly. Tak došlo k porušení integrity dat na „sdíleném“ úložišti a po opravě sítě DRBD nenastartovalo správně:

[root@clemenza ~]# /etc/init.d/drbd start Starting DRBD resources: [ d(mysql) s(mysql) n(mysql)]. degr-wfc-timeout has to be shorter than wfc-timeout degr-wfc-timeout implicitly set to wfc-timeout (20s) degr-wfc-timeout has to be shorter than wfc-timeout degr-wfc-timeout implicitly set to wfc-timeout (20s) .......... *************************************************************** DRBD's startup script waits for the peer node(s) to appear. - In case this node was already a degraded cluster before the reboot the timeout is 120 seconds. [degr-wfc-timeout] - If the peer was available before the reboot the timeout will expire after 20 seconds. [wfc-timeout] (These values are for resource 'mysql-data'; 0 sec -> wait forever) To abort waiting enter 'yes' [ 19]: /dev/drbd1: State change failed: (-2) Refusing to be Primary without at least one UpToDate disk Command '/sbin/drbdsetup /dev/drbd1 primary' terminated with exit code 17 command exited with code 17 

Startovací skript nenahodil některá sdálená úložiště, proto jsem se podíval na stav do /proc/drbd na obou serverech, stav byl u obou stejný:

[root@clemenza ~]# cat /proc/drbd
version: 8.2.6 (api:88/proto:86-88)
GIT-hash: 3e69822d3bb4920a8c1bfdf7d647169eba7d2eb4 build by buildsvn@c5-x8664-build, 2008-10-03 11:30:32

 1: cs:WFConnection st:Secondary/Unknown ds:Consistent/DUnknown C s---
    ns:0 nr:0 dw:0 dr:0 al:0 bm:1 lo:0 pe:0 ua:0 ap:0 oos:48835976

V logu /var/log/messages najdete pravděpodobně podobné hlášení:

May 10 17:54:58 tessio kernel: drbd1: Split-Brain detected, dropping connection!
May 10 17:54:58 tessio kernel: drbd1: self B45D64BD11EEDD92:020B5BBE576A3AAD:FCBF81E5FC307650:CA9A394126491F41
May 10 17:54:58 tessio kernel: drbd1: peer 4CE6BCAD514CEF86:020B5BBE576A3AAD:FCBF81E5FC307651:FCBF81E5FC307650
May 10 17:54:58 tessio kernel: drbd1: helper command: /sbin/drbdadm split-brain
May 10 17:54:58 tessio kernel: drbd1: conn( WFReportParams -> Disconnecting )
May 10 17:54:58 tessio kernel: drbd1: error receiving ReportState, l: 4!

Pokud dojde k takovéto situaci, zastavte vše co by mohlo s daným drbd zařízením praocvat. Určitě na obou serverech přepněte drbd zařízení do RO – secondary:

[root@tessio ~]# drbdadm -- secondary mysql # přepneme zařízení do RO režimu

Na obou strojích byly tedy aktuální data, ale na každém trochu jiná. No a je na adminovi aby se rozhodl. Proto jsem na jednom serveru, který jsem si prohlásil za sekundární, spustil následující sadu příkazů:

[root@clemenza ~]# drbdadm -- disconnect mysql # odpojení drbd zařízení od síťě
[root@clemenza ~]# drbdadm -- --discard-my-data connect mysql # zneplatnění dat na tomto serveru a připojíme se

Na druhém stroji, který jsem prohlásil za primární (zdroj dat) jsem spustil:

[root@tessio ~]# drbdadm -- disconnect mysql # odpojíme od sítě
[root@tessio ~]# drbdadm -- connect mysql # zkusíme připojit

Po provedení těchto příkazů se začalo dané úložitě synchronizovat. Výpis ukazuje trochu jiné hodnoty u ostatních úložišť, ty jsem přepnul do stavy primary-primary ručně pomocí drbdadm — primary resource případně syncnul výše popsaným postupem. Pokud se vám plete výpis Primary/Secondary a Inconsistent/UpToDate tak se přesvědčete pomocí programu iostat o tom kdo čte a kdo zapisuje. První uvedená hodnota je lokální stroj, kde výpis provádíte, hodnota za lomítkem je jeho protějšek. Primar/Secondary určuje na kterém serveru můžete zapistovat, Inconsistent/UpToDate určuje který má data v pořádku. Můžete zapisovat do drbd zařízení i na nekonzistentním serveru, přes síť si to spolu servery vyříkají.

[root@clemenza ~]# cat /proc/drbd
version: 8.2.6 (api:88/proto:86-88)
GIT-hash: 3e69822d3bb4920a8c1bfdf7d647169eba7d2eb4 build by buildsvn@c5-x8664-build, 2008-10-03 11:30:32

 1: cs:SyncTarget st:Primary/Secondary ds:Inconsistent/UpToDate C r---
    ns:0 nr:36899264 dw:36899264 dr:0 al:0 bm:2253 lo:0 pe:96 ua:0 ap:0 oos:11936712
        [==============>.....] sync'ed: 75.6% (11656/47691)M
        finish: 0:06:06 speed: 32,552 (30,696) K/sec

Timestamp v MSSQL

MSSQL sice obsahuje datový typ „timestamp“, který je aktualizován při každé změně záznamu. Problém je ale v tom, že nemá žádný vztah k datumu a času. Jde o číslo v binárním formátu.
Pokud tedy chceme zjistit čas, kdy došlo ke změně záznamu, musíme použít trigger, který bude nastavovat hodnotu do pole typu „datetime“.

Spustíme MS SQL Server Manager Studio.
Předpokládáme, že máme v databázi tabulku „users“ a v ni sloupec „mtime“ typu „datetime“.
Pole „mtime“ bude obsahovat timestamp.
Jako defaultni hodnotu nastavime „getDate()“. Tím zajistíme, že nově vložený záznam bude mít v poli „mtime“ aktuální čas.
Teď už zbývá pouze zajistit, aby se aktuální čas nastavil i v případě, že budeme záznam modifikovat.
K tomu nám poslouží trigger na událost update.
Ten vytvoříte tak, že v kontextovém menu vybereme možnost „New Trigger…“. Viz obrázek.

Jak vytvořit nový trigger

Otevře se okno, kam napíšeme samotný kód.
Ten by mohl vypadat nějak takto:

CREATE TRIGGER tr_UserTimestamp
   ON  users
   FOR UPDATE
AS
BEGIN
    DECLARE changedRecords Cursor
    FOR
    SELECT d.id FROM Deleted d, Inserted i
    WHERE d.id = i.idz
    AND (d.firstName <> i.firstName OR d.lastName <> i.lastName)

    DECLARE @recordId CHAR(12)

    OPEN changedRecords
    FETCH NEXT FROM changedRecords INTO @recordId
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
        UPDATE users
        SET mtime=getDate()
        WHERE id=@recordId

        FETCH NEXT FROM changedRecords INTO @recordId
    END

    CLOSE changedRecords
    DEALLOCATE changedRecords
END

Tím se říká, aby se vytvořil trigger s názvem „tr_UserTimestamp“ spoustěný při updatu tabulky „users“. V logických tabulkách „Inserted“ a „Deleted“ jsou záznamy, na kterých byl proveden update. V tabulce „Deleted“ jsou staré hodnoty, kdežto v „Inserted“ jsou hodnoty po provedeni update. Zrada je ale v tom, že pokud provedeme update na záznam, během něhož nastavite atributu hodnotu stejnou, jako měl před tím, tak se i tento záznam v tabulkách objeví. Proto jsou v uvedeném kódu porovnávány hodnoty odpovídajicích atributů a záznam je vybrán, pouze pokud se liší. Jen taková malá odbočka – existuje funkce Update() s názvem atributu jako parametr, která vrátí true, pokud byl atribut updetován. Což znamená, že vrátí true vždy, když se vyskytne atribut v UPDATE příkazu bez ohledu na to, jestli se hodnota změní, nebo ne.
Vybrané záznamy jsou po té procházeny pomocí kurzoru, přičemž je nastavován jejich atribut mtime na aktuální čas.

Až máme kód napsány, můžeme kliknout na „Execute“ a tím vytvoříme samotný trigger.

STONITH – když se oba nody HA clusteru nedohodnout a musí se vzájemně sestřelit

STONITH je mechanismus k řešení sporů ohledně přístupu ke zdrojům/službám clusteru. V rámci clusteru se musí vždy jednotlivé uzly dohodnout kde která služba poběží – tzv. fencing. Pokud se uzly dohodnou, vše je v pořádku. Když se náhodou nedohodnou, přijde ke slovu STONITH – sestřelení protějšího uzlu, který danou službu zadržuje a nechce vydat.

Konkrétní případ:

Při konfiguraci NFS clusteru jsem nastavil všechny potřebné služby – spouštění DRDB, LVM, Filesystem, IP adresy pro NFS a NFS samotné. DRBD je v režimu primary/slave = služba může běžet pouze na jednom z uzlů clusteru. Při studeném startu clusteru vše naběhlo správně a fungovalo, ovšem při migraci služby mezi uzly clusteru vždy nastala situace, že stávající aktivní uzel nechtěl uvolnit souborový systém aby se odmountoval a proto nešlo přehodit DRBD na druhý uzel. Cluster to vyřešil jednoduše – několikrát zkusil zda umount projde a když zjistil, že ne tak odstřelil porouchaný uzel (ten který nechtěl uvolnit filesystem).

Cluster po odstřelení vadného uzlu mohl správně nahodit DRDB, LVM, přimountovat souborový systém, nahodit IP a nfsd a vše bylo v pořádku. Tedy dokud jsem nechtěl zase přemigrovat služby zpátky po naběhnutí odstřeleného uzlu – vše se opakovalo.

STONITH mechanismus v tomto případě „řešil“ situaci rozbitého skriptu pro nfs. Závadu jsem vyřešil – opravil jsem rozbitý skript pro ovládání NFS. Skript pro ovládání NFS totiž při ukončení nechal běžet některé procesy a ty neuvolnily souborový systém. Na tom všem to selhávalo.

Má konfigurace STONITH je následující:

Servery a jejich management rozhraní přístupné po síti přes IPMI:

  • server: node0.domain.tld, IPMI: 179.100.1.72
  • server: node1.domain.tld, IPMI: 179.100.1.70

V management rozhraní každého serveru jsem vytvořil uživatele „stonith“ s právy pro restart serveru.
Přes program crmadmin jsem v konfiguraci CRM vytvořil 2x stonith resource – vždy pro daný server. Pomocí pravidel (location) jsem každý resource umístil na protější uzel – tak aby služba pro restart barzini.klenot.eu běžela na uzlu tommasino a naopak.

primitive stonith-node1 stonith:external/ipmi \
        params hostname="node1.domain.tld" ipaddr="179.100.1.70"\
                      userid="stonith" passwd="HESLO" interface="lan"
primitive stonith-node0 stonith:external/ipmi \
        params hostname="node0.domain.tld" ipaddr="179.100.1.72"\
                      userid="stonith" passwd="HESLO" interface="lan"
location loc-stonith-node1 stonith-node1 \
        rule $id="loc-stonith-node1-rule" -inf: #uname ne node0.domain.tld
location loc-stonith-node0 stonith-node0 \
        rule $id="loc-stonith-node0-rule" -inf: #uname ne node1.domain.tld

Postup úpravy konfigurace clusteru

Nejdříve si vypíšu dostupné stonith moduly:

[root@barzini ~]# crm ra list stonith
apcmaster               apcsmart                baytech
bladehpi                cyclades                external/drac5
external/hmchttp        external/ibmrsa         external/ibmrsa-telnet
external/ipmi           external/kdumpcheck     external/rackpdu
external/riloe          external/sbd            external/ssh
external/vmware         external/xen0           external/xen0-ha
ibmhmc                  ipmilan                 meatware
null                    nw_rpc100s              rcd_serial
rps10                   ssh                     suicide
wti_nps

Protože mám dostupné IPMI na serverech přes lokální síť, použiju external/ipmi modul. Možnosti konfigurace modulu zjistím následujícím příkazem:

[root@barzini ~]# crm ra meta external/ipmi stonith
IPMI STONITH external device (stonith:external/ipmi)

IPMI-based host reset

Parameters (* denotes required, [] the default):

hostname (string): Hostname
    The name of the host to be managed by this STONITH device

ipaddr (string): IP Address
    The IP address of the STONITH device

userid (string): Login
    The username used for logging in to the STONITH device

passwd (string): Password
    The password used for logging in to the STONITH device

interface (string): IPMI interface
    IPMI interface to use, such as "lan" or "lanplus"

Operations' defaults (advisory minimum):

    start    timeout=15
    stop     timeout=15
    status   timeout=15
    monitor  timeout=15 interval=15

Spustím tedy konfiguraci a napíši potřebné „příkazy“ pro vytvoření stonith zařízení, výsledek mohu ověřovat příkazem „show“, který vypíše aktuální stav. Konfiguraci potvrdím příkazem „commit“ a protože pracuji na živé konfiguraci běžícího clusteru, budou okamžitě nově vytvořená zařízení stonith spuštěna na správných uzlech clusteru:

[root@barzini ~]# crm configure
crm(live)configure# primitive stonith...
...
...
crm(live)configure# commit

Na závěr by bylo vhodné STONITH otestovat – pro základní test stačí na jednom z uzlů násilně zabít heartbeat pomocí „pkill -9 heartbeat“.

Webhosting Klenot.cz – Navýšení memory_limit pro PHP skripty na 64MB

Vzhledem k zvyšujícím se paměťovým nárokům CMS systémů jako jsou Joomla nebo Drupal navyšujeme u tarifu STANDARD memory_limit pro PHP na 64MB. Věříme, že touto změnou pomůžeme všem zákazníkům k běhu náročnějších aplikací a zkvalitnění stránek.

Pro nejmenší tarif BASIC je možné využít 32MB, pro STANDARD 64MB a pro ENTERPRISE až 96MB. Více o tarifech se dozvíte zde: ve srovnání hostingových tarifů.

Network failover – bonding

Požadavek na připojení serveru do sítě pomocí dvou síťových karet kvůli zabezpečení proti výpadku trasy připojení je běžný a v případě řešení HA clusteru i nevyhnutelný. V Linuxu je možné zdvojené připojení řešit velmi snadno pomocí tzv. bondingu. Skutečná síťová rozhraní se spojí do jednoho virtuálního přes které poté probíhá veškerá komunikace.

V případě výpadku jednoho ze skutečných fyzických rozhraní bonding tento výpadek odstíní a přesměruje komunikaci přes fungující rozhraní. Proto je vhodné podobné zapojení použít například pro propojení dvou serverů u DRDB (mirror disků přes síť) nebo u replikace MySQL a podobných aplikací.

Dnes používané distribuce jsou na bonding připravené – v jádře je třeba podpora (modul bonding) a nainstalovaný program ifenslave. Popis, který zde uvedu je platný pro RedHat a Fedoru. Samozřejmě lze spojit více rozhraní i v jiných distribucích, ale konfigurace se může mírně lišit. V počítači mám víc síťových karet, ale použiji pouze dvě. Je vhodné vybrat porty na různých kartách – například kartu integrovanou na desce a druhou přidanou do pci-e slotu. Pro konfiguraci použiji síťová rozhraní eth0 a eth1, vytvořím virtuální rozhraní bond0, které dostane IP adresu 172.16.100.10.

Bonding v Linuxu může pracovat ve více režimech, podrobnější popis je možné najít v dokumentaci u jádra nebo například na webu http://www.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/5/html/Deployment_Guide/s2-modules-bonding.html.

.

V souboru /etc/modprobe.conf si určím jaký modul pro které zařízení použít:

alias eth0 e1000e
alias eth1 e1000e
alias eth2 e1000e
alias eth3 e1000e
alias bond0 bonding

Soubor /etc/sysconfig/network-scripts/ifcfg-eth1:

DEVICE=eth1
ONBOOT=yes
USERCTL=no
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
HWADDR=00:23:8b:cc:bb:aa

Soubor /etc/sysconfig/network-scripts/ifcfg-eth2:

DEVICE=eth2
ONBOOT=yes
USERCTL=no
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
HWADDR=00:23:8b:ac:ab:ee

Soubor /etc/sysconfig/network-scripts/ifcfg-bond0:

DEVICE=bond0
IPADDR=172.16.100.10
NETMASK=255.255.255.0
NETWORK=172.16.100.0
BROADCAST=172.16.100.255
ONBOOT=yes
BOOTPROTO=none
USERCTL=no

Po nakonfigurování stačí spustit příkaz ifup bond0 a je hotovo. Konfiguraci a nahození rozhraní je samozřejmě nutné provést na obou serverech.

Výsledek po provedení konfigurace by měl být přibližně následující:

[root@clemenza network-scripts]# for i in eth1 eth2 bond0; do ip l show $i; done
5: eth1:  mtu 1500 qdisc pfifo_fast master bond0 qlen 1000
    link/ether 00:23:8b:64:da:0b brd ff:ff:ff:ff:ff:ff
2: eth2:  mtu 1500 qdisc pfifo_fast master bond0 qlen 1000
    link/ether 00:23:8b:64:da:0b brd ff:ff:ff:ff:ff:ff
7: bond0:  mtu 1500 qdisc noqueue
    link/ether 00:23:8b:64:da:0b brd ff:ff:ff:ff:ff:ff

[root@clemenza network-scripts]# for i in eth1 eth2 bond0; do ip a show $i; done
5: eth1:  mtu 1500 qdisc pfifo_fast master bond0 qlen 1000
    link/ether 00:23:8b:64:da:0b brd ff:ff:ff:ff:ff:ff
2: eth2:  mtu 1500 qdisc pfifo_fast master bond0 qlen 1000
    link/ether 00:23:8b:64:da:0b brd ff:ff:ff:ff:ff:ff
7: bond0:  mtu 1500 qdisc noqueue
    link/ether 00:23:8b:64:da:0b brd ff:ff:ff:ff:ff:ff
    inet 172.16.100.10/24 brd 172.16.100.255 scope global bond0
    inet6 fe80::223:8bff:fe64:da0b/64 scope link
       valid_lft forever preferred_lft forever