Skip to content

svex99/docker-class

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introducción a Docker

Autor: Enmanuel Verdesia Suárez

Docker brinda la capacidad de empaquetar y ejecutar una aplicación en un entorno aislado llamado contenedor. Los contenedores son livianos y contienen todo lo necesario para ejecutar la aplicación, por lo que no dependen de lo que está instalado en el host. Además, se pueden compartir fácilmente y asegurarse de que todas las personas con las que se comparte obtengan una versión que funciona de la misma manera. Adicionalmente permite manejar la infraestructura de la misma forma que se manejan las aplicaciones, lo que disminuye el tiempo entre escribir el código y llevarlo a producción.

Instalación

El proceso de instalación es específico para la plataforma en que se vaya a usar. Para realizarlo es recomendable ir a la documentación oficial pues puede sufrir actualizaciones o modificaciones.

Siga los pasos de acuerdo a su sistema operativo e instale Docker desde aquí.

Objetos de Docker

Imágenes

Una imagen es una plantilla de solo lectura con las instrucciones necesarias para crear un contenedor de Docker. Frecuentemente una imagen se construye sobre otra imagen con información adicional.

Por ejemplo para una aplicación desarrollada en Python se puede tomar como imagen base una versión de Python apropiada y sobre esta crear una nueva imagen con el código y todas las dependencias instaladas para ponerla en producción

De esta forma se pueden usar imágenes propias o usar desarrolladas por terceros. Para construir una imagen es necesario crear un Dockerfile con los pasos necesarios para crear la imagen y ejecutarla. Cada instrucción en este archivo crea una capa en la imagen, de esta forma si se cambia una instrucción solo es necesario modificar las capas afectadas. Este proceso hace que las imágenes sean mucho más livianas que otros métodos de virtualización

A continuación se muestra un Dockerfile básico para crear la imagen de un programa en Python.

# define la imagen base
FROM python:3.9.5
# crea el directorio /app y se ubica en el
WORKDIR /app
# copia el contenido del programa
COPY . .
# instala las dependencias
RUN pip3 install -r requirements.txt    
# comando ejecutado por defecto cuando se crea un contenedor
# a partir de la imagen
CMD ["python3", "main.py"]

Cada una de estas instrucciones crea una capa en la imagen. Por ejemplo si otra imagen también tiene como imagen base FROM python:3.9.5 compartirá junto con la anterior el mismo espacio en disco para esa capa.

💡 Puede descargar las imágenes de Docker sin consumo de internet a través del proxy de la UCLV docker.uclv.cu. Por ejemplo en el Dockerfile anterior puede usar la imagen base: FROM docker.uclv.cu/python:3.9.5.

💡 Puede instalar las dependencias en Python sin consumo de internet a través del proxy de la UCI http://nexus.prod.uci.cu/repository/pypi-proxy/simple/. Por ejemplo en el Dockerfile anterior puede instalar las dependencias con el comando: pip install -r requirements.txt --index-url http://nexus.prod.uci.cu/repository/pypi-proxy/simple/ --trusted-host nexus.prod.uci.cu.

Puede obtener más información sobre como crear Dockerfiles en la documentación oficial.

Una vez creado el Dockerfile anterior basta correr el siguiente comando para crear su imagen:

docker image build -t my-first-image .

El parámetro -t <name> permite asignarle un nombre específico a la imagen. Este comando toma como receta el Dockerfile en el directorio en que se ejecuta, para especificar uno se usa el parámetro -f <file>.

Como probablemente usted no tenga la imagen base de Python 3.9.5 en su repositorio local este comando lo primero que va a hacer es descargarla. Esto puede demorar unos minutos de acuerdo con su conexión. Este proceso se realiza solo una vez, en un futuro siempre que use esa imagen como base será la misma que descargó.

💡 Para descargar una imagen puede usar docker pull <image:tag>. Por ejemplo: docker pull docker.uclv.cu/python:3.9.5.

