Toto je druhá část blogu “A Guide to Pgpool for PostgreSQL”. První část týkající se vyvažování zátěže, sdružování relací, mezipaměti a instalace naleznete zde.
Mnoho uživatelů hledá pgpool speciálně pro funkce vysoké dostupnosti a má co nabídnout. Návodů k pgpool HA je na webu málo (např. delší a kratší), takže by nemělo smysl je opakovat. Nechceme ani poskytovat další slepou sadu konfiguračních hodnot. Místo toho navrhuji hrát proti pravidlům a zkusit to udělat špatným způsobem, takže uvidíme nějaké zajímavé chování. Jednou z hlavních očekávaných funkcí (alespoň je to v horní části stránky) je schopnost rozpoznat použitelnost „mrtvého“ bývalého mistra a znovu ji použít s pg_rewind. Mohlo by to ušetřit hodiny obnovování nového pohotovostního režimu s velkými daty (protože vynecháme rsync nebo pg_basebackup, které efektivně zkopírují VŠECHNY soubory z nového hlavního serveru). Přísně vzato, pg_rewind je určen pro plánované převzetí služeb při selhání (během upgradu nebo migrace na nový hardware). Ale viděli jsme, kdy to výrazně pomáhá s neplánovaným, ale přesto elegantním vypnutím a automatickým převzetím služeb při selhání – například ClusterControl jej využívá při provádění automatického převzetí služeb při selhání replikačních slave zařízení. Předpokládejme, že máme případ:potřebujeme (jakéhokoli) mistra, aby byl co nejvíce přístupný. Pokud z nějakého důvodu (selhání sítě, překročení maximálního počtu připojení nebo jakékoli jiné „selhání“, které znemožňuje spouštění nových relací) již nemůžeme používat master pro operace RW, máme nakonfigurovaný cluster s podporou převzetí služeb při selhání s podřízenými zařízeními, které mohou přijímat připojení. Pak můžeme povýšit jednoho z otroků a přepadnout na něj.
Nejprve předpokládejme, že máme tři uzly:
- 10.1.10.124:5400 s /pg/10/m (pgpool se točí i zde)
- 10.1.10.147:5401 s /pg/10/m2
- 10.1.10.124:5402 s /pg/10/s2
To jsou ve skutečnosti stejné uzly jako v první části, ale uzel pro převzetí služeb při selhání je přesunut na jiného hostitele a $PGDATA. Udělal jsem to, abych se ujistil, že jsem ve vzdáleném příkazu ssh nepřeklepl nebo nezapomněl nějakou další citaci. Také informace o ladění budou vypadat jednodušeji, protože IP adresy jsou různé. Nakonec jsem si nebyl jistý, zda budu moci tento nepodporovaný případ použití uvést do provozu, takže to musím vidět na vlastní oči.
Přepnutí při selhání
Nejprve nastavíme failover_command a spustíme pgpool reload a pokusíme se o převzetí služeb při selhání. Zde a dále odpovím nějaké informace do /tmp/d na serveru pgpool, abych mohl sledovat tok pomocí tail -f /tmp/d.
[email protected]:~$ grep failover_command /etc/pgpool2/pgpool.conf
failover_command = 'bash /pg/10/fo.sh %D %H %R'
[email protected]:~$ cat /pg/10/fo.sh
rem_cmd="pg_ctl -D $3 promote"
cmd="ssh -T [email protected]$2 $rem_cmd"
echo "$(date) $cmd" >>/tmp/d
$cmd &>>/tmp/d
Pozn.:Máte $PATH nastavenou v .bashrc na vzdáleném hostiteli?...
Zastavme mistra (vím, že katastrofa se tak neděje, očekáváte, že alespoň nějaká obrovská opice nebo červeně zářící robot rozbije server obrovským kladivem, nebo alespoň zemřou nudné pevné disky, ale já používám tento elegantní varianta k ukázce možného použití pg_rewind, takže zde bude převzetí služeb při selhání výsledkem lidské chyby nebo selhání sítě půl sekundy za health_check_period), takže:
/usr/lib/postgresql/10/bin/pg_ctl -D /pg/10/m stop
2018-04-18 13:53:55.469 IST [27433] LOG: received fast shutdown request
waiting for server to shut down....2018-04-18 13:53:55.478 IST [27433] LOG: aborting any active transactions
2018-04-18 13:53:55.479 IST [28855] postgres t FATAL: terminating connection due to administrator command
2018-04-18 13:53:55.483 IST [27433] LOG: worker process: logical replication launcher (PID 27440) exited with exit code 1
2018-04-18 13:53:55.484 IST [27435] LOG: shutting down
2018-04-18 13:53:55.521 IST [27433] LOG: database system is shut down
done
server stopped
Nyní kontrolujeme výstup příkazu převzetí služeb při selhání:
[email protected]:~$ cat /tmp/d
Wed Apr 18 13:54:05 IST 2018 ssh -T [email protected]
pg_ctl -D /pg/10/f promote
waiting for server to promote.... done
server promoted
A kontrola po chvíli:
t=# select nid,port,st, role from dblink('host=localhost port=5433','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int);
nid | port | st | role
-----+------+------+---------
0 | 5400 | down | standby
1 | 5401 | up | primary
2 | 5402 | up | standby
(3 rows)
Také vidíme v ex-failover clusterových protokolech:
2018-04-13 14:26:20.823 IST [20713] LOG: received promote request
2018-04-13 14:26:20.823 IST [20713] LOG: redo done at 0/951EC20
2018-04-13 14:26:20.823 IST [20713] LOG: last completed transaction was at log time 2018-04-13 10:41:54.355274+01
2018-04-13 14:26:20.872 IST [20713] LOG: selected new timeline ID: 2
2018-04-13 14:26:20.966 IST [20713] LOG: archive recovery complete
2018-04-13 14:26:20.998 IST [20712] LOG: database system is ready to accept connections
Kontrola replikace:
[email protected]:~$ psql -p 5401 t -c "select now() into test"
SELECT 1
[email protected]:~$ psql -p 5402 t -c "select * from test"
now
-------------------------------
2018-04-13 14:33:19.569245+01
(1 row)
Slave /pg/10/s2:5402 přešel na novou časovou osu díky recovery_target_timeline =nejnovější v recovery.conf, takže jsme dobří. Nemusíme upravovat recovery.conf tak, aby ukazoval na nového hlavního serveru, protože ukazuje na ip a port pgpool a ty zůstávají stejné bez ohledu na to, kdo hraje roli primárního hlavního serveru.
Kontrola vyvažování zátěže:
[email protected]:~$ (for i in $(seq 1 9); do psql -h localhost -p 5433 t -c "select current_setting('port') from ts limit 1" -XAt; done) | sort| uniq -c
6 5401
3 5402
Pěkný. Aplikace za pgpoolem zaznamenají druhý výpadek a budou nadále fungovat.
Opětovné použití bývalého mistra
Nyní můžeme přepnout ex-master do pohotovostního režimu při převzetí služeb při selhání a vrátit jej zpět (bez přidání nového uzlu do pgpool, protože tam již existuje). Pokud nemáte povoleno wal_log_hints nebo kontrolní součty dat (úplný rozdíl mezi těmito možnostmi je zde), musíte znovu vytvořit cluster na ex-master, abyste mohli sledovat novou časovou osu:
[email protected]:~$ rm -fr /pg/10/m
[email protected]:~$ pg_basebackup -h localhost -p 5401 -D /pg/10/m/
Ale nespěchejte se spuštěním výše uvedených prohlášení! Pokud jste si dali pozor na wal_log_hints (vyžaduje restart), můžete zkusit použít pg_rewind pro mnohem rychlejší přepnutí bývalého mastera na nového slave.
Takže ATM máme ex-master offline, nový master s další časovou osou spuštěnou. Pokud byl bývalý master offline kvůli dočasnému selhání sítě a vrátí se, musíme ho nejprve vypnout. Ve výše uvedeném případě víme, že je dole, takže můžeme zkusit přetočit zpět:
[email protected]:~$ pg_rewind -D /pg/10/m2 --source-server="port=5401 host=10.1.10.147"
servers diverged at WAL location 0/40605C0 on timeline 2
rewinding from last common checkpoint at 0/4060550 on timeline 2
Done!
A znovu:
[email protected]:~$ pg_ctl -D /pg/10/m2 start
server started
...blah blah
[email protected]:~$ 2018-04-16 12:08:50.303 IST [24699] LOG: started streaming WAL from primary at 0/B000000 on timeline 2
t=# select nid,port,st,role from dblink('host=localhost port=5433','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int);
nid | port | st | role
-----+------+------+---------
0 | 5400 | down | standby
1 | 5401 | up | primary
2 | 5402 | up | standby
(3 rows)
Ops. Fuj! Navzdory skutečnosti, že cluster na portu 5400 je online a sleduje novou časovou osu, musíme pgpoolu říci, aby jej rozpoznal:
[email protected]:~$ pcp_attach_node -w -h 127.0.0.1 -U vao -n 0
pcp_attach_node -- Command Successful
Nyní jsou všechny tři aktivní (a pgpool to ví) a synchronizované:
[email protected]:~$ sql="select ts.i::timestamp(0), current_setting('data_directory'),case when pg_is_in_recovery() then 'recovering' else 'mastering' end stream from ts order by ts desc"
[email protected]:~$ psql -h 10.1.10.147 -p 5401 t -c "$sql";
i | current_setting | stream
---------------------+-----------------+-----------
2018-04-30 14:34:36 | /pg/10/m2 | mastering
(1 row)
[email protected]:~$ psql -h 10.1.10.124 -p 5402 t -c "$sql";
i | current_setting | stream
---------------------+-----------------+------------
2018-04-30 14:34:36 | /pg/10/s2 | recovering
(1 row)
[email protected]:~$ psql -h 10.1.10.124 -p 5400 t -c "$sql";
i | current_setting | stream
---------------------+-----------------+------------
2018-04-30 14:34:36 | /pg/10/m | recovering
(1 row)
Nyní zkusím použít recovery_1st_stage_command pro opětovné použití bývalého mistra:
[email protected]:~# grep 1st /etc/pgpool2/pgpool.conf
recovery_1st_stage_command = 'or_1st.sh'
Recovery_1st_stage_command ale nenabízí potřebné argumenty pro pg_rewind, které uvidím, když přidám do recovery_1st_stage_command:
echo "online recovery started on $(hostname) $(date --iso-8601) $0 $1 $2 $3 $4"; exit 1;
Výstup:
online recovery started on u2 2018-04-30 /pg/10/m2/or_1st.sh /pg/10/m2 10.1.10.124 /pg/10/m 5401
No - použití pg_rewind je jen v seznamu úkolů - co jsem čekal?.. Takže musím udělat nějaký opičí hack, abych získal hlavní IP a port (nezapomeňte, že se to po selhání bude neustále měnit).
Stáhněte si Whitepaper Today Správa a automatizace PostgreSQL s ClusterControlZjistěte, co potřebujete vědět k nasazení, monitorování, správě a škálování PostgreSQLStáhněte si WhitepaperOpicí hack
Takže v recovery_1st_stage_command mám něco takového:
[email protected]:~# cat /pg/10/or_1st.sh
pgpool_host=10.1.10.124
pgpool_port=5433
echo "online recovery started on $(hostname) $(date --iso-8601) $0 $1 $2 $3 $4" | ssh -T $pgpool_host "cat >> /tmp/d"
master_port=$(psql -XAt -h $pgpool_host -p $pgpool_port t -c "select port from dblink('host=$pgpool_host port=$pgpool_port','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int) where role='primary'")
master_host=$(psql -XAt -h $pgpool_host -p $pgpool_port t -c "select hostname from dblink('host=$pgpool_host port=$pgpool_port','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int) where role='primary'")
failover_host=$(psql -XAt -h $pgpool_host -p $pgpool_port t -c "select hostname from dblink('host=$pgpool_host port=$pgpool_port','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int) where role!='primary' order by port limit 1")
src='"port=$master_port host=$master_host"'
rem_cmd="'pg_rewind -D $3 --source-server=\"port=$master_port host=$master_host\"'"
cmd="ssh -T $failover_host $rem_cmd"
echo $cmd | ssh -T $pgpool_host "cat >> /tmp/d"
$cmd
tmp=/tmp/rec_file_tmp
cat > $tmp <<EOF
standby_mode = 'on'
primary_conninfo = 'host=$master_host port=$master_port user=postgres'
trigger_file = '/tmp/tg_file'
recovery_target_timeline = latest
EOF
scp $tmp $failover_host:$3/recovery.conf
rem_cmd="pg_ctl -D $3 start"
cmd="ssh -T $failover_host $rem_cmd"
echo $cmd | ssh -T $pgpool_host "cat >> /tmp/d"
$cmd
echo "OR finished $(date --iso-8601)" | ssh -T $pgpool_host "cat >> /tmp/d"
exit 0;
Teď jaký nepořádek! No - pokud se rozhodnete použít neexistující funkci - připravte se - bude to vypadat špatně, fungovat hůř a budete se trvale stydět za to, co jste udělali. Takže krok za krokem:
- Potřebuji IP a port pgpool, abych se k němu mohl vzdáleně připojit, a to jak pro dotaz „show pool_nodes“, tak pro protokolování kroků a spouštění příkazů.
- Předávám nějaké informace dbg do /tmp/d přes ssh, protože příkaz bude proveden na hlavní straně, což se po selhání změní
- Mohu použít výsledek „show pool_nodes“ k získání informací o běžícím hlavním připojení jednoduchým filtrováním pomocí klauzule WHERE
- Potřebuji dvojité uvozovky v argumentu pro pg_rewind, který bude muset běžet přes ssh, takže příkaz jen rozdělím kvůli čitelnosti, pak ho echo a spustím
- Příprava recovery.conf na základě výstupu z „show pool_nodes“ (při jeho psaní se ptám sám sebe – proč jsem místo toho nepoužil IP a port pgpool?..
- Spuštění nového podřízeného zařízení s přepnutím při selhání (vím, že bych měl použít 2. krok – toto bylo přeskočeno, aby nedošlo k opětovnému získání všech IP adres a portů)
Teď, co zbývá – pokusit se použít tento nepořádek v pcp:
[email protected]:~# pcp_recovery_node -h 127.0.0.1 -U vao -n 0 -w
pcp_recovery_node -- Command Successful
[email protected]:~# psql -h localhost -p 5433 t -c"select nid,port,st,role from dblink('host=10.1.10.124 port=5433','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int)"
nid | port | st | role
-----+------+----+---------
0 | 5400 | up | standby
1 | 5401 | up | primary
2 | 5402 | up | standby
(3 rows)
Kontrola /tmp/d na serveru pgpool:
[email protected]:~# cat /tmp/d
Tue May 1 11:37:59 IST 2018 ssh -T [email protected] /usr/lib/postgresql/10/bin/pg_ctl -D /pg/10/m2 promote
waiting for server to promote.... done
server promoted
online recovery started on u2 2018-05-01 /pg/10/m2/or_1st.sh /pg/10/m2
ssh -T 10.1.10.124 'pg_rewind -D --source-server="port=5401 host=10.1.10.147"'
ssh -T 10.1.10.124 pg_ctl -D start
OR finished 2018-05-01
Nyní jej samozřejmě chceme znovu převrátit, abychom zjistili, zda funguje na jakémkoli hostiteli:
[email protected]:~$ ssh -T 10.1.10.147 pg_ctl -D /pg/10/m2 stop waiting for server to shut down.... done
server stopped
[email protected]:~$ psql -h localhost -p 5433 t -c"select nid,port,st,role from dblink('host=10.1.10.124 port=5433','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int)"
nid | port | st | role
-----+------+------+---------
0 | 5400 | up | primary
1 | 5401 | down | standby
2 | 5402 | up | standby
(3 rows)
[email protected]:~# pcp_recovery_node -h 127.0.0.1 -U vao -n 1 -w
[email protected]:~$ psql -h localhost -p 5433 t -c"select nid,port,st,role from dblink('host=10.1.10.124 port=5433','show pool_nodes') as t (nid int,hostname text,port int,st text,lb_weight float,role text,cnt int,cur_node text,del int)"
nid | port | st | role
-----+------+----+---------
0 | 5400 | up | primary
1 | 5401 | up | standby
2 | 5402 | up | standby
(3 rows)
Protokol vypadá podobně - změnily se pouze IP a porty:
Tue May 1 11:44:01 IST 2018 ssh -T [email protected] /usr/lib/postgresql/10/bin/pg_ctl -D /pg/10/m promote
waiting for server to promote.... done
server promoted
online recovery started on u 2018-05-01 /pg/10/m/or_1st.sh /pg/10/m 10.1.10.147 /pg/10/m2 5400
ssh -T 10.1.10.147 'pg_rewind -D /pg/10/m2 --source-server="port=5400 host=10.1.10.124"'
ssh -T 10.1.10.147 pg_ctl -D /pg/10/m2 start
online recovery started on u 2018-05-01 /pg/10/m/or_1st.sh /pg/10/m
ssh -T 10.1.10.147 'pg_rewind -D --source-server="port=5400 host=10.1.10.124"'
ssh -T 10.1.10.147 pg_ctl -D start
OR finished 2018-05-01
V tomto karanténě se master přesunul na 5401 při převzetí služeb při selhání a poté, co tam chvíli bydlel, se přesunul zpět na 5400. Použití pg_rewind by to mělo urychlit. Dříve byla děsivá část automatického převzetí služeb při selhání - pokud jste opravdu zpackali konfiguraci a nepředvídali nějakou vyšší moc, mohli jste narazit na automatické převzetí služeb při selhání na další slave a další a další, dokud nezůstane žádný volný slave. A poté skončíte s několika mastery s rozděleným mozkem a žádnou rezervou pro převzetí služeb při selhání. V takovém scénáři je špatnou útěchou mít ještě více otroků proti selhání, ale bez pg_rewind byste neměli ani to. „Tradiční“ rsync nebo pg_basebackup zkopírují VŠECHNA $PGDATA a vytvoří pohotovostní režim a nemohou znovu použít „příliš odlišné“ ex master.
Na závěr tohoto experimentu bych chtěl ještě jednou zdůraznit - toto není řešení vhodné pro slepé kopírování. Použití pg_rewind se pro pg_pool nedoporučuje. Není použitelný v žádném bankomatu. Chtěl jsem přidat trochu čerstvého vzduchu do konfigurace pgpool HA, aby nubes jako já trochu blíže sledovali, jak to funguje. Aby se Coryphaeus usmíval nad naivistickým přístupem a možná to viděl našima očima.