Contenerización de aplicaciones

Los contenedores son una forma de ejecutar nuestras aplicaciones. Construir contenedores seguros, rápidos y de tamaño pequeño es esencial en nuestras arquitecturas. En este artículo veremos cómo hacerlo.

Un Containerfile o Dockerfile, no es más que un fichero de testo con las etiquetas adecuadas para indicar cómo tiene que ser construido y ejecutado nuestro contenedor. Del correcto uso de dichas etiquetas o instrucciones, dependerá la correcta ejecución y optimización de nuestras aplicaciones.

En este artículo mostraré las principales opciones a la hora de construir contenedores. Además, utilizaremos una aplicación de ejemplo, programada en Golang, la cuál compilaremos y ejecutaremos sobre un contenedor.

Comandos principales

A continuación muestro una tabla con las principales instrucciones a indicar en un fichero de contenedor y una breve descripción indicando para qué se usa.

Cabe recordar que cada línea de nuestro fichero de contenedor, será una instrucción a ejectar, y consecuentemente, una capa del mismo.

Comando Descripción
FROM declara la imagen a partir de la cuál generar nuestro contenedor
LABEL metadata
MAINTAINER indica el autor
RUN ejecuta comandos shell
EXPOSE indica los puertos que expone el contenedor
ENV variables de entorno
ADD copia ficheros o carpetas desde el host al contenedor. Además, descomprime ficheros .tar
COPY copia ficheros desde el host al contenedor
USER especifíca el usuario o UID que ejecuta la aplicación del contenedor
ENTRYPOINT especifíca el comando por defecto de entrada en el contenedor. Por defecto es: /bin/sh -c
CMD indica los parámetros que recibirá por defecto el ENTRYPOINT

Application

Construir la imagen

La mejor forma de entender el proceso de creación de una imagen es construyendo una.

Normalmente, utilizo una aplicación, desarrollada en Go con GinGonic que expone un “Hello world”. Esta aplicación la utilizo solo a modo de test, sin entrar en el código. La aplicación se puede encontrar en el siguiente enlace: https://github.com/dbgjerez/golang-k8s-helm-helloworld

Nuestro objetivo final es ejecutar dicha aplicación en un contenedor, así que tenemos que construirla y construir el contenedor.

Es una buena práctica utilizar un contenedor para construir nuestras aplicaciones, de este modo aseguramos que el entorno de construcción siempre será el mismo y consecuentemente, nuestras construcciones serán idempotentes.

En este caso, utilizaré la misma imagen para construir, como para ejecutar la aplicación:

FROM golang:1.18-alpine

ENV APP_NAME=golang-k8s-helm-helloworld
ENV GIN_MODE=release
ENV PORT=8080
ENV WORKDIR=/go/src/app

WORKDIR $WORKDIR
COPY . .

RUN go get -d -v ./...
RUN go install -v ./...
RUN rm -rf $WORKDIR
EXPOSE $PORT

ENTRYPOINT $APP_NAME

Como se puede ver los pasos que sigo son los siguientes:

  • Copio el código fuente
  • Construyo la aplicación
  • Elimino el código fuente

De esta forma, con un mismo proceso tengo mi imagen disponible y sin los ficheros fuentes.

Para la construcción y ejecución, hago uso de podman, el cuál no es objetivo de este artículo. Puedes ver cómo utilizarlo y sus principales comandos en el siguiente enlace: Podman

podman build \
  -t b0rr3g0/golang-hello-world:v0.6 \
  .

Finalmente, una vez finalizado el proceso de construcción tenemos nuestra imagen.

Ejecutar la imagen

Al finalizar la construcción, nuestra imagen quedará en nuestro registry local. Para ejecutar la imagen tenemos varias opciones, en mi caso voy a exponer el puerto 8080, ejecutarla como un demonio y darle a mi contenedor el nombre api-test:

podman run \
  -d \
  -p 8080:8080 \
  --name api-test \
  localhost/b0rr3g0/golang-hello-world:v0.6

Para comprobar el correcto funcionamiento del contenedor, haremos una petición a la aplicación:

curl localhost:8080/api/v1/greetings
{"msg":"Hello world"}

Y… listo, ya tenemos un contenedor portable con nuestra aplicación ejecutándose.

References


Ver también