Ciele
- Jednoduchý docker-compose
- Viaceré appky
- Pomenované volume
- Vlastné siete
Viaceré prepojené kontajnery
Naposledy sme si vyskúšali Docker pre nasadenie jednoduchej web aplikácie. Často sa ale jeden systém neskladá len z jednej aplikácie, ale je potrebné minimálne pripojiť nejakú databázu alebo aj iné služby. Napríklad pri mikroservisoch už to potom nie je len prepojenie aplikácie a databázy, ale celý systém sa skladá z omnoho viac časti ako sú workers, databázy, key-value store, web frontend, atď. Pričom všetky tieto jednotlivé časti systému je vhodné mať dostupné samostatne ako individuálne kontajnery.
Príprava prostredia
Docker-compose je samostatný program vytvorený v jazyku Python za účelom zjednodušenia spúštania viacerých Docker kontajnerov. V podstate je to len nadstavba, ktorá využíva možnosti samotného Docker daemona ale v prehľadnejšej podobe. Teda všetko, čo zvládnete prostredníctvom docker-compose sa dá zvládnuť aj prostredníctvom čistého Docker, avšak často je to zložitejšie na pochopenie a zapísanie.
Ak ste si minule nainštalovali a spojazdnili Docker pre Windows, tak docker-compose už máte nainštalovaný. Ak ste si inštalovali Docker na Linuxe, tak si potrebujete ešte docker-compose nainštalovať podľa pokynov na webe.
Alternatíva ako testovať Docker ale aj docker-compose bez lokálne inštalácie je použitie webu play-with-docker.com, kde je potrebné si len vytvoriť DockerID (zadarmo) a môžete si na 4 hodiny vytvoriť vlastný virtuálny stroj s dockerom. Po uplynuti 4 hodín bude tento stroj kompletne zmazaný ale môžete si kedykoľvek vytvoriť nový.
Postup
Krok 1: Jeden kontajner cez docker-compose
Zapisovanie celého docker príkazu s rôznymi parametrami môže byť často neprehľadné aj kvôli tomu, že sa to zapisuje na jeden riadok. Docker-compose používa format YAML pre zapisanie jednotlivých kontajnerov a ich vlastnosti, čím je to omnoho prehľadnejšie.
Úloha 1.1
Vytvorte databázu mysql
prostredníctvom docker-compose ako jeden samostatný kontajner.
Vytvorte súbor docker-compose.yml
s nasledujúcim obsahom:
version: '3'
services:
mysql:
image: mysql:8.0.3
restart: always
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mydb
Poznámka
Význam väčšiny hodnôt by sme už mali poznať z minulého cvičenia ale aj tak si ich poďme všetky vysvetliť:
version: '3'
definuje verziu docker-image deklaračného jazyka, aktuálne je najnovšia verzia3
avšak pre naše potreby by postačovala aj verzia2
mysql:
je názov našej služby, tento názov si môžete zvoliť akýkoľvek, dôležite je ale potom to, že pod takýmto DNS názvom bude táto služba adresovateľná z iných kontajnerov, no adresa sa dá modifikovať aj cezhostname
vlastnosťimage
je používaný docker obraz, teraz je to mysql vo verzii (tag) 8.0.3, namiestoimage
sa môže použiť ajbuild: .
ak chceme použiťDockerfile
dostupný v aktuálnom adresáriports
je zadefinovanie namapovania portovenvironment
slúži na zadefinovanie premenných prostredia, tu konkrétne aby sme povedali aké ma byť heslo a vytvorená databázarestart
slúži na zadefinovanie kedy sa má kontajner reštartovať alebo spustiť, hodnotaalways
hovorí, že tento kontajner sa ma stále zapnúť, aj keď náhodou bol nekorektne vypnutý alebo aj keď bol korektne vypnutý ale nastal reštart celého systému, možné hodnoty nájdete v dokumentácii
Teraz spustite kontajner zavolaním príkazu:
docker-compose up -d
Poznámka
Príkaz up
znamená, že sa stiahnú obrazy pre všetky služby (services) a zároveň sa aj spustia. Ak už boli spustené a nič sa nezmenilo v ich konfigurácii, tak sa nespušťajú znova, ak ale sa zmenilo niečo v ich konfigurácii, tak sa dané kontajnery reštartuju.
Parameter -d
značí rovnaké odpojenie (detach
) ako pri čistom Docker príkaze, avšak je tam podstatná zmena vo funkcionalite ak ho neuvediete. Pri klasickom Docker príkaze sa pri štarte bez -d
mohla použiť klávesová skratka CTRL+C
na odpojenie pričom kontajner ostal bežať, pri docker-compose to už ale neplatí, ak stlačite CTRL+C
tak sa všetky bežiace kontajnery daného docker-compose vypnú.
Poznámka
Iné príkazy, ktoré sa bežne používaju pri docker-compose si viete zobraziť cez príkaz:
docker-compose --help
Podstatné je ale poznať aspoň príkaz docker-compose down
, čo je vlastné opak príkazu down
. Pozor, lebo pri tomto príkaze sa odstraňuje všetko, ale úplne všetko, čo bolo vytvorené príkazom up
, teda môžu sa týmto príkazom stratiť aj niektoré údaje. K tomu ako zadefinovať údajove zložky sa ale dostaneme v ďalších krokoch tohto cvičenia.
Krok 2: Viaceré kontajnery cez docker-compose
Keď už poznáme základny formát docker-compose špecifikácie, tak si poďme konečne vyskúšať vytvorenie viacerých kontajnerov a ich prepojenie.
Vyskúšame si vytvoriť kombináciu MySQL databázy a webového rozhrania Adminer, ktoré dokážeme spustiť jedným príkazom.
Úloha 2.1
Vytvorte nový súbor alebo upravte pôvodný docker-compose.yml
tak aby obsahoval nasledujúci kód:
version: '3'
services:
mysql:
image: mysql:8.0.3
restart: always
# porty nepotrebujeme mapovať, necháme ich dostupne len vo vnútornej docker sieti, kde sa Adminer bude vedieť dostať
# ports:
# - '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mydb
adminer:
image: adminer
restart: always
ports:
- '8080:8080'
depends_on:
- mysql
Poznámka
Poznáme asi takmer všetky príkazy okrem príkazu depends_on
, ktorý zabezpečí, že sa kontajner spustí až keď je spustený iný kontajner, v našom prípade sa adminer
spustí až po tom ako bude spustený kontajner mysql
.
Verzia MySQL bola zvolená 8.0.3 preto, lebo adminer
v štandardnej konfigurácii bez úpravy sa nevie pripojiť na novšie verzie MySQL.
Teraz sa vyskúšajte pripojiť cez web rozhranie Adminera na MySQL databázu a pridať tam novú tabuľku či iný záznam.
Úloha 2.2
Vyskúšajte si pridať k tomuto stacku obsahujúcemu MySQL a Adminer aj ďalšiu databázu PostgreSQL a to všetko v jednom docker-compose súbore.
Nastavte jej štandardné meno root
a heslo root
a nech vytvorí taktiež databázu mydb
. Následne sa na túto PostgreSQL databázu pokuste pripojiť z Adminer rozhrania.
Krok 3: Perzistentné ukladanie
Pracujme teraz s tým, že ste si v predošlom kroku vytvorili v MySQL databáze nejaké údaje (tabuľku alebo aj riadky v tabuľke). Ak ste tak nespravili, tak to rýchlo napravte. Čo sa stane s týmito údajmi, keby sme zmenili nejaké nastavenia v docker-compose súbore?
Úloha 3.1
Zmeňte v docker-compose
súbore parameter restart
pre službu mysql
na hodnotu unless-stopped
, aby sa nám databáza nerozbehla sama, keď sme ju manuálne vypli.
Aplikujte túto zmenu zavolaním príkazu:
docker-compose up -d
Takto dokážete zmeniť niektoré vlastnosti kontajneru aj bez jeho úplneho zrušenia, avšak niektoré zmeny môžu si vyžiadať úplne zrušenie. Poďme si nasimulovať úplne zrušenie kontajnera.
Úloha 3.2
Pre zrušenie všetkých kontajnerov zavolajte príkaz
docker-compose down
a následne ich znova nechajte vytvoriť príkazom
docker-compose up -d
Skontrolujte, že či Vám v databáze ostali vytvorené tabuľky/riadky. Prečo je to tak?
Všetky zmeny v kontajneri sa ukladajú len pre ten daný kontajner. Ak sa kontajner mení, tak väčšinou je potrebné ho odstrániť a znova vytvoriť nový kontajner s novými nastaveniami. Preto teda ak údaje nenecháme namapovať, tak sa môžu ľahko stratiť.
Mapovanie do súborového systému
Rovnako ako to bolo pri bežnom spúštani kontajnerov cez Docker príkaz aj pri docker-compose je možné mapovať vnútorné adresáre kontajnera na adresáre hosťujúceho stroja.
Stačí pridať mapovanie adresárov (volumes
) pre službu:
services:
meno:
# všetko ostatné
volumes:
- /adresar/podadresar:/adresar_v_kontajneri
- /iny_adresar:/iny_adresar_vo_vnutri_kontajnera:ro
# čokoľvek ďalšie
Poznámka
V docker-compose je možné mapovať aj od aktuálneho adresára použitím relatívnej cesty (znakom bodky) ./podadresar/
. Takéto mapovanie nebolo možné v prípade Docker príkazu, tam sme museli stále používať absolútne cesty. Taktiež je možné použiť :ro
na označenie pre read-only namapovanie adresára.
Mapovanie do pomenovaného volume
Ak nechcete riešiť mapovanie adresárov do nejakého fixného lokálneho adresára, alebo chcete aby vo vnútri kontajnera fungovali aj symbolické linky v namapovanom adresári, tak odporúčam použiť pomenovaný volume
. Táto možnosť je súčasťou aj klasického dockera, kde je možné tieto volumes taktiež vytvoriť a potom ich použiť.
Úloha 3.3
Namapujte adresár s údajmi pre MySQL kontajner (/var/lib/mysql
) do pomenovaného volume mysql-data
.
Dokážete to nasledujúcimi fragmentami:
services:
mysql:
# ...
volumes:
- mysql-data:/var/lib/mysql
# ...
volumes:
mysql-data: {} #vytvorí pomenovaný default volume
Poznámka
Takto vytvorený volume sa nezmaže ani keď zavoláte príkaz docker-compose down
, čiže je možné ho znova použiť v novom kontajneri bez straty údajov.
Ak by ste chceli zmazať aj všetký takéto volume, tak je možné použiť príkaz docker-compose down -v
.
Alternatíva je zadefinovanie volume ako externý, vtedy ho docker-compose ani nebude vytvárať a bude očakávať, že takýto volume už existuje. Ako ho vytvoriť si viete pozrieť v Docker dokumentácii.
volumes:
mysql-data:
external: true
Vyskúšajte či údaje budu perzistované aj v prípade zmeny špecifikácie kontajnera (služby v docker-compose) prípadne jej zrušením príkazom docker-compose down
.
Úloha 3.4
Vytvorte nový perzistentný volume aj pre databázu PostgreSQL.
Krok 4: Definovanie vlastných vnútorných sieti
Štandardne docker-compose vytvára pre každý stack služieb (teda služby v jednom docker-compose.yml
súbore) samostatnú sieť. Týmto spôsobom je každý kontajner vytvorený v jednom docker-compose nedostupný z iného docker-compose. Naopak ale všetky kontajnery z toho istého docker-compose môžu navzájom medzi sebou bezproblémov komunikovať.
Niekedy môžete kvôli lepšiemu rozdeleniu jednotlivých služieb v jednej aplikácii alebo aj kvôli bezpečnosti si chcieť jednotlivé služby oddeliť do samostatných sieti.
Každý kontajner môže byť napojený aj do viacerých sieti.
Úloha 4.1
Za účelom oddelenia databáz MySQL a PostgreSQL do samostatných sieti si prv definujte novú sieť mysql-net
a pridajte ju do služieb mysql
a adminer
:
services:
adminer:
# ...
networks:
- mysql-net
mysql:
# ...
networks:
- mysql-net
networks:
mysql-net:
driver: bridge # štandardný ovladač
Vyskúšajte si znova toto nastavenie spustiť a zistite, že či sa viete dostať z Adminera na MySQL databázu a potom aj na PostgreSQL databázu.
Teraz máme samostatnú sieť pre MySQL databázu, teda ak by sa nám aj niekto dostal do MySQL databázy, tak by mal problém sa dostať do PostgreSQL databázy. Avšak ako ste mohli zistiť, tak náš Adminer sa taktiež nevie dostať do tejto databázy.
Úloha 4.2
Spojazdnite Adminera, aby sa vedel dostať aj PostgreSQL databáze.
Pridajte novú sieť pg-net
a použite ju v službách postgres
a adminer
(adminer
bude mať napojené 2 siete).
Vyskúšajte či funguje pripojenie z Adminera na obidve databázy.
Teraz už by nám malo fungovať spojenie z Adminera na obidve databázy, avšak Adminer môže predstavovať bezpečnostné riziko, že ak sa niekto dostane na neho, tak sa môže dostať hneď na obidve databázy. Skúsme to ešte poriešiť.
Úloha 4.3
Vytvorte dve samostatné služby Adminer, pričom každá služba sa bude vedieť dostať len jednú databázu, čo zabezpečíte pripojením na vhodné siete.
Teda bude existovať Adminer pre PostgreSQL a iný Adminer pre MySQL. Pričom každý Adminer bude dostupný z iného portu.
Skvele, už máme stack skladajúci sa z 4 kontajnerov. To samozrejme nie je všetko, čo je možné. Dalo by sa ísť ďalej a riešiť prepájanie medzi viacerými kontajnermi z rozličných docker-compose.yml
súborov. Alebo je možné pri Dockeri ísť aj do takzvaného Swarm módu, kde existuje viacero strojov s Docker daemonom a jednotlivé kontajnery sú na nich dynamický spúštané. No to už nie je súčasťou tohto kurzu.
Upozornenie
Všetky vytvorené kontajnery, volume, siete a čokoľvek iné sa automatický vytvára v dockeri s menom ktoré okrem špecifikovaného názvu obsahuje aj prefix podľa adresára v ktorom sa nachádza aktuálny docker-compose.yml
súbor. Teda ak špecifikujete názov volume ako mysql-data
a nachádzate sa v adresári iot2
, tak výsledný názov volume bude iot2_mysql-data
.
Preto ak by ste chceli použiť sieť alebo volume definovaný v inom docker-compose.yml
súbore je potrebné pamätať, že ho musíte adresovať už celým názvom, teda aj s prefixom.
Krok 5: Doplňujúce úlohy
Úloha 5.1
Zložitý stack
Vyskúšajte si stiahnuť a rozbehnuť vlastný Sentry server tým, že si stiahnete vzorový príklad s použitím docker-compose:
git clone https://github.com/getsentry/onpremise.git
Pozrite si README.md a postupujte podľa neho.
Poznámka
Budete musieť pred spustením docker-compose príkazu ešte niečo spustiť so samostatným docker
príkazom ako je uvedené v návode.
Koľko kontajnerov sa Vám po úspešnom štarte vytvorilo? Rozumiete použitému docker-compose.yml
?
Poznámka
YAML formát umožňuje aj vytvárať odkazy na existujúce fragmenty pomocou pomenovania fragmentov cez &nazov
a následne použitím týchto fragmentov konštrukciou <<: *nazov
.
Úloha 5.2
Oboznámte sa s prácou v rozhraní Portainer, ktoré je pre študentov dostupné na KPI serveri studentapp.kpi.fei.tuke.sk. Toto rozhranie umožňuje vytvárať nové kontajnery alebo aj celé množiny (stack) aplikácii prostredníctvom použitia docker-compose syntaxe. Tento systém je v BETA verzii a môže sa stať, že aplikácia sa aj spustí ale Vám sa nezobrazi v GUI rozhrani, alebo nastanú iné problémy. V prípade akýchkoľvek problémov ma môžete kontaktovať na adrese dominik.lakatos@tuke.sk
Môžete použit toto rozhranie na skušobné nasadenie svojej aplikácie. Následne bude táto aplikácia dostupná na doméne studentapp.kpi.fei.tuke.sk a vami zvolenom porte. Ak by už bol port obsadený iným študentom, tak si zvoľte iný port.
Ďalšie ukážky docker-compose
konfigurácii:
Ukážka MongoDB s web UI Mongo-Express:
version: '3'
services:
mongo:
image: mongo:latest
restart: always
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=example
volumes:
- mongo-data:/data/db
mongoex:
image: mongo-express
restart: always
environment:
- ME_CONFIG_MONGODB_SERVER=mongo
- ME_CONFIG_MONGODB_ADMINUSERNAME=root
- ME_CONFIG_MONGODB_ADMINPASSWORD=example
ports:
- "8081:8081"
links:
- mongo
volumes:
mongo-data: