Virtuelne mašine i Linux kontejneri – Deo II

U prethodnom članku smo se upoznali sa dva načina virtuelizacije, virtuelnim mašinama i videli kako možemo postaviti NodeJS aplikaciju na Ubuntu 18.04 operativni sistem. Sada ćemo se upoznati sa Linux kontejnerima i sa dva alata pomoću kojih njima možemo da upravljamo, a to su LXC i LXD.

LXC

LXC je prvobitni korisnički interfejs za upravljanje kontejnerskim osobinama Linux kernela. Pomoću API-ja i jednostavnih alata, omogućava korisnicima Linux operativnih sistema da lako kreiraju i upravljaju sistemskim i aplikativnim kontejnerima.

Zanimljivo je da su ranije verzije Docker-a, sve do verzije 1.10, koristile LXC za upravljanje izvršavanjem kontejnera.

Instalacija LXC alata za upravljanje Linux kontejnerima

LXC paket se može instalirati pomoću komande

sudo apt install lxc

Ova komanda povlači sve potrebne pakete, a takođe i postavlja mrežni most za upotrebu od strane kontejnera.

Uobičajena upotreba

Linux kontejneri se mogu koristi na dva načina – kao privilegovani – pokretanjem lxc komandi kao root (ili pomoću sudo komande) i neprivilegovani – pokretanjem lxc komandi kao korisnik koji nije root. Razlika između ove dve vrste kontejnera je u tome što neprivilegovani kontejneri imaju veća ograničenja od privilegovanih (ne mogu da kreiraju nove čvorove ili da montiraju fajl sisteme sa rezervnim kopijama). Međutim, oni su bezbedniji po host sistem, s obzirom da je userid root korisnika iz kontejnera mapiran na userid korisnika koji nije root na host sistemu.

Korisnički prostor imena

Neprivilegovani kontejneri dozvoljavaju korisnicima da kreiraju i upravljaju kontejnerima bez ikakvih root privilegija. Funkcija koja ovo podupire je korisnički prostor imena. Korisnički prostor imena je hijerarhijski uređen tako da privilegovani zadaci imaju mogućnost mapiranja svojih id-eva na podređene prostore imena. Podrazumevano, svaki task na host sistemu radi u inicijalnom korisničkom prostoru, gde se ceo opseg id-eva mapira na ceo opseg. Počevši od Ubuntu 14.04 verzije, kada se kreiraju novi korisnici, njima se podrazumevano ponudi opseg userid-eva. Liste dodeljenih id-eva se mogu videti u fajlovima /etc/subuid i /etc/subgid. Subuid i subgid vrednosti po konvenciji počinju od 100000, kako bi se izbegli konflikti sa sistemskim korisnicima.

Ako se radi na nekoj od ranijih verzija (pre 14.04), korisniku se može dodeliti opseg id-eva sledećom komandom:

sudo usermod -v 100000-200000 -w 100000-200000 user1

Kreiranje i upravljanje neprivilegovanim kontejnerima

Da bismo kreirali neprivilegovane kontejnere, potrebno je da prvo kreiramo i popunimo fajl za podrazumevanu konfiguraciju u kome ćemo specificirati mapiranje id-eva, podešavanja mreže, kao i konfigurisati host da dozvoli neprivilegovanom korisniku da se prikači na host mrežu.

Naredni primer pretpostavlja da su mapirani user i group id-evi u opsegu od 100000-165536. Potrebno je proveriti stvarna mapiranja na računaru, kako bi se primer mogao prilagoditi stvarnom stanju na sistemu. Provera se vrši pokretanjem sledećih komandi:

grep $USER /etc/subuid
grep $USER /etc/subgid

Kreiranje i popunjavanje konfiguracionog fajla:

mkdir -p ~/.config/lxc
echo "lxc.idmap = u 0 100000 65536" > ~/.config/lxc/default.conf
echo "lxc.idmap = g 0 100000 65536" >> ~/.config/lxc/default.conf
echo "lxc.net.0.type = veth" >> ~/.config/lxc/default.conf
echo "lxc.net.0.link = lxcbr0" >> ~/.config/lxc/default.conf 
echo "$USER veth lxcbr0 2" | sudo tee -a /etc/lxc/lxc-usernet

