Docker 2020 2021
Docker 2020 2021
Docker 2020 2021
Docker y DockerHub
Manejo de contenedores
Configurando el repositorio
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
Añadimos el repositorio
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
Instalamos
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
>_
Los comandos tienen a su vez opciones. Los nombres de las opciones van precedidas de los caracteres -- y entre la opción
y su valor se puede escribir un espacio o el carácter =.
sudo docker COMANDO --OPCIÓN=VALOR
sudo docker COMANDO --OPCIÓN VALOR
Para borrar una imagen (se deben borrar previamente los contenedores basados en esa imagen):
sudo docker image rm IMAGEN
sudo docker rmi IMAGEN
Si queremos ver la secuencia de arranque del contenedor, podemos poner en marcha el contenedor en modo
pseudo-tty, que trabaja en primer plano, pero del que podemos salir con Ctrl+C.
sudo docker run -t --name=CONTENEDOR REPOSITORIO
Para ver información detallada de una red (entre ella, los contenedores incluidos y sus IP privadas):
sudo docker network inspect RED
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
Crea un contenedor con la aplicación de ejemplo hello-world. La imagen de este contenedor se llama hello-
world:
sudo docker run hello-world
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 9 months ago 13.3kB
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
10a829dbd778 hello-world "/hello" About a minute ago Exited (0) About a minute ago strange_cerf
Cada contenedor tiene un identificador (ID) y un nombre distinto. Docker "bautiza" los contenedores con un
nombre peculiar, compuesto de un adjetivo y un apellido.
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f3059d7adaf9 hello-world "/hello" 13 seconds ago Exited (0) 12 seconds ago
affectionate_varahamihira
10a829dbd778 hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago strange_cerf
... se mostrará el contenedor con el nombre que hemos indicado y podremos trabajar con él
Si intentamos crear un segundo contenedor con un nombre ya utilizado ...
sudo docker run -d --name=hola-1 hello-world
Salimos de la imagen
exit
¿Qué ha pasado?
El contenedor tendrá asociado el nombre miapache que podrá ser utilizando a partir de su creación para realizar cualquier operación
sobre él
Se ha mapeado el puerto 80 del contenedor sobre el puerto 5555 de la máquina real. De esa manera, conectándonos al puerto 5555 de
nuestro equipo podremos ver que ocurre en el 80 del contenedor, justamente donde estará escuchando el Apache del mismo
Hemos mapeado la ruta actual de nuestro equipo con la ruta /usr/local/apache2/htdocs del contenedor, que es donde Apache va a ir a
buscar la web que aloje. Suponemos que el directorio actual de mi máquina tengo el sitio web que quiero testear
Indicamos que queremos crear y lanzar un contenedor utilizando la imagen del Hub de Docker, concretamente con la versión latest
También hemos indicado con la opción -d que queremos que la máquina se lance en segundo plano
con it dejamos que tengamos acceso a la consola por defecto.
Puedes entrar a la maquina con
docker exec -it miapache /bin/bash
y ver en la carpeta htdocks que esta alli todo.
Prueba a crear una página nueva a ver que pasa
FROM: indica la imagen base sobre la que se construirá la aplicación dentro del contenedor.
FROM <imagen>
FROM <imagen>:<tag>
Por ejemplo la imagen puede ser un sistema operativo como Ubuntu, Centos, etc. O una imagen ya existente en la cual con base a esta queramos
construir nuestra propia imagen.
RUN: nos permite ejecutar comandos en el contenedor, por ejemplo, instalar paquetes o librerías (apt-get, yum install, etc.). Además, tenemos dos formas
de colocarlo:
Opción 1 -> RUN <comando>
Esta instrucción ejecuta comandos Shell en el contenedor.
Opción 2 -> [“ejecutable”,” parametro1”,” parametro2”]
Esta otra instrucción bastante útil, que permite ejecutar comandos en imágenes que no tengan /bin/sh.
Generalmente se utiliza para ejecutar comandos o preparar distintos elementos que queremos que estén a la hora de tener nuestra imagen lista: librerías,
dependencias, programas.
ENV: establece variables de entorno para nuestro contenedor, en este caso la variable de entorno es DEBIAN_FRONTEND noninteractive, el cual nos permite
instalar un montón de archivos .deb sin tener que interactuar con ellos.
ENV <key><valor>
José Luis González Sánchez. https://github.com/joseluisgs - https://twitter.com/joseluisgonsan 36
Dockerfile
ADD: esta instrucción copia archivos a un destino especifico dentro del contenedor, normalmente nos sirve para dejar ubicados ciertos
archivos que queremos mover entre directorios, podemos usar como origen una URL o un .tar y descomprimirlo
ADD <fuente> <destino>
ADD ./script.sh /var/tmp/script.sh
COPY: esta instrucción copia archivos o directorios a un destino especifico dentro del contenedor.
COPY <fuente> <destino>
COPY ./script.sh /var/tmp/script.sh
MAINTAINER: Este nos permite indicar el nombre del autor del dockerfile.
MAINTANER <nombre> <” correo”>
MAINTAINER JL Gonzalez “[email protected]”
CMD: esta instrucción nos provee valores por defecto a nuestro contenedor, es decir, mediante esta podemos definir una serie de comandos
que solo se ejecutaran una vez que el contenedor se ha inicializado, pueden ser comandos Shell con parámetros establecidos.
CMD [“ejecutable”, “parámetro1”, “parámetro2”], este es el formato de ejecución.
CMD [“parámetro1”, “parámetro2”], parámetro por defecto para punto de entrada.
CMD comando parámetro1 parámetro2, modo shell
VOLUME: esta instrucción crea un volumen como punto de montaje dentro del contenedor y es visible desde el host anfitrión marcado con
otro nombre.
VOLUME /var/tmp
USER: determina el nombre de usuario a utilizar cuando se ejecuta un contenedor, y adicionalmente cuando se ejecutan comandos como
RUN, CMD, ENTRYPOINT o WORKDIR.
WORKDIR ruta/de/Proyecto
# Indica los puertos TCP/IP los cuales se pueden accede a los servicios del contenedor
EXPOSE 80
CMD [“nginx”]
Lanzamos la imagen
docker run -dit --name miapache-php -p 5555:80 miapache-php
Este es el ejem01
En este modelo de trabajo, cada ves que cambiemos el contenido de nuestra web deberemos regenerar la imágen local,
podemos ( o no ) eliminar los contenedores viejos y volver a crear/correr el contenedor.
Si queremos almacenar datos (una web, una base de datos, etc.) dentro de un contenedor necesitamos una manera de
almacenarlos sin perderlos.
Lo normal es usar volúmenes, pero habrá ocasiones en que es preferible montar directamente un directorio de nuestro
espacio de trabajo. Por ejemplo, para guardar los datos de una base de datos usaremos volúmenes, pero para guardar el
código de una aplicación o de una página web montaremos el directorio.
La razón para esto último es que tanto nuestro entorno de desarrollo como el contenedor tengan acceso a los archivos del
código fuente. Los volúmenes, al contrario que los directorios montados, no deben accederse desde la máquina anfitrión.
Los volúmenes son entidades independientes de los contenedores, pero para acceder al contenido del
volumen hay que hacerlo a través contenedor, más exactamente a través del directorio indicado al crear el
contenedor.
vol-apache
Comprueba que el volumen ya no existe:
docker volume ls
El principal cambio en docker run con respecto a la última vez es que no hemos usado -p (el parámetro para
publicar puertos) y hemos añadido el parámetro -d.
Lo primero que habremos notado es que el contenedor ya no se queda en primer plano. El parámetro -d
indica que debe ejecutarse como un proceso en segundo plano. Así no podremos pararlo por accidente con
Control+C.
Dependiendo de la red que estemos usando (la red puente por defecto o una red definida por el usuario) el
mecanismo de enlace entre contenedores será distinto.
Dependiendo de la red que estemos usando (la red puente por defecto o una red definida por el usuario) el
mecanismo de enlace entre contenedores será distinto.
Para realizar la asociación entre contenedores hemos utilizado el parámetro --link, donde se indica el
nombre del contenedor enlazado y un alias por el que nos podemos referir a él.
Otro mecanismo que se realiza para permitir la comunicación entre contenedores asociados es
modificar el fichero /etc/hosts para que tengamos resolución estática entre ellos.
En este caso no se comparten las variables de entorno, y la resolución de nombres de los contenedores se
hace mediante un servidor dns que se ha creado en el gateway de la red que hemos creado:
Creamos un contenedor desde la imagen mariadb con el nombre servidor_mysql, conectada a la red creada:
docker run -d --name servidor_mariadb --network red_wp -e MYSQL_DATABASE=bd_wp -e
MYSQL_USER=user_wp -e MYSQL_PASSWORD=asdasd -e MYSQL_ROOT_PASSWORD=asdasd mariadb
A continuación vamos a crear un nuevo contenedor, con el nombre servidor_wp, con el servidor web a partir
de la imagen wordpress, conectada a la misma red y con las variables de entorno necesarias:
docker run -d --name servidor_wp --network red_wp -e WORDPRESS_DB_HOST=servidor_mariadb -e
WORDPRESS_DB_USER=user_wp -e WORDPRESS_DB_PASSWORD=asdasd -e WORDPRESS_DB_NAME=bd_wp -p 80:80
wordpress
Accede a la ip del servidor docker y comprueba la instalación de wordpress. Como ves se está haciendo la
resolución de nombres usando DNS (no hace falta la opción de link).
Compose tiene comandos para manejar todo el ciclo de vida de nuestra aplicación:
Iniciar, detener y rehacer servicios.
Ver el estado de los servicios.
Visualizar los logs.
Ejecutar un comando en un servicio.
services:
db:
image: mariadb:10.5
container_name: mariadb
volumes:
- data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=wordpress
- MYSQL_USER=manager
- MYSQL_PASSWORD=secret
web:
image: wordpress:4.9.8
container_name: wordpress
depends_on:
- db
volumes:
- ./wordpress:/var/www/html
environment:
- WORDPRESS_DB_USER=manager
- WORDPRESS_DB_PASSWORD=secret
- WORDPRESS_DB_HOST=db
ports:
- 8080:80
volumes:
data:
docker-compose ps solo muestra información de los servicios que se define en docker-compose.yaml, mientras que docker muestra todos.
El nombre de cada contenedor se define con container_name: mariadb, si no se pone será el nombre del directorio y de la imagen
Detener servicios
docker-compose stop
Borrar servicios
docker-compose down
Esto borra los contenedores, pero no los volúmenes. Así que si hemos creado bien la aplicación nuestros datos están a salvo.
Si queremos borrar también los volúmenes:
docker-compose down -v
Nos saltamos la sección de redes (networks) y vamos a la sección de servicios, que son los contenedores que
precisa o componen nuestra aplicación
Después de abrir la parte de servicios, el primer nivel indica el nombre del servicio db, que genera el contenedor wordpress_db. Lo que vemos a continuación es lo mismo que hicimos en la
sección anterior pero de forma parametrizada. Si recordamos, para levantar nuestra base de datos, indicamos la imagen (línea 3), luego montamos los volúmenes (línea 4), y después indicamos
las variables de entorno que configuraban el contenedor (línea 6).
web:
image: wordpress:4.9.8
depends_on:
- db
volumes:
- ./target:/var/www/html
environment:
- WORDPRESS_DB_USER=manager
- WORDPRESS_DB_PASSWORD=secret
- WORDPRESS_DB_HOST=db
ports:
- 8080:80
--link wordpress-db:mysql \
--mount type=bind,source="$(pwd)"/wordpress,target=/var/www/html \
-e WORDPRESS_DB_USER=manager \
-e WORDPRESS_DB_PASSWORD=secret \
-p 8080:80 \
wordpress:4.9.8
volumes:
mariadb-volume:
Usamos links para segurarnos que se enlanza en la misma red con MariaDB y comparte las variables de
entorno (usuarios y contraseña)
volumes:
mariadb-volume:
networks:
lamp-network:
driver: bridge
# Exponemos el puerto 80
EXPOSE 80
# Quien lo ha realizado
MAINTAINER JL Gonzalez “[email protected]”
Creamos nuestra imagen ya sea ejecutando docker run y echando andar el contenedor (crea la imagen
antes) o si no usamos:
docker build . o docker build --tag=apache-php .
Para ello vamos a ver cómo Docker/Docker Hub permite el despliegue contínuo de nuestro proyecto basado
en repositorios de GitHub
Creamos el repositorio y lo enlazamos a nuestro repositorio GitHub. Podemos elegir las reglas del despliegue,
como que sea cuando haya cambios en una rama, o creemos una etiqueta
Lo unico que necesitamos es que en nuestro repositorio en el directorio raiz de la rama en cuestión
tengamos nuestro Dockerfile
En futuros tutoriales veremos como hacerlo automatizado sin salir de GitHub, con GitHub Actions
Eliminar todas las imágenes sin etiquetar que suelen crearse cuando falla una creación
alias drmi='docker rmi $(docker images -q -f "dangling=true")'
SIEMPRE INTENTAR:
Minimizar el tamaño de la imagen, el tiempo de compilación y el número de capas.
Maximizar el uso de la caché de compilación y la legibilidad del Dockerfile.
Queremos que trabajar con nuestro contenedor sea lo más agradable posible.
2. El contenedor debe hacer una sola cosa. Técnicamente, PUEDES iniciar múltiples procesos dentro de un contenedor Docker. PUEDES
poner aplicaciones de base de datos, frontend y backend, ssh, y supervisor en una imagen de docker. Pero muchas cosas no te irán bien:
Los tiempos de compilación serán largos (un cambio en, por ejemplo, el frontend te obligará a volver a compilar todo el backend)
Las imágenes serán muy grandes
Tendrás un registro duro de datos desde muchas aplicaciones (no un simple stdout)
Escalado horizontal innecesario
Problemas con los procesos "zombies" - Tendrás que acordarte de cuál es el proceso init adecuado
Mi consejo es que prepares una imagen Docker separada para cada componente y que utilices Docker Compose para iniciar fácilmente varios
contenedores al mismo tiempo.
FROM ubuntu
RUN apt-get update && apt-get install -y nodejs
ADD . /app
RUN cd /app && npm install
CMD npm start
"start" )
# we can modify files here, using ENV variables passed in
# "docker create" command. It can't be done during build process.
echo "db: $DATABASE_ADDRESS" >> /app/config.yml
export NODE_ENV=production
exec npm start
;;
* )
# Run custom command. Thanks to this line we can still use
# "docker run our_image /bin/bash" and it will work
exec $CMD ${@:2}
;;
esac
https://www.campusmvp.es/recursos/post/mejores-practicas-para-crear-dockerfiles-
excelentes.aspx
ENV PROJECT_DIR=/app
WORKDIR $PROJECT_DIR
COPY . $PROJECT_DIR
ENV MEDIA_DIR=/media \
NODE_ENV=production \
APP_PORT=3000
VOLUME $MEDIA_DIR
EXPOSE $APP_PORT
ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]
Estas variables estarán disponibles en el contenedor. Si necesitas variables de compilación solamente, usa build args en su lugar.
• Host: El último paso para terminar la configuración es añadir una entrada en el fichero hosts. En los sistemas Linux y OsX lo podemos encontrar
en la ruta /etc/hosts y en sistemas Windows en c:\Windows\System32\drivers\etc\hosts. Hay que añadir al final la siguiente línea:
• 127.0.0.1 myapp.dev
• Al guardar el fichero con el nuevo cambio se está indicando al sistema que cada vez que se solicite la url myapp.dev se resuelva al host local.
podemos crear tantas entradas como proyectos tengamos.
Probamos nuestra imagen, solo construyendo, no hace falta hacer el run. A parte he puesto mi nombre de
usuario de DockerHub por si quisiera publicarla, como ya hemos visto anteriormente.
$ docker build -t joseluisgs/php-fpm .
Para este ejemplo solo necesitamos poner esta configuración, pero si necesitamos ver un fichero php.ini
completo para modificar algún parámetro, podemos tomar como referencia los fichero del repositorio de php
para desarrollo o producción
Con el fichero php.ini actualizado, arranquemos ahora el contenedor con el siguiente comando:
docker run -d --name php7 \
--link mariadb \
-v "/path_proyecto/config/php":/usr/local/etc/php \
-v "/path_proyecto/code/myapp":/var/www/html/myapp \
joseluisgs/php-fpm
server_name myapp.dev;
error_log /var/log/nginx/myapp_error.log;
access_log /var/log/nginx/myapp_access.log;
root /var/www/html/myapp;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php7:9000;
fastcgi_index index.php;
include fastcgi_params;
También se indica donde está la ruta del código fuente de la aplicación (/var/www/html/myapp) de nuevo con la ruta
dentro del sistema de ficheros del contenedor y finalmente una de las características más importantes, en la línea 14 se
indica donde se está ejecutando el proceso php-fpm.
Sabemos que este proceso se está ejecutando en el contenedor que hemos arrancado antes, pero una característica que
tienen los contenedores Docker es que su ip puede variar cada vez que arranque el contenedor.
Por ello, en vez de poner la ip, se indica el nombre que le hemos dado al contenedor al arrancar, si recordamos era php7.4.
Con esto e indicando a la hora de arrancar este nuevo contenedor que cree un enlace con el contenedor de php
automáticamente se resolverá ese nombre a la ip con la que haya arrancado el contenedor php.
version: '3.7'
services:
# PHP
php7:
container_name: php7
build: ./php
volumes:
- /path_proyecto/config/php:/usr/local/etc/php
- /path_proyecto/code/myapp:/var/www/html/myapp
depends_on:
- mariadb
networks:
- lemp-network
version: '3.7'
services:
# PHP
php7:
container_name: php7
build: ./php
volumes:
- /path_proyecto/config/php:/usr/local/etc/php
- /path_proyecto/code/myapp:/var/www/html/myapp
depends_on:
- mariadb
networks:
- lemp-network
nginx:
container_name: nginx
image: nginx
ports:
- 8080:80
volumes:
- /path_proyecto/config/nginx:/etc/nginx/conf.d
- /path_proyecto/code/myapp:/var/www/html/myapp
- /path_proyectp/logs:/var/log/nginx
depends_on:
- php7
networks:
- lemp-network
mariadb:
container_name: mariadb
image: mariadb:latest
volumes:
- /path_proyecto/mariadb/data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=docker_sample
ports:
- 3306:3306
networks:
- lemp-network
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: phpmyadmin
links:
- 'mariadb:db'
depends_on:
- mariadb
ports:
- 8081:80
networks:
- lemp-network
networks:
lemp-network:
driver: bridge
Con los contenedores arrancados ya podemos volver a acceder a la url de nuestra aplicación y ver el
resultado. Además del comando up para manejar docker compose tenemos los siguientes comandos:
Para arrancar las contenedores una vez que están creados ejecutaremos:
docker-compose start
Recordemos que podemos desviar o por nombre de dominio o puerto o ambos. Lo importante es añadir estas líneas por cada servicio a usar
server {
listen 80;
server_name site2.example.com;
location / {
proxy_redirect off;
El primer caso prático es desviando las peticiones por puestos, por ejemplo 8080 y 8081 para cada uno de los
servicios.
El resultado se puede ver en el fichero docker-compose del ejemplo 09 si metemos la dirección
http://localhost:8081 y http://localhost:8082. Es importante analizar el fichero de nginx.conf de la carpeta
reverse.
services:
reverseproxy:
build: ./reverse
container_name: reverseproxy
ports:
- 8080:8080
- 8081:8081
networks:
- mi-red
restart: always
# image: nginx:alpine
build: ./site1
container_name: nginx
# volumes:
# - ./site1/src/.:/usr/share/nginx/html/
depends_on:
- reverseproxy
networks:
- mi-red
restart: always
# image: httpd:alpine
build: ./site2
container_name: apache
# volumes:
# - './site2/src/.:/usr/local/apache2/htdocs/'
depends_on:
- reverseproxy
networks:
- mi-red
restart: always
services:
reverseproxy:
image: jwilder/nginx-proxy
container_name: reverseproxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- mi-red
restart: always
privileged: true
# image: nginx:alpine
build: ./site1
container_name: nginx
environment:
VIRTUAL_HOST: site1.example.com
# volumes:
# - ./site1/src/.:/usr/share/nginx/html/
depends_on:
- reverseproxy
networks:
- mi-red
restart: always
# image: httpd:alpine
build: ./site2
container_name: apache
environment:
VIRTUAL_HOST: site2.example.com
# volumes:
# - './site2/src/.:/usr/local/apache2/htdocs/'
depends_on:
- reverseproxy
networks:
- mi-red
restart: always