Docker té dues opcions perquè els contenidors emmagatzemin fitxers a la màquina amfitrió, de manera que els fitxers es mantinguin fins i tot després que el contenidor s'aturi: volums i muntatges d'enllaç.
Introducció
Arbre de fitxers
Si vols començar amb un entorn net, esborra-ho tot:
$ docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
Are you sure you want to continue? [y/N] y
...
Total reclaimed space: 27.96MB
A diferència d'altres sistemes operatius, Linux unifica tot l'emmagatzematge en un sol arbre.
A Windows tens C:\
, D:\
, etc.:
Però, en Linux tot està muntat a /
.
Punt de muntatge
Els dispositius d'emmagatzematge, com ara particions de disc o particions de disc USB, estan connectats a ubicacions específiques d'aquest arbre.
Aquests llocs s'anomenen punts de muntatge.
Un punt de muntatge defineix la ubicació a l'arbre, les propietats d'accés a les dades en aquest punt (per exemple, l'escriptura) i la font de les dades muntades en aquest punt (per exemple, un disc dur, un dispositiu USB o un disc virtual en memòria).
Aquí tens un dibuix que representa un sistema de fitxers construït a partir de diversos dispositius d'emmagatzematge, amb cada dispositiu muntat en una ubicació i nivell d'accés específics.
RAM disk
Crear un sistema de fitxers en RAM permet que no quedi rastre de fitxers confidencials temporals o utilitzar fitxers temporals amb un accés super ràpid.
Crea un directori i permet que tothom tingui accés:
$ sudo mkdir /var/fast
$ sudo chmod 777 /var/fast
Ara crea un sistema de fitxers tmpfs de 1024 MB i nom de dispositiu ramdisk
:
$ sudo mount -t tmpfs -o size=1024m ramdisk /var/fast
Pots veure que la unitat ramdisk s’ha muntat correctament:
$ mount | grep ramdisk
ramdisk on /var/fast type tmpfs (rw,relatime,size=1048576k,inode64)
Prova de crear un fitxer, veuras que no notaras la diferencia:
$ echo "Hello" > /var/fast/message.txt
$ ls -l /var/fast/
total 4
-rw-rw-r-- 1 isard isard 6 de maig 21 15:34 message.txt
Si desmuntes la carpeta, perds tota la informació perquè no estava guardada en un dispositiu físic sinó en memòria.
$ sudo umount /var/fast
$ ls -l /var/fast/
total 0
Contenidor
Els punts de muntatge permeten que el programari i els usuaris utilitzin l'arbre de fitxers en un entorn Linux sense saber exactament com s'assigna aquest arbre a dispositius d'emmagatzematge específics.
Cada contenidor té un MTN namespace i una arrel única d'arbre de fitxers.
L’arbre de fitxers d’un contenidor està format per un conjunt de punts de muntatge de 4 tipus diferents:
-
ImageFS – Les imatges que s’han descarregat es munten en diferents punts del sistema de fitxers del contenidor.
-
In-memory storage – Un disc RAM com el que hem vist abans
-
Bind mounts – Un directori concret del host
-
Docker Volumes – Un directori lògic del host
A l’esquerra està el sistema de fitxers vist des del contenidor, i a la dreta els punts de muntatge reals en sistema de fitxers del host:
Bind mount
Un bind mount et permet muntar un directori del host en qualsevol punt de muntatge del sistema de fitxers del contenidor.
Arrenca un contenidor Apache amb l’opció -p 80:80
perquè poguem connectar-nos des de fora del host:
$ docker run --rm -d --name apache -p 80:80 httpd
...
97d2d3f55150795b03ae2a3131ee3cfd469dbfc82e6f793c514853cc73da662b
$ curl localhost
<html><body><h1>It works!</h1></body></html>
A Docker - Xarxa ja explicarem com funciona l’opció -p
.
Si volem canviar el missatge, podem entrar dins el contenidor i modificar el fitxer index.html
:
$ docker exec -it apache bash
...$ echo "Hello!" > htdocs/index.html
...$ exit
exit
$ curl localhost
Hello!
Una altra opció és crear una carpeta www
fora del contenidor amb un fitxer index.html
i altre contingut.
$ mkdir www
$ echo "Bye!" > www/index.html
Eliminem el contenidor que està en execució:
$ docker stop apache
apache
Creem un contenidor nou montant el directori /usr/local/apache2/htdocs
a la nostra carpeta www
.
$ docker run --rm -d --name apache -p 80:80 --mount type=bind,source="$(pwd)"/www,target=/usr/local/apache2/htdocs httpd
9dacc8d3d17cd2a4347732ca785e4a8f57fe2031b3baaae96a3399c14c9985c9
Si fas un curl
a localhost pots veure que ara respon amb el teu fitxer index.html
no amb el del contenidor:
$ curl localhost
Bye!
Modifica el fitxer index.html
i verifica que el servidor apache contesta amb el nou contingut:
$ echo "Patufet"> www/index.html
$ curl localhost
Patufet
Entra dins el contenidor, modifica el fitxer index.html
, surt del contenidor i verifica que desde fora també s'ha modificat:
$ docker exec -it apache bash
...$ cat htdocs/index.html
Patufet
...$ echo "Patufet" > htdocs/index.html
...$ exit
$ cat www/index.html
Patufet
Amb l’ordre docker inspect
pots verificar en la secció Mount
que el muntatge d’enllaç s'ha creat correctament:
$ docker inspect apache --format '{{ json .Mounts }}' | jq
[
{
"Type": "bind",
"Source": "/home/isard/www",
"Destination": "/usr/local/apache2/htdocs",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
Com que hem separat el contingut web del servidor web podem crear un altre servidor Apache que escolti al port 8080
i serveixi el mateix contingut:
$ docker run --rm -d --name apache2 -p 8080:80 --mount type=bind,source="$(pwd)"/www,target=/usr/local/apache2/htdocs httpd
292f7b72277524fc656593b9504676e782e50e7d407eff6686155376ae699082
$ curl localhost:80
Patufet
$ curl localhost:8080
Patufet
També podem crear un altre servidor Nginx que escolti al port 3000
i serveixi el mateix contingut.
L’única diferència és que la carpeta www
la tindrem que montar a /usr/share/nginx/html
:
$ docker run --rm -d --name nginx -p 3000:80 --mount type=bind,source="$(pwd)"/www,target=/usr/share/nginx/html nginx
...
c1f360dbc7e2ca97ca74648441f6a62bcadaf029954dfb23bf63361993df0e4a
$ curl localhost:3000
Patufet
Pots veure que hi ha tres servidors web servint el mateix contingut en ports diferents, i tots tenen el contingut de la carpeta www
on ells volen:
$ docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"
CONTAINER ID NAMES PORTS
20c8ebb1885d nginx 0.0.0.0:3000->80/tcp, :::3000->80/tcp
292f7b722775 apache2 0.0.0.0:8080->80/tcp, :::8080->80/tcp
9dacc8d3d17c apache 0.0.0.0:80->80/tcp, :::80->80/tcp
A part del contingut també pots montar els fitxer de configuració, d’aquesta manera ens separat el que és teu (estat) del programa que s’ha d’executar (comportament).
Eliminem els contenidors i ens quedem amb l’únic que ens importa: la carpeta www
:
$ docker stop apache apache2 nginx
apache
apache2
nginx
Emmagatzematge en memòria
La majoria d’aplicacions web utilitzen fitxers de claus privades, contrasenyes de bases de dades, fitxers de claus API o altres fitxers de configuració amb informació sensible.
És millor guardar aquesta informació en memòria perquè no deixa rastre.
Creem un contenidor amb un punt de muntatge en memòria:
$ docker run -it --name memory --mount type=tmpfs,dst=/secret alpine
...$ echo "P@ssw0rd" > /secret/password.txt
...$ echo "Hello" > /hello.txt
...$ exit
Si aturem el contenidor i el tornem a arrencar, l’arxiu password.txt
haurà desaparegut mentres que el fitxer hello.txt
encara estarà:
$ docker stop memory
memory
$ docker start memory
memory
$ docker exec -it memory sh
... $ cat /hello.txt
Hello
... $ cat /secret/password.txt
cat: can't open '/secret/password.txt': No such file or directory
Volums
Enlloc de crear un carpeta i tenir que dir la ruta on està, pots utilitzar carpetes lògiques que es poden referenciar amb un nom.
Per exemple, quan executes una wiki en un contenidor t’interessa conservar les dades quan elimines el contenidor.
Enlloc de crear una carpeta pots crear un volum i guardar les dades allà.
Imagina’t que vols crear una wiki amb dokuwiki.
El primer pas NO es instal.lar dokuwiki com faries normalment, sinó crear un emmagatzematge on es guarda la wiki.
Crea un volum wiki-nature
:
$ docker volume create wiki-nature
wiki-nature
$ docker volume ls
DRIVER VOLUME NAME
local wiki-nature
$ docker volume inspect --format "{{ json .Mountpoint }}" wiki-nature
"/var/lib/docker/volumes/wiki-nature/_data"
Pots veure que el volum és local (està en una carpeta local), concretament a /var/lib/docker/volumes/wiki
.
Per defecte, un volum és crea en el sistema de fitxers del host, però hi ha opcions més avançades.
Ha continuació has d’arrencar un contenidor dokuwiki amb el volum que has creat perquè totes les pàgines de la wiki s’escriguin en aquest volum.
$ docker run --rm -d --name wiki -p 80:8080 --volume wiki-nature:/bitnami/dokuwiki bitnami/dokuwiki
...
f11acc644df8f1a2f985be03be66d75766c17f1d15b86d04f2a1fb7a2f85fbf7
Obre un navegador a http://localhost i edita la pàgina principal:
Aquí tens una explicació de com pots utilitzar la imatge: https://hub.docker.com/r/bitnami/dokuwiki/dockerfile/.
-
Edita la primera pàgina amb algun contingut.
-
Para el contenidor (aquest s’eliminarà perquè has fet servir l’opció
--rm
)
$ docker stop wiki
wiki
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad7832d4d2c8 alpine "/bin/sh" 24 minutes ago Up 20 minutes memory
- Pots verificar que les pàgines estan guardades al volum i no es perden.
$ sudo ls /var/lib/docker/volumes/wiki-nature/_data
- Si vols torna a veure o editar la wiki només has de tornar a arrencar un altre contenidor dokuwiki muntant el volum:
$ docker run --rm -d --name wiki -p 80:8080 --volume wiki-nature:/bitnami/dokuwiki bitnami/dokuwiki
c9794ef60a22dac1e2a35dce8cc1285b68b499a0b925d541c86e0d2d4dfcdb22
Pots veure que el contenidor té un ID diferent i que el contingut s'ha guardat:
Activitat
- Crea un volum
wiki-football
i genera contingut creant un contenidor.
$ docker volume create wiki-football
$ docker run --rm -d --name wiki-football -p 90:8080 --volume wiki-football:/bitnami/dokuwiki bitnami/dokuwiki
a12f77e9b2d9fcf93e1d0cc4502869386fad1fdad1656af15470a8ab3e1a1908
Pots veure que:
-
Pots tenir dos dokuwiki funcionant a la vegada en ports diferents.
-
No has de tenir instal.lat dokuwiki o tenir contenidors funcionat si no els necessites en aquell moment (tot el contingut està guardat en volums).
$ docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"
CONTAINER ID NAMES PORTS
a12f77e9b2d9 wiki-football 8443/tcp, 0.0.0.0:90->8080/tcp, :::90->8080/tcp
c9794ef60a22 wiki 8443/tcp, 0.0.0.0:80->8080/tcp, :::80->8080/tcp
$ docker volume ls
DRIVER VOLUME NAME
local wiki-football
local wiki-nature
Neteja de volums
Com has vist abans pots veure tots els volums que tens en el sistema amb l'ordre docker volume ls
:
$ docker volume ls
DRIVER VOLUME NAME
local wiki-football
local wiki-nature
Per suprimir un volum has d’executar l’ordre docker volume rm
:
$ docker volume rm wiki-nature
Error response from daemon: remove wiki-nature: volume is in use - [c9794ef60a22dac1e2a35dce8cc1285b68b499a0b925d541c86e0d2d4dfcdb22]
Però primer has d'aturar el contenidor:
$ docker stop wiki
wiki
$ docker volume rm wiki-nature
wiki-nature
Activitats
Compartir fitxers
Si tenim un conjunt de fitxers html que volem fer servir mitjançant un servidor web, només volem un servidor web que funcioni i res més.
Per tant, enlloc d’executar un contenidor ubuntu i instal.lar un servidor apache, farem servir un contenidor amb una versió concreta d’apache ja instal.lat.
Una de les capacitats més rellevants d’un contenidor, es que podem executar un servidor apache sense haver de insta.lar res en el nostre host, i eliminar-lo quan ja no el necessitem. Pert tant, afegim l’opció –rm
perquè s’elimini quan el parem.
Això si, hem de permetre que es pugui accedir al servidor apache desde fora del contenidor fent servir un port forward a la xarxa virtual privada on està el contenidor:
$ docker run --rm -d --name apache -p 8000:80 httpd
ad5087500ed6abe77f6226bac5ef6693c8f71b8e2aaf3b0ce8e92a498417109a
$ curl localhost:8000
<html><body><h1>It works!</h1></body></html
Pots veure que tens accés al servidor web des del host en el port 8000.
El primer que pots observar, és que la pàgina per defecte d’apache és molt senzilla: It works!.
Si vols canviar el contingut de la pàgina pots entrar dins el contenidor i modificar la pàgina:
$ docker exec -it apache bash
... $ echo "Hello World!" > htdocs/index.html
... $ exit
exit
$ curl localhost:8000
Hello World!
Però, quan el contenidor s’aturi i s’elimini es perdrà tot … Que hem de fer?
Una de les maneres de fer servir un contenidor apache és deixar que el contenidor tingui accés a la carpeta on tenim el nostre lloc web.
Atura el servidor apache:
$ docker stop apache
apache
$ curl localhost:8000
curl: (7) Failed to connect to localhost port 8000 after 0 ms: Connection refused
Crea el teu lloc web al host: site
$ mkdir site
$ echo "Estic fora" > site/index.html
Aquesta vegada arrenca apache montant el directori /usr/local/apache2/htdocs/
del contenidor a la teva carpeta site
en mode només lectura:
$ docker run --rm -d --name apache -p 8000:80 --mount type=bind,src="$(pwd)"/site,dst=/usr/local/apache2/htdocs,readonly=true httpd
2cde52ea8c9087bd9fbee769696e447a022044b0cb3bd55b462a8605636a2e43
$ curl localhost:8000
Estic fora
Si modifiques el contingut del fitxer site/index.html
del host el servidor respondrà amb el nou fitxer:
$ echo "Encara estic fora :-)" > site/index.html
$ curl localhost:8000
Encara estic fora :-)
Com és possible? Si entres al contenidor veurás que encara que el contenidor superposi els fitxers de la imatge httpd, la nostra comanda superposa la carpeta site a la del contenidor, i el resultat és que ja no té accés al fitxer index.html que està a la imatge, sinò al de la nostra carpeta.
$ docker exec -it apache bash
... $ cat htdocs/index.html
Encara estic fora :-)
... $ exit
exit
I si proves de modificar el contingut del fitxer index.html
no pots, perquè has montat la carpeta site amb permisos de només readonly
.
$ docker exec -it apache bash
... $ echo "Estic dins" > htdocs/index.html
bash: htdocs/index.html: Read-only file system
Com pots veure ja no estas sotmès a la dictadura d’apache o nginx: Tens el teu site al lloc que tu vols.
Versió
Quan executes un contenidor a partir d’una imatge pots dir exactament quina versió vols executar.
Per tant, a més de poder dir exactament la versió d’apache que vull, puc tenir vàries versions a la vegada en ports diferents i servint el mateix lloc web!
A https://hub.docker.com/_/httpd pots trobar totes les versions.
Què tal la 2.2.34 del 2017?
$ docker run --rm -d --name apache-old -p 8001:80 --mount type=bind,src="$(pwd)"/site,dst=/usr/local/apache2/htdocs,readonly=true httpd:2.2.34
I perquè no fas servir nginx?
$ docker run --rm -d --name nginx -p 8002:80 --mount type=bind,src="$(pwd)"/site,dst=/usr/share/nginx/html,readonly=true nginx
Fixa’t que nginx fa servir un directori diferent a apache.
Tens 3 servidors web servint el mateix contingut a ports diferents.
$ curl localhost:8000
Encara estic fora :-)
$ curl localhost:8001
Encara estic fora :-)
$ curl localhost:8002
Encara estic fora :-)
Certificats
Si no saps com funcionen els certificats mira aquesta activitat: Certificats
Apache
Crea una carpeta server i dins de la carpeta baixa l’script apache.sh
: https://gitlab.com/xtec/smx-6/-/raw/main/certs/apache.sh.
$ curl https://gitlab.com/xtec/smx-6/-/raw/main/certs/apache.sh -o apache.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1570 100 1570 0 0 3363 0 --:--:-- --:--:-- --:--:-- 3369
Aquest script executa un servidor apache amb un certificat autofirmat en els ports 8080 (HTTP) i 8443 (HTTPS).
Atenció! L'script crea un contenidor amb el nom apache, i no pot haver cap contenidor amb aquest nom.
Executa l’script i mira el contingut que s’ha generat:
$ chmod u+x apache.sh
$ ./apache.sh
...
a423ee4e93007fa64220b1e638d368c13fdcd55ee000325a4b4c90d2d5ed7b13
$ ls
apache.sh certs html httpd-ssl.conf httpd.conf site
En aquest cas, a més del lloc web en la carpeta html, tenim uns arxius de configuració externs al contenidor que es munten en punts concrets per modificar el comportament del servidor.
La carpeta certs
té els certificats, la carpeta html
el contingut, i httpd-ssl.conf
i httpd.conf
són fitxers de configuració.
I el que és més important, tenim agrupats els únics fitxers que són necessaris per configurar un apache amb connexió HTTPS on nosaltres volem.
Pots verificar que el servidor funciona correctament, amb redirecció del port 8080 (HTTP) al ports 8443 (HTTPS):
$ curl -I localhost:8080
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>307 Temporary Redirect</title>
</head><body>
<h1>Temporary Redirect</h1>
<p>The document has moved <a href="https://localhost:8443/">here</a>.</p>
</body></html>
Si mires les capçaleres HTTP pots veure que el servidor torna un resposta 307 Temporary Redirect
:
$ curl -I localhost:8080
HTTP/1.1 307 Temporary Redirect
Date: Tue, 21 May 2024 21:04:38 GMT
Server: Apache/2.4.59 (Unix) OpenSSL/3.0.11
Location: https://localhost:8443/
Content-Type: text/html; charset=iso-8859-1
Executa curl
amb les opcions -L
(segueix el redirect) i -k
(no valida el certificat), i verifica que funciona:
$ curl -L -k localhost:8080
<p>Add content to html folder</p>
Atura (stop) el contenidor apache.
Obre l’script apache.sh
, i mira que hi fa per entendre que hauries de fer tu manualment.
Nginx
A continuació baixa aquest script que executa un servidor nginx amb un certificat autofirmat en els ports 8080 (HTTP) i 8443 (HTTPS): https://gitlab.com/xtec/smx-6/-/raw/main/certs/nginx.sh.
Executa l’script.
Si fas un ls
pots veure que l’única diferència que hi ha respecte apache, és que nginx necessita el seu propi fitxer de configuració nginx.conf
, la resta és el mateix.
$ ls
apache.sh certs html httpd-ssl.conf httpd.conf nginx.conf nginx.sh site
Pots verificar que funciona igual que apache:
$ curl -L -k localhost:8080
<p>Add content to html folder</p>
D’aquesta manera tens separat el que és particular del que és genèrica.
Pots passar de nginx a apache en un moment.
Obre l’script nginx.sh
i mira que fa per entendre que hauries de fer tu manualment.
Còpies
Pots realitzar còpies del host al contenidor i a la inversa amb l’ordre docker cp
:
Crea un servidor web caddy amb un volum amb la mateixa ordre:
$ docker run --rm -d --name caddy -p 80:80 -v caddy-data:/data caddy
19fdc78f78b9eeb8feee71ec9535676fc125fc89efcc744c546710c274d1882a
$ docker volume ls | grep caddy
local caddy-data
Verifica que el servidor funciona:
$ curl -s localhost | head -n 10
<!DOCTYPE html>
<html>
<head>
<title>Caddy works!</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:,">
<style>
* {
box-sizing: border-box;
Enlloc de muntar una carpeta o entra dins el servidor per modificar el fitxer index.html
podem crear un fitxer (si no el tinc creat) i copiar-lo al servidor:
$ echo "Hello Caddy!" > index.html
$ docker cp index.html caddy:/usr/share/caddy/index.html
Successfully copied 2.05kB to caddy:/usr/share/caddy/index.html
$ curl localhost
Hello Caddy!
També es pot copiar del contenidor al servidor, i fer còpies recursives.
Volums
Si vull copiar el contingut d’un volum, primer l’haig de montar en un contenidor.
Si el volum és local, pots accedir directament al directori amb sudo
, però els contenidors poden estar en altres dispositius mitjançant btrfs, ZFS, etc.
Per tant, has de muntar el volum en un contenidor.
Montem el volum wiki
en un contenidor alpine
:
$ docker run -d --rm --name wiki -v wiki-nature:/wiki alpine tail -f /dev/null
8b56c20ad2b6badc5b33e8aeab28397ec280fcc41f5817aa21cec51a619fe304
Ja pots copiar del contenidor:
$ docker cp wiki:/wiki/data/pages/wiki .
Successfully copied 29.7kB to /home/david/docker/.
$ ls wiki
dokuwiki.txt syntax.txt welcome.txt