Költözés és káosz

Mennyire lehet bonyolult egy Git szerver migrálása Docker Swarm-ba?

Ha ezt a bejegyzést olvasod, a kísérlet sikerrel zárult. Átmigráltam a fejlesztői eszközöket egy felhős virtuális gépről egy fizikai gépre, ami itt zümmög a hátam mögött. De ne szaladjunk ennyire előre. Kezdjük az elején.

Adott egy virtuális gép, amin fejlesztéssel kapcsolatos alkalmazások futnak. Egy Gogs Git szerver, egy Jenkins, egy Docker Registry és egy Composer repository (Satis által generált statikus site). Nincs túl nagy forgalom rajta és a Jenkins-t leszámítva nem is esznek sokat az alkalmazások. A Jenkins környékén voltak azonban problémák. Néha például nem tudott Docker image-eket build-elni, mert elfogyott a memória.

Többek között ennek a megoldására építettem egy mini szervert (4 processzormag és 16 GB RAM egy alig 20x20x6 centiméteres kis dobozkában), amire át akartam ezt az egészet költöztetni. Egy magányos manager node-ból álló Docker Swarm fut rajta, Traefik intézi a route-olást, Portainer-rel vannak manage-elve a stack-ek. Az előzetes felmérések azt mutatták, hogy a Git szerver körül lesznek gondok, úgyhogy nem álltam neki a migrálásnak, egészen mostanáig.

Mivel erőforrásból itt nincs hiány, ezért úgy döntöttem, hogy a fentebb említett négy alkalmazást kiváltom egy GitLab telepítéssel. Tud már Docker Registry-ként és különböző Package Registry-kként (köztük Composer-ként) is viselkedni, a Jenkins-t leváltja a GitLab CI, talán így egyszerűbb lesz az életem (nem).

A probléma

Az SSH nem egy mai jószág. Nincsenek hozzá olyan divatos kiegészítők, mint például a TLS-hez az SNI így nem lehet egyszerűen azt megoldani, hogy van egy router alkalmazásod, ami bizonyos kritériumok alapján (például, hogy milyen host-ra érkezett a kapcsolat) más-más backend alkalmazáshoz irányítaná át az adatokat. Esetünkben ez azért okoz problémát, mert a szerverünk futtat egy SSH szervert a 22-es porton, viszont a Docker-ben futó Git szerver is szeretne egyet futtatni, lehetőség szerint szintén a 22-es porton. Mert ha nem ott fut, hanem mondjuk a 2022-es porton, akkor ebből:

$ git clone git@git.example.com:group-name/repository-name.git

Ez lesz:

$ git clone ssh://git@git.example.com:2022/group-name/repository-name.git

Vagy pedig minden egyes gépen, ahol repository-kat klónozunk ki, fel kell vennünk a .ssh/config fájlunkba valami ilyesmit:

.ssh/config
Host git.example.com
    Port 2022

Nem akkora katasztrófa, de nem akartam megkötni ezt a kompromisszumot. Vannak persze alternatív megoldások, a Gogs Docker image leírása például ezt a cikket linkeli, de az én ízlésemnek ez is túl összetákoltnak érződött.

A megoldás?

Aztán eszembe jutott egy ötlet. Nem tudom miért pont most, már egy jó ideje parkolópályán volt a migrációs projekt. A lényeg a következő lenne: adjunk több IP címet a gépnek, a Git-es host egy másik IP címre mutasson, amin csak a Git-es SSH hallgatózik, a régi IP-n meg a host gép sima SSH-ja.

$ host example.com
example.com has address 192.168.0.23
$ host git.example.com
git.example.com has address 192.168.0.24

Magával ragadott az ötlet eleganciája, bele is vágtam a megvalósításba. Először is szereznem kellett egy második IP címet. Ez a helyi hálózaton nem egy nagy cucc, ha nincs is egy extra hálózati kártya a gépben, akkor is egyszerűen meg tudjuk oldani. A host gép Debian-t futtat, ott a következőt kellett csak tennem:

/etc/network/interfaces
auto enp3s0:0
iface enp3s0:0 inet dhcp

