8. týždeň

Kontajnery

Docker

Ciele

  1. Vyskúšať jednoduchý kontajner
  2. Vytvoriť vlastný web prostredníctvom existujúceho Docker image
  3. Vytvorenie vlastného Docker image
  4. Nasadenie do registra
  5. Automatizácia cez CI

Prečo kontajnery?

Mali ste už niekedy problém s tým, že ste pripravili svoj super program, ale zrazu keď ste ho dali svojmu spolužiakovi alebo učiteľovi, tak mu nefungoval? Chybali mu niektoré knižnice, alebo ich mal v inej verzii, či dokonca mal inú distribúciu OS a mu to preto vôbec nefungovalo. Samozrejme stále môžete zobrať celý svoj počítač a ukazovať výsledok len na ňom, lebo inde by to trvalo príliš dlho aby sa to spojazdnilo.

No a tento problém často môže nastať nie len pri ukazovaní, vývoji ale aj pri samotnom nasadení aplikácie.

Jedno z riešení je používanie virtuálnych strojov, kde máte svoju aplikáciu pekne nainštalovanú aj s celým operačným systémom. Avšak ak je to malý program, ktorý sa má spustiť rýchlo a častejšie, tak štart celého operačného systému vo virtuálke bude trvať o niečo dlhšie.

Kontajnery sa snažia riešiť tento problém tak, že namiesto štartovania celého operačného systému využívajú už naštartovaný systém ale používaju len jadro a ináč celý operačný systém s podpornými nástrojmi ale hlavne samotná aplikácia je súčasťou obrazu (image), ktorý sa spúšťa.

Výhoda je teda ľahká prenositeľnosť aplikácii medzi rôznymi strojmi. Z toho vyplýva aj možnosť rýchleho nasadenia pri potrebnom škálovaní ale aj pri použití v IoT prostredí, kde často potrebujeme nasadiť naše riešenie na rôzne typy zariadení. Tu môžem spomenuť celkom úspešný projekt použitia kontajnerov v IoT a to balena.io (premenovaný z projektu resin.io)

Poďme si ale vyskúšať samotnú prácu s najúspešnejším kontajnerovým systémom a tým je Docker.

Postup

Krok 1: Inštalácia

V prvom kroku je potrebné si zabezpečiť funkčný Docker na nejakom zariadení, kde máte root práva.

Úloha 1.1

Nainštalujte si Docker podľa svojho OS z uvedených zdrojov:

Krok 2: Spustenie Docker kontajnera

Po úspešnej inštalácii si môžeme vyskúšať spustenie prvého jednoduchého kontajnera.

Docker vám v systéme sprístupnil v príkazovom riadku svojho klienta docker. Poďme si ho vyskúšať.

Úloha 2.1

Spustite si hello world príklad zadaním príkazu:

docker run hello-world

Ako výsledok by ste mali vidieť dlhší výpis.

Pojmy

Vysvetlíme si ešte základne pojmy:

  • obraz (image) - obraz z ktorého sa vytvára kontajner, je to v podstate binárny archív, ktorý je zložený z viacerých vrstiev
  • kontajner - jedná inštancia obrazu so všetkými lokálnymi nastaveniami, táto inštancia môže bežať ale môže byť aj stopnutá

V našej úlohe s hello world sme použili obraz (image) s názvom hello-world, ktorý sa automatický stiahol z Docker repozitára (Docker Hub) do našej cache na disku, následne sa vytvoril nový kontajner z tohto obrazu a ten sa spustil.

Upozornenie

Náš hello-world kontajner sa síce automaticky zastavil, ale ostal stále vytvorený a môže byť znova spustený pomocou príkazu docker start container_name Príkazom docker ps si vieme zobraziť všetky bežiace kontajnery a príkazom docker ps -a vieme zobraziť aj zastavené kontajnery.

Úloha 2.2

Vymažte zbytočný kontajner vytvorený z obrazu hello-world použitím príkazu docker rm container_name. Názov (name) si zistite zobrazením všetkých kontajnerov, zvyčajne ten názov je zvláštny a skladá sa z 2 slov oddelených podčiarkovníkom (napr. elegant_darwin).

Poznámka

