Možná si pamatujete, nebo stále zažíváte takovou tu situaci, kdy vám program funguje krásně na vašem počítači, ale pošlete jej kolegovi a on ho zaboha nespustí. Nebo co hůř, aplikaci nainstalujete na produkční server a ta se prostě nerozběhne. Příčin může být spousta: jiný operační systém, jiná verze operačního systému, špatná verze knihoven potřebných pro chod aplikace, jiné adresářové cesty atd. Nejedná se prostě o laboratorní prostředí, ve kterém by se aplikace spouštěla vždycky stejně. A právě tady přichází na řadu Docker.
Stručně řečeno je Docker virtualizační nástroj, pomocí kterého můžete vytvořit virtuální stroj, kontejner, ve kterém běží operační systém v konkrétní verzi, všechny potřebné knihovny pro běh vaší aplikace a aplikace samotná. Máte plnou kontrolu nad tím, co je v kontejneru nainstalováno, v jakých verzích a v jakých cestách. Pokud spustíte kontejner na svém počítači a všechno bude běžet jak má, je vysoká pravděpodobnost, že tomu bude i na cizím počítači, případně nějakém serveru.
Jak vytvořit Docker image
K vytvoření image, obrazu, který se po spuštění stane kontejnerem, budeme potřebovat naši python aplikaci (můžeme použít například tu, ze článku První webová stránka ve Flasku) a Dockerfile, což je takový náš "recept", jak se má image sestavit. Jedná se o textový soubor obsahující za sebou jdoucí příkazy, pomocí nichž se vrstva po vrstvě tvoří výsledný spustitelný obraz.
FROM debian:bullseye
COPY projekt /app
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip install uwsgi
RUN pip install -r /app/requirements.txt
WORKDIR /app
EXPOSE 5000
CMD ["uwsgi", "--http", ":5000", "--wsgi-file", "manage.py", "--callable", "app"]
Zkusíme si Dockerfile projít řádek po řádku. Na prvním řádku máme příkaz FROM, který nám říká z jakého základního image budeme vycházet. V rámci maximálního zjednodušení si klidně můžeme docker kontejner představit jako cibuli, která se skládá z vrstev, a každý jeden příkaz v Dockerfile nám přidává jednu vrstvu výsledné cibule. FROM je naše jádro. Příkazem RUN spustíme příkaz v kontejneru. Jelikož vycházíme z Debian image, můžeme použít apt-get pro nainstalování potřebných knihoven. Pokud příkazy dává smysl spouštět dohromady (jako tady update && install), spouštějte je dohromady. Vrstvy se totiž cachují a šetříme tak místo a čas. Příkazem WORKDIR se v kontejneru přepneme do konkrétního adresáře a další příkazy pak můžeme spouštět relativně vůči němu. EXPOSE říká, jaké porty chceme z kontejneru vystavovat ven. Jelikož aplikaci spouštíme na portu 5000, bude to právě tento port. Poslední příkaz je CMD, kterým říkáme, co se má uvnitř kontejneru spustit.
Jak spustit Docker image
"Ubalený" image získá svůj hash, který vypadá podobně jako 7bbd36e21811d305f8a3b2b825dd8e12f42dda8cb52bbfad3815e9c3f2410b0b. Takový image můžeme spustit pomocí příkazu docker run [hash]. 💁♂️ Pro zajímavost: pokud se dá docker image jedinečně identifikovat pomocí prvních N znaků hashe, stačí nám právě ty ke spuštění. Můžeme tedy vyzkoušet image spustit pomocí docker run 7bbd3 (nahraďte začátkem svého hashe).
Pokud je všechno v pořádku, měl by se nám spustit kontejner a na standardním výstupu bychom měli vidět uwsgi hlášky. Kontejner nám běží, ale i když máme v Dockerfile EXPOSE nějakého portu, zatím se do aplikace nedostaneme. Při spouštění kontejneru budeme muset využít přepínač -p, který nám zpropaguje port ven do hostujícího stroje. Zkusme:
docker run -p 5000:5000 7bbd3
Po tomto spuštění kontejneru si můžeme otevřít okno prohlížeče a zadat do něj http://localhost:5000. Pokud jsme všechno udělali správně, měla by se nám zobrazit naše webová stránka napsaná v Pythonu a spuštěná v Dockeru. 💪
Užitečné příkaz
docker build -f ./Dockerfiledocker ps- zobrazí seznam spuštěných kontejnerů
docker images- zobrazí seznam existujících obrazů
docker logs [id kontejneru]- zobrazí logy (poslané na standardní výstup) daného kontejneru
- můžeme přidat přepínač
-f(follow), který bude výstup neustále číst
docker stop [id kontejneru]- zastaví spuštěný kontejner
docker rm [id kontejneru]- smaže existující kontejner
docker rmi [id image]- smaže vytvořený obraz
docker exec [id kontejneru] [příkaz]- nad spuštěným kontejnerem spustí zadaný příkaz
- přepínačem
-itmůžeme přejít do interaktivního režimu a tak nad běžícím kontejnerem například spustit bash
docker run [id image]- spustí se aplikace definovaná v Dockerfile
- můžeme přidat další parametr, kterým "přepíšeme", co se má vlastně v dockeru spustit.
- přepínačem
-itmůžeme spustit interaktivní režim, takže například nad vytvořeným obrazem nějaké linuxové distribuce spustit bash.
docker tag [id image] [tag]- tímto příkazem můžeme "otagovat", "pojmenovat" image nějakým vypovídajícím názvem, než hashem, který nám vznikne při ubalení.