Docker

De Cacauet Wiki
Dreceres ràpides: navegació, cerca

Docker és una plataforma de contenidors, una alternativa interessant a la virtualització. Un contenidor comparteix el kernel del sistema operatiu però disposa dels seus propis recursos de sistema de fitxers (similar a un chroot), espai de memòria i temps de microprocessador.

Conceptes bàsics[modifica]

Referència bàsica: https://docs.docker.com/engine/userguide/

Imatge: sistema base del què partim per crear nous containers. No son més que un seguit d'arxius dels què disposarà el container quan es posi en marxa. Per crear una imatge podem fer-ho de diverses formes:

  • Descarregar-la de Docker Hub (docker.com) amb docker pull <img:versio> (un docker run també la descarregarà automàticament).
  • Generar-la amb un Dockerfile a partir d'una imatge prèvia. D'aquest tipus d'imatge en diem automated, és a dir, creada a partir d'imatges estàndard i automatitzant la instal·lació de paquets.
  • Generar una imatge binària: en aquest cas caldrà crear un TAR.GZ amb tots els binaris que farem servir. Es podria dir que son menys de fiar, ja que no sabem si l'usuari que l'ha creat ens ha pogut posar algun binari malintencionat, mentre que les automated podem saber els seus orígens en tot moment.

Container: subsistema que corre comandes o serveis. El generem a partir d'una imatge base.

  • El sistema el "bateja" quan l'inicia amb nom i cognom (a l'estil "fervent_aryabhata", "drunk_sammet", etc.).
  • El container només està actiu mentre executa alguna comanda o servei, si no, es para.
  • Es pot reactivar en background amb un start, i podem attachar-nos o desatachar-nos de la shell que hem obert inicialment.

Volum: directori a compartir entre la màquina amfitriona i el container.

  • També pot ser un device (p.ex. /dev/snd o /dev/sda , però es tracta diferent en el settings).


Quick reference[modifica]

ULL!

  • <img> és una imatge base (p.ex. debian, wordpress, centos, etc.)
  • <nom> és un container prèviament creat (p.ex. "fervent_aryabhata", "drunk_sammet", etc.)

Creació de containers[modifica]

Per arrencar una shell en un nou container (podem posar qualsevol comanda enlloc de la shell):

$ docker run -ti <img> /bin/bash

"-ti" = (t)erminal (i)nteractive

Per arrencar un container i deixar-lo en background:

$ docker run -d <img>

Per executar una comanda en un container en marxa:

$ docer exec <nom> <cmd>

Per arrencar un container (prèviament creat) en background:

$ docker start <nom>

Parar un container:

$ docker stop <nom>

Gestió de containers[modifica]

Llistat d'instàncies creades localment (sense -a les que estan en execució):

$ docker ps -a

Per desatachar-nos d'un contenidor sense parar-lo:

CTRL + P + Q

Per attachar-nos a un contenidor en marxa:

$ docker attach <nom>

Per eliminar un container:

$ docker rm <nom>

Per llistar totes les instàncies i eliminar-les:

$ docker ps -aq | xargs docker rm

Info:

$ docker inspect <nom>

Renombrar:

$ docker rename <nom_antic> <nom_nou>

Gestió d'imatges[modifica]

Llistat d'imatges disponibles en local:

$ docker images

Cerca d'imatges al repositori de Docker.com:

$ docker search <cadena>

Descarregar una imatge (sense arrencar cap container):

$ docker pull <img:versió>

