Zabalenie služby do Docker obrazu
o tom, ako distribuovať svoju aplikáciu vo forme Docker obrazu
Video pre cvičenie
Upozornenie
Predtým, ako prídete na cvičenie, tak:
nainštalovať vývojové prostredie PyCharm Professional
vytvoriť si účet na hub.docker.com
nainštalovať si lokálne Docker
zvládať základy práce s technológiou Docker
stiahnite si obraz
python:3.13-slim
príkazom:$ docker image pull python:3.13-slim
Motivácia
Na minulom cvičení sme si vytvorili jednoduchú notifikačnú službu s názvom Notifier, pomocou ktorej bude môcť ktokoľvek, kto je šúčasťou infraštruktúry Smart department posielať notifikácie pomocou knižnice [Apprise]. A keďže budeme chcieť na vytvorenej službe zarobiť obrovské prachy, pokúsime sa ju dnes zabaliť do Docker obrazu, aby sme ju tým pádom vedeli nasadiť kdekoľvek.
Ciele
naučiť sa vytvoriť Docker obraz pre svoju aplikáciu
extrahovať konfiguráciu aplikácie mimo jej kód
naučiť sa ovládať aplikáciu bežiacu v kontajneri pomocou premenných prostredia
Postup
Make Something Working
V prvom kroku sa pokúsime spraviť nutné minimum na to, aby sme vytvorili Docker obraz s našou službou. Nutné minimum znamená, že neaplikujeme best practices, ale že sa sústredíme len na to, aby sme službu ako Docker kontajner vedeli úspešne spustiť.
Task
Vo svojom projekte vytvorte súbor Dockerfile
, pomocou
ktorého vytvoríme Docker obraz služby.
Súbor Dockerfile
vytvorte v koreňovom priečinku vášho
projektu. Podoba projektu po jeho vytvorení bude vyzerať nasledovne:
./
├── Dockerfile
├── readme.md
├── requirements.txt
└── src/
├── main.py
└── models.py
Task
Vytvorte minimálny funkčný Docker obraz.
Vytvorenie každého Docker obrazu sa obyčajne skladá z týchto základných častí:
- výber základného obrazu pomocou kľúčového slova
FROM
, - inštalácia potrebných balíkov pre spustenie aplikácie pomocou
kľúčového slova
RUN
, - prekopírovanie zdrojových súborov aplikácie pomoocu kľúčového slova
COPY
, a - spustenie aplikácie pri spustení kontajnera pomocou kľúčového slova
CMD
aleboENTRYPOINT
Základná podoba tohto súboru bude pre nás vyzerať nasledovne:
FROM python:3.13-slim
RUN pip3 install apprise loguru paho-mqtt pydantic pydantic-settings
COPY src/ /app
WORKDIR /app
CMD [ "/usr/bin/env", "python3", "main.py" ]
Task
Na základe vytvoreného súboru Dockerfile
vytvorte
Docker obraz s názvom notifier
.
Docker obraz môžete vytvoriť nasledovným príkazom:
$ docker image build --tag notifier .
Overiť si úspešne vytvorený obraz môžete spustením príkazu na vypísanie zoznamu všetkých obrazov:
REPOSITORY TAG IMAGE ID CREATED SIZE
notifier latest 2a290634240b 6 hours ago 161MB
python 3.12-slim 668757ec60ef 4 weeks ago 124MB
Task
Overte vytvorený Docker obraz spustením kontajnera.
Overiť vytvorený obraz môžete spustením nasledujúceho príkazu z príkazového riadku:
$ docker container run --rm -it notifier
Poznámka
Ak používate vývojové prostredie PyCharm, môžete súbor
Dockerfile
spustiť - buď stlačením klávesovej skratky
Ctrl + Shift + F10
alebo vybratím položky
Run 'Dockerfile'
. Aby ste však videli obraz aj v zozname
všetkých obrazov, upravte si konfiguráciu spustenia pridaním
značky (z angl. tag). Tým pádom zakaždým, keď spustíte
Dockerfile
, sa okrem spustenia kontajnera v prípade potreby
vytvorí aj nová verzia obrazu.
Poznámka
Čím viac zmien spravíte v súbore Dockerfile
, tým viac
obrazov v zozname všetkých repozitárov, bude vyzerať nasledovne:
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 1c3f710c6719 2 days ago 15.1MB
Tieto obrazy zmažete príkazom:
$ docker image prune
Application Settings
Momentálne je súčasťou kódu služby aj jej konfigurácia. To však nie je dobre, pretože tým pádom nebude naša služba prenositeľná na iné riešenia. Rovnako je v aplikácii bezpečnostné riziko, pretože prihlasovacie meno a heslo pre prístup k MQTT broker-u je tiež súčasťou kódu.
V tomto kroku teda zabezpečíme všetko potrebné, aby sme všetko, čo súvisí s konfiguráciou, dostali z aplikácie von. A keďže sa služba bude spúšťať vo forme Docker kontajneru, bude možné konfiguráciu podľa odporúčaní Twelve factor app zadať pomocou premenných prostredia (tzv. environment variables).
Task
Vytvorte nový model Settings
, ktorý bude obsahovať
konfiguráciu aplikácie.
Model Settings
bude tentokrát potomkom triedy
BaseSettings
. Do konfigurácie uložíme nasledovné
informácie:
broker
- adresa MQTT brokeraport
- číslo portu, na ktorom MQTT broker komunikuje, predvolená hodnota nech je1883
user
- prihlasovacie meno, predvolená hodnota nech jeNone
password
- prihlasovacie heslo, predvolená hodnota nech jeNone
base_topic
- základná téma, na ktorej bude služba pracovať
Výsledný model bude vyzerať nasledovne:
class Settings(BaseSettings):
str
broker: int = 1883
port: str | None = None
user: str | None = None
password: str
base_topic:
= SettingsConfigDict(
model_config ='NOTIFIER_',
env_prefix='utf-8'
env_file_encoding )
Poznámka
Premenné prostredia pre konkrétnu aplikáciu majú obyčajne prefix. Tým
je možné odlíšiť konfiguráciu medzi viacerými službami v prípade, že
majú rovnaké názvy konfiguračných premenných. To zabezpečí členská
premenná model_config
. Okrem prefixu nastavíme aj kódovanie
na utf-8
.
Task
Vytvorte inštanciu triedy Settings
v module
main.py
ako globálnu premennú a v kóde nahraďte príslušné
konštanty hodnotami z nastavení.
Task
Overte správnosť svojej implementácie.
Vlastnosti služby je možné nastaviť pri spustení pomocou premenných prostredia. To je možné urobiť dvoma spôsobmi:
Pomocou IDE - Premenné prostredia je možné nastaviť v dialógu pre spustenie ako aplikácie z prostredia, tak aj pre spustenie kontajnera zo súboru
Dockerfile
.Z príkazového riadku pomocou príkazu:
$ docker container run --rm -it \ --name notifier \ --env NOTIFIER_BROKER=147.232.205.176 \ --env NOTIFIER_USER=maker \ --env NOTIFIER_PASSWORD=mother.mqtt.password \ --env NOTIFIER_BASE_TOPIC=services/notifier/ab123cd \ notifier
Premenné prostredia je možné zadať aj pomocou tzv. .env
súboru. Jedná sa o súbor s premennými prostredia. To je výhodné najmä
vtedy, ak je premenných prostredia veľa. Vyzerať môže napríklad
takto:
# local.env
NOTIFIER_BROKER=147.232.205.176
NOTIFIER_USER=maker
NOTIFIER_PASSWORD=mother.mqtt.password
NOTIFIER_BASE_TOPIC=services/notifier/ab123cd
Opäť je možné vybrať cestu k súboru aj pomocou IDE a rovnako je možné ho použiť aj pri spúšťaní z príkazového riadku:
$ docker container run --rm -it \
--name notifier \
--env-file local.env \
notifier
Publishing Image to Repository
Obraz sme vytvorili a je pripravený na používanie. Vypublikujeme ho do repozitára hub.docker.com, aby sme ho vedeli použiť odkiaľkoľvek a rovnako, aby ho vedel použiť ktokoľvek.
Task
Prihláste sa do účtu hub.docker.com
.
$ docker login
Task
Odošlite do svojho účute v repozitári hub.docker.com vytvorený obraz služby notifier.
Najprv označkovať obraz:
$ docker image tag notifier:latest USER/notifier:latest
$ docker image tag notifier:latest USER/notifier:2024
$ docker image tag notifier:latest USER/notifier:2024.1
Následne obraz odošleme spolu so všetkými značkami:
$ docker image push --all-tags USER/notifier
Task
Overte úspešnosť odoslania.
Ak ste postupovali správne, tak po zobrazení svojho profilu na serveri hub.docker.com, uvidíte svoj obraz.
(Some) Best Practices
Ak sa budete snažiť nasadzovať svoje riešenia vo forme Docker kontajnerov, určite budete hľadať spôsoby, ako to robiť čo najlepšie. Na internete nájdete obrovské množstvo videí a iných zdrojov, ktoré sa vám budú snažiť ponúkať tie najlepšie best practices. My sa v tomto kroku pozrieme na niekoľko z nich.
Task
Zabezpečte, aby sa aplikácia v kontajneri spustila pod právami
používateľa maker
.
Používateľa treba v obraze najprv vytvoriť. To je možné napr.
nasledovným rozšírením príkazu RUN
v súbore
Dockerfile
:
RUN groupadd --gid 1000 maker \
&& useradd --uid 1000 --gid 1000 --no-create-home maker \
&& pip3 install apprise loguru paho-mqtt pydantic pydantic-settings
Po poslednom príkaze RUN
použíjeme príkaz
USER
s uvedením mena používateľa, pod ktorým sa spustí
aplikácia v kontajneri. Výsledný súbor Dockerfile
bude
vyzerať nasledovne:
FROM python:3.13-slim
RUN groupadd --gid 1000 maker \
&& useradd --uid 1000 --gid 1000 --no-create-home maker \
&& pip3 install apprise loguru paho-mqtt pydantic pydantic-settings
COPY src/ /app
WORKDIR /app
USER maker
CMD [ "/usr/bin/env", "python3", "main.py" ]
Task
Overte úspešnosť vykonaných zmien.
Do spusteného kontajnera sa môžete dostať spustením interpretera príkazov v ňom. To je možné opäť pomocou vývojového prostredia ako aj priamo z príkazového riadku príkazom:
$ docker container exec --it notifier bash
Meno používateľa uvidíte rovno v prompte alebo si aktuálneho
používateľa spolu s jeho skupinou môžete zistiť spustením príkazu
id
v kontajneri:
$ id
uid=1000(maker) gid=1000(maker) groups=1000(maker)
Task
Vytvorte samostatný skript healthcheck.py
, pomocou
ktorého zabezpečíte kontrolu stavu služby.
Healthcheck mechanizmus je veľmi dôležitý a umožní nám overiť, či služba naozaj funguje tak, ako má. V našom prípade spravíme jednoduché overenie, ktoré bude vyzerať asi takto:
- Pripojíme sa na MQTT broker. Tým overíme aj to, či konfigurácia, ktorú pre službu máme, je správna.
- Prihlásime sa na odber témy o stave našej služby. Po jej prijatí overíme, či služba stále pracuje a nedošlo náhodou k uviaznutiu v kóde, kedy proces zostal spustený, ale prestal komunikovať s MQTT brokerom. Netreba zabudnúť overiť situáciu, ak správa prijatá nebola.
- Odpojíme sa.
Dôležité však bude nastaviť návratový kód (tzv. exit status) podľa výsledku kontroly. To znamená, že:
- ak kontrola prebehla neúspešne (nesprávna konfigurácia, nesprávna
správa v téme
/status
, žiadna prijatá správa z témy/status
, správaoffline
), ukončite aplikáciu s kódom1
- ak kontrola prebehla úspešne, ukončite aplikáciu s kódom
0
Task
Overte správnosť svojej implementácie.
Skontrolujte stavový kód sputenej aplikácie pre všetky možné prípady.
Task
Pridajte do súboru Dockerfile
kontrolu stavu zdravia
pomocou kľúčového slova HEALTHCHECK
.
Kontrolu zdravia nastavte nasledovne:
- interval kontrol nastavte na každých
30
sekúnd - interval
timeout
nastavte na10
sekúnd - stav nezdravý vyhláste po
3
pokusoch
Výsledná konfigurácia môže vyzerať takto:
HEALTHCHECK \
--interval=30s \
--timeout=10s \
--retries=3 \
CMD /usr/bin/env python3 /app/healthcheck.py || exit 1
Task
Overte správnosť svojej konfigurácie.
Ďalšie úlohy
Konfigurácia sa načíta raz a to pri štarte aplikácie. Konfigurácia sa počas behu aplikácie preto nebude meniť. Vytvorili sme preto síce globálnu premennú, ale nie je to úplne ideálny prístup. Lepšie je spraviť buď jedináčika alebo funkciu, ktorá bude dekorovaná dekorátorom z balíka
functools
- buď@cache
alebo@lru_cache
. Ak takýto dekorátor použijeme nad funkciou, výsledok jej volania sa uloží do cache a pri každom ďalšom volaní sa tento výsledok z cache aj použije.Vytvorte preto v module
models.py
funkciuget_settings()
, ktorá vráti inštanciu triedySettings
. Nad touto funkciou použite dekorátor@lru_cache
alebo@cache
.Zabezpečte aktuailizovaný kód tak, aby nedošlo k vzniku žiadnej neošetrenej výnimky. Ošetrite aj situáciu, ak pre spustenie aplikácie chýbajú potrebné nastavenia.
Aktualizujte súbor
readme.md
a spravte z neho podklad pre opis obrazu pre váš obraz na stránke hub.docker.com.
Ďalšie zdroje
- Dockerfile
reference - referenčná príručka pre súbor
Dockerfile
- Dockerfile cheatsheet -
ťahák pre súbor
Dockerfile
- Docker Hub - webové rozhranie repozitára Docker obrazov hub.docker.com
- Dockerfile Best Practices - Jeden zo zoznamov tzv. best practices pre tvorbu Docker obrazov.
- dockerdocs: Building best practices - Odporúčania na zostavovanie Docker obrazov priamo v dokumentácii Docker-a.
- hadolint - Dockerfile linter, validate inline bash, written in Haskell