Ovim komandama smo mapirali user i group id-eve, podesili tip mreže da bude virtual ethernet i podesili mrežni most koji će koristiti kontejneri (lxcbr0, formiran pri instalaciji lxc alata).

Nakon ovih podešavanja, možemo kreirati i koristiti neprivilegovane kontejnere.

Kontejneri se, pomoću LXC alata kreiraju putem komande:

lxc-create -t download -n ubuntu1 -d ubuntu -r bionic -a amd64

Opcije:

  • -t označava template koji će se koristiti, a „download“ označava da želimo da preuzmemo sliku operativnog sistema sa Linux Containers Image server-a
  • -n je opcija za specificiranje imena kontejnera
  • -d je opcija za označavanje distribucije linux operativnog sistema
  • -r služi za odabir izdanja odabranog operativnog sistema
  • -a služi za označavanje arhitekture

LXC omogućava i interaktivni odabir distribucije, izdanja i arhitekture. To možemo postići istom komandom, samo bez navođenja oznaka za odabir ovih opcija:

lxc-create -t download -n ubuntu1

Ova komanda će interaktivno pitati za odabir distribucije, izdanja i arhitekture za preuzimanje, uz štampanje svih dostupnih opcija. Ovaj način možete koristiti ukoliko niste sigurni koje su sve opcije dostupne. Mi biramo Ubuntu distribuciju, bionic (18.04) izdanje za amd64 arhitekturu.

Sve kontejnere možemo videti pomoću komande:

lxc-ls --fancy

Izlaz iz ove komande je tabela nalik sledećoj:

Spisak kontejnera
Spisak kontejnera

Iz ove tabele možemo videti neke osnovne informacije o svim kontejnerima korisnika – naziv, status, ipv4 adresu, ipv6 adresu, …

Da bi se dobilo više informacija o konkretnom kontejneru (u ovom slučaju – ubuntu1), potrebno je izvršiti sledeću komandu:

lxc-info ubuntu1

koja daje sledeći izlaz:

Informacije o kontejneru ubuntu1

Automatsko pokretanje kontejnera

Pošto želimo da u kontejner postavimo web aplikaciju kojoj možemo pristupiti preko Interneta u bilo koje doba dana, poželjno je da se kontejner pokreće zajedno sa sistemom, kako ne bismo morali da ručno pokrećemo kontejner ukoliko dođe do nekog neočekivanog prekida rada host sistema.

Kao što vidimo na slici, svaki kontejner ima svojstvo autostart, koje označava da li se kontejner pokreće zajedno sa sistemom (ima vrednost 1) ili ne (0). Međutim, ovo funkcioniše kod privilegovanih kontejnera, ali ne i kod neprivilegovanih. Na sreću, moguće je podesiti ručno da ovo funkcioniše, uz pomoć komande lxc-autostart kojom korisnik pokreće sve kontejnere koji imaju 1 kao vrednost autostart svojstva.

Prvo nam je potrebno da postavimo flag za autostart u konfiguracionim fajlovima kontejnera koje želimo da pokrećemo zajedno sa sistemom. Svaki neprivilegovani kontejner ima svoj direktorijum u ~/.local/share/lxc direktorijumu. Unutar tog direktorijuma, nalazi se config fajl kontejnera. Kako bismo postavili flag za autostart, potrebno je da unutar ovog direktorijuma izvršimo sledeću komnandu:

echo "lxc.start.auto = 1" >> config

Sada ćemo kreirati systemd servis koji će pokrenuti komandu lxc-autostart prilikom pokretanja host sistema. Prvo je potrebno da kreiramo fajl ~/.config/systemd/user/lxc-autostart.service u home direktorijumu korisnika čije kontejnere hoćemo da pokrećemo. Popunjavamo fajl na sledeći način (pomoću nekog editora – vim, nano, …):

[Unit]
Description="lxc autostart for lxc user"

[Service]
Type=oneshot
ExecStart=/usr/bin/lxc-autostart
ExecStop=/usr/bin/lxc-autostart -s
RemainAfterExit=1

[Install]
WantedBy=default.target

Ovde kao tip navodimo oneshot jer skripta izvršava jedan posao i završava se. Međutim, postavljamo RemainAfterExit na 1 kako bi systemd video servis kao aktivan i nakon izvršavanja skripte. ExecStart i ExecStop upućuju na ono što će se pokretati pri startovanju i stopiranju servisa, respektivno.

Nakon kreiranja ovog fajla, potrebno je izvršiti sledeću komandu, kako bi se omogućio kreirani servis:

systemctl --user enable lxc-autostart

(--user opcija govori systemctl procesu da ga koristimo u korisničkom modu)

Na kraju, moramo da predočimo systemd procesu da pokrene korisničku systemd instancu za korisnika prilikom pokretanja sistema. U suprotnom, ta instanca će biti pokrenuta tek kada se korisnik prijavi na sistem. Ovo postižemo komandom:

sudo loginctl enable-linger $USER

Mrežna podešavanja

U prethodnom članku smo objasnili kako se postavlja NodeJS aplikacija na Ubuntu 18.04 operativni sistem. S obzirom da u kontejneru imamo instaliran upravo ovaj sistem, potrebno je ispratiti iste korake za prenos fajlova, kreiranje pm2 skripte i pokretanje aplikacije. Takođe, potrebno je omogućiti port forwarding na ruteru, kako bismo mogli da pristupimo aplikaciji preko Interneta, što se isto može pročitati u prethodnom članku.

Nakon postavljanja aplikacije i podešavanja portova na ruteru, ostaje samo da se sav dolazni saobraćaj na određenom portu host sistema preusmeri na adresu i odgovarajući port kontejnera.

Za ovo nam je prvo potrebno da dodelimo statičku IP adresu željenom kontejneru. Ovo se može učiniti tako što mu se eksplicitno dodeli hardverska (MAC) adresa, za koju se vezuje IP adresa prilikom prvog sledećeg pokretanja. Hardverska adresa se specificira na sledeći način (naravno, u konfiguracionom fajlu kontejnera):

lxc.net.0.hwaddr = 00:16:3e:b3:48:f2

Nakon toga, prva sledeća IP adresa koja bude dodeljena tom kontejneru postaje njegova adresa i ne menja se, ma koliko puta se zaustavljao i pokretao.

Sada je potrebno da sav nadolazeći saobraćaj (pakete) na host sistem na određenom portu preusmerimo na dodeljenu statičku adresu kontejnera i port na kome sluša aplikacija. Ovo ćemo učiniti pomoću Uncomplicated Firewall-a (ufw).

Na host sistemu, prvo ćemo proveriti da li je ufw već aktivan, sledećom komandom:

sudo ufw status

Ukoliko nije aktivan, potrebno ga je aktivirati komandom:

sudo ufw enable

Nakon ovoga treba proveriti da li je omogućena opcija za preusmeravanje paketa na nivou sistema. Ukoliko ova opcija nije omogućena, neće biti moguće preusmeravanje, bez obzira na pravila koja se dodaju. Ovo možemo proveriti putem komande:

sudo sysctl net.ipv4.ip_forward

Ukoliko je vrednost net.ipv4.ip_forward 1, onda jeste omogućeno. U suprotnom, treba ukucati komandu:

sudo sysctl net.ipv4.ip_forward=1

nakon čega će važiti pravila za preusmeravanje.

Dodaćemo pravilo za presumeravanje tcp paketa u fajl before.rules u direktorijumu /etc/ufw. U ovom fajlu se nalaze pravila koja se pokreću pre pravila dodatih ufw komandom. NAT pravila se dodaju na početak fajla, pre filter sekcije, pa ćemo tu smestiti i naše pravilo:

*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 3001 -j DNAT --to-destination 10.0.3.236:3000
COMMIT

Napomena:

  • 3001 je port na host sistemu sa kojeg želimo da preusmerimo pakete
  • 10.0.3.236 je ip adresa kontejnera u kojem je postavljena aplikacija
  • 3000 je port na kome sluša aplikacija u kontejneru

Nakon dodavanja pravila, fajl bi trebalo da izgleda nalik sledećem:

Fajl before.rules nakon dodavanja pravila za preusmeravanje paketa

Na kraju, potrebno je restartovati ufw izvršavanjem komandi:

sudo ufw disable
sudo ufw enable

nakon čega je aplikacija spremna za korišćenje i pristup preko Interneta u bilo koje doba dana (pod uslovom da je host sistem uključen).

Još neke LXC komande:

  • lxc-attach -n container-name command – izvršavanje komande u navedenom kontejneru
  • lxc-stop -n container-name – zaustavljanje kontejnera sa navedenim imenom
  • lxc-info -n container-name – prikaz informacija o kontejneru
  • lxc-execute -n container-name command – pokretanje kontejnera sa navedenim imenom i izvršavanje komande command u njemu
  • lxc-destroy container-name – uklanjanje navedenog kontejnera sa sistema

LXD

LinuX Container Daemon je menadžer sistemskih kontejnera. On kreira pozadinski proces (system daemon) koji izlaže REST API kojem aplikacije mogu pristupiti lokalno preko Unix soketa ili preko mreže koristeći HTTP protokol.

Glavne razlike u odnosu na LXC:

  • Host moze da pokrene vise LXC kontejnera koristeci jedan system deamon, što olakšava upravljanje i štedi memoriju i vreme. Sa čistim LXC-om, za svaki kontejner je potreban poseban proces.
  • LXD daemon moze da iskoristi bezbednosne opcije host sistema kako bi kontejneri bili sigurniji, dok je kod upotrebe čistog LXC-a upravljanje bezbednošću kontejnera znatno teže.
  • Kako LXD daemon upravlja umrežavanjem i skladištenjem podataka, ikorisnici mogu da kontrolišu ove stvari kroz LXD CLI interfejs – deljenje ovih resursa sa kontejnerima je pojednostavljeno.
  • LXD nudi napredne opcije koje nisu dostupne sa čistim LXC-om, kao što su migracija kontejnera u realnom vremenu i mogućnost kreiranja snimka trenutnog stanja (snapshot) pokrenutog kontejnera.

Dakle, LXD i LXC nisu potpuno različite i nezavisne stvari, kao što nisu ni klonovi jedno drugog. LXD se oslanja na, i koristi LXC – što znaci da nije moguće koristiti LXD bez upotrebe LXC-a.

Sa druge strane, moguće je koristiti LXC bez LXD-a, ali nije preporučljivo za produkcijsko okruženje, pošto LXC nudi samo osnovne opcije.

Instalacija LXD alata

Počevši od Ubuntu 16.04 verzije, lxd je uključen u operativni sistem, pa ga nije potrebno naknadno instalirati. Ukoliko se radi o prethodnim verzijama sistema, potrebno ga je instalirati pokretanjem sledećih komandi:

sudo apt update
sudo apt upgrade
sudo apt install lxd

Ako imate operativni sistem Ubuntu 16.04 ili kasniji, možete omogućiti opciju za čuvanje kontejner fajlova u ZFS fajlsistemu. Bez ZFS-a, kontejneri bi bili sačuvani kao zasebni fajlovi na host fajlsistemu. Sa ZFS-om, imamo opcije kao što je copy-on-write što olakšava korišćenje.

Da bismo instalirali zfsutils-linux paket, pokrenućemo sledeću komandu:

sudo apt install zfsutils-linux

Nakon instalacije LXD paketa, instalacione skripte bi trebalo da su ubacile prijavljenog korisnika u lxd grupu, što se može proveriti pokretanjem komande groups. U slučaju da se lxd grupa ne nalazi u spisku grupa, potrebno je odjaviti se i ponovo se prijaviti na sistem ili se samo može pokrenuti komanda newgrp lxd koja će prijavljenog korisnika ubaciti u lxd grupu.

Inicijalizacija LXD-a

Sada ćemo inicijalizovati lxd kako bismo obavili podešavanja skladišta i mreže:

Opcije prilikom pokretanja lxd init komande

Ovim je inicijalizacija lxd-a završena.

Kreiranje i upravljanje kontejnerima

Sve upravljačke komande su dostupne preko komande lxc (ne treba je mešati sa prethodno navedenim komandama za upravljanje kontejnerima putem LXC-a).

Izlistavanje kreiranih kontejnera se obavlja pomoću komande:

lxc list

Kako bismo videli dostupne slike operativnih sistema za preuzimanje i pokretanje kontejnera, potrebno je da ukucamo sledeću komandu:

lxc image list images:

Umesto „images“ može se navesti konkretna distribucija Linux operativnog sistema, kako bi se izlistale samo njene slike.

Kontejner kreiramo sledećom komandom:

lxc launch ubuntu:b ubuntu1

Ova komanda kreira i pokreće kontejner sa Ubuntu 18.04 (b – bionic) operativnim sistemom.

Lista postojećih kontejnera nakon kreiranja kontejnera ubuntu1

Neke od osnovnih informacija o kontejneru možemo videti izvršavanjem komande:

lxc info ubuntu1
Osnovne informacije o kontejneru ubuntu1

Automatsko pokretanje kontejnera

Za razliku od LXC-a, ovde se kontejneri koji su pokrenuti pre gašenja host sistema, svakako automatski pokreću prilikom sledećeg podizanja host sistema, tako da nije potrebno ništa dodatno podešavati.

Postavljanje aplikacije i mrežna podešavanja

Postavljanje aplikacije, kao i mrežna podešavanja radi redirekcije paketa funkcionišu isto kao i kod LXC-a, pa ih nećemo ponovo obrađivati.

Još neke LXD komande:

  • lxc stop container-name command – zaustavljanje kontejnera sa navedenim imenom
  • lxc restart container-name – ponovno pokretovanje kontejnera
  • lxc info container-name – prikaz informacija o kontejneru
  • lxc exec container-name -- command – pokretanje kontejnera sa navedenim imenom i izvršavanje komande command u njemu
  • lxc delete container-name – uklanjanje navedenog kontejnera sa sistema

Ostatak dostupnih komandi se može videti izvršavanjem komande

lxc --help

Zaključak

U seriji članaka „Virtuelne mašine i Linux kontejneri“ upoznali smo se sa osnovnim osobinama i načinom upotrebe različitih metoda virtuelizacije. Međutim, kako se odlučiti za jedan metod?

Sa jedne strane, virtuelne mašine koriste dosta sistemskih resursa. Svaka virtuelna mašina nije samo kopija celokupnog operativnog sistema, već i virtuelna kopija svog hardvera koji je potreban da bi operativni sistem mogao da radi. Nasuprot tome, sve što je kontejneru potrebno je dovoljan deo postojećeg operativnog sistema i neophodne bibilioteke i programi. Posledica ovoga jeste što se može postaviti 2-3 puta više aplikacija na serveru sa kontejnerima nego sa virtuelnim mašinama.

S druge strane, problem sa kontejnerima jeste bezbednost. U odnosu na virtuelne mašine, koje simuliraju hardver i potpuno su odvojene od host sistema, kontejneri dele resurse sa host sistemom, što može da napravi sigurnosne probleme. Zato je potrebno biti pažljiv pri upotrebi kontejnera, koristiti neprivilegovane kontejnere umesto privilegovanih kad god je to moguće.

Iako brži i manje zahtevni, kontejneri su relativno mladi i imaju prostora za napredak, pa se ne bismo još uvek u potpunosti odrekli virtuelnih mašina. Oba pristupa imaju svoje prednosti i mane, a na korisniku je da odabere šta želi, prema svojim potrebama.

Literatura i korisni linkovi

Autor: Jelena Colic

Studentkinja završne godine Informatike na Prirodno-matematičkom fakultetu u Kragujevcu. Tokom studija je bila polaznica radionica i praksi nekoliko IT firmi, kao i mentor programiranja na Matematičkoj radionici mladih. Vredna i motivisana osoba, sa željom za učenjem novih stvari.

Jelena Colic

Studentkinja završne godine Informatike na Prirodno-matematičkom fakultetu u Kragujevcu. Tokom studija je bila polaznica radionica i praksi nekoliko IT firmi, kao i mentor programiranja na Matematičkoj radionici mladih. Vredna i motivisana osoba, sa željom za učenjem novih stvari.