auto enp3s0:1
iface enp3s0:1 inet static
  address 192.168.0.24
  netmask 255.255.255.0

Amint az látszik, egy hálózati csatolónak lehet több IP címe is. Az elsőt a régi jól bevált módon, DHCP-vel kapja a router-től (igazából a MAC cím alapján ez is egy fix IP cím lesz az esetünkben), a másodiknak pedig beállít egy statikusat saját magának. Egy kis módosítás még az SSH szerver configjában és kész is vagyunk:

/etc/ssh/sshd_config
ListenAddress 192.168.0.23

Át is nyargalhatunk a Docker-es részre. Bár ez esetben nem lenne szükséges és nincs is sok értelme én ezt a kapcsolatot is átvezettem a Traefik-en, csak azért, hogy az összes Docker Swarm-mal kapcsolatos dolog azon keresztül menjen.

traefik.yaml
version: "3.2"
services:
  traefik:
    image: traefik:v2.7.0
    command:
      - "--entrypoints.gitssh.address=:22"
    ports:
      - target: 22
        host_ip: 192.168.0.24
        published: 22
        protocol: tcp
        mode: host
    deploy:
      mode: global

A teljes config nagyon hasonló ahhoz, amit egy korábbi bejegyzésben raktunk össze. Az első próbálkozás így hibát dobott arra hivatkozva, hogy a host_ip-t nem ismeri, úgyhogy átírtam - 192.168.0.24:22:22 formátumra és azzal már nem volt gondja.

Minden működik, elindítottam egy Gogs szervert tesztelési céllal, tudtam klónozni, push-olni, öröm és boldogság volt. Egészen addig, amíg be nem zártam a normál SSH kapcsolatomat a szerverrel. Amikor megpróbáltam újracsatlakozni, sikertelen volt minden próbálkozásom. Mintha a Git-es SSH futott volna a másik IP címen is. De az ugye lehetetlen, épp az előbb mondtuk meg neki, hogy csak egy IP-n figyeljen.

Itt egy kis szünet következett, amíg kerestem egy VGA kábelt, egy felesleges billentyűzetet és visszaállítottam a szerveren az eredeti SSH-t. Azt is kiderítettem egy kézi docker stack deploy-jal, hogy az IP címre bind-olást figyelmen kívül hagyta a configomból, de ezt a lényegtelen kis warning-ot nem osztotta meg velem a Portainer.

Némi keresgélés után rá is leltem egy Github issue-ra, ami szerint a Swarm-ban futó szolgáltatások csak 0.0.0.0-ra tudnak bind-olni.

Úgy tűnt, hogy a gyönyörűen kigondolt megoldásom mehet a kukába. Persze futtathatnám a Git szervert a Swarm-on kívül, de azzal pont ugyanaz a bajom, mint a 2022-es porton való futtatással.

A megoldás!

Nem kellett több néhány napnál és előálltam egy újabb ötlettel. A 2022-es porton fogunk figyelni. Tudom, gáz, de had fejezzem be. Ha érkezik egy kapcsolat a 22-es portra a 192.168.0.24 IP címen, akkor azt átirányítjuk a 2022-es portra. Ehhez a következő két iptables szabályra van szükség:

# iptables -t nat -I PREROUTING 1 -d 192.168.0.24/32 -p tcp -m tcp --dport 22 -j REDIRECT --to-port 2022
# iptables -A INPUT -i enp3s0 -p tcp -m tcp --dport 2022 -j ACCEPT

Apró hátránya a dolognak, hogy a Git-es SSH szerver a 2022-es porton is elérhető lesz, de talán ennyi kompromisszum még belefér. Nem maradt más hátra, mint a GitLab szerver beállítása és a meglévő dolgok átmigrálása a régi gépről. Az sem ment teljesen zökkenőmentesen, de arról talán egy másik alkalommal mesélek majd.

This post is also available in english: Migration and madness

Hozzáfűznél valamit?

Dobj egy emailt a blog kukac deadlime pont hu címre.

Feliratkoznál?

Az RSS feed-et ajánljuk, ha kedveled a régi jó dolgokat.