Resumen rápido ↬
Usar solo Docker para construir y ejecutar aplicaciones y comandos elimina la necesidad de conocimientos previos en alguna herramienta o lenguaje de programación. Además, evita la necesidad de instalar nuevos módulos y dependencias directamente al sistema, lo que hace que el desarrollo sea independiente de la máquina.

Imagina la siguiente situación: comienzas a trabajar en un nuevo proyecto, quizás también con un lenguaje de programación diferente al que no estás acostumbrado. Entonces, el proyecto está ahí, y debería poder ejecutarlo.

Espera que haya alguna documentación que le diga qué hacer, lo cual no es tan común, y si hay alguna, generalmente no funciona. Necesitas saber qué instalar, dónde para instalarlo, cómo para configurarlo, etc. No es un escenario poco común, y en realidad puede esperar eso en algún momento. Pero, ¿y si hubiera una manera de garantizar que esto no vuelva a suceder?

A lo largo de esta publicación, veremos diferentes enfoques que podríamos usar para hacer esto más fácil usando solo Docker.

Lenguajes de programación
(Fuente: pngfind.com) (Vista previa grande)

Nivel uno: uso de alias con Docker

Ejemplo con Java + Maven

Consideremos un proyecto Java, por ejemplo. Por lo general, para ejecutar una aplicación Java, ejecuta java -jar application.jar .

Para generar el jar archivar y administrar las dependencias del proyecto, puede usar muchas herramientas diferentes, siendo las más conocidas Maven y Gradle. Consideremos a Maven para este ejemplo. Veamos algunos comandos de Maven:

  • mvn dependency:copy-dependencies
    Descarga las dependencias si aún no se han descargado.
  • mvn package
    Construye la aplicación y genera el jar . También descarga las dependencias si aún no se han descargado. Si desea omitir la ejecución de las pruebas en el proceso de construcción, también puede pasar el -Dmaven.test.skip=true parámetro.

Suponiendo que necesitamos Maven 3 y Java 11, así es como podría usar Docker:

alias java="docker run -v "$PWD":/home -w /home openjdk:11-jre-slim java"
alias mvn='docker run -it --rm --name maven -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3-jdk-11-slim mvn'

De esta manera, puede ejecutar cualquier comando de Maven y Java sin tener que instalar Java o Maven. Puede probar los comandos ejecutando java -version o mvn -version . Por lo general, la imagen oficial de Docker de estas herramientas le brinda instrucciones sobre cómo ejecutarlas, y puede crear un alias para eso.

Ventajas:

  • Si ya no necesita usarlo, puede eliminar la imagen de Docker relacionada.
  • Es fácil cambiar la versión.

Contras:

  • En este ejemplo, aún debe averiguar qué versión de Java se usa y qué herramienta se usa (en este caso, Maven) y su versión.
  • Si se trata de un lenguaje de programación que no conoce, le llevará aún más tiempo entender qué hacer.
  • Todavía necesita saber qué comandos ejecutar.

Es un enfoque justo, especialmente si sabes lo que estás haciendo. Pero eso no viene con el proyecto en sí. Entonces, intentemos mejorarlo un poco.

¡Más después del salto! Continúe leyendo a continuación ↓

Nivel dos: uso de Docker para ejecutar la aplicación

Ahí es donde Dockerfile comienza a brillar. Sabemos cómo ejecutar comandos usando solo Docker, pero ¿cómo ejecutar la aplicación?

Un Dockerfile común para esa situación podría ser:

FROM openjdk:11-jre-slim
ARG JAR_FILE=target/*.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

Y puede construirlo de la forma en que normalmente construye una imagen acoplable, usando docker build -t my-application . por ejemplo.

Se puede ver que depende de un existente JAR archivo para construirlo. Como viste en el tema anterior, sabemos cómo generarlo, pero estaríamos en problemas si fuera otro lenguaje de programación o herramienta.

Parece una mejora realmente menor, pero eso ya ayuda mucho, como puede ver en sus pros/contras:

ventajas

  • El Dockerfile debe venir dentro del proyecto. Por lo tanto, ya le dice cómo ejecutar la aplicación independientemente del conocimiento del lenguaje de programación.
  • También le dice qué versión e imagen se están utilizando.
  • Hereda las ventajas del Nivel uno tema si también aplica Nivel uno conocimiento.

Contras

  • Todavía necesita averiguar cómo construir la aplicación.
  • También significa que aún necesita saber qué comandos ejecutar.

Es un buen enfoque. puedes fusionar Nivel uno y Nivel dos para lograr un mejor resultado. El proyecto debería tener Dockerfile, y la vida ya se vuelve un poco más fácil. Pero veamos cómo la vida puede ser incluso más fácil.

Nivel tres: uso de Docker para compilar y ejecutar la aplicación

¿Qué pasaría si no supiera nada sobre Java y Maven, y aun así pudiera crear la aplicación con solo un comando que ya conoce?

Ahí es donde brillan las construcciones multietapa.

Con compilaciones de varias etapas, utiliza varias declaraciones `FROM` en su Dockerfile. Cada instrucción `FROM` puede usar una base diferente, y cada una de ellas comienza una nueva etapa de la construcción. Puede copiar artefactos de forma selectiva de una etapa a otra, dejando atrás todo lo que no desea en la imagen final.

¿Cómo puede eso ayudarnos? Bueno, consideremos el Dockerfile anterior. necesitábamos un JAR para construir la imagen de Docker. Con Multi-Stage Build, el propio Dockerfile puede ser responsable de generarlo. En un enfoque simple, ese Dockerfile se vería así:

# ============= DEPENDENCY + BUILD ===========================
# Download the dependencies on container and build application
# ============================================================
FROM maven:3-jdk-11-slim AS builder
COPY ./pom.xml /app/pom.xml
COPY . /app
WORKDIR /app
RUN mvn package $MAVEN_CLI_OPTS -Dmaven.test.skip=true
# ============= DOCKER IMAGE ================
# Prepare container image with application artifacts
# ===========================================
FROM openjdk:11-jre-slim
COPY --from=builder /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

Veamos qué está pasando aquí.

Desde el principio FROM al primero RUN declaración, está haciendo cosas relacionadas con Maven: copiar los archivos que deben copiarse y ejecutar el comando que descarga las dependencias y crea la aplicación. Está haciendo eso usando el maven:3-jdk-11-slim imagen, y está configurando el nombre builder .

Después de eso, ves el segundo. FROM declaración usando el openjdk:11-jre-slim imagen. También ves un COPY declaración que copia de un lugar llamado builder . pero que es eso lugar? Que es eso constructor?

Ese es el nombre que establecimos para la imagen de Maven al principio FROM declaración. esta copiando el jar archivo de ese contenedor. Entonces, literalmente puedes jugar con diferentes FROM entradas para construir lo que quieras, y el comando para construir la imagen de Docker sigue siendo el mismo: docker build -t my-application . .

Ventajas:

  • Independientemente del lenguaje de programación, si el proyecto tiene este enfoque, puede ejecutar la aplicación sin instalar nada más que Docker.
  • Hereda las ventajas de Nivel uno y Nivel dos.

Vale la pena decir que también puede usar este Dockerfile con Docker Compose, que puede ser realmente poderoso, especialmente si su aplicación necesita exponer puertos, compartir volúmenes o depende de otras imágenes.

Apéndice: uso de Docker para cada comando principal

Ahora que sabes cómo jugar con diferentes FROM declaraciones, otro posible Dockerfile podría ser:

# ============= DEPENDENCY RESOLVER =============
# Download the dependencies on container
# ===============================================
FROM maven:3-jdk-11-slim AS dependency_resolver
# Download all library dependencies
COPY ./pom.xml /app/pom.xml
WORKDIR /app
RUN mvn dependency:copy-dependencies $MAVEN_CLI_OPTS
# ============= TESTING =================
# Run tests on container
# =======================================
FROM dependency_resolver AS tester
WORKDIR /app
CMD mvn clean test $MAVEN_CLI_OPTS
# ============= BUILDER =================
# Build the artifact on container
# =======================================
FROM dependency_resolver as builder
# Build application
COPY . /app
RUN mvn package $MAVEN_CLI_OPTS -Dmaven.test.skip=true
# ============= DOCKER IMAGE ================
# Prepare container image with application artifacts
# ===========================================
FROM openjdk:11-jre-slim
COPY --from=builder /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

Entonces, ahora tenemos cuatro pasos diferentes: dependency_resolver , tester , builder y la propia aplicación.

Si queremos construir la aplicación o probarla, necesitamos las dependencias del proyecto. Así, hay un dependency_resolver paso allí. En el segundo y el tercero FROM declaraciones, se puede ver que dependen de dependency_resolver .

Importante: Si intenta construir la imagen de la ventana acoplable con docker build -t my-application . solo el primero, el tercero y el último paso ( dependency_resolver , builder y la propia aplicación, respectivamente) se ejecutaría.

¿Pero por qué?

Cuando intente construir la imagen, intentará ver cuál es el estado final de la imagen, que sería la aplicación en sí. Como sabemos y podemos ver, el último paso depende de builder ( COPY declaración). Si revisamos el builder paso, veremos que depende de dependency_resolver ( FROM declaración). Entonces, se ejecutará en este orden:

dependency_resolver -> builder -> aplicación

Entonces, ¿cuál es el tester paso haciendo allí? ¿Hay alguna manera de llegar a él?

Puede especificar un destino utilizando --target : docker build --target tester -t my-application . .

Esto también es compatible con Docker Compose.

Conclusión

Vimos como usar solo Docker para construir y ejecutar aplicaciones y comandos, eliminando la necesidad de conocimientos previos en alguna herramienta o lenguaje de programación. Además, vale la pena decir que aunque usamos Docker para estos ejemplos, esto también funcionaría con otros tiempos de ejecución de contenedores como Podman.

Espero que encuentre útil esta publicación y corra la voz sobre Docker.

editorial aplastante
(Sí, sí)

Deja una respuesta