Docker kontejnerizace
21.12.2020Docker je nástroj pro tzv. aplikační kontejnerizaci (dockerizace :-), která se stává standardem. Co to znamená, přehled základních příkazů a příklad s asp.net core službou se pokusím popsat v tomto článku.
Slovníček
Docker host | Fyzický nebo virtuální počítač, na kterém běží docker engine. Docker engine je možné nainstalovat na Linux, Windows nebo macOS a umožňuje provoz docker kontejnerů. |
Docker engine | Skládá se ze serveru (na pozadí běžící proces docker daemon), REST API a docker klienta (CLI). |
Docker klient | CLI (aplikace příkazového řádku) pro správu šablon a kontejnerů. |
Docker desktop | GUI pro správu šablon (image) a kontejnerů. |
Docker register | Repozitář již vytvořených a připravených šablon, které si můžeme stáhnout a použít. Veřejným repositářem je hub.docker.com. |
Image | Šablona, podle které se vytváří konkrétní kontejner nebo více kontejnerů. Obsahuje build instrukce. |
Kontejner | Spustitelný balíček, který obsahuje aplikační kód včetně potřebných systémových částí (runtime, knihovny, nastavení, ...). Lze ho spustit všude, kde je nainstalovaný docker engine. |
Zapisovací vrstva kontejneru | Kontejner zapisuje data do zapisovací vrstvy (writeable layer), která je součástí kontejneru a po smazání kontejneru se také smaže. Zapisovací vrstva je pomalejší než nativní file systém. Pokud bychom chtěli kontejner optimalizovat nebo některé soubory zachovat i po smazání kontejneru, musíme použít tzv "volume". |
Volume | Úložiště plně spravované dockerem a není součástí kontejneru. Může sloužit jako sdílené úložiště pro více kontejnerů zároveň. |
Bind mount | Alternativní možnost jak propojit zapisovací vrstvu kontejneru se souborovým systémem na hostitelském počítači. Pomalejší a méně preferovaná varianta než volume, ale může se hodit v některých případech. |
Obsah
Úvod
Docker kontejner si můžeme představit jako odlehčenou virtualizaci, kdy nespouštíme znova celý operační systém, ale pouze aplikační část. Naše řešení se může skládat z několika kontejnerů, kde každý kontejner bude zastupovat konkrétní službu, například databázi, proxy server, webový server, cache úložiště a podobně. Tyto služby (šablony) se dají stáhnout z veřejného repositáře hub.docker.com a můžeme si je dále upravovat.
To má hned několik výhod
- Kontejner je výpočetně méně náročný než plná virtualizace (VMware, VirtualBox, Hyper-V, KVM), to nám umožňuje rychlou správu a zjednodušuje tvorbu složitější infrastruktury.
- Infrastrukturu si připravíme u sebe na počítači a můžeme jí pak nasadit kdekoliv. Například jí sdílet kolegovi, který si buildne šablony a nemusí už nic instalovat a konfigurovat, což ušetří čas a případné komplikace.
- Po smazání kontejnerů nám zůstane počítač čistý, než pokud bychom si vše instalovali přímo na hostitelský systém.
- Šablony můžeme verzovat a s kontejnerama dále pracovat, škálovat je, rollbacknout službu na předchozí verzi, automatizovat deployment apod.
Architektura
Docker architektura se skládá z hosta, na kterém je nainstalovaný docker engine (daemon, klient). Pomocí klienta vše ovládáme, z registru si stáhneme připravenou šablonu, tu buildneme, tím se vytvoří kontejner a ten poté můžeme spustit a ovládat.
Rozdíl mezi kontejnerem a plnou virtualizací
Hlavní rozdíl je v tom, že kontejner neobsahuje operační systém, ale pomocí dockeru sdílíme operační systém hosta. Nevirtualizujeme tedy celý server, ale pouze aplikaci. Tím se šetří výpočetní výkon a práce s kontejnerama je rychlejší.
Přehled příkazů
Tam kde není specifikováno jinak můžeme pro šablonu nebo kontejner zadat jak název tak i identifikátor nebo jen část identifikátoru (pokud by část identifikátoru nebyla unikátní tak nás docker upozorní, že neví který kontejner máme na mysli).
# Stáhnutí šablony z hub.docker.com
> docker pull image_name
# Výpis stáhnutých šablon
> docker images
# Vytvoření a spuštění kontejneru,
# --name jméno pro nový kontejner,
# -p namapování portu hosta na vnitřní port služby v kontejneru,
# -d (daemon), kontejner se spustí na pozadí a my můžeme s příkazovou řadkou hosta dále pracovat.
> docker run --name kontejner -p port:port -d image_name
# Spuštění nebo zastavení již vytvořeného kontejneru
> docker stop container
> docker start container
# Výpis běžících kontejnerů
> docker ps
# Výpis všech kontejnerů (běžících i pozastavených)
> docker ps -a
# Smazání kontejneru a šablony,
# -v smaže kontejner i s volume.
> docker rm -v container
> docker rmi image
# Přejmenování existujícího kontejneru
> docker rename container new_container_name
# Zobrazení konfigu šablony nebo kontejneru
> Docker inspect image_or_container
# Zobrazení logu. Hodí se například pokud spustíme šablonu, ale vytvořený kontejner neběží.
> docker logs container
# Spuštění terminálu u běžícího kontejneru. Příkazem exit z terminálu zase vyskočíme.
> docker exec -it container bash
Spuštění webové aplikace v kontejneru
Jako příklad si připravíme asp.net core webovou aplikaci, kterou spustíme v docker kontejneru. Nejdříve jí spustíme v kontejneru, který bude obsahovat dotnet SDK, tomu nasdílíme přímo zdrojové kódy aplikace, které budou uložené ve složce na souborovém systému hosta. Poté jí spustíme v kontejneru, který bude obsahovat pouze dotnet runtime, kdy kontejneru nasdílíme pouze výsledný build aplikace. Nakonec si vyzkoušíme variantu, kdy si vytvoříme vlastní šablonu, která vytvoří kontejner, který již build aplikace bude obsahovat v sobě. První dvě varianty jsou spíše pro vyzkoušení si jak docker funguje, variantu s konfiguračním souborem dockerfile již můžeme použít pro deployment.
Vytvoření aplikace
Vytvoříme si asp.net core mvc projekt. Zároveň musí existovat solution, abychom mohli později použít dotnet run příkaz.
dotnet new sln
dotnet new mvc
dotnet sln add Webapp.csproj
Do Home/Index.cshtml vložíme následující kód, abychom viděli v jakém prostředí nám aplikace běží (proměnná ASPNETCORE_ENVIRONMENT).
<environment names="Development">
<p class="alert alert-success">Development</p>
</environment>
<environment names="Production">
<p class="alert alert-danger">Production</p>
</environment>
Namapování zdrojových souborů aplikace do kontejneru a spuštění
Stáhneme si oficiální šablonu od Microsoftu, která obsahuje vývojové prostředí dotnet (SDK). Do kontejneru namapujeme adresář se zdrojovými kódy a pomocí příkazu dotnet run aplikaci spustíme.docker pull mcr.microsoft.com/dotnet/sdk
"Development": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": false,
"applicationUrl": "http://0.0.0.0:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Foreground mód
Spustíme kontejner v tzv. foreground módu, kdy máme k dispozici příkazovou řádku, ve které spustíme aplikaci ručně.docker run --rm -it -p 5000:5000 -v %cd%:/app -w "/app" --name webapp-dev mcr.microsoft.com/dotnet/sdk bash
dotnet run --launch-profile=Development
- --rm smaže kontejner po jeho zastavení
- -it foreground mód, používá se zároveň s příkazem bash
- -p mapování portu hosta na port kontejneru, na kterém naslouchá služba
- -v mapování adresáře hosta na adresář v kontejneru
- %cd% windows cmd zkratka pro aktuální adresář. V powershellu lze použít ${PWD} a na Linuxu $(pwd)
- -w adresář, ve kterém se spouští následné příkazy
- --name jméno nově vytvářeného kontejneru
- mcr.microsoft.com/dotnet/sdk šablona, podle které kontejner vytváříme
- dotnet run --launch-profile=Development příkaz, který se spustí v kontejneru po jeho vytvoření
místo parametru -v můžeme použít novější parametr --mount, který funguje velmi podobně
a použití by bylo následovné
--mount type=bind,source=%cd%,target=/app
Background mód
Ve druhé variantě spustíme kontejner na pozadí. Díky tomu můžeme příkazovou řádku hosta použít pro další příkazy nebo jí zavřít. Místo bashe spustíme přímo aplikační službu, ze dvou příkazů nám tím vznikne jediný.docker run --rm -d -p 5000:5000 -v %cd%:/app -w "/app" --name webapp-dev mcr.microsoft.com/dotnet/sdk dotnet run --launch-profile=Development
- -d background mód
Namapování buildu aplikace do kontejneru a spuštění
Stáhneme si šablonu s nainstalovaným dotnet runtime
docker pull mcr.microsoft.com/dotnet/aspnet
Vytvoříme build aplikace do out složky
dotnet publish -c release -o out
docker run --rm -d -p 5000:5000 -v %cd%/out:/app -w "/app" --name webapp-dev mcr.microsoft.com/dotnet/aspnet dotnet Webapp.dll --urls=http://0.0.0.0:5000 --environment=Development
Vytvoření vlastní šablony pomocí dockerfile souboru
Dockerfile je konfigurační soubor, pomocí kterého můžeme automatizovat proces buildování aplikace a vytvořit šablonu, která již bude aplikační soubory obsahovat (nemusíme je tedy mapovat z hostitelského počítače). Tím významně zjednodušíme distribuci a spouštění aplikace/služby.
Build již máme k dispozici
Pokud náhodou ne, tak build vytvoříme
dotnet publish -c release -o out
Vytvoříme si dva dockerfile soubory, jeden pro vývoj a druhý pro produkci.
webapp/development.dockerfile# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
# Copy published asp.net core application to /app folder in docker
COPY out/ /app
ENTRYPOINT ["dotnet", "Webapp.dll", "--urls=http://0.0.0.0:5000", "--environment=Development"]
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
# Copy published asp.net core application to /app folder in docker
COPY out/ /app
ENTRYPOINT ["dotnet", "Webapp.dll", "--urls=http://0.0.0.0:5000", "--environment=Production"]
docker build -f development.dockerfile -t lukasurban/webapp-dev .
docker build -f production.dockerfile -t lukasurban/webapp-prod .
- -f nám specifikuje konfigurační soubor, který chceme použít. Bez parametru se jinak použije Dockerfile.
- -t jméno naší šablony.
- . nesmíme zapomenout na tečku, která řiká, že chceme použít adresář, ve kterém se nacházíme jako kořenový adresář pro build.
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
lukasurban/webapp-prod latest 5d06b2155018 35 minutes ago 210MB
lukasurban/webapp-dev latest 6ab303cfad64 35 minutes ago 210MB
docker run -d -p 81:5000 --name webapp-dev-01 lukasurban/webapp-dev
docker run -d -p 82:5000 --name webapp-dev-02 lukasurban/webapp-dev
docker run -d -p 80:5000 --name webapp-prod lukasurban/webapp-prod
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42a4eb940283 lukasurban/webapp-prod "dotnet Webapp.dll -…" 43 seconds ago Up 39 seconds 0.0.0.0:80->5000/tcp webapp-prod
5df22f443cb3 lukasurban/webapp-dev "dotnet Webapp.dll -…" About a minute ago Up About a minute 0.0.0.0:82->5000/tcp webapp-dev-02
68a8eda8c3d0 lukasurban/webapp-dev "dotnet Webapp.dll -…" About a minute ago Up About a minute 0.0.0.0:81->5000/tcp webapp-dev-01
Vytvoření šablony ze zdrojových souborů
Pokud bychom chtěli vytvořit šablonu přímo ze zdrojových souborů, musíme upravit stávající dockerfile na tzv. multi-stage dockerfile build. Ten nám také umožní pracovat s více šablonami najednou. V našem případě použijeme dotnet/sdk pro build aplikace a dotnet/aspnet pro spuštění. Důvod proč nepoužít dotnet/sdk i pro spuštění je ten, že výsledný kontejner by byl zbytečně velký. To nám může a nemusí vadit, jako vždy záleží na kontextu.
webapp/development.multistage.dockerfileFROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish Webapp.sln -c release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "Webapp.dll", "--urls=http://0.0.0.0:5000", "--environment=Development"]
docker build -f development.multistage.dockerfile -t lukasurban/webapp-dev-from-source .
docker run -d -p 5000:5000 --name webapp-dev-from-source lukasurban/webapp-dev-from-source
Závěr
Nyní umíme vytvářet, spouštět a spravovat kontejnery a také si vytvářet vlastní šablony :-)
Některé věci jsem popsal do většího detailu než jsem původně zamýšlel, ale věřím, že je to lepší než se pak
zbytečně zaseknout na nějakém detailu. Chtěl jsem také popsat docker compose, který se mi ale již do tohoto
článku bohužel nevešel, tak ho snad popíšu v příštím článku,
který bude již v novém roce