Docker Images 101
čo je to Dockerfile
a ako zabaliť vlastnú aplikáciu do
Docker obrazu
About
Doteraz sme spúšťali kontajnery z obrazov, ktoré niekto pripravil pre nás. Na tomto cvičení sa ale naučíme vytvárať vlastné obrazy a baliť do nich vlastné aplikácie.
Goals
- rozumieť obsahu konfiguračného súboru
Dockerfile
- naučiť sa vytvárať vlastné
Dockerfile
súbory - naučiť sa vytvárať vlastné Docker obrazy
Content
The Dockerfile
Dockerfile
je textový dokument obsahujúci všetky
potrebné príkazy, pomocou ktorých je možné zostaviť obraz. Ako
referenčnú príručku pre jednotlivé príkazy môžete použiť oficiálnu
dokumentáciu.
Najlepším zdrojom dokumentácie sú však Dockerfile
súbory
existujúcich obrazov, ktoré je možné stiahnuť zo servera Docker Hub. My sa v tomto kroku
pozrieme bližšie na Dockerfile
, z ktorého sa zostavuje
webový server nginx.
Task
Prejdite na stránku Docker Hub a vyhľadajte obraz pre webový server nginx.
Task
V časti Supported tags and respective Dockerfile links
prejdite na odkaz pri značke latest
.
Pod každou jednou značkou sa skrýva konkrétny
Dockerfile
, z ktorého bol obraz postavený. Najčastejšie
môžete vidieť rozličné verzie pre rozličné verzie výslednej aplikácie
alebo rozličné systémy, na ktorých je obraz založený.
Po kliknutí na linku budete presmerovaní na GitHub, kde sa nachádza konfigurácia
uvedeného Dockerfile
súboru. V priečinku, kde sa tento
súbor nachádza, sa môžu nachádzať aj ďalšie súbory na zostavenie tohto
obrazu.
Task
Prejdite si obsah zobrazeného súboru Dockerfile
.
Každý riadok súboru začína špeciálnym kľúčovým slovom. Dokumentáciu k nim nájdete na tejto stránke.
Prejdite si uvedený súbor riadok po riadku:
FROM
- obsahuje názov distribúcie, z ktorej je obraz zostavený.- táto distribúcia je výrazne menšia ako inštalátor oficiálnej distribúcie
- ušetrí nám kopec času, pretože nemusíte začínať od piky (scratch) - na to sa môžeme pozrieť na Dockerfile Alpine linuxu, ktorý je tiež základným obrazom alebo sa môžeme “preklikať” cez odkazy na obrazy uvedené v tomto príkaze vždy vyššie a vyššie až na FROM scratch narazíme
- ma uz kopec predinstalovanych veci
- vyhodou je, ze oficialne obrazy su vzdy aktualne a obsahuju vzdy posledne verzie, ako bezpecnostne fixy a da sa na to spolahnut
- jednou z obrovských vyhod je, ze pouziva balickovacieho manazera distribucie
ENV
- premenne prostredia- sposob, ako nastavit premenne prostredia
- mozeme si ich pozrieť tym, ze sa dostaneme do kontajnera a spustime prikaz env alebo si pozrieme obraz pomocou príkazu image inspect
- jeden z dolezitych mechanizmov, ktory sa pouziva pri zostavovani obrazov, pretoze tymto sposobom mozeme dat vediet mnozstvo veci
- vyhodou je podpora na každej platforme/operačnom systéme
- nastavujeme tu teda verziu Nginx a od tohto momentu je mozne túto premennú pouzit kdekoľvek v Dockerfile (ako aj v zostavovanom systeme)
RUN
- prikazy, ktore sa spustia pri zostavovani image-u- kazdy jeden RUN je vo vysledku reprezentovaný ako samostatna vrstva. preto sa castokrat ako prvy RUN nachádza pomerne dlhy prikaz, resp. prve prikazy su si velmi podobne
- používa sa najmä na inštalovanie softvéru do obrazu a konfigurovanie obrazu
- poznamka ku logovaniu - odporucany sposob, ako logovat, nie je do suborov, ale na obrazovku (standardny vystup). v image/kontajneri nie je totiz ziadny syslog ani nic podobne - o toto sa stara docker sam. v opačnom prípade, ak teda logujete do suboru, budete mat problem, ako sa k tomu suboru dostat a vytiahnut tieto udaje von z kontajnera. v tomto prípade teda linkujeme log subor so standardnym vystupom a standardnym chybovym vystupom.
EXPOSE
- porty 80 a 443 sú otvorené v kontajneri- len tieto porty nasledne vieme otvorit/publishnut z kontajnera do hostovskeho pocitaca
CMD
- nastavuje príkaz (spolu s parametrami), ktorý sa vykoná pri spustení kontajnera.- to je ten prikaz, ktory vidime pri vypise vsetkych kontajnerov v stĺpci COMMAND
- tento príkaz môže byť prepísaný pri spúšťaní kontajnera
ENTRYPOINT
- configures a container that will run as an executable
Building Minimal Image
Naším dnešným cieľom je zabaliť do obrazu vlastnú aplikáciu. Začneme
tým, že vytvoríme obraz, ktorý nebude založený na žiadnej distribúcii.
To znamená, že v príkaze FROM
uvedieme hodnotu
scratch
. Tým vytvoríme čistý obraz, ktorý bude obsahovať
len základ Alpine Linuxu.
Task
Vytvorte si samostatný priečinok s názvom mini/
, v
ktorom vytvoríme konfiguráciu pre náš obraz.
Každý obraz sa snažte vytvárať v samostatnom priečinku. Obraz totiž
nie je tvorený len súborom Dockerfile
, ale aj ďalšími
súbormi (napr. konfiguračnými), ktoré sa do výsledného obrazu
kopírujú.
Task
Zo stránky Alpine Linux - Downloads si do vytvoreného priečinku stiahnite Mini Root Filesystem pre vašu architektúru.
Ak pracujete v OS Linux, vašu architektúru si môžete
zobraziť napríklad príkazom uname
:
$ uname -p
x86_64
Podľa nej si teda správne stiahnite príslušný minimálny súborový systém.
Task
Vo vytvorenom priečinku vytvorte súbor Dockerfile
a
vložte do neho minimálnu konfiguráciu pre zostavenie obrazu od
základu.
Pre vytvorenie minimálneho obrazu použijeme tri príkazy:
FROM
- na akom inom obraze bude tento obraz založený - v našom prípade tedascratch
ADD
- ktorý súbor (alebo balík) bude vložený (a rozbalený) na ktoré miesto do vznikajúceho obrazu - v našom prípade rozbalíme stiahnutý Mini Root Filesystem do koreňového priečinku/
vznikajúceho obrazuCMD
- príkaz, ktorý sa vykoná pri spustení kontajneru
Obsah súboru Dockerfile
bude teda vyzerať
nasledovne:
FROM scratch
ADD alpine-minirootfs-3.13.2-x86_64.tar.gz /
CMD [ "/bin/sh" ]
Task
Do súboru Dockerfile
pridajte značku
maintainer
, pomocou ktorej uvediete autora obrazu (teda
seba).
Metaúdaje je možné do obrazu pridávať pomocou príkazu
LABEL
v tvare:
LABEL <key>=<value>
Správcu obrazu (maintainer) teda pridáme pridaním nasledujúceho riadku:
LABEL maintainer="Juraj Jánošík <juraj.janosik@student.tuke.sk>"
Poznámka
Na mieste značiek v súbore nijako nezáleží, keďže sa nijako
neprejavia na funkčnosti obrazu. Nájdete ich však v metaúdajoch obrazu
(a následne aj kontajneru) použitím príkazu inspect
.
Značka maintainer
sa však zvykne vkladať na začiatok
súboru hneď po príkaze FROM
.
Upozornenie
Hlavne v staršej dokumentácii sa môžete stretnúť aj so samostatným
príkazom MAINTAINER
. Tento príkaz je v dokumentácii
označený ako zastaralý (z angl. deprecated) a odporúča sa miesto neho
použiť práve príkaz LABEL
.
Task
Zostavte výsledný obraz s menom mini
.
V priečinku, kde sa nachádza konfiguračný súbor
Dockerfile
spustíme nasledovný príkaz:
$ docker image build -t mini .
Sending build context to Docker daemon 2.732MB
Step 1/4 : FROM scratch
--->
Step 2/4 : LABEL maintainer="Juraj Jánošík <juraj.janosik@student.tuke.sk>"
---> Running in 5368bf4f2695
Removing intermediate container 5368bf4f2695
---> 42fff6bea630
Step 3/4 : ADD alpine-minirootfs-3.13.2-x86_64.tar.gz /
---> e47bc82d1dcc
Step 4/4 : CMD [ "/bin/sh" ]
---> Running in 4250a89e248e
Removing intermediate container 4250a89e248e
---> 537b16496761
Successfully built 537b16496761
Successfully tagged mini:latest
Docker obraz pracuje na princípe vrstvového súborového systému AuFS, teda sa skladá z vrstiev, kde sa každá odkazuje na svojho „rodiča“. Každá inštrukcia v konfiguračnom súbore Dockerfile vytvára novú vrstvu závislú na predošlých, vďaka tomu sa pri zostavovaní nového obrazu nevykonáva každá inštrukcia v Dockerfile, ale len tie, ktoré následujú po tej, ktorá sa zmenila.
Task
Overte, či bol obraz naozaj vytvorený.
To, či bol obraz naozaj vytvorený, overíme podpríkazom
ls
príkazu docker image
:
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
mini latest 537b16496761 About a minute ago 5.61MB
Task
Z vytvoreného obrazu spustite/vytvorte kontajner.
Ak ste postupovali správne, spustením kontajnera sa spustí príkazový riadok:
$ docker container run -it --rm mini
/ #
Packaging Own Dashboard Application
Prázdny obraz už máme a použijeme ho ako základ pre obraz, do ktorého zabalíme našu aplikáciu. Bude to jednoduchý Dashboard napísaný v jazyku Python pomocou skvelého rámca FastAPI.
Task
Vytvorte si priečinok s názvom dashboard/
, prekopírujte
do neho všetky súbory z priečinku mini/
.
Poznámka
Ako sme uvádzali skôr, pre každý obraz, ktorý budete vytvárať, si vytvorte samostatný priečinok.
Task
Pomocou príkazu ADD
pridajte do obrazu aplikáciu
zabalenú v súbore app.tgz rozbalením do
priečinku /
.
Príkaz ADD
samozrejme napíšte až po tom, ako sa v obraze
rozbalil Mini Root Filesystem.
Task
Z vytvoreného súboru Dockerfile
zostavte obraz s názvom
dashboard
a spustite z neho kontajner v interaktívnom
režime.
Task
Pomocou príkazu RUN
pridajte do obrazu všetky závislosti
pre spustenie aplikácie dashboard.py
v priečinku
/app
.
Pre realizáciu tejto úlohy vám odporúčame tento postup:
- závislosti riešte postupne v spustenom kontajneri zadávaním potrebných príkazov priamo z príkazového riadku
- vždy, keď sa pohnete o krok ďalej (vyriešite niektorú zo závislostí)
pripíšte potrebný príkaz do súboru
Dockerfile
- pokračujte opäť krokom 1, kým nevyriešite všetky závislosti a aplikácia sa nespustí
Začneme teda spustením Python skriptu dashboard.py
v
priečinku /app
:
$ cd /app
$ python3 -m dashboard.main
/bin/sh: python3: not found
Na základe chybovej hlášky je zrejmé, že v systéme sa zatiaľ
nenachádza interpreter jazyka Python. Takže ho doinštalujeme
príkazom apk add
:
$ apk add python3
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
(1/10) Installing libbz2 (1.0.8-r1)
...
(10/10) Installing python3 (3.8.7-r1)
Executing busybox-1.32.1-r3.trigger
OK: 53 MiB in 24 packages
Po nainštalovaní vyskúšame spustiť skript znova:
$ python3 -m dashboard.main
raceback (most recent call last):
File "dashboard.py", line 4, in <module>
import uvicorn
ModuleNotFoundError: No module named 'uvicorn'
V tomto momente vieme, že inštalácia interpretera jazyka
Python bola úspešná. Aktualizujeme teda súbor
Dockerfile
o inštaláciu balíka python3
:
RUN apk add python3
Ďalší problém hovorí o tom, že Python nepozná modul
uvicorn
. Pre inštaláciu ďalších modulov jazyka sa používa
nástroj pip
. Pokúsime sa teda nainštalovať tento modul:
$ pip3 install uvicorn
/bin/sh: pip3: not found
Chybová hláška opäť hovorí, že príkaz pip3
sa v systéme
nenachádza. Doinštalujeme teda balík py3-pip
, ktorý
nainštaluje príkaz pip3
spolu so všetkými závlosťami:
$ apk add py3-pip
Poznámka
Pre inštaláciu jednotlivých balíov v distribúcii Alpine
Linux môžete použiť nástroj na ich vyhľadávanie priamo na
stránke distribúcie. Pri vyhľadávaní môžete používať wildcard
*
, pretože bez nej vyhľadávač rozumie iba celým názvom
balíkov.
A môžeme opäť vyskúšať spustiť predchádzajúci príkaz:
$ pip3 install uvicorn
Collecting uvicorn
...
Successfully installed click-7.1.2 h11-0.12.0 uvicorn-0.13.4
Po úspešnej inštalácii môžeme aktualizovať príkaz RUN
pridaním ako inštalácie balíku py3-pip
do systému:
RUN apk add python3 py3-pip
Opäť sa pokúsime spustiť aplikáciu:
$ python3 dashboard.py
Traceback (most recent call last):
File "dashboard.py", line 5, in <module>
from fastapi import FastAPI, Request, Form
ModuleNotFoundError: No module named 'fastapi'
Inštaláciou balíka uvicorn
sme teda vyriešili ďalšiu
závislosť a pridáme ho teda do príkazu RUN
v súbore
Dockerfile
:
RUN apk add python3 py3-pip \
&& pip3 install uvicorn
Podobným spôsobom budeme pokračovať ďalej, až kým nevyriešime všetky závislosti a aplikácia sa spustí. V tom prípade bude výstup vyzerať takto:
$ python3 dashboard.py
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
INFO: Started reloader process [44] using statreload
INFO: Started server process [47]
INFO: Waiting for application startup.
INFO: Application startup complete.
Aktuálna podoba súboru Dockerfile
bude vyzerať
nasledovne:
FROM scratch
LABEL maintainer="Juraj Jánošík <juraj.janosik@student.tuke.sk>"
ADD alpine-minirootfs-3.13.2-x86_64.tar.gz /
ADD app.tgz /
RUN apk add python3 \
pip3 install uvicorn fastapi jinja2 python-multipart
CMD [ "/bin/sh" ]
Task
Pomocou príkazu EXPOSE
pridajte do súboru
Dockerfile
číslo portu, na ktorom balená aplikácia
komunikuje.
Z výstupu aplikácie je vidieť, že port, na ktorom komunikuje, je
5000
. Do súboru Dockerfile
teda pridáme
príkaz:
EXPOSE 5000
Task
Na základe vytvoreného súboru Dockerfile
zostavte obraz
s menom dashboard
.
Výsledná podoba súboru Dockerfile
je nasledovná:
FROM scratch
LABEL maintainer="Juraj Jánošík <juraj.janosik@student.tuke.sk>"
ADD alpine-minirootfs-3.13.2-x86_64.tar.gz /
ADD app.tgz /
RUN apk add python3 py3-pip \
&& pip3 install uvicorn fastapi jinja2 python-multipart
EXPOSE 5000
CMD [ "/bin/sh" ]
Task
Z vytvoreného obrazu dashboard
spustite kontajner a v
ňom aplikáciu dashboard.py
tak, aby bol port
5000
dostupný z vonku kontajnera na rovnakom porte.
Ak ste postupovali správne, po spustení kontajnera a v ňom aplikácie
dashboard.py
môžete otvoriť prehliadač na adrese http://localhost:5000, kde sa vám
zobrazí výsledná aplikácia.
Running Application when Container Starts
Aktuálny príkaz, ktorý sa spustí po štarte kontajnera, spustí len
štandardný interpeter príkazov /bin/sh
. V tomto kroku
zabezpečíme, aby sa po štarte kontajnera automaticky spustila aplikácia
zo súboru /app/dashboard.py
.
Task
Nastavte pracovný priečinok pre príkaz uvedený v príkaze
CMD
na /app
.
Tento priečinok nastavíme pomocou príkazu WORKDIR
:
WORKDIR /app
Task
Aktualizujte príkaz CMD
tak, aby zabezpečil spustenie
aplikácie dashboard.py
pomocou interpretera
python3
.
Samozrejme vieme spustenie zabezpečiť viacerými spôsobmi. My však budeme spúšťať tento skript takto:
$ /usr/bin/env python3 dashboard.py
Tým zabezpečíme prenositeľnosť a síce, že umiestnenie interpretera jazyka Python nie je podmienená konkrétnym umiestnením v systéme.
Príkaz CMD
v súbore Dockerfile
teda
aktualizujeme nasledovne:
CMD [ "/usr/bin/env", "python3", "dashboard.py" ]
Task
Overte, či sa po spustení kontajnera z uvedeného obrazu aplikácia automaticky spustí sama.
Base Your Image on Another Image
Nie vždy je nutné vytvárať obraz od nuly. Je totiž výhodné vytvoriť svoj obraz z už existujúceho obrazu. To so sebou prináša niekoľko výhod, kedy sa nemusíte zbytočne sústreďovať na prípravu systému samotného, ale vyslovene len na vašu aplikáciu.
V našom prípade teda založíme obraz na obraze python:alpine
,
ktorý už obsahuje predinštalovaný systém s Python
interpreterom.
Task
Aktualizujte váš súbor Dockerfile
tak, aby sa zakladal
na obraze python:alpine
.
Zmena v príkaze FROM
nie je jediná, ktorú bude potrebné
vykonať. Vždy je potrebné zvážiť, ktoré ďalšie príkazy je potrebné
upraviť. Preto je dobré napr. zakomentovať príkaz RUN
,
zostaviť obraz nanovo (už na základe nového obrazu), spustiť ho len s
intepreterom príkazov /bin/sh
a postupne jednotlivé príkazy
skúšať ručne. Tým zistíte, ktoré z nich sú potrebné, a ktoré naopak
nie.
Touto úpravou zistíte, že nie je potrebné inštalovať balíky
python
a py3-pip
, pretože sú predinštalované v
rodičovskom obraze.
Výsledný súbor Dockerfile
bude vyzerať nasledovne:
FROM python:alpine
LABEL maintainer="Juraj Jánošík <juraj.janosik@student.tuke.sk>"
ADD alpine-minirootfs-3.13.2-x86_64.tar.gz /
ADD app.tgz /
RUN pip3 install uvicorn fastapi jinja2 python-multipart
EXPOSE 5000
WORKDIR /app
CMD [ "/usr/bin/env", "python3", "dashboard.py" ]
Task
Overte správnosť vašej úpravy.
Ak ste postupovali správne, aplikácia sa spustí a vy k nej môžete pristúpiť otvorením prehliadača na adrese http://localhost:5000.
Ensuring the Data Persistence
Zakaždým, keď sa aplikácia spustí, je jej hodnota rovnaká: teplota
-3°C nameraná v novembri 2020. Ak nechceme prísť o
predchádzajúce merania, potrebujeme zabezpečiť trvácnosť vytvorených
dát, ktoré sa nachádzajú v priečinku /app/data
.
Task
Pomocou príkazu VOLUME
pridajte do súboru
Dockerfile
cestu k priečinku /app/data
.
Tento priečinok bude slúžiť ako bod pripojenia pre zabezpečenie trvácnosti údajov mimo spustený kontajner.
VOLUME /app/data
Task
Zostavte obraz a overte trvácnosť zaznamenaných údajov vytvorením pomenovaného zväzku ako aj lokálneho pripojenia.
Po opätovnom zostavení obrazu vytvoríme pomenovaný zväzok nasledovným príkazom:
$ docker container run -it --rm --volume dashboard_data:/app/data dashboard
Lokálny priečinok data/
vieme zasa pripojiť týmto
príkazom:
$ docker container run -it --rm --volume ${pwd}/data:/app/data dashboard
In Next Episode of IoT…
Nabudúce sa pozrieme na nástroj Node-RED. Predtým, ako sa však do neho pustíme, si ho potrebujete nainštalovať. To samozrejme znamená - stiahnuť príslušný Docker obraz.
Okrem toho si vytvorte účet v službe openweathermap.org, aby ste mali prístup k jeho API. Budeme ho používať ako zdroj dát.
Task
Spustite docker container nodered/node-red
v
interaktívnom režime s presmerovaním portu 1880
lokálne a
pripojením lokálneho priečinku node_red_data/
na priečinok
kontajnera /data
.
$ docker run --rm -it -p 1880:1880 -v node_red_data:/data --name nodered nodered/node-red
Ak ste postupovali správne, zobrazí sa vám v prehliadači webová aplikácia Node-RED.
Poznámka
Podrobné informácie o spustení a jednotlivých voľbách nájdete na stránke Running under Docker.
Task
Otvorte prehliadač na adrese http://127.0.0.1:1880/
Task
Zaregistrujte sa v službe openweathermap.org a z9skajte po registrácii svoj API kľúč.
Tento API kľúč budeme potrebovať na získavanie informácií o aktuálnom počasí.
Additional Tasks
- Aplikácia je postavená na rámci [FastAPI], pre ktorú existuje osobitný Docker obraz od autora tohto rámca tiangolo/uvicorn-gunicorn-fastapi. Založte váš obraz na tomto obraze.
Additional Links
- Dockerfile reference
- Node-RED: Running under Docker