V poslední době jsem si velmi navyknul na používání kontejnerizační technologie Podman. Podman je kompatibilní se známějším Dockerem, ale pro nás je výhodnější, díky skvělé provázanosti na Fedoru a celý technologický stack. V tomto krátkém seriálu si ukážeme, k čemu se Podman hodí (zabalíme Python Flask aplikaci). Pak bychom se podívali jak tyto pody spojovat v logické celky, a jak je integrovat se systemd a na závěr si ukážeme API Podmanu a budeme kontrolovat životní cyklus aplikací. Text se budu snažit psát jednoduše, aby byl snadno přístupný i začínajícím vývojářům (nebojte se proto přeskakovat). Děkuji předem za připomínky, či doplnění v komentářích.

K čemu se kontejnery hodí?

Vynechám nevýhody kontejnerů, případně se k tomu vrátím později. Obecně pokud běžně chcete instalovat libovolnou aplikaci, budete se spoléhat na balíček, který pro vás připravil někdo v distribuci. Tento balíček obsahuje program, jenž ovšem často závisí na dalších knihovnách (balíčcích). Tyto balíčky sdílí i s jinými programy. Z toho vyplývá následující (víceméně):

  1. Spoléháte se na verzi aplikace v distribuci.
  2. Aplikace musí pracovat s knihovnami a verzemi, které distribuce nabízí.

Kontejnery jsou postavené na funkci jádra, která umožní izolovat procesy. Pokud si chcete něco přečíst o implementaci (doporučuji) a jít trochu do hloubky, je tu odkaz na dokumentaci společnosti Red Hat a její úvod do Linuxových kontejnerů (anglicky). Pro naši představu můžeme velmi zjednodušeně říci, že jde o malé virtuální počítače, které mezi sebou (a s hostem) sdílí jádro operačního systému a vzájemně jsou jinak naprosto nezávislé (síť, disky, výkon, procesy ...). Tento koncept tedy přináší další výhody

  1. Izolované prostředí, procesy bežící uvnitř kontejneru můžete lépe kontrolovat a zakázat jim přístup k některým funkcím.
  2. Jednotné prostředí, díky sestavení takovéhoto kontejneru, můžete mít jednotné prostředí, ve kterém vaše aplikace běží. Máte pod kontrolou verze knihoven, interpretů programovacích jazyků atd.

Životní cyklus kontejneru, od kódu k běžící aplikaci

Ta cesta vlastně není složitá. Pokud máte kód, který jste si předtím napsali (v následujícím příkladu použiji Python Flask), je nutné napsat předpis, který řekne, jak sestavit odpovídající kontejner. Tento předpis se jmenuje Dockerfile a popisuje, co vzít jako základ a co doplnit (případně uvnitř kontejneru nainstalovat, nebo spustit). No a nakonec řeknete jaký skript/program se má pustit po načtení kontejneru. Ten označujeme jako entrypoint (vstupní bod).

Sestavení kontejneru probíhá právě podle tohoto Dockerfile, kde interpretujete jednotlivé kroky, až se dostanete na konec, kde získáte archiv se zabaleným souborovým systémem. Mimochodem, jednotlivé kroky jsou ukládané jako vrstvy, takže po změně nějaké části bude opětovné sestavení rychlejší. Balíček (obraz kontejneru) můžete nahrát do repozitáře, kde se tyto kontejnery schraňují, nebo přímo spustit. Při spouštění kontejneru je nutné specifikovat, jak přesně bude přistupovat do sítě, jestli s ním chcete sdílet některé adresáře, nebo jestli rovnou může otevřít port na vašem systému (vlastně NAT)

První praktický příklad

Budeme pracovat s podmanem, který rychle nainstalujete na Fedoru/CentOS, případně se podívejte, jak ho dostat do vaší oblíbené distribuce.
dnf install podman

Podman narozdíl od Dockeru umí běžet v uživatelském sezení a není potřeba mít přístup na socket. Následující příklad spustí nginx (webový server) na lokálním portu 8000. Všimněte si, že v systému máte předdefinované nějaké repozitáře, které už obsahují kontejnery, které zabalil někdo jiný (tak mu musíte věřit, že zabalil, co měl, a ne nic navíc). Následující příklad také obsahuje přepínač -p, co říká jak mapovat porty kontejneru (o tom později). Tady mapuje port 8000 hostitelského systému na port 80 kontejneru.
podman run -p 8000:80 nginx

Náhled na obsah nginx kontejneru puštěného přes Podman

Nginx kontejner

Otestovat můžete v prohlížeči, nebo použitím příkazu curl localhost:8000. Pak asi budete chtít tento experiment ukončit. To by mělo být jednoduché, že v okně s běžíčím kontejnerem zmáčknete klávesovou zkratku Ctrl+C. Ovšem taky je možné vypnout kontejner z hosta. Tady rovnou zjistíme, že kontejnery dostávají nějaké unikátní ID (hash). Pamatovat si ovšem sha256 je prakticky nemožné, proto kontejnery mají i jména (nedáte-li ho jim vy, dostanou náhodné). Zjistíme tedy jaké jméno kontejner má a tento kontejner "zabijeme" a smažeme.

Podman ps zobrazí ID a název kontejneru, které je možné (kromě Ctrl + C uvnitř kontejneru) možné použít k zavření

Přehled běžících kontejnerů přes Podman ps

podman ps
podman kill <jméno/id kontejneru>
podman rm <jméno/id kontejneru>

Vlastní aplikace

Vytvoříme banální aplikaci napsanou v Pythonu s frameworkem Flask. Doporučuji vytvořit si nějaký adresář, třeba HelloWorld. Nainstalujte si Flask pip install --user flask abychom mohli aplikaci lokálně otestovat (lokálně doporučuji používat virtuální prostředí). Teď už je triviální vytvořit soubor app.py obsahující aplikaci, co na portu 8000 spustí webový server, co pozdraví a řekne jméno systému, kde běží:

from flask import Flask
import os

app = Flask("HelloWorld")

@app.route("/")
def root():
    return "<h1>Ahoj z kontejneru</h1><p>{}</p>".format(os.environ.get("HOSTNAME", "Neznámý systém"))

app.run()

Aplikaci si samozřejmě otestujte (python app.py) a podívejte se v prohlížeči na adresu localhost:5000, kde by snad měla běžet. Pokud je vše v pořádku, přejdeme na vytvoření Dockerfile, kde vytvoříme kontejner postavený na Fedoře. Tedy zapišme do souboru Dockerfile následující (řádky s # jsou komentáře, klidně je vynechte):

# Na jakém obraze stavíme
FROM fedora:latest
# Instaluji python
RUN dnf install -y python python3-pip
# Instaluji flask
RUN pip install flask
# Kopíruji dovnitř soubor s aplikací
ADD app.py /app.py
# Definuji vstupní bod
ENTRYPOINT python app.py

Sestavení aplikace (za předpokladu, že jsme v umístění se soubory app.py a Dockerfile) už vyvoláme snadno - podman build -t muj-kontejner .. Teď to chvilku potrvá a můžete vidět, jak se postupně zpracovávají jednotlivé kroky a jak se mezistavy ukládají (vidíte otisky jednotlivých vrstev).

Připravený kontejner spustíme podman run --rm -p 5000:5000 muj-kontejner. Přidávám přepínač -p 5000:5000 (lze také zapsat jako -p 5000), pro předání portu 5000 ze spouštěcího počítače na kontejner a přepínač --rm pro okamžité smazání kontejneru při jeho ukončení. Nezapomeňte ověřit, že se všechno povedlo, pomocí prohlížeče, nebo příkazu curl localhost:5000. Všimněte si, že aplikace vrací jméno systému, na kterém běží a ten je v tomto případě jiný (a při opakovaném spuštění se změní). Pokud máte problémy s ukončením kontejneru, podívejte se o pár řádek výš.

Náhled na stránku běžící z kontejneru

Náhled na běžící aplikaci v kontejneru

Závěr

Dnes jsme si nastínili velmi triviální úvod do kontejnerů a jejich implementaci (použití) pomocí nástroje Podman. Pokud Vám dnešní díl nic nepřinesl, příště to snad bude už lepší. Podíváme se, jak kontejnery seskupovat, aby mohla aplikace přistupovat k databázi a stále šlo o plně izolované nasazení. Máte-li nějakou otázku, nebo jste narazili na nepřesnost, pište do komentářů, rád se přiučím, nebo odpovím. Ideální prostor na jakékoli otázky je naše fórum.