Ak chceme aby sa kontajner automatický vymazal po jeho ukončení, môžeme pri jeho vytvárani použiť prepínač --rm (napr. docker run --rm hello-world)

Krok 3: Namapovanie existujúceho adresára

Vo väčšine prípadov nám nestačí len spustiť existujúci kontajner, ale potrebujeme do neho aj dostať nejaké vlastné údaje. Prípadne môžeme chcieť aj ukladať údaje, ktoré bežiaci kontajner vytvorí, aby sme tieto údaje si uložili trvalejšie. Keďže štandardne pri odstránení kontajneru sa odstránia aj všetký údaje, ktoré sme si nenechali ukládať.

Pre akýkoľvek kontajner si vieme namapovať adresáre z hosťujúceho systému. Namapovanie adresarov vieme vykonať použitím volume teda pri spúštaní použitím parametra -v lokalna_cesta:cesta_v_kontajneri. P

Poznámka

Pri používaní Docker Desktop for Windows je potrebné povoliť mapovanie lokálnych diskov. Nájdete to v Settings časti (návod spojazdnenie zdieľania Windows ciest)

Spojazdnite si to, ak pracujete na Windows.

Vyskúšajme si teraz spustenie jednoduchého webového servera NGINX s tým, že do neho vložíte vlastný HTML súbor.

Úloha 3.1

Identifikujte adresár v ktorom ste si spravili HTML súbor z predošlích cvičení.

Zverejnite tento adresár cez NGINX server prostredníctvom Docker image.

Použite túto kostru príkazu:

docker run -d --name iot-html -v c:/adresar/podadresar_s_html_subormi:/usr/share/nginx/html:ro -p 80:80 nginx:alpine

Poznámka

Parameter -v rieši práve namapovanie adresárov. Ak ste v linuxe, tak namiesto cesty c:/adresar/podadresar_s_html_subormi použite vlastnú cestu v tvare /adresar/podadresar_s_html_subormi. Adresár v kontajneri /usr/share/nginx/html je štandardný adresár, kde NGINX očakáva koreňový adresár pre web zložku. Ešte tam máme jednú časť a to :ro na konci, tento zápis namapuje adresár do kontajnera len v režime čítania (read-only).

Poznámka

Parameter -d je skratka pre detach(odpojiť), čo znamená, že po spusteni kontajneru nechceme mať zablokovanú konzolu a vidieť len výpisy z daného kontajnera ale chceme sa odpojiť od procesu kontajnera a pracovať s konzolou ďalej. K jednotlivým logom sa vieme stále dostať použitím príkazu docker logs container_name.

Poznámka

Parameter --name my_name nám slúži na to aby sme si pomenovali kontajner vlastným menom. Je to vhodné preto aby si Docker nevygeneroval vlastný názov, ale aby sme použili vlastné meno.

Poznámka

Parameter -p 80:80 nám slúži zasa na to, aby sme namapovali port medzi lokálnym strojom (Docker host) a strojom bežiacim v kontajneri. Pričom sa úvadza prv port na lokálnom stroji a potom port v kontajneri. Teda ten zápis je -p host_port:container_port. Vďaka mapovaniu portov je možné aplikáciu, ktorá sa publikuje na nejakom sieťovom porte zverejniť cez lokálny stroj (Docker host) aj na verejnosť.

Poznámka

Ak ste si vzorový príkaz všimli dostatočne dobre, tak ste mohli vidieť, že názov docker obrazu (image) obsahoval aj nejakú časť za dvojbodkou (nginx:alpine). Toto označenie za dvojbodkou sa nazýva značka (tag). Pomocou takejto značky vieme odlíšiť viacero verzii toho istého docker obrazu. Teda viete takto použiť napríklad aj konkrétnu verziu nginx:1.14.2. To že aké značky (tags) sa používajú pri tom ktorom obraze je čisto na autorovi daného image a preto je potrebné si pozrieť, čo je dostupné (najlepšie na Docker Hub).

Štandardne sa uvádza značka :latest aj v prípade, keď autor explicitne neuvedie žiadnú značku. Značka :alpine je taktiež často používana pre rôzne obrazy a označuje to, že tento obraz je postavený na veľmi odľahčenej verzii linuxu, ktorá zaberá len zopár MB priestoru - Alpine Linux

