Mustand: sisu ei ole veel tehniliselt ega keeleliselt täielikult kontrollitud ega toimetatud.

Peatüki vaade

Linux/Unix/macOS käsurea kiirõpik

Praegu loed peatükki Dockeri alused, mis kuulub osasse Osa V: Arendus ja töövood.

Dockeri alused

Selles peatükis teeme praktilise sissejuhatuse konteineritesse ja Dockeri põhimõistetesse.

Loogika

Docker võimaldab käivitada tarkvara eraldatud keskkonnas nii, et sama image annaks võimalikult sarnase tulemuse eri masinates. See on seotud arenduskeskkonna, paketihalduse ja serveritööga.

Oluline on kohe alguses eristada:

  • venv hoiab lahus Pythoni paketid
  • Docker hoiab lahus terve käivituskeskkonna

See on põhjus, miks Docker ei ole lihtsalt "suurem venv".

Põhimõtted

  • image on valmis ehituskihiline pakend
  • container on image'ist käivitatud protsess
  • registry on koht, kust image'eid alla laadida
  • port ühendab konteineri teenuse sinu masina pordiga
  • volume või bind mount annab püsiva või jagatud failisüsteemi osa

Image, container, port ja volume

Need mõisted tasub võimalikult vara selgeks teha:

  • image
  • retseptist ehitatud valmis pakend

  • container
  • selle image'i põhjal käivitatud konkreetne protsess

  • port
  • viis, kuidas konteineri teenus nähtavaks teha hostmasinas

  • volume või bind mount
  • viis, kuidas andmeid püsivalt või jagatult kasutada

Lihtne võrdlus:

  • image on nagu retsept või valmis pakend
  • container on sellest tehtud päris jooksutatud eksemplar

Sa võid ühest image'ist käivitada mitu konteinerit.

Miks Docker üldse kasulik on

Docker lahendab teistsugust probleemi kui venv.

Kõige tavalisemad põhjused Dockeri kasutamiseks on:

  • sama rakendus peab töötama eri masinates ühtemoodi
  • projekt vajab lisaks Pythonile või Node'ile ka süsteemipakette
  • rakendus käib koos teiste teenustega nagu Postgres, Redis või Nginx
  • arendus, test ja tootmine peavad olema võimalikult sarnased

Lühidalt:

  • venv aitab küsimusega "millised Pythoni paketid siin on"
  • Docker aitab küsimusega "milline terve keskkond siin üldse jookseb"

Kiirspikker

  • docker pull tõmbab image'i
  • docker run käivitab konteineri
  • docker ps näitab jooksvaid konteinereid
  • docker images näitab image'eid
  • docker exec -it siseneb konteinerisse
  • docker logs näitab konteineri väljundit

Kõige tavalisemad lipud

  • docker run --rm
  • docker run -it
  • docker exec -it
  • docker ps

Praktiliselt tähendavad need enamasti:

  • docker run --rm kustuta konteiner pärast lõpetamist
  • docker run -it interaktiivne terminal
  • docker exec -it sisene töötavasse konteinerisse
  • docker ps vaata jooksvaid konteinereid

Kasulikud lisad:

  • docker logs konteiner vaata konteineri väljundit
  • docker run -p 8000:8000 ava port hostmasinasse
  • docker run -v "$PWD":/app jaga kohalik kaust konteineriga

venv vs Docker

Hea otsustuspuu on:

  • ainult Pythoni sõltuvused lähevad segamini: venv
  • vajad kindlat Linuxi keskkonda või süsteemipakette: Docker
  • vajad mitut teenust koos: Docker
  • tahad ainult kiiret lokaalset Pythoni arendust: venv

Näide, kus piisab venv-st:

  • väike Pythoni skript
  • üks CLI-tööriist
  • andmetöötlusnotebook või väike teek

Näide, kus Docker on loomulikum:

  • veebirakendus koos andmebaasiga
  • projekt, mis vajab postgresql-client, ffmpeg või muud süsteemipaketti
  • tiimitöö, kus keskkonnad kipuvad masinate vahel erinema

Kas konteineris on vaja veel venv-i

Tavaliselt mitte.

