Zero-downtime nasadenia s použitím Supervisord a Nginx Loadbalancera: Ako na to?

Keď prevádzkujete webovú aplikáciu v produkčnom prostredí, mali by ste sa snažiť o jej 100-percentnú dostupnosť. Ak často nasadzujete nové verzie a spôsobujete pritom výpadky, je to vnímané ako veľká chyba. Klientovi totiž môže niekoľko minút “off” spôsobiť nemalú stratu.

Pre spúšťanie a monitorovanie procesov na Unixových a Unix-podobných systémoch používame nástroj Supervisord. Osvedčil sa nám ako flexibilný, rozšíriteľný a navyše umožňuje automatizáciu a zjednodušenie správy procesov. Obyčajne nám spravuje Node.js REST API služby. Za bežných okolností všach dochádza pri reštarte takejto služby k niekoľko sekundovému výpadku. Na server sa nahrajú nové zdrojové kódy a služba sa musí na novo načítať.

Tento problém sme schopní odstrániť za pomoci nastavenia Nginxu a Supervisora. Je potrebné podotknúť, že aj keď sa jedná o rýchle, jednoduché a pomerne efektívne riešenie, nie je to systém, čo “myslí” na všetky problémy, ktoré sú s deployom aplikácie spojené. Práve preto treba mať oči neustále otvorené a operatívne nasadenie vyladzovať.

Pôvodná konfigurácia s výpadkami

Predpokladajme, že na spustenie REST API používame nasledujúci príkaz:

$ npm run production 

Zjednodušená prislúchajúca Supervisor konfigurácia je nasledujúca: 

[program:mojweb-api]
 command=npm run production 
 directory=/var/www/mojweb.sk/www/api
 user=mojweb
 stopasgroup=true
 killasgroup=true
 autostart=true
 autorestart=true

Takouto konfiguráciu dosiahneme stav, že služba sa zapne hneď po spustení virtuálneho stroja. Supervisor zároveň zabezpečí, aby sa proces naštartoval vždy, keď dôjde k výpadku.

Samotný Node.js proces “počúva” na porte alebo na sockete, ktorý bol v našom prípade definovaný v konfiguračnom súbore. Predpokladajme teda, že API po spustení bude počúvať na porte 10000.

Zjednodušená NGINX konfigurácia je takáto: 

        location /api-eshop {
             rewrite ^/api-eshop/(.*) /$1  break;
             proxy_pass http://localhost:10000;
         }

Requesty, ktoré dorazia na linku /api-eshop, nasmerujeme na port 10000. 

Pre reštart služby používame Supervisor:

$ supervisorctl restart mojweb-api 

Nová konfigurácia

Princíp spúšťania zmien v novej konfigurácii bude fungovať nasledovne.

  1. Na serveri beží inštancia API 
  2. Zmeny v zdrojovom kóde sa nahodia na web 
  3. Na serveri sa zapne druhá inštancia API už s novým zdrojovým kódom 
  4. Stará inštancia API sa vypne 
  5. Na serveri beží už iba jedna zaktualizovaná aplikácia

Aby sme dosiahli bezvýpadkové nasadzovanie zmien na web, musíme vykonať niekoľko drobných úprav. 

Na strane aplikácie potrebujeme dosiahnuť, aby bolo možné konfigurovať port, na ktorom počúvame, priamo pri spúšťaní z príkazového riadku. 

Pôvodný príkaz zameníme za: 

$ npm run production -p 10000

A Supervisor konfiguráciu upravíme nasledujúcim spôsobom:

[program:mojweb-api1]
 command=npm run production -p 10000
 directory=/var/www/mojweb.sk/www/api
 user=mojweb
 stopasgroup=true
 killasgroup=true
 autostart=true
 autorestart=true

 [program:mojweb-api2]
 command=npm run production -p 10001
 directory=/var/www/mojweb.sk/www/api
 user=mojweb
 stopasgroup=true
 killasgroup=true
 autostart=true
 autorestart=true

Takto sme schopní spustiť 2 inštancie API na rôznych portoch.

Nginx Load Balancer

Pokiaľ sa jedna inštancia reštartuje, musia byť requesty nasmerované na druhú. Tento krok vie zabezpečiť NGINX Load Balancer. 

Upravená konfigurácia pre NGINX:

   upstream magazin-ssr {
  server localhost:10000;
  server localhost:10001;
     }
location /api-eshop {
             rewrite ^/api-eshop/(.*) /$1  break;
             proxy_pass http://magazin-ssr;
         }

Nginx sa takto rozhodne, ktorú inštanciu API použije. Ak je jedná vypnutá, traffic je nasmerovaný na druhú, a naopak.

Na vyreštartovanie služby následne používame jednoduchý SHELL script. 

supervisorctl start ssr-edsi1
 sleep 10s
 supervisorctl restart ssr-edsi
 sleep 10s
 supervisorctl stop ssr-edsi1

Hotovo!