Infraštruktúra postavená na kontajneroch

o kontajneroch, o (mikro) službách spustených v kontajneroch, o metodológii The Twelve-Factor App

Motivácia

Smart Department: Architektúra
  • (slide) doteraz sme sa pohybovali na nižších vrstvách, dnes sa porozprávame o tom, ako to vyzerá v cloud-e a akým spôsobom tam budem nasadzovať naše riešenia/služby
  • urobíme to na príklade jednoduchej webovej služby (tzv. microservice) a porozprávame si o tom, ako ju pripraviť pre produkčné nasadenie, na čo si dávať pozor

The Microservice

  • (slide) urobíme jednoduchú mikroslužbu na získanie info o počasí

  • zacneme tym, ze vytvorime náš projekt. v ňom vytvoríme priečinok app/ a v nom vytvorime subor main.py s tymto obsahom:

from fastapi import FastAPI
import pendulum

app = FastAPI()

@app.get('/api/weather/{query}')
def get_weather(query: str):
    url = 'http://api.openweathermap.org/data/2.5/weather'
    params = {
        'q': query,
        'units': 'metric',
        'appid': '98113cc2ea891cc2246900c7dc6a8038'
    }
    response = httpx.get(url, params=params)
    return response.json()


if __name__ == '__main__':
    uvicorn.run('main:app', reload=True, host='0.0.0.0', port=8000)

Nasadenie aplikácie

  • starý spôsob

    • na čistom stroji/virtuálnom stroji
    • ručné nasadenie (nemusí byť)
    • aktualizácia môže dať zabrať vzhľadom na to, čo je nainštalované na samotnom stroji. môže byť výhodnejšie (aj ked zdlhavejšie vytvorenie nového virtuálneho stroja a nahradiť s ním predchádzajúcu verziu)
  • pomocou kontajnerov

  • našim cieľom bude nasadiť túto aplikáciu pomocou Docker kontajneru

Virtuálne stroje vs kontajnery

Virtual Machines vs Containers

Čo je to Docker?

Ak sa pozrieme priamo do dokumentácie Docker-a, nájdeme ako odpoveď na túto otázku toto:

Docker je otvorená platforma pre vývoj, nasadzovanie a spúšťanie aplikácií.

Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications. By taking advantage of Docker’s methodologies for shipping, testing, and deploying code, you can significantly reduce the delay between writing code and running it in production.

Čo je to kontajner?

  • Kontajner je inštanciou obrazu. Pre jednoduchosť sa zatiaľ môžeme na obraz pozerať ako na zip balík, v ktorom je zabalená aplikácia. Spustením obrazu vznikne kontajner, pričom z jedného obrazu je možné spustiť viacero kontajnerov.

  • proces spustený v izolovanom prostredí nad jadrom lokálneho OS

Čo je to obraz?

Obrazy sú nemenné!

Ak by sme hľadali nejakú paralelu, tak sa môžeme pozrieť na súbory uložené na disku:

  • (spustiteľný) súbor reprezentuje obraz
  • súbor je nemenný (je určený na čítanie)
  • spustením súboru vzniká proces, ktorý reprezentuje kontajner
  • z jedného súboru viem spustiť ľubovoľný počet procesov
  • každý spustený proces je jednoznačne identifikovaný svojim PID

Vytvorenie obrazu pre aplikáciu

# zakladny obraz
FROM python:3.12-slim

# nainstalujem potrebne softverove vybanie
RUN pip install httpx fastapi[standard]

# prekopirujem svoj zdrojovy kod
COPY app/ /app

# poviem, co sa ma spustit pri spusteni kontajnera
EXPOSE 8000
WORKDIR /app
#CMD [ "fastapi", "dev", "main.py" ]
CMD [ "uvicorn", "main:app", "--reload", "--host=0.0.0.0" ]

Obraz zostavíte príkazom:

$ docker image build --tag pocko .

Best Practices

The Twelve-Factor app

