V přechozím díle jsme si ukázali, jak lze vytvořit obraz pro databázi PostgreSQL, který lze použít v PaaS jako je OpenShift. Dnes si ukážeme, jak vytvořit druhý důležitý prvek a to je předlohu pro aplikační obraz.
Princip builder obrazu
Proč používám slovo "předlohu" a ne samotný aplikační obraz je celkem zřejmé, pokud si uvědomíme, že chceme vytvářet znovupoužitelný obraz (platformu) pro více aplikací. Pro jednotlivé aplikace totiž chceme sdílet takový základ (base obraz), tedy chceme, aby se stejně spouštěly, podobně se chovaly apod. Pojďme se podívat, jak bude vypadat takový společný předobraz pro celou škálu Python aplikací.
Začneme podobně, jako jsme začali s obrazem PostgreSQL, nyní již na první pokus nainstalujeme balíky pythonu s platformou Python 3.4, bez uchování dočasných souborů DNF a zbytečných dokumentačních souborů:
FROM fedora:23
RUN yum install -y --setopt=tsflags=nodocs python python-setuptools python-pip && yum clean all
Obraz zbuildíme takto:
sudo docker build -t python-34-fedora
Jelikož zde nemáme dosud žádného démona a Python obraz chceme použít pouze jako základ pro další aplikační obrazy, můžeme nyní říci, že máme hotovo a uživatelé budou používat naší předlohu takto:
FROM python-34-fedora
ADD install-app /usr/bin/
RUN /usr/bin/install-app
CMD ["/usr/bin/python", "/opt/app-root/guestbook-pgsql/guestbook/bin.py"]
Jak již asi většina porozuměla, v ukázkovém Dockerfilu jsme přidali soubor (install-app
) s instalačním skriptem aplikace a nastavili jsme jistý příkaz jako výchozí příkaz kontejneru. Takový instalační skript může dělat například toto:
#!/bin/bash
cd /opt/app-root
git clone https://github.com/hhorak/guestbook-pgsql.git
cd guestbook-pgsql/guestbook
./setup.py
Tedy skript naklonuje repositář git s aplikací a spustí setup.py
script, který provede samotnou instalaci aplikace. To lze s úspěchem považovat za hotové dílo, nicméně instalační skript a Dockerfile by musel vytvářet každý uživatel a to není moc efektivní. O to méně efektivní bude případ, kdy aplikaci máme někde lokálně a ne v repositáři git.
Této neefektivity si všimli i autoři OpenShift a přišli s nápadem standardizovat způsob, jak do obrazu dostat koncovou aplikaci. Přesně k tomuto účelu slouží aplikace source-to-image, která dodává binárku s2i
psanou v Go, tedy použitelnou bez dalších závislostí (Go je zatím stále staticky linkovaný jazyk) na různých platformách.
Jak dostat aplikaci do kontejneru aneb s2i
Po nainstalování tohoto nástroje nám tento umožní vytvořit nový obraz na základě aplikačního builder obrazu jedním příkazem, takto:
#> dnf -y install source-to-image
#> s2i build /path/to/guestbook python-34-fedora guestbook
Syntaxe příkazu s2i
je celkem jednoduchá, poslední parametr v ukázce nahoře je název výsledného obrazu a předposlední parametr je název výchozího obrazu. Ve výsledku tedy uděláme to samé, co můžeme udělat s Dockerfilem a pár příkazy, nicméně nástroj s2i nám dává možnost dělat to samé efektivně a opakovaně. Krom toho to můžeme dělat stejně pro všechny podobné typy kontejnerů, ať již se jedná o Python, Ruby, NodeJS nebo obraz s kompilovaným jazykem.
Výsledný obraz potom můžeme spustit takto jednoduše:
#> docker run -p 8080:8080 guestbook
Aby to mohlo být takto jednoduché, musí builder obraz (tak říkáme obrazům, které neobsahují žádnou konkrétní aplikaci a jsou předlohou pro finální obrazy) obsahovat minimálně dva spustitelné skripty. Jedná se o skripty assemble
a run
-- pojďme se na ně podívat podrobněji.
Princip skriptů run a assemble
První skript assemble
se spouští při samotném volání příkazu s2i
a jeho úkolem je zkopírovat, nainstalovat a připravit pro běh aplikaci, která je při vykonávání skriptu assemble
umístěna v předem domluvené složce. Tou složka je /tmp/src/
a následující skript ukazuje konkrétní jednoduchou ukázku skriptu assemble
pro Python:
#!/bin/bash
cp -Rf /tmp/src/. ./
if [[ -f requirements.txt ]]; then
pip install --user -r requirements.txt
fi
Jak vidíme, skript se snaží být chytrý a pokud aplikace obsahuje soubor requirements.txt
, tak z něho nainstaluje potřebné balíky pomocí instalátoru pip
. Podobných standardů můžeme podporovat více.
Naproti tomu skript run
se spouští až v době, kdy uživatel spouští výsledný kontejner s aplikací, při s2i build
se tento skript tedy pouze nastaví jako výchozí příkaz při spouštění. Jednoduchý skript run
pro Python může vypadat následovně:
#!/bin/bash
function is_django_installed() {
python -c "import django" &>/dev/null
}
manage_file=$(find . -maxdepth 2 -type f -name 'manage.py' | head -1)
if is_django_installed; then
exec python "$manage_file" runserver 0.0.0.0:8080
fi
Jak vidíme, opět se snažíme odhadnout podle standardů Python aplikací, co se má vlastně provést. Pokud aplikace obsahuje manage.py
a je nainstalován modul django
, jedná se pravděpodobně o aplikaci Django a tím pádem víme, jak ji spustit. Zde bych doporučil zaměřit se vždy na podporu oblíbených frameworků a ty podporovat jak v assemble
, tak ve skriptu run
.
Kromě zmíněných dvou skriptů bychom měli vytvořit i třetí skript, usage
, který, jak název napovídá, má při spuštění vytisknout informace o tom, jak daný obraz používat. Všechny skripty se potom instalují do společné složky, ideálně /usr/libexec/s2i
.
Trochu štábní kultury nakonec, aneb metadata určená právě pro OpenShift, která říkají, kam se instalují skrypty assemble
a run
:
LABEL io.openshift.s2i.scripts-url=image:///usr/libexec/s2i
ENV STI_SCRIPTS_PATH=/usr/libexec/s2i
Tím jsme dokončili podporu s2i
, nicméně když už jsme u Dockerfilu builder obrazu, pojďme se zaměřit na pár dalších věcí. Ve světě kontejnerů budujeme mikroslužby, tedy aplikace komunikující po síťovém portu, ne aplikace pracující například na bázi stdin/stdout. Aby se daly všechny aplikace dále zpracovávat stejným způsobem, bude šikovné pracovat vždy se stejným portem, v builder obrazu tedy nastavíme:
EXPOSE 8080
Bezpečnost a prostředí
Nejen v OpenShiftu, který to zatím ve výchozí konfiguraci vyžaduje, budeme kvůli bezpečnosti chtít výsledné kontejnery spouštět jako jiný uživatel, než root, pro ochranu hostitelského stroje. Nastavíme tedy výchozího uživatele na nově vytvořeného uživatele default
:
RUN useradd -u 1001 -r -g 0 -d /opt/app-root/src -s /sbin/nologin \
-c "Default Application User" default
Pro ochranu kontejnerů mezi sebou se potom může hodit spouštět každý kontejner s jiným, libovolným UID. Znamená to tedy, že aplikace při instalaci musí být schopna zapisovat do adresáře, kde se nachází instalovaná aplikace, ať již běží pod jakýmkoliv UID. To vše musíme taktéž zařídit již při vytváření builder obrazu, protože v době provádění skriptů assemble
a run
již operujeme pod tímto ne-root uživatelem.
RUN chown -R 1001:0 /opt/app-root && chmod -R g+rw /opt/app-root
USER 1001
Při instalaci Python modulů pomocí pip
se rekurzivní závislosti nainstalují spolu s modulem. Nicméně zejména pokud pracujeme s minimálním prostředím, kterým kontejner bezesporu je, instalace některých binárních extenzí může selhat, protože na systému (tedy v kontejneru) není přítomen nějaký hlavičkový soubor nebo knihovna (obojí se v případě Fedory nachází v balících -devel). Jelikož balíky -devel z principu nejsou nikterak velké, můžeme si dovolit nainstalovat alespoň nejčastěji používané, abychom případným problémům přecházeli. Zároveň se může hodit mít k dispozici pár základních utilit jako find
, lsof
, patch
a další. Seznam takových závislostí byl identifikován při vývoji builder obrazů pro OpenShift na tyto:
RUN dnf -y install \
autoconf \
automake \
bsdtar \
findutils \
gcc-c++ \
gd-devel \
gdb \
gettext \
git \
libcurl-devel \
libxml2-devel \
libxslt-devel \
lsof \
make \
mariadb-devel \
mariadb-libs \
openssl-devel \
patch \
postgresql-devel \
procps-ng \
sqlite-devel \
tar \
unzip \
wget \
which \
yum-utils \
zlib-devel" && \
dnf -y clean all
Mnoho z těchto věcí je použito v trochu komplikovanějším obrazu, který je postaven na balících s Python 3.4, dostupného jako Softwarové kolekce na https://github.com/openshift/sti-python/tree/master/3.4. Ten navíc nevychází z čistého distribučního base obrazu, ale ze společné mezivrstvy pro builder obrazu s podporou s2i
, který je dosutpný na https://github.com/openshift/sti-base. Pro další detaily si můžete projít zdrojové soubory pro vytvoření obrazu tamtéž. Hodně zdaru při vytváření vlastních s2i obrazů!