Ya hemos hablado, en anteriores capítulos, de que podemos tener centenares de contenedores funcionando a la vez. Si, por ejemplo, son servidores web, ocurriría que cada uno de ellos tendría corriendo un servicio web, como Apache o Nginx. Sabemos que los puertos por defecto para estos servicios son el 80 o bien el 443 si usa una conexión web cifrada y utiliza el protocolo https. ¿Cómo hacemos para llegar a todos estos servidores web si solo tenemos su dirección privada?
La solución es la exposición de puertos, que consiste en reservar un puerto en el servidor Docker con el fin de redirigir las peticiones a un puerto específico.
Por ejemplo, podemos indicar que el puerto 80 del contenedor sea redirigido al puerto 23250 del servidor anfitrión.
Para realizar dicha tarea utilizaremos las opciones –p o –P, más los puertos indicados.
- -P o –publish-all, selecciona un puerto libre aleatorio del servidor anfitrión donde se van a escuchar las peticiones.
- -p o –publish-value, se debe especificar un puerto de manera manual donde deseamos realizar la escucha. Si ese puerto está en uso el contenedor fallará.
En el caso de la opción –P, Docker revisará los puertos que se han configurado en la imagen para ser expuestos y, por cada uno de ellos, generará otro aleatorio.
Veamos un ejemplo:
[root@centos7 /]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@centos7 /]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE fanfoni/teampass latest 59c69279adb4 6 days ago 383MB httpd latest 7239615c0645 6 days ago 177MB teampass/teampass latest 30e63d67f3bf 6 days ago 280MB nickistre/centos-lamp latest 3d8b86c3bd87 2 weeks ago 538MB hello-world latest f2a91732366c 3 weeks ago 1.85kB debian latest 6d83de432e98 6 weeks ago 100MB driket54/glpi latest 9a17476d3cbe 15 months ago 267MB [root@centos7 /]# docker run -dti --name web1 -P 3d8b86c3bd87 07a38e60bcc2e6b786f30a6a9ee0309443b5f32a01a9d5274350e2837cb4e37e [root@centos7 /]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 07a38e60bcc2 3d8b86c3bd87 "supervisord -n" 4 seconds ago Up 3 seconds 0.0.0.0:32777->22/tcp, 0.0.0.0:32776->80/tcp, 0.0.0.0:32775->443/tcp web1 [root@centos7 /]#
Al principio no teníamos ningún contenedor funcionando. Hemos listado las imágenes disponibles y escogido “nickistre/centos-lamp”, que es una imagen de un servidor web LAMP. Generamos el contenedor, utilizando la opción “-P”, que redirecciona los puertos indicados en el contenedor a puertos aleatorios. En este caso, el contenedor tiene el puerto 80 redirigido al 32776 en el host anfitrión.
Un imagen de ejemplo del funcionamiento de la exposición de puertos:
Vamos a crear unos cuantos contenedores más pero, esta vez, indicamos el puerto que queremos utilizar de manera manual de la siguiente manera:
[root@centos7 /]# docker run -dti --name web2 -p 22140:80 -p 22141:22 -p 22143:443 3d8b86c3bd87 64e19c4957c1353190b2d2fa43e7202012913dae35e2fa43787b356dad0c42e2
Podemos ver ambos contenedores funcionando:
[root@centos7 /]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 64e19c4957c1 3d8b86c3bd87 "supervisord -n" 4 seconds ago Up 2 seconds 0.0.0.0:22141->22/tcp, 0.0.0.0:22140->80/tcp, 0.0.0.0:22143->443/tcp web2 07a38e60bcc2 3d8b86c3bd87 "supervisord -n" 5 minutes ago Up 5 minutes 0.0.0.0:32777->22/tcp, 0.0.0.0:32776->80/tcp, 0.0.0.0:32775->443/tcp web1
A nivel de sistema operativo podemos ver que cada uno de estos contenedores tienen su propio número de proceso (pid):
[root@centos7 /]# ps -ef | grep -i docker root 15985 1 0 12:21 ? 00:00:03 /usr/bin/dockerd root 15990 15985 0 12:21 ? 00:00:01 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc root 26119 15985 0 12:54 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 32775 -container-ip 172.17.0.2 -container-port 443 root 26129 15985 0 12:54 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 32776 -container-ip 172.17.0.2 -container-port 80 root 26139 15985 0 12:54 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 32777 -container-ip 172.17.0.2 -container-port 22 root 26143 15990 0 12:54 ? 00:00:00 docker-containerd-shim 07a38e60bcc2e6b786f30a6a9ee0309443b5f32a01a9d5274350e2837cb4e37e /var/run/docker/libcontainerd/07a38e60bcc2e6b786f30a6a9ee0309443b5f32a01a9d5274350e2837cb4e37e docker-runc root 26313 15985 0 12:59 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 22143 -container-ip 172.17.0.3 -container-port 443 root 26323 15985 0 12:59 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 22140 -container-ip 172.17.0.3 -container-port 80 root 26333 15985 0 12:59 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 22141 -container-ip 172.17.0.3 -container-port 22 root 26337 15990 0 12:59 ? 00:00:00 docker-containerd-shim 64e19c4957c1353190b2d2fa43e7202012913dae35e2fa43787b356dad0c42e2 /var/run/docker/libcontainerd/64e19c4957c1353190b2d2fa43e7202012913dae35e2fa43787b356dad0c42e2 docker-runc root 26495 25325 0 13:02 pts/0 00:00:00 grep --color=auto -i docker
Se observa también cómo el servicio “docker-proxy” se encarga de redirigir los puertos entre la IP privada del contenedor a la IP pública del servidor, con su correspondiente puerto.
Si utilizamos la herramienta “netstat” vemos los puertos de Docker que están escuchando, en este momento, en el servidor anfitrión:
[root@centos7 /]# netstat -putnoea | grep -i docker tcp6 0 0 :::22140 :::* LISTEN 0 83848 26323/docker-proxy off (0.00/0/0) tcp6 0 0 :::22141 :::* LISTEN 0 83887 26333/docker-proxy off (0.00/0/0) tcp6 0 0 :::22143 :::* LISTEN 0 83809 26313/docker-proxy off (0.00/0/0) tcp6 0 0 :::32775 :::* LISTEN 0 82304 26119/docker-proxy off (0.00/0/0) tcp6 0 0 :::32776 :::* LISTEN 0 82343 26129/docker-proxy off (0.00/0/0) tcp6 0 0 :::32777 :::* LISTEN 0 82382 26139/docker-proxy off (0.00/0/0)
Un comando muy útil, para saber que puertos utilizará un contenedor tras generarlo desde una imagen, es : “docker inspect”
[root@centos7 /]# docker inspect 30e63d67f3bf | grep -i tcp "443/tcp": {}, "80/tcp": {}, "9000/tcp": {}
No hace falta que diga que cada uno de los puertos que expongamos de los contenedores deben estar accesibles en el cortafuegos. Para ellos, si utilizamos CentOS 7, utilizando el servicio “firewalld”, sería:
[root@centos7 /]# firewall-cmd --permanent --add-port=22140/tcp success [root@centos7 /]# firewall-cmd --reload success [root@centos7 /]#
Lo dejamos por hoy. Sé que este apartado es muy técnico, pero es esencial conocerlo. En el siguiente capítulo hablaremos de cómo realizar las copias de seguridad de los contenedores. Cualquier duda u observación la podéis dejar en los comentarios.
La imagen de portada ha sido extraída de Flickr y tiene derechos de Public Domain
Hola, muchas gracias por estos artículos sobre Docker.
Si pudieras hablar sobre la persistencia de datos y compartirlos entre varios contenedores, he leído la documentación pero aún no consigo que funcione como espero.
De cualquier forma muchas gracias por su labor.
Qué tal?
Hay alguna manera de abrir puertos una vez creado y corriendo el contenedor?