Ak máte nejaké otázky, tak sa pýtajte, aj preto je tu váš cvičiaci.

Krok 4: Dockerfile

Doteraz sme používali existujúci obraz (image), ktorý sme spúštali s namapovanými adresármi s naším HTML projektom. Tento prístup je fajn, keď skúšame si rôzne súbory počas vývoja, alebo aj keď chceme poskytnuť konfiguráciu. Čo ale ak by sme chceli niekomu tento projekt sprostredkovať vo forme už hotového obrazu, bez toho aby potreboval mapovať nejaké adresáre?

Ak chceme vytvoriť vlastný obraz (image), potrebujeme si vytvoriť opisný súbor pre tvorbu nového docker obrazu. Tento súbor sa štandardne volá Dockerfile (to je názov súboru bez akejkoľvek prípony). Na základe tohto súboru bude Docker vedieť vytvárať nové obrazy (image).

Každý Dockerfile musí začínať príkazom FROM, pričom za týmto kľúčovým slovom sa uvedie názov obrazu z ktorého vychádzame. Stále musíme vychádzať z nejakého existujúceho obrazu. Jednotlivé možné príkazy je možné si detailne pozrieť v dokumentácii k Dockerfile

Úloha 4.1

Vytvorte si vlastný Dockerfile, ktorý zabezpečí vytvorenie nového obrazu (image) postaveného na základe nginx:alpine a do ktorého sa skopíruju vaše HTML súbory.

Môžete sa inšpirovať nasledujúcim obsahom:

# Definovanie docker image z ktorého začíname budovať náš docker image
FROM nginx:alpine

# prídavne informácie o vytváranom docker image, nie je povinné
LABEL maintainer="dominik.lakatos@tuke.sk"

# Definujeme len informačne, že kontajner z tohto image bude mať niečo podstatné bežiace na porte 80
EXPOSE 80

# Nastavenie fixného časového pásma - nepovinné, ale ukážka na RUN a ENV príkazy
RUN apk add --no-cache tzdata
ENV TZ Europe/Bratislava

# Skopírujeme naše staticke HTML súbory (všetko čo je v `html` adresári - má tam byť aj css,js, atď ak potrebujem)
COPY html /usr/share/nginx/html

# ENTRYPOINT pre tento image nie je nutný, keďže image nginx už obsahuje spôsob spustenia

Keď už máme vytvorený súbor Dockerfile, môžeme ho nechať aj spustiť aby sa vytvoril nový obraz (image).

Úloha 4.2

Vytvorte nový obraz na základe existujúceho Dockerfile a pomenujte ho vas_nick/iot2web (vas_nick si uveďte čokoľvek).

Inšpirujte sa týmto príkazom:

docker build --tag=vas_nick/iot2web .

Upozornenie

Bodka na konci príkazu znamená, že sa má vytvoriť obraz z Dockerfile nachádzajúceho sa v lokálnom adresári. Môžete túto bodku nahradiť aj cestou do iného adresára.

Úloha 4.3

Overte si funkčnosť vášho nového obrazu tým, že si ho spustite ako nový kontajner pričom namapujete pri spustení aj porty medzi kontajnerom a Docker host.

Krok 5: Uloženie obrazu do registra

Je pekné mať Docker obraz (image) pripravený na použitie u seba, ale niekedy je vhodné mať tento obraz uložený na nejakom serveri, aby sa k nemu vedeli dostať aj ostatní, alebo aby ste sa vedeli k nemu dostať aj z iného zariadenia. Existuje síce aj možnosť si len preniesť Dockerfile, ale pokiaľ tento potrebuje omnoho viac zdrojov, tak to môže byť náročnejšie, preto obraz (image) poskytuje omnoho lepšiu možnosť.

Katedrový Gitlab ponúka možnosť ku každému projektu si ukladať aj Docker obrazy. Preto využijeme túto možnosť a vy si svoj hotový obraz uložite k svojmu projektu.

Úloha 5.1

Vytvorte si vlastný projekt na Gitlabe. V časti registry si nájdite povolené názvy obrazov (image) pre svoj projekt.

