Construyendo Imágenes Personalizadas en Docker con Dockerfile

Advertencia
Este artículo se actualizó por última vez el 2021-09-23, el contenido puede estar desactualizado.

Antes de comenzar, quería contarles que hay una promoción en DigitalOcean donde te dan un crédito de USD 100.00 durante 60 días para que puedas probar los servicios que este Proveedor Cloud ofrece. Lo único que tienes que hacer es suscribirte a DigitalOcean con el siguiente botón:

DigitalOcean Referral Badge

O a través del siguiente enlace: https://bit.ly/digitalocean-itsm


En artículos anteriores aprendimos qué es Docker, para que nos sirve, algunos comandos básicos, redes, volumenes, etc. Sin embargo, las imágenes que descargamos y probamos están ya definidas por un proveedor o usuario que amablemente las compartió a través de DockerHub, por lo que nos viene la duda: ¿Cómo podríamos contruír nuestras propias imágenes y además de esto, colocar ahi nuestra aplicación para que otros usuarios la prueben?


Es un archivo de texto que tiene todos los comandos con la que podemos contruir una imagen personalizada de Docker en base a una imagen predefinida.

Según la documentación, las directivas más importantes que debe tener un Dockerfile son los siguientes:

FROM – Aquí definimos la imagen base que se va a utilizar para contruír nuestra imagen.

RUN – Son los comandos que vamos a ejecutar dentro de la imagen, por ejemplo, instalar paquetes, correr scripts, etc

CMD – Tiene una función similar al comando RUN, con la diferencia que los comados serán ejecutados una vez el contenedor sea lanzado.

ENTRYPOINT – es el punto de entrada de la aplicación en la imagen cuando el contenedor es creado.

ADD o COPY – Copia archivos desde el origen al destino (dentro de contenedor).

ENV – Con esta directiva podemos configurar variables de entorno.

Supongamos que tenemos una página web en HTML, y queremos que esa página esté disponible en nuestro contenedor, en la misma carpeta donde tenemos el proyecto o aplicacion, generamos el Dockerfile:

1
touch Dockerfile

Y la abrimos con nuestro editor de textos de confianza.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Con el numeral podemos colocar comentarios

# Aqui definimos nuestra imagen base, recomiendo usar imagenes con alpine
# ya que es una distro muy pequeña que tiene lo basico para funcionar
FROM alpine

# Instalamos nginx
RUN apk --no-cache add nginx

# Añadimos el index.html al contenedor en la ruta donde sirve nginx 
ADD index.html /var/www/html/

# Exponemos el puerto 80 en el contenedor
EXPOSE 80

# Ejecutamos nginx
CMD [“nginx”,”-g”,”daemon off;”]

Para construir la imagen, ejecutamos el siguiente comando:

1
docker build -t usuario/app:latest .

-t: especificamos una etiqueta o un Tag, si no lo colocamos la imagen no tendrá nombre y en caso de querer borrarla tendremos que eliminarla con el ID.

latest: le indicamos a Docker que contruya la imagen y la etiquete como la mas actual. Es recomendado y una buena práctica colocarle también la versión de nuestra aplicación, podemos colocar v1, v2, etc.

usuario: el nombre de nuestra cuenta en DockerHub.

app: el nombre de nuestra imagen.

El punto al final no es un error de typo (no te asustes), ese punto al final lo que le va a indicar a Docker es que queremos contruír la imagen en base al directorio actual donde nos encontramos y que haya un Dockerfile. Si queremos especificarle un Dockerfile específico (se me ocurre uno para test y otro para prod), ejecutamos:

1
2
docker build -f Dockerfile.test -t usuario/app:tag .
docker build -f Dockerfile.prod -t usuario/app:tag .

Antes que nada, Regístrate es grátis, y luego que tengas tus credenciales, ejecutamos el siguiente comando en una terminal:

1
docker login 

Nos pedirá nuestro usuario (solo el usuario, no el email) y nuestra contraseña:

1
2
3
4
5
6
7
8
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: usuario_obvio
Password: password_mas_que_obvio
WARNING! Your password will be stored unencrypted in /home/usuario/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

Para envíar nuestra imagen a DockerHub ejecutamos el siguiente comando:

1
docker push usuario/app:latest

Y subir la version específica:

1
docker push usuario/app:v1

Si todo va bien, entramos a nuestro DockerHub con nuestro usuario, y podremos ver la imagen generada 😄

Hay casos donde queremos utilizar una imagen base para compilar toda nuestra aplicación (Java, Go, Angular, React etc.) esta resulta ser muy pesada para colocarla en producción, ya que contiene muchas bibliotecas que podríamos omitir; y luego de eso colocar nuestra aplicación ya compilada en una imagen limpia. Para este caso particular usaremos Multi-Stage Builds

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Imagen que vamos a usar para compilar la aplicación
FROM golang:1.16 as BUILDER
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

# Imagen limpia donde vamos a colocar la aplicación
FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]  

Como podemos ver, en la linea donde dice FROM golang:1.16 as BUILDER BUILDER es un alias que le colocamos al Dockerfile para que tenga en cuenta que se trata de la imagen donde vamos a compilar nuestra aplicación. La segunda imagen será la más livianita donde vamos a colocar nuestra aplicación.

Contruímos la imagen:

1
2
docker build -t usuario/goapp:latest .
docker build -t usuario/goapp:v1 .

Algo super importante, es que cuando le haces cualquier cambio a tu código o necesitas instalar algún paquete a la imagen, Docker almacena las capas (que no son más que comandos ejecutados) y las próximas contrucciones serán mucho más rápidas.

Para finalizar la subimos a DockerHub:

1
2
docker push usuario/goapp:latest
docker push usuario/goapp:v1

Bueno, solo nos resta probar si la imagen funciona:

1
docker run -d -p 80:8080 --name app app:latest

Exponemos el puerto 80 de contenedor al puerto 8080 del host.

Con el siguiente comando nos deberia responder nuestra página web.

1
curl localhost:8080

Hasta aquí el artículo, espero les haya gustado, ¡hasta la próxima!



Si te pareció útil este artículo y el proyecto en general, considera brindarme un café :)

Buy me a coffeeBuy me a coffee