Crear una nova imatge a partir de Dockerfile (l'anomenem amb -t <tag>):

$ docker build -t <img:versió> <path-to-Dockerfile>

Crear una imatge "binària" (ens donarà el hash de la imatge):

$ docker commit <nom> [<repo>:<tag>]

Per nombrar amb un tag la imatge creada (si no ho hem fet abans):

$ docker tag <hash> <tag>


Gestió de volums i devices[modifica]

Podem muntar a l'arrencada del container al fer "run":

  • Carpetes del filesystem de l'amfitrió amb
    -v <carpeta>:<mount_point>
  • Carpetes persistents per tots els containers amb:
    -v <alias>:<mount_point>
  • Devices amb
    --device <dev>:<mount_point>

Veure volums actuals:

$ docker volumes ls

Exemples:

$ docker run --device /dev/snd:/dev/snd -v ~/Baixades:/downloads -it debian /bin/bash



Instal·lació[modifica]

Als repositoris d'Ubuntu podeu trobar el paquet docker.io, però no és la darrera versió, necessària si volem utilitzar algunes de les darreres funcionalitats.

Es recomana, doncs, instal·lar els repositoris segons indica la pàgina de Docker.io.

També es recomana que un cop finalitzada la instal·lació, afegim el nostre usuari principal al grup "docker", per tal de permetre treballar sense privilegis. Seria algo així com:

$ sudo adduser enric docker

...així no caldrà sudo per treballar amb docker.

Recorda sortir i tornar a logar-te al terminal per què els canvis al grup s'activin.

Troubleshooting DNS[modifica]

Existeix un problema quan es treballa en determinats entorns, particularment en Debians i derivats, és molt possible que docker no reconegui el DNS local per culpa del dnsmasq del NetworkManager, i acabi provant d'accedir a 8.8.8.8 . Si veus que els paquets no s'actualitzen (problemes amb update i similars) , pots aplicar aquests parxes.

En particular, a mi m'ha funcionat deshabilitar el dnsmasq del NetworkManager per a Ubuntu 16.04. Simplement, canvia la linia de /etc/NetworkManager/NetworkManager.conf i comenta la línia del dnsmasq

#dns=dnsmasq

...i dp reiniciem els serveis:

$ sudo service network-manager restart
$ sudo service docker restart

...et voilà!


Primeres passes[modifica]

El primer que ens explica la mateix web d'instal·lació és:

$ docker run hello-world

I, tal i com explica el què surt per consola, veuràs que docker descarrega la imatge i la posa en marxa, mostra el missatge i finalitza i destrueix el contenidor.

La majoria de contenidors es quedaran, però, en marxa, ja que solen executar un servei. Per saber quins contenidors hi ha corrents, fes:

$ docker ps

En principi et sortirà buit.

Descarregarem ara una Ubuntu per fer barrabassades vàries:

$ docker run ubuntu

Però no veiem res, i tampoc si fem docker ps veurem cap conenidor en marxa. Per "ficar-nos" dintre del contenidor podem posar-nos en mode terminal interactiu (-ti):

$ docker run -ti ubuntu

...i veurem que immediatament s'ha executat i ens ofereix un prompt # per executar ordres com a root. Dins d'aquest contenidor podem provar coses diverses, però tingues en compte que quan surtis amb CTRL+D es finalitzarà i es destruirà to el contingut. 2 coses importants:

  • CTRL+D surt del container (és el caràcter estàndard de EOF). Si no està daemonitzat el container es destruirà.
  • CTRL+P+Q surt del container però el deixa corrents en background ("desatatxar-nos")

Obre un altre terminal (per no finalitzar el contenidor) i fes docker ps per veure els contenidors en marxa. També pots sortir amb CTRL+P+Q i veuràs el mateix.

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
5c141065b101        ubuntu              "/bin/bash"         5 minutes ago       Up 4 minutes                            small_goldwasser

Ara sí que podem veure el contenidor funcionant!

Aquesta és una imatge mínima de Ubuntu, i té molt poques comandes. Per exemple, ni tan sols podem editar un arxiu de text amb nano o vi. Si volem instal·lar-los caldrà primer de tot actualitzar les fonts (ve amb les fons buides) i instal·lar com fem habitualment:

# # OJU! dintre del container!!
# apt-get update
# apt-get install nano vim

...i ja disposaràs dels teus editors favorits :)

Attachant-nos a un container en marxa[modifica]

Ara obrirem un altre terminal i entrarem al docker que tenim creat. Per identificar el contenidor es pot fer per nom o per ID, per suposat el nom és més fàcil.

En aquest cas i com podeu veure al docker ps, el mateix docker ha batejat el meu contenidor com a small_goldwasser, així que hi accediré a ell simplement executant la comanda bash en mode interactiu. OJU que ara la comanda és EXEC, i no RUN com abans:

$ docker exec -ti small_goldwasser bash

...i ja soc a dins de nou! (y)

Fent neteja dels containers aturats[modifica]

Si anem fent experiments us adonareu que teniu molts containers "aturats", ja que no s'estan executant en aquest moment. Per comprovar-ho, feu:

$ docker ps -a

Aquests contenidors estan ocupant espai de disc dur. Amb aquest mini-loop podeu esborrar-los tots:

$ for i in `docker ps -aq`; do docker rm $i; done

Senzillament hem llistat tots els dockers però amb l'opció "-q" (quiet) que només ens dona el seu ID, que hem utilitzat per fer un "docker rm". Tot en una línia. Potser convé fer un alias d'això si estàs fent molts experiments.


Buscant i instal·lant imatges[modifica]

Els contenidors es poden utilitzar de moltes maneres, però potser la més típica és instal·lar un software o servei que requereix diverses passes d'instal·lació encapsulat en un docker.

Podem buscar softwares que ens interessin, per exemple, per instal·lar una galeria d'imatges web amb Zenphoto, podríem fer:

$ docker search zenphoto

...i us sortirà un docker que he fet jo 8) . Aprofiteu per posar-me "stars", please ;)

El baixem:

$ docker pull emieza/zenphoto

El posem en marxa en mode daemon amb "-d":

$ docker run -p 8080:80 -d emieza/zenphoto

Si fas un cop d'ull al [Dockerfile], veuràs que Aquesta imatge és una Ubuntu Xenial (16.04) on s'instal·la Apache, MySQL i el software de Zenphoto. Amb "-p" hem redirigit el port 80 del contenidor al localhost, per tant podrem accedir al contenidor amb:

http://localhost:8080

... et voilà!

Però encara tenim el mateix problema de sempre: si per algun motiu tanquem el contenidor, perdrem el contingut :(

Aprofito per comentar que per parar un contenidor cal fer:

$ docker stop <nom>

Si està parat el podríem retornar a la vida amb:

$ docker start <nom>

I finalment podrem eliminar-lo si un cop parat amb stop fem:

$ docker rm <nom>


Exercici[modifica]

Cada contenidor té la seva interfície de xarxa. Mira d'esbrinar quina IP té el contenidor del zenphoto accedint al contenidor a través de la comanda docker exec.

Quan l'hagis esbrinat, mira d'accedir al contenidor (al port 80 per defecte, enlloc del 8080) amb:

http://<ip_del_contenidor_zenphoto>


Volums i persistència[modifica]

Per poder conservar les dades que té el contenidor el millor és connectar carpetes locals de la màquina host sobre el sistema d'arxius del contenidor. Això es fa amb la directiva "-v":

$ docker run ... -v /path/al/teu/sistema/local:/path/al/contenidor ...

En el cas del zenphoto ho farem així:

docker run -p 9002:80 --name=my_zenphoto \
	-v /home/enric/vol/zenphoto/mysql:/var/lib/mysql \
	-v /home/enric/vol/zenphoto/www:/var/www/html \
	--restart=always \
	-d emieza/zenphoto

Com podem veure, necessitem muntar 2 volums: el del MySQL per persistir les dades de la BD, i també els arxius de PHP que estan a /var/www/html

Aquest cop sí: si parem i eliminem el contenidor, les dades seran permanents (i visibles des de la màquina amfitriona) i al fer un RUN amb un nou contenidor, disposarem dels usuaris i les fotos ja carregades anteriorment.

Fixa't, a més, en què:

  • Amb --name=my_zenphoto "bategem" el container.
  • Amb --restart=always farà que si reiniciem la màquina amfitriona, el container es posarà en marxa automàticament.


Crear una imatge amb Dockerfile[modifica]

S'ha de nombrar exactament així, Dockerfile.

Referència: https://docs.docker.com/engine/reference/builder/

Exemple bàsic:

FROM library/debian
MAINTAINER Enric Mieza <[email protected]>

RUN apt-get update && \
apt-get install -y mplayer && \
apt-get clean && apt-get autoclean && \
rm -rf /var/lib/apt/lists/*

VOLUME ["/tmp:/vol1"]

Dintre de RUN posem les comandes per inicialitzar la màquina (típicament instal·lar paquets).

Amb VOLUME podem muntar devices i volums de la màquina amfitriona.

Per crear una nova imatge només cal fer:

$ docker build -t <tag_img> <ruta_dockerfile>

El típic és executar-ho en el mateix directori on tenim el Dockerfile. Si vull crear una imatge en el meu repositori intern "enric", podria, per exemple, anomenar-la enric/debian-nou:

$ docker build -t enric/debian-nou .

Publicant a GITHUB i DOCKER HUB[modifica]

Git és un sistema de versionat per a codi font de software, i és un standard de facto avui en dia per treballar col·laborativament. Mira't l'article Git: comandes per saber més.

GitHub és una web que hostatja projectes basats en GIT. Gratuïtament per als projectes que siguin públics, i pagant els projectes privats.

El Docker Hub és el repositori oficial de Docker. Les imatges que podem descarregar amb "docker search" es busquen aquí.

Un cop desenvolupat un Dockerfile, podem publicar el nostre docker pujant el codi a Github, i després connectant-lo amb el compte de dockerhub. Aquest article explica com connectar-los:

https://docs.docker.com/docker-hub/builds/

Per acabar de publicar un element a dockerhub primer caldrà que feu un primer build manualment.


Imatge binària i comparació amb chroot[modifica]

Un contenidor Docker es pot crear a partir d'una imatge "binària" basada en una col·lecció d'arxius executables que vulguem utilitzar en el nostre contenidor. Com que el kernel és el de la màquina amfitriona, només cal que afegim els binaris i les llibreries que ens calen per executar el servei o aplicació desitjada (que de passada augmenta la seguretat).

Així, doncs, per fer una imatge només cal que fem un arxiu TAR amb les comandes que requerim. Per exemple, si desitjem tenir un contenidor que pugui executar scripts bash, només caldrà el binari /bin/bash i les llibreries de les que tingui dependències.

Per saber quin és l'executable del bash podem fer:

$ which bash

I per saber de quines llibreries dinàmiques (shared objects) depèn, només caldrà fer:

$ ldd /bin/bash

En la meva Ubuntu 14.04 em surt:

	linux-vdso.so.1 =>  (0x00007ffe58b43000)
	libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f6c1106d000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6c10e69000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6c10aa4000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6c11296000)

Així, per crear la nostra imatge per a bash scripts només caldrà posar tots aquests arxius en un directori (on se suposa que tindríem l'arrel), dins dels seus pertinents subdirectoris (/bin, /lib, etc.)

Comprimim amb TAR i importem l'invent dins de Docker amb "docker import".

Imatge binària amb Dockerfile "from scratch"[modifica]

Referència:

https://hub.docker.com/_/scratch/


Desplegament de Wordpress amb MySQL[modifica]

Utiltizarem les imatges oficials:

Ull, haurem de crear els volums persistents si volem que quan parem els contenidors tot continui funcionant. En particular caldrà muntar:

  • /var/lib/mysql a la màquina MySQL
  • /var/lib/html a la màquina Wordpress (és una apache2 en debian)

Arranquem MySQL:

$ docker run --name mysql1 -ti -e MYSQL_ROOT_PASSWORD=wordpress -v /path/al/dir/local/mysqldata:/var/lib/mysql mysql

Arranquem Wordpress:

$ docker run --name wp1 -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=wordpress -e WORDPRESS_DB_NAME=wp1 -v /path/al/wpdata:/var/www/html --link mysql1:mysql -ti wordpress

Fent desplegaments més segurs[modifica]

Si volem afegir seguretat, hauríem d'arrencar els serveis amb un usuari particular, per exemple, mysql:

$ docker run --name mysql1 -ti -e MYSQL_ROOT_PASSWORD=wordpress -v /path/al/mysqldata:/var/lib/mysql --user=mysql mysql

En alguns casos (no en MySQL però en d'altres sí) l'usuari no podrà escriure a alguns llocs del sistema d'arxius. Es pot solventar creant volums per als serveis clau


Docker Compose[modifica]

Ens facilita configurar sistemes de contenidors, els seus enllaços, ports, volums, etc.

Docker compose: https://docs.docker.com/compose/

  • Comença per la instal·lació: no ens serveix el package de Ubuntu, cal que descarregueu la darrera versió com explica en la web.
  • Segueix el tutorial del "getting started"
  • Segueix amb el de "Wordpress"

Cal crear prèviament un arxiu docker-compose.yml amb la configuració del sistema. Totes les comandes cal cridar-les dins del directori on hi ha l'arxiu.

Arrencar en mode dimoni:

$ docker-compose up -d

Parar el sistema:

$ docker-compose down

També es pot fer start/stop.

Per saber què hi ha en marxa:

$ docker-compose ps

Per esborrar un sistema de contenidors

$ docker-compose rm

Servidor de email amb docker-compose[modifica]

Pots fer l'exercici de E-mail Linux amb Docker , podràs muntar un sistema de correu electrònic full-stack (amb antivirus, antispam, SSL, autenticació SASL, LDAP, etc.).


Escalant contenidors de Wordpress[modifica]

Segueix l'exmeple del Wordpress amb docker-compose. Mira't bé els volums perquè si no poses ./db_data enlloc de "db_data a seques", no funciona.

L'escalat en serveis web serveix per augmentar la capacitat d'acceptar un major nombre de requests/segon. Hi ha diverses maneres d'aconseguir això, nosaltres ho abordarem augmentant les instàncies de Apache que tenim. Hem de mantenir, però, una sola instància de la BD, ja que és la que ens donarà consistència a les nostres dades com els posts, etc.

Loadbalance.png

Per escalar podem arrencar diversos contenidors. A l'exemple de Wordpress (caldria treure el mapeig dels ports perquè si no, dona error):

$ docker-compose down
$ docker-compose up -d
$ docker-compose scale wordpress=3 db=1

Ara pots accedir a qualsevol de les 3 màquines amb wordpress, instal·lar-ho i veure que des de les altres màquines també funciona. Típicament:

http://172.21.0.3/

Per estar segur mira't la IP de les màquines que tinguis (en el meu cas es diu mywp_wordpress_1, _2, _3, etc.), i visita la IP amb el browser.

$ docker-compose ps
$ docker inspect mywp_wordpress_1 | grep IPAddress

Si tens problemes comprova això:

  • buida la caché del teu navegador abans d'accedir a la URL (típicament es queda la redirecció permanent de l'anterior instal·lació).
  • destrueix la instal·lació anterior (down) i els directoris .data que s'hagin creat.

Ara tenim 3 Apaches funcionant, però normalment el què necessitem és un únic punt d'entrada a la web que reparteixi la càrrega als diferents nodes. Si tens temps, fes un node de balanceig de càrrega utilitzant el mod_proxy_balancer de Apache (o busca, si vols, l'equivalent de Nginx):

https://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html

NOTA: per habilitar les directives de proxy d'Apache et caldrà activar els mòduls proxy, proxy_http, proxy_html i lbmethod_byrequests .


Docker Machine i Swarm[modifica]

Docker Machine gestiona l'accés remot a un servidor docker. Enlloc de treballar en local podrem treballar sobre la màquina virtual remota. Té drivers pels diversos sistemes de virtualització més coneguts (VMware, Virtualbox, Xen, etc.) i un driver genèric.

El típic us és:

$ docker-machine create -d virtualbox <nom_maquina>

Veure les màquines disponibles:

$ docker-machine ls

Arrencar una màquina ja creada:

$ docker-machine start <nom_maquina>

Aturar-la:

$ docker-machine stop <nom_maquina>

Això crearà una màquina virtual sobre el nostre virtualbox, instal·larà el SO Linux i els paquets necessaris per tenir un servidor de Docker, a més de les claus SSH per poder accedir remotament des de la màquina local de forma còmoda.

Swarm[modifica]

https://docs.docker.com/swarm/provision-with-machine/

Podem gestionar contenidors que han de treballar plegats, però sobre diferents màquines (virtuals o no). Té diverses estratègies de repartiment. Necessitem un token proveït per hub.docker.com per tal de sincronitzar tots els contenidors.

Seguint el tutorial de swarm podrem crear un seguit de nodes als quals podrem assignar contenidors. L'estratègia per defecte és rotatòria.

La principal dificultat del swarm és que si volem desplegar un cluster en què els nodes han de comunicar-se entre sí necessitarem una xarxa overlay que ens agrupi les diferents màquines on tenim els contenidors. Per aquesta xarxa ens serà imprescindible disposar d'una base de dades key/value (datastore) del tipus etcd, consul o similars.

Utilitzarem Consul. És un paquet de serveis que inclou DNS, datastore, ACLs entre d'altres. Referències:

Les passes a realitzar serien:

  1. Crear datastore Consul. Per HA caldria replicar-la, però per provar podem fer una sola instància en un contenidor local.
  2. Crear overlay network per comunicar nodes.
  3. Crear swarm amb el docker-machine
  4. Configurar swarm per comunicar-se a través de la xarxa overlay.


Host integration o com arrencar dockers a l'inici de la màquina[modifica]

Un senzill truc és arrencar els contenidors amb la directiva --restart=always, per exemple:

$ docker run --restart=always -d mailserver

Així aconseguirem que al reiniciar la màquina es reinicii també el contenidor. Tot i això, si parem el contenidor amb un stop, llavors no tornarà a arrencar.

L'altra opció és utilitzar un script d'arrencada amb algun dels ja coneguts Process Manager. Alguns d'aquests son:

  • systemd
  • uptsart
  • supervisord

En aquesta URL trobareu la manera de configurar els 2 primers (systemd i upstart):

https://docs.docker.com/engine/admin/host_integration/