Docker na Windows efektivně
Docker je perfektní technologie, která dokáže udělat váš život příjemnější (za předpokladu, že jste vývojář, jinak asi moc ne). Její používání (nejen) na Windows však dokáže být pěkný opruz a místo nových možností máte chuť se na něj spíš vybodnout. Jak z toho ven?
Update
Momentálně existuje lepší způsob, jak používat Docker na Windows – Docker for Windows, který přináší lepší výkon a jednodušší ovládání.
Co je to vlastně ten Docker?
Řekněme že je to jakási alternativa k virtualizaci. Klasická virtualizace emuluje hardware, na kterém následní běží hostovaný systém (guest). Docker oproti tomu využívá kontejnerů, které sdílejí kernel se hostujícím systémem (hostem). To umožňuje mnohem větší výkon a není tak problém si na desktopu spustit třeba 200 různých kontejnerů zaráz. Na druhou stranu jelikož se sdílí kernel, konkrétně Linuxový, nelze Docker spustit na ledajakém systému. Na Windows (či OSX) se proto používá lehký virtuální stroj (VM) s malou linuxovou distribucí – boot2docker postavený na TinyCore linux.
Proč bych ho měl chtít?
- Můžu vyvíjet projekty s různými verzemi závislostí (PHP7 x PHP5, různé verze MySQL, apod)
- Můžu testovat aplikaci ve stejném prostředí jako na serveru
- Můžu experimentovat – Jak asi pofrčí tahle aplikace na PHP7? Pojede na HHVM? Bum, vyměníme image a testujeme.
- Rychlost – Kontejner spustím během vteřiny, stejně rychle ho i zničím.
Konec nudné přednášky, jdem si zprovoznit Docker!
(Pro vysvětlení budu používat Windows 10, na jiných verzích se můžou hlavně názvy lišit)
Instalace Toolboxu
Stáhneme a nainstalujeme Docker Toolbox – balíček obsahující Virtualbox, Docker, Docker Machine a Docker Compose (později).
Po instalaci spustíme Docker Quickstart Terminal – skript, který najdete v nabídce Start. Ten nám ve Virtualboxu vytvoří VM s nainstalovaným Dockerem. V tomto VM budou žít všechny naše kontejnery. Název machine je ve výchozím nastavení default.
Pokud skript selže, zkuste se podívat, jestli nemáte ve Windows zapnuté Hyper-V. Jestli jo, tak ho vypněte, nejsou s Virtualboxem kámoši.
Vytvořený VM má out-of-the-box nastavené sdílení složek s hostem. Konkrétně adresář C:/Users (ve VM namountovaný jako /c/Users).
První kontejner
Na stránkách Dockeru je popsané vytváření prvního kontejneru, ale to je docela nuda.
Vyzkoušíme si rovnou spustit nějaký PHP skript v kontejneru.
Všechny následující příkazy se budou odehrávat v emulátoru bashe (Git Bash, MinTTY, apod.) Vzhledem k tomu, že tyto emulátory mají na Windows spoustu chyb znemožňujících rozumnou práci přímo s Dockerem, tak budeme pro definování našich kontejnerů používat Docker Compose.
Vytvoříme si nový adresář někde ve sdílené složce – třeba C:/Users/{USER}/dev.
V něm si vytvoříme náš soubor docker-compose.yml s následujícím obsahem:
version: '2'
services:
app:
image: php:7-apache
volumes:
- .:/var/www/html
ports:
- 80:80
Co tato konfigurace říká:
- Chceme vytvořit kontejner app
- Kontejner bude obsahovat image php:7-apache (existující image na Docker Hubu)
- složka ve které se soubor nachází se namountuje do adresáře /var/www/html v kontejneru
- Port 80 (http) kontejneru bude přístupný na portu 80 našeho VM
Spustíme bash a provedeme příkaz:
$ eval $(docker-machine env default)
Tím nastavíme v bashi proměnné, podle kterých Docker pozná, že u všech dalších příkazů budeme vždy chtít použít náš VM s názvem default.
Teď už můžeme spustit náš kontejner:
$ docker-compose up
Zjistíme si IP našeho VM a otestujeme ji v prohlížeči:
$ docker-machine ip
Stejného výsledku bychom dosáhli i pomocí příkazu (po nastavení proměnných pomocí eval)…
$ docker run -it --name dev_app -d -v $(pwd):/var/www/html -p 80:80 php:7-apache
…což je o něco ošklivější, ale hlavně to nejspíš nebude fungovat.
Teď stačí jen přidat do adresáře nějaký PHP skript (klidně v bashi):
$ echo "<?php phpinfo();" > index.php
Po aktualizaci stránky v prohlížeči uvidíme výpis informaci o nainstalovaném PHP.
V kontejneru nám běží Apache s PHP 7. Málokdo z nás však staví aplikace sestávající z jednoho skriptu. Zkusíme něco většího. Co takhle Nette Sandbox?
Otevřeme si bash v kontejneru:
$ docker exec -it dev_app bash
Pokud příkaz vrátí chybu “cannot enable tty mode on non tty input”, napište před něj winpty.
Nainstalujeme git, Composer a stáhneme sandbox:
$ apt-get update -y && apt-get install git
$ curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
$ composer create-project nette/sandbox
Otevřeme v prohlížeči http://{VM IP}/sandbox/www a přivítá nás úvodní obrazovka sandboxu.
Možná si všimnete delší odezvy oproti sandboxu běžícímu na lokále. U mě asi čtyřnásobné. V případě složitější aplikace (Doctrine, hodně cachování, události) vám běžný request trvá až několik vteřin. Not cool. Problémem je vboxsf, filesystem pro sdílené složky ve Virtualboxu, který je zaprvé strašně pomalý a za druhé zrovna rozbitý. Co s tím?
Alternativa k vboxsf
Jako první jsem zkusil Sambu, kterou doporučuje většina windowsáků okolo Dockeru, ale ta přinesla jen mírné zvýšení rychlosti. Já chci ale rychlost minimálně srovnatelnou s lokálem! Hledal jsem, až jsem konečně objevil Unison.
Není to vyložené filesystem pro namountování adresáře do virtuálního stroje. Jedná se o nástroj pro synchronizaci souborů – něco jako rsync, ale s plně funkčním oboustraným provozem.
Jeho zprovoznění je navíc hračka. Stačí stáhnout unison na Windows a využít malý skript, který jsem si pro tuto potřebu napsal. Jdeme na to.
Pokud nám stále běží původní kontejner, tak ho zničíme pomocí
$ docker-compose kill
Naklonujeme si repozitář někde mimo složku s projektem, přesuneme se do adresáře, a spustíme skript pro synchronizaci souborů.
$ git clone git@github.com:fmasa/docker-machine-unison.git
$ cd docker-machine-unison
$ ./provision.sh
Unison zůstává běžet podobně jako watch task v Gulpu nebo Gruntu. První spuštění může trvat i několik vteřin/výjimečně minut (záleží na velikosti sdílené složky).
V základním nastavení se sdílí právě dev v domovské složce uživatele.
Zkusíme si opět vytvořit kontejner pro Nette sandbox. Na mém PC šla průměrná délka requestu u prázdného sandboxu z 350ms na 50ms, to už není špatné!
Budu rád za náměty na zlepšení či pull requesty.