Možný príklad je git.kpi.fei.tuke.sk:4567/open-lab/info-screen/info-screen-simple-html-example

Teraz je potrebné vytvoriť obraz, ktorý správne pomenujete, aby vyhovoval pomenovaniu uvedenému v Gitlabe pre projekt.

Úloha 5.2

Vytvorte obraz, ktorý pomenujete podľa názvu, ktorý ste si našli v svojom Gitlab projekte.

Príklad príkazu:

docker build --tag=git.kpi.fei.tuke.sk:4567/open-lab/info-screen/info-screen-simple-html-example

Skontrolujte si, či sa tento obraz nachádza vo vašom lokálnom zozname obrazov príkazom docker images, kde by ste mali vidieť jeho názov.

Úloha 5.3

Vykonajte upload lokálneho obrazu na Docker register KPI Gitlabu.

Príklad príkazu:

# prihlásenie sa na špecifický Docker registry - bude pýtať prihlasovacie údaje
docker login git.kpi.fei.tuke.sk:4567

# upload lokálne existujúceho obrazu do Docker registry
docker push git.kpi.fei.tuke.sk:4567/open-lab/info-screen/info-screen-simple-html-example

Skontrolujte si, či sa reálne nachádza tento obraz vo vašom projekte. Použite na to časť registry.

Krok 6: Automatizácia procesu cez Gitlab

Posledným krokom bude automatizácia procesu vytvorenia docker obrazu prostredníctvom Gitlab-CI (Continuos integration). Vytvoríme postup, kde Gitlab automatický po každom push do master vetvy vytvorí docker obraz a uloží ho do docker registra.

Podstatné bude aby ste zabezpečili, že adresárova štruktúra súborov v git repozitári bude sedieť s očakávaným umiestnením špecifikovaným v súbore Dockerfile pri kopírovaní.

Úloha 6.1

Do vytvoreného projektu na katedrovom gite umiestnite svoje statické HTML súbory do adresára html a taktiež tam vložte aj súbor Dockerfile, kde upravíte cestu k HTML súborom.

Úloha 6.2

Automatizujte proces buildu, tým že si vytvorte v tomto repozitári súbor .gitlab-ci.yml, ktorý bude popisovať spôsob automatického buildu.

Inšpirujte sa týmto vzorovým príklad:

# zoznam možných časti (stage) buildu
stages:
  - build           #aktuálne nepouzivany stage, ale mohol by sa pouzit na minimalizovanie html,css,js
  - build-docker

# jeden z krokov buildu, aktuálne jediný
build:docker:
  stage: build-docker #v ktorej časti (stage) sa vykonáva
  image: docker:latest #aký docker image sa použije pri vykonávaní nasledujúceho kódu, tu potrebujeme mať docker
  services:
    - docker:dind #aby nám v Gitla-CI docker fungoval správne potrebujeme ešte podporu Docker-in-Docker (dind)
  variables:
    IMAGE_TAG: $CI_REGISTRY_IMAGE #pre názov použijeme existujúcu premennu, ale pre istotu si vložíme do vlastnej, ak by sme to chceli upravovať
  script:
    - 'docker build --tag="$IMAGE_TAG" .'
    - 'docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY' # Gitlab-CI použije vlastný username a token pre prihlásenie do registra
    - 'docker push $IMAGE_TAG'
  only: #vykoná sa to len pre `master` vetvu (branch)
    - master

Poznámka

Dokumentáciu k jednotlivým príkazom vo vytvorenom súbore .gitlab-ci.yml nájdete v dokumentácii ku Gitlabu

Vyskúšajte si, či funguje automatický build cez Gitlab CI tým, že nejaký kód pushnete. Môže to byť aj len pridanie samotného súboru .gitlab-ci.yml. Následne by ste mali na Gitlabe v svojom projekte v časti CI/CD vidieť vykonávanie nového pipeline.

Krok 7: Dopĺňujúce úlohy

Úloha 7.1

Prejdite si vzorové kompletné príklady použitia Docker a Gitlab-CI:

Zdroje

  1. Docker dokumentácia
  2. Docker Hub - hlavné a verejné úložisko docker obrazov