El comando anterior ejecuta una a una las instrucciones del Dockerfile y una vez completado usted debe tener la nueva imagen creada y además de ella la imagen base que tuvo q descargar. Para mostrar sus imágenes pude usar el siguiente comando:

docker image ls

Obtendrá la lista de sus imágenes

REPOSITORY                     TAG       IMAGE ID       CREATED              SIZE
my-first-image                 latest    ae1d2c9f75d3   About a minute ago   892MB
docker.uclv.cu/python          3.9.5     9b0d330dfd02   About a minute ago   886MB

Contenedores

Los contenedores pueden ser vistos com instancias de las imágenes. A partir de una imagen se puede crear múltiples contenedores que pueden funcionar de forma independiente. Estos se pueden crear, iniciar, detener, mover o borrar usando la API de Docker o la consola de comandos. Además se puede conectar un contenedor a una o más redes, vincularle un almacenamiento o incluso crear una nueva imagen basada en su estado actual.

Un contenedor esta definido por su imagen así como cualquier opción de configuración proveída a la hora de crearlo o iniciarlo. Cuando un contenedor es eliminado, cualquier cambio a su estado no almacenado en un medio persistente desaparece.

Para iniciar un contenedor puede usar el comando docker run, por ejemplo:

docker container run my-first-image

Esto creará un contenedor basado en la imagen y ejecutar su punto de entrada, en este caso main.py. El ejemplo encontrado en basic-python/ da como resultado:

❯ docker run my-first-image             
Hello world. Do you like Go?

Al comando run se le pueden pasar diferentes parámetros que se pueden consultar con docker container run --help como son:

  • -d: desvincula el contenedor de la consola actual.
  • -e: añade variables de entorno al contenedor.
  • --expose: expone un puerto del contenedor al host, útil para acceder al contenedor desde una aplicación externa.
  • -v: vincula un volumen al contenedor, útil para persistir información.
  • --name: asigna un nombre al contenedor creado, se usa para referenciarlo de una forma más fácil, por defecto se le asigna al azar un adjetivo junto al nombre de un científico famoso.

De igual forma a con las imágenes se pueden mostrar los contenedores que están en ejecución o detenidos con el siguiente comando:

❯ docker container ls -a
CONTAINER ID   IMAGE            COMMAND             CREATED          STATUS                      PORTS   NAMES
2b8af4003e6d   my-first-image   "python3 main.py"   13 minutes ago   Exited (0) 13 minutes ago           quizzical_proskuriakova

Se puede crear otro contenedor a partir de la misma imagen y este funciona independiente al anterior:

❯ docker container run my-first-image        
Hello world. Do you like Typescript?

❯ docker container ls -a             
CONTAINER ID   IMAGE            COMMAND             CREATED          STATUS                      PORTS   NAMES
3479cfd234fd   my-first-image   "python3 main.py"   12 seconds ago   Exited (0) 12 seconds ago           dreamy_robinson
2b8af4003e6d   my-first-image   "python3 main.py"   33 minutes ago   Exited (0) 33 minutes ago           quizzical_proskuriakova

Estos contenedores anteriores esta detenidos y se pueden reiniciar, lo que causa que se invoque nuevamente el punto de entrada:

❯ docker container restart dreamy_robinson quizzical_proskuriakova 
dreamy_robinson
quizzical_proskuriakova

❯ docker container logs dreamy_robinson                           
Hello world. Do you like Typescript?
Hello world. Do you like C#?

❯ docker container logs quizzical_proskuriakova 
Hello world. Do you like Go?
Hello world. Do you like Haskell?

💡 Para consultar los logs de un contenedor puede usar el comando docker container logs <container-name-or-id>.

Como se puede observar los contenedores volvieron a ejecutar su punto de entrada a partir del estado que tenían anteriormente.

Para destruir los contenedores puede usar el comando docker container rm <container-name-or-id>:

❯ docker container rm dreamy_robinson quizzical_proskuriakova     
dreamy_robinson
quizzical_proskuriakova

❯ docker container ls -a                                     
CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

Adicionalmente se puede usar el parámetro -f para forzar y destruir un contenedor que esté en ejecución.