Hea algreegel on:

  • väljaspool Dockerit: kasuta projektis venv-i
  • Dockeri sees: ära lisa venv-i ainult harjumusest

Põhjus:

  • konteiner juba isoleerib keskkonna
  • üks rakendus ühes konteineris on tavaliselt piisavalt eraldatud

Seega on väga tavaline ja täiesti normaalne, et Dockerfile teeb lihtsalt:


FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Siin ei ole eraldi venv-i vaja, sest konteiner ise ongi eralduskiht.

Praktiline soovitus sinu jaoks

Kui hakkad Dockerit aktiivselt kasutama, siis üsna hea mõtteviis on:

  • kohaliku puhta Pythoni arenduse jaoks kasuta venv-i
  • kui projekt peab jooksma ühtemoodi eri masinates või vajab teenuseid, tee Docker
  • ära pane lisaks konteinerisse venv-i, kui sul ei ole selleks väga selget põhjust

See hoiab süsteemi lihtsana:

  • arenduse ajal tead, kas oled venv-s või konteineris
  • tootmise ja tiimitöö jaoks on keskkond ühtsem
  • veaotsingus on vähem kihte, mida segamini ajada

Käivita need käsud


docker pull alpine
docker run --rm alpine echo tere

docker run -it --rm ubuntu bash
docker ps
docker images

Kõige väiksem arusaadav Dockerfile

Kui tahad Dockeri mõttest päriselt aru saada, siis kõige kasulikum on teha üks väga väike rakendus ja panna see image'isse.

Näiteks fail app.py:


print("Tere konteinerist")

Ja selle kõrvale Dockerfile:


FROM python:3.13-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]

Siin toimub:

  • FROM python:3.13-slim valib Pythoni baaskeskkonna
  • WORKDIR /app määrab töökausta konteineri sees
  • COPY app.py . kopeerib faili image'isse
  • CMD ["python", "app.py"] ütleb, mis käivitub konteineri startimisel

See on hea esimene näide, sest siin ei ole veel kõrvalist keerukust.

docker build ja docker run

Kui Dockerfile ja app.py on samas kaustas, siis:


docker build -t tere-rakendus .
docker run --rm tere-rakendus

Tulemus peaks olema umbes:


Tere konteinerist

Oluline loogika:

  • docker build teeb image'i
  • docker run käivitab selle image'i põhjal konteineri
  • --rm kustutab konteineri pärast lõpetamist

Kui tahad konteinerisse sisse vaadata, on abiks:


docker run -it --rm python:3.13-slim bash

See on hea viis vaadata, milline keskkond konteineri sees päriselt on.

Portide avamine

Kui konteineri sees jookseb veebiserver, siis ei piisa ainult sellest, et protsess töötab. Sageli on vaja port hostmasinasse edasi anda.

Näide:


docker run --rm -p 8000:8000 python:3.13-slim python -m http.server 8000

Siin:

  • konteineri sees kuulab server porti 8000
  • -p 8000:8000 seob selle sinu masina pordiga 8000

Pärast seda saad tavaliselt minna brauseris aadressile:


http://localhost:8000

Bind mount ja püsivad andmed

Kui tahad, et konteiner näeks sinu kohalikku kausta, on väga tavaline kasutada bind mount'i.

Näide:


docker run --rm -it -v "$PWD":/app -w /app python:3.13-slim bash

Siin:

  • -v "$PWD":/app jagab praeguse kausta konteinerisse
  • -w /app teeb selle konteineri töökaustaks

See on arenduses väga kasulik, sest saad muuta faile hostmasinas ja näha neid kohe konteineris.

Kui räägitakse volume'itest, siis mõeldakse sageli kahte eri asja:

  • bind mount: kindel hosti tee nagu "$PWD":/app
  • named volume: Dockeri hallatav püsiv andmeala

Algajale on bind mount tavaliselt kõige lihtsam esimene samm.

Konteineris arendamine: mis jääb hosti ja mis jookseb konteineris

See on koht, kus Docker muutub päriselt arendustööriistaks, mitte ainult demo- või deploy-vahendiks.

Kõige olulisem loogika on:

  • lähtekood ja Git ajalugu elavad tavaliselt hostmasina kaustas
  • konteiner annab sellele koodile ühtse jooksutuskeskkonna
  • muudatusi teed tavaliselt oma redaktoris või IDE-s
  • rakendust käivitad, testid ja silud konteineri sees

Hea algreegel on:

  • ära kirjuta olulist koodi "anonüümselt" ainult konteineri sees
  • hoia projekt failidena oma kaustas ja jaga see kaust bind mount'iga konteinerile

Muidu võib juhtuda, et:

  • konteiner kustub
  • sisse kirjutatud failid kaovad
  • sa ei saa hästi aru, mis on hostis ja mis oli ainult konteineri sees

Kõige lihtsam arendusvoog bind mount'iga

Kui sul on näiteks väike Pythoni projekt, siis väga praktiline esimene arendusvoog on:


docker run --rm -it -v "$PWD":/app -w /app python:3.13-slim bash

Selle loogika:

  • "$PWD":/app jagab sinu praeguse projekti kausta konteinerisse
  • -w /app teeb selle konteineri töökaustaks
  • sa saad hostmasinas faile muuta ja konteiner näeb neid kohe

Näiteks:


python -m pip install -r requirements.txt
python app.py
pytest

See on hea siis, kui tahad:

  • proovida sõltuvusi puhtas keskkonnas
  • mitte solkida hostmasina Pythoni
  • kontrollida, et projekt töötab ka mujal kui sinu enda masinas

Miks see erineb "docker build && docker run" töövoost

Neid kaht töövoogu tasub eristada:

  • docker build + docker run
  • hea siis, kui tahad kontrollida valmis image'it

  • bind mount'iga interaktiivne konteiner
  • hea siis, kui tahad aktiivselt arendada

Teisisõnu:

  • valmis rakenduse proovimiseks ehitad image'i
  • igapäevaseks arendamiseks tahad sageli, et kood oleks mount'iga kohe nähtav

Praktiline arendus docker compose abil

Kui projektis on rohkem kui üks teenus, muutub arenduses mugavaks docker compose.

Väga tüüpiline arenduskuju on:

compose.yaml


services:
  app:
    image: python:3.13-slim
    working_dir: /app
    volumes:
      - .:/app
    command: bash -lc "python -m pip install -r requirements.txt && python app.py"
    ports:
      - "8000:8000"

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: opik
      POSTGRES_USER: opik
      POSTGRES_PASSWORD: opikpass

Selle mõte:

  • kood elab hostmasinas
  • app teenus kasutab seda otse läbi mount'i
  • db annab teise teenusena andmebaasi
  • kogu komplekti saad käivitada ühe käsuga

Tüüpilised arenduskäsud:


docker compose up
docker compose logs -f app
docker compose exec app bash
docker compose down

Need on arenduses väga tähtsad:

  • up käivitab tööruumi
  • logs -f näitab jooksvaid logisid
  • exec lubab töötavasse konteinerisse sisse minna
  • down peatab komplekti

Mida konteineris teha ja mida mitte

Hea praktiline jaotus on:

  • hostmasinas:
  • muuda faile, tee Git commit'e, halda dokumentatsiooni

  • konteineris:
  • installi sõltuvusi, käivita rakendus, testi, silu keskkonda

See ei ole absoluutne reegel, aga algajale väga tervislik tööjaotus.

Eriline hoiatus:

  • kui teed konteineri sees git clone, lood uusi faile ja ei kasuta mount'i
  • siis töötad sisuliselt konteineri sisemises failisüsteemis
  • see võib olla ajutine ja segadust tekitav

Kuidas konteineris arendust mõelda

Hea vaimne mudel on:

  • hostmasin on sinu töölaud
  • Git repo on päris "tõde"
  • konteiner on vahetatav tööpink

Kui tööpink maha võtta ja uuesti püsti panna, peaksid olulised asjad alles olema:

  • lähtekood
  • konfiguratsioon
  • andmed, mida tahtsid säilitada

See on põhjus, miks:

  • kood pannakse tavaliselt mount'iga hostist sisse
  • andmebaasi andmed pannakse volume'isse
  • buildi- ja arenduskäsud kirjutatakse Dockerfile-i või compose.yaml-i

docker logs ja docker exec

Konteinerit ei pea alati kohe interaktiivselt käivitama, et aru saada, mis toimub.

Sageli piisab neist kahest käsust:


docker logs konteineri-nimi
docker exec -it konteineri-nimi bash

Loogika:

  • docker logs näitab, mida protsess on kirjutanud stdout-i ja stderr-i
  • docker exec -it lubab sul töötavasse konteinerisse sisse minna

Need on veaotsingus ühed kõige kasulikumad käsud.

Kui rakendus vajab pakette

Siis lisandub tavaliselt requirements.txt.

Näide:

requirements.txt


requests==2.32.3

Dockerfile


FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]

Siin on oluline:

  • Pythoni paketid lähevad image'i sisse
  • konteineri sees ei ole vaja eraldi venv-i
  • image ise on selle rakenduse eraldatud keskkond

Millal tuleb mängu docker compose

docker run sobib hästi ühe konteineri jaoks.

Kui aga projektis on mitu seotud teenust, näiteks:

  • rakendus
  • andmebaas
  • vahemälu

siis muutub mugavamaks docker compose.

Hea mõtteviis on:

  • docker run on üksiku konteineri käivitamine
  • docker compose on mitme seotud teenuse kirjeldamine ühes failis

Lihtne docker compose näide: Python + Postgres

See on juba päris eluline näide, sest väga paljud rakendused ei jookse üksi, vaid koos andmebaasiga.

compose.yaml


services:
  app:
    build: .
    command: python app.py
    depends_on:
      - db
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: opik
      POSTGRES_USER: opik
      POSTGRES_PASSWORD: opikpass

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: opik
      POSTGRES_USER: opik
      POSTGRES_PASSWORD: opikpass

Siin:

  • app on sinu Pythoni rakendus
  • db on Postgres
  • nimi db toimib teenusevõrgus hostinimena
  • depends_on ütleb, et rakendus sõltub andmebaasikonteinerist

Käivitus:


docker compose up --build

Peatamine:


docker compose down

Oluline täpsustus:

  • depends_on aitab käivitusjärjekorraga
  • see ei tähenda automaatselt, et Postgres on juba päriselt valmis ühendusi vastu võtma
  • päris projektis on sageli vaja ka "oota kuni DB on valmis" loogikat

Millal valida kumb

Kasuta:

  • docker run, kui tahad kiiresti testida üht image'it või üht käsku
  • docker compose, kui projektis on mitu teenust

Hea rusikareegel on:

  • üks konteiner: docker run
  • rakendus + andmebaas + muud teenused: docker compose

Seos arenduskonteineritega

Kui kasutad VS Code'i või mõnd muud IDE-d, võib sama loogika olla pakitud arenduskonteineri kujule.

Siis:

  • Docker käivitab konteineri
  • IDE ühendub sinna sisse
  • sinu projektikaust mount'itakse tööruumina konteinerisse

See tähendab, et arenduskonteiner ei ole "teine asi" kui Docker. See on pigem Dockeri peal ehitatud arendusmugavuskiht.

Üks lihtne võrdlus

venv-ga töövoog:


python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests
python app.py

Dockeri töövoog:


docker build -t minu-rakendus .
docker run --rm minu-rakendus

Esimesel juhul isoleerid Pythoni paketid.

Teisel juhul isoleerid kogu rakenduse käivituskeskkonna.

Praktiline soovitus alustamiseks

Kui hakkad Dockerit õppima või arendama, mine selles järjekorras:

  1. tee kõige väiksem Dockerfile
  2. õpi docker build
  3. õpi docker run
  4. alles siis mine docker compose juurde

Nii püsib selge:

  • mis on image
  • mis on container
  • millal vajad mitme teenuse haldust

Minitest

  1. Tõmba alla mõni väike image.
  2. Käivita konteiner, mis väljastab ühe rea.
  3. Selgita oma sõnadega image'i ja containeri vahet.
  4. Selgita ühe lausega, mis vahe on venv-il ja Dockeril.
  5. Kirjelda ühe lausega, millal kasutaksid docker compose-i tavalise docker run asemel.