The Twelve-FactorApp
  • (slide) The Twelve-Factor App je methodológia pre tvorbu aplikácií typu softvér ako služba (z angl. software-as-a-service). :

    • Use declarative formats for setup automation, to minimize time and cost for new developers joining the project;
    • Have a clean contract with the underlying operating system, offering maximum portability between execution environments;
    • Are suitable for deployment on modern cloud platforms, obviating the need for servers and systems administration;
    • Minimize divergence between development and production, enabling continuous deployment for maximum agility;
    • And can scale up without significant changes to tooling, architecture, or development practices.

Konfigurácia pomocou premenných prostredia

Aby sme mohli komunikovať so službou OpenWeather, potrebujeme do každého dopytu vložiť aj API token. Ten získame po registrácii do služby a prípadne aj zaplatením poplatku. Tento token je však súkromný a každý používateľ aj našej služby by mal používať ten svoj.

Aktuálne je súčasťou kódu našej aplikácie aj token pre prístup k API služby OpenWeather. Tento prístup je porušením metodológie The Twelve-Factor App, ktorá vyžaduje striktné oddelenie konfigurácie od kódu aplikácie. Odporúčaním metodológie je miesto toho umiestniť konfiguráciu do tzv. premenných prostredia.

Premenné prostredia

(slide) Premenné prostredia (z angl. environment variables) sú premenné nastavené používateľom v operačnom systéme, pomocou ktorých je možné ovplyvňovať správanie spustených aplikácií.

Premenné prostredia sú nastavené mimo zdrojového kódu aplikácie a aj mimo konfiguračných súborov aplikácie. Používajú sa na nastavenie tých hodnôt, ktoré sa menia medzi nasadeniami aplikácie, ako napríklad:

  • umiestnenie databázového serveru a prístup k nemu
  • prihlasovacie údaje a umiestnenie vzdialenej služby, od ktorej závisí tá naša
  • port, na ktorom bude aplikácia spustená

Operačný systém samotný obsahuje množstvo premenných prostredia. Ich zoznam si viete zobraziť príkazom printenv. Od ostatných premenných ich odlíšite najmä tým, že používajú tzv. SCREAMING_SNAKE_CASE notáciu pre pomenovávanie premenných.

Rozličné konvencie pre pomenovávanie

Ak však budete chcieť premenné prostredia vytvoriť z príkazového riadku systému Linux, musíte ich exportovať. Napríklad:

$ export DB_URI=postgresql+psycopg2://scott:tiger@localhost:5432/mydatabase

Premenné prostredia vo forme konfigurácie aplikácie sa často ukladajú do súboru .env alebo do súborov s príponou .env, ako napríklad local.env. Tento súbor obsahuje zoznam premenných spolu s ich hodnotami vo formáte kľúč=hodnota. Vyzerať môže napr. takto:

DB_HOST=localhost
DB_USER=scott
DB_PASSWORD=tiger
DB_NAME=mydatabase

Kolízia mien sa rieši prefixom.

Konfigurácia aplikácie pomocou premenných prostredia

V jazyku Python na prácu s premennými môžeme použiť priamo balík os a v ňom slovník environ. Obsahuje premenné prostredia pre spustenú aplikáciu.

Miesto toho však použijeme balík pydantic-settings, ktorý je rozšírením modulu pydantic a je možné vytvárať modely pre potreby nastavenia. Výhodou je, že automaticky model zvaliduje a dokáže rovno načítať údaje zo súboru .env, ak sa taký v priečinku, z ktorého sa aplikácia spúšťa, nachádza.

class Settings(BaseSettings):
    token: str = 'bar'
    apple: int = 1

    model_config = SettingsConfigDict(
        env_prefix='POCKO_',
        env_file_encoding='utf-8',
        env_file='.env',
    )

Následne upravíme hlavný kod tak, aby konfiguráciu použil:

@app.get('/api/weather/{query}')
def get_weather(query: str):
    url = 'http://api.openweathermap.org/data/2.5/weather'
    params = {
        'q': query,
        'units': settings.units,
        'appid': settings.token
    }
    response = httpx.get(url, params=params)
    return response.json()