Volúmenes

Los volúmenes son la mejor forma de persistir datos en Docker. Otras opciones son bind mounts y tmpfs mounts.

Los volúmenes permiten compartir espacio del almacenamiento entre el host y contenedores. Ofrecen una serie de ventajas, entre ellas:

  • Fáciles de copiar y migrar.
  • Se pueden administrar usando la API de Docker o la consola de comandos.
  • Funcionan tanto en contenedores de Windows como de Linux.
  • Pueden ser compartidos de forma sencilla entre múltiples contenedores.
  • Pueden obtenerse diferentes funcionalidades a través de drivers.

Para crear un volumen se puede usar el comando docker volume create <vol-name>.

❯ docker volume create my-vol  
my-vol

Puedes listar todos los volúmenes con docker volume ls e inspeccionar un volumen con docker volume inspect <vol-name>.

❯ docker volume ls           
DRIVER    VOLUME NAME
local     my-vol

❯ docker volume inspect my-vol                                                          
[
    {
        "CreatedAt": "2022-05-29T15:17:36-04:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

Al inspeccionar un volumen podemos ver la información relacionada a este. En el campo Mountpoint se puede ver la dirección del host donde se va a persistir la información. Cuando un volumen se mapea a una dirección interna del contenedor los cambios son reflejados de forma bidireccional entre un y otro.

Para usar este volumen en un contenedor se puede usar el parámetro -v al crear el contenedor:

docker container run -v my-vol:/app --name my-app my-first-image

Ahora podemos inspeccionar el contenedor y verificar que está usando efectivamente el volumen designado, para ello se usa el comando docker container inspect <container-name-or-id>.

❯ docker container inspect my-app
# Omitted output...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "my-vol",
                "Source": "/var/lib/docker/volumes/my-vol/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],
# Omitted output...

Se puede apreciar que en la seccion de Mounts el volumen usado es el definido previamente y que Source coincide con el Mountpoint del volumen. El directorio interno del contenedor que apunta al volumen está indicado por Destination, en este caso /app.

De hecho podemos comprobar que cuando se creó el contenedor los ficheros del contenedor ubicados en /app fuero copiados al volumen:

❯ sudo bash
root@verdesia-laptop:/home/verdesia# cd /var/lib/docker/volumes/my-vol/_data
root@verdesia-laptop:/var/lib/docker/volumes/my-vol/_data# ls
Dockerfile  main.py  requirements.txt

Sí se añade una archivo a este directorio, por ejemplo ejecutando touch file.txt este estará disponible dentro del contenedor.

Puede obtener más información sobre como manejar volúmenes en la documentación oficial.

Otros objetos

Además existen otro objetos como redes, plugins, etc. los cuales ofrecen más funcionalidades pero que están fuera del alcance de esta introducción.

Tarea Propuesta

En el directorio fastapi-homework/ se encuentra una aplicación simple escrita con FastAPI. El Dockerfile se encuentra vació.

Usted debe:

  • Escribir el Dockerfile.
    • Use la versión 3.9.5 de Python.
    • Use como comando de inicio de la imagen uvicorn main:app.
  • Crear la imagen a partir del Dockerfile.
  • Crear dos contenedores a partir de la imagen anterior.
    • Cada contenedor debe exponer un puerto distinto al host para recibir conexiones.
    • Cada contenedor debe recibir una variable de entorno MSG la cual es un mensaje que va a devolver la API cuando se haga una petición GET a /.
  • Añadir persistencia a los logs de la API.
    • Crear un volumen llamado api-logs
    • Modificar el código de tal forma que cada vez que se haga una petición al endpoint dentro de uno de los contenedores, este lo registre de forma persistente en un fichero que almacene sus logs en el volumen creado.
  • Conectarse con su navegador a http://localhost:<puerto>/ de cada contenedor.
    • Comprobar que el mensaje recibido es el esperado.
    • Comprobar que ambos contenedores persisten sus logs en el volumen.

Referencias

About

Introducción básica a Docker

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published