Tým sme vyriešili konfiguráciu služby podľa metodológie The Twelve-Factor App.

Healthcheck

Problém

spravovať distribuované systémy je náročné. skladajú sa totiž z veľkého množstva súčastí, ktoré potrebujú pracovať (a spolupracovať), aby systém mohol ako celok fungovať.

ak sa niektorá jeho súčasť pokazí, systém ju musí:

  • detegovať, napr. vytvorením alertu,
  • obísť ju, napr. load balancer nebude posielať požiadavky na nefungujúcu službu, a
  • opraviť ju, resp. zotaviť sa z nej.

a to všetko sa musí udiať automagicky.

ako teda detegovať, že bežiaca služba nedokáže spracovávať požiadavky?

Čo je to Healthcheck?

Health check poskytuje jednoduchý mechanizmus, pomocou ktorého je možné overiť, či daná služba pracuje správne. obyčajne má služba vytvorené Health Check API pomocou protokolu HTTP (napr. /health), ktorá pomocou HTTP stavových kódov hovorí o stave služby. Je možné sa však stretnúť aj s prípadmi, kedy sa kontrola stavu služby vykonáva pomocou metódy HEAD nad niektorým existujúcim endpointom.

okrem stavového kódu sa môže vo výsledku HTTP požiadavky nachádzať aj krátky JSON dokument, ako napr.:

{
    "status" : "UP" 
}

What to Check in Health Check API

v rámci kontroly zdravia služby sa môže overovať napr.:

  • stav spojenia s ostatnými službami,
  • stav stroja, napr. jeho diskový priestor, využitie pamäte a pod.,
  • špecifická logika aplikácie,
  • stav databázy,
  • a iné.

Z-Pages

(slide) Ako endpoint pre Health Check API sa dá stretnut s názvom /healthz, ktorý používa napr. Grafana S týmto konceptom prišiel Google a endpoint-y, ktoré končia s písmenom Z sa nazývajú Z-Pages. Ide o to, aby nedošlo k zameneniu názvov mnozneho cisla endpointov REST API, aby bolo jasne, ze toto tu je specialny endpoint

Jednoduchý healthcheck

@app.head('/api/healthz')
@app.get('/api/healthz')
def check_health():
    return {
        'status': 'up'
    }

Health Check in Containers

okrem toho však s kontrolou stavu súvisí aj kontrola stavu kontajnera, v ktorom je aplikácia spustená. kontajnery totiž bežia 24/7 a aj tu môže dôjsť k niektorým situáciám:

  • stav kontajneru
  • aktualizácia aplikácie v kontajneri

takže ak aplikácia beží v kontajneri a ten je spustený, nemusí to hneď znamenať, že aplikácia vo vnútri kontajneru pracuje správne. kontrolu teda treba vykonávať ako na úrovni aplikácie, tak aj na úrovni kontajnera.

na urovni kontajnerov mame podporu priamo pri definicii obrazu v súbore Dockerfile. k dispozícii je klucove slovo HEALTHCHECK a kontrola moze vyzerat napriklad takto:

HEALTHCHECK \
  --interval=30s \
  --timeout=3s \
  --retries=3 \
  --start-period=0 \
  CMD curl --fail http://localhost:8000/api/health || exit 1

Vyznam jednotlivych volieb klucoveho slova HEALTHCHECK je nasledovny:

  • --interval=<interval> - Interval medzi dvoma kontrolami, predvolena hodnota je 30 sekúnd.
  • --timeout=<time length> - Prikaz na kontrolu sa bude vykonavat zadany cas. Ak tento cas uplynie, kontrola zdravia bude považovaná za neuspesnu. Predvolena hodnota je 30 sekúnd.
  • --retries=<number> - Ak dany pocet krat po sebe kontrola zdravia zlyhá, kontajner bude považovaný za nezdravy. Predvolena hodnota su 3 pokusy.
  • --start-period=<duration> - Po akom čase sa má kontrola spustiť. Tento čas je určený pre naštartovanie samotnej aplikácie.

Kompozícia

db + app + klient do db

services:

Záver

Ďalšie zdroje