Contenedorización de una aplicación Java
En esta unidad, containerizas una aplicación Java.
Como se mencionó anteriormente, los contenedores se ejecutan directamente sobre el sistema operativo host, el kernel y el hardware como procesos normales del sistema. Los contenedores requieren menos recursos del sistema, lo que da lugar a una superficie más pequeña, menos sobrecarga y tiempos de inicio de aplicaciones más rápidos. Estas ventajas son casos de uso excelentes para el escalado a petición.
Hay contenedores de Windows y contenedores de Linux. En este módulo, usará el entorno de ejecución de Docker ampliamente usado para compilar una imagen de contenedor de Linux. A continuación, implemente la imagen de contenedor de Linux en el sistema operativo host de la máquina local. Por último, implementas la imagen de contenedor de Linux en Azure Kubernetes Service.
Introducción a Docker
El entorno de ejecución de Docker se usa para compilar, extraer, ejecutar e insertar imágenes de contenedor, como se muestra en el diagrama siguiente:
En la tabla siguiente se describe cada comando de Docker:
| Comando de Docker | Descripción |
|---|---|
docker build |
Compila una imagen de contenedor que consta de las instrucciones o capas necesarias para que Docker cree un contenedor en ejecución a partir de una imagen. El resultado de este comando es una imagen. |
docker pull |
Descarga una imagen de un registro en el almacén de imágenes del demonio de Docker seleccionado por el cliente. Se pueden reutilizar las capas de imagen existentes. En este módulo, el entorno local de Docker usa imágenes de registros de contenedores durante el desarrollo; más adelante, los nodos de AKS extraen la imagen de la aplicación de Azure Container Registry para la implementación. |
docker run |
Crea e inicia un nuevo contenedor a partir de una imagen. El contenedor ejecuta su proceso de inicio configurado, sujeto a anulaciones mediante la línea de comandos. Las capas de imagen proporcionan el sistema de archivos del contenedor; Las instrucciones de compilación de Dockerfile no se ejecutan de nuevo cuando se inicia el contenedor. |
docker push |
Azure Container Registry almacena las imágenes para que estén fácilmente disponibles y cercanas a la red para las implementaciones y escalabilidad de Azure. |
Clonación de la aplicación Java
En primer lugar, clone el repositorio de Flight Booking System for Airline Reservations y navegue a la carpeta del proyecto de aplicación web para compañías aéreas.
Nota:
Si la creación de Azure Kubernetes Service finaliza en la pestaña CLI, use esa pestaña. Si aún se está ejecutando, abra una nueva pestaña y vaya a la ubicación en la que prefiere clonar el sistema de reservas de vuelos para reservas aéreas.
Ejecute los comandos siguientes:
git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines
Opcionalmente, si tiene Instalado Java y Maven, puede ejecutar el siguiente comando en la consola de terminal para obtener una idea de la experiencia de compilación de la aplicación sin Docker. Si no tiene Instalado Java y Maven, puede continuar con seguridad a la sección siguiente , Construir un archivo docker. En esa sección, usará Docker para extraer Java y Maven para ejecutar las compilaciones en su nombre.
mvn clean package
Nota:
Hemos usado el mvn clean package comando para ilustrar los desafíos operativos de no usar compilaciones de varias fases de Docker, que tratamos a continuación. De nuevo, este paso es opcional. En cualquier caso, puede continuar de forma segura sin ejecutar el comando maven.
Si el proceso se realizó correctamente, Maven creó correctamente el artefacto de archivo web del sistema de reserva de vuelos para reservas de aerolíneas AirlinesReservationSample-0.0.1-SNAPSHOT.war, como se muestra en la siguiente salida histórica. La marca de tiempo y la duración de compilación pueden diferir:
[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------
Imagine que es desarrollador de Java y acaba de compilar AirlinesReservationSample-0.0.1-SNAPSHOT.war. Probablemente, el siguiente paso sea trabajar con los ingenieros de operaciones para implementar este artefacto en un servidor local o en una máquina virtual. Para que la aplicación se inicie y ejecute correctamente, los servidores y las máquinas virtuales deben estar disponibles y configurados con las dependencias necesarias. Este proceso es complicado y requiere mucho tiempo, especialmente a petición, cuando el aumento de la carga está afectando a la aplicación. Con los contenedores, estos desafíos se mitigan.
Construcción de un Dockerfile
Ya estás listo para crear un Dockerfile. Un Dockerfile es un documento de texto que contiene todos los comandos que un usuario ejecutaría en la línea de comandos para ensamblar una imagen de contenedor. Una imagen de contenedor puede contener varias capas de sistema de archivos de solo lectura. Docker puede reutilizar los resultados de compilación almacenados en caché para pasos sin cambios. Las capas se superponen unas sobre otras.
Por ejemplo, el sistema de reservas de vuelos para reservas aéreas debe implementarse y ejecutarse dentro de un servidor de aplicaciones. Un servidor de aplicaciones no se empaqueta dentro de AirlinesReservationSample-0.0.1-SNAPSHOT.war. Es una dependencia externa necesaria para que AirlinesReservationSample-0.0.1-SNAPSHOT.war se ejecute, escuche y procese solicitudes HTTP, administre sesiones de usuario y facilite las reservas de vuelos. Si usó una implementación tradicional no en contenedores, los ingenieros de operaciones instalarían y configurarían un servidor de aplicaciones en algún servidor físico o máquina virtual antes de implementar airlinesReservationSample-0.0.1-SNAPSHOT.war en él. Estos ingenieros de operaciones también necesitarían asegurarse de que el JDK que se usa en la máquina ,que es lo que mvn clean package se usa para compilar el archivo WAR, de hecho corresponde al mismo JRE que usa el servidor de aplicaciones. La administración de estas dependencias es difícil y lenta.
Con un Dockerfile, puede escribir las instrucciones o capas necesarias para lograr este objetivo automáticamente, añadiendo los pasos necesarios en capas para asegurarse de que el sistema de reservaciones aéreas tiene todas las dependencias necesarias para desplegar en el entorno de ejecución del contenedor de Docker. Esta solución es convincente cuando se trabaja con escalabilidad a petición en intervalos no planeados. Cada capa usa la memoria caché de Docker, que contiene el estado de la imagen de contenedor en cada hito informativo, optimizando el tiempo de proceso y la reutilización. Si una capa no cambia, se usan capas almacenadas en caché. Los casos de uso comunes para las capas almacenadas en caché son el entorno de ejecución de Java, el servidor de aplicaciones y otras dependencias para la aplicación web Flight Booking System for Airline Reservations. Si y cuando cambia una versión en una capa almacenada previamente en caché, se crea una nueva entrada almacenada en caché.
Una imagen de contenedor contiene capas de sistema de archivos de solo lectura. En este ejemplo, el Dockerfile copia Tomcat y el archivo Flight Booking System WAR en la imagen en tiempo de compilación. Docker agrega una capa grabable independiente cuando crea un contenedor a partir de la imagen. La capa grabable de cada contenedor registra sus propios cambios en el sistema de archivos sin modificar las capas de imagen.
Nota:
En el caso de los archivos almacenados en la capa grabable del contenedor, las adiciones, modificaciones y eliminaciones afectan a ese contenedor en lugar de a la imagen original. El cambio de archivos procedentes de una capa de imagen usa el comportamiento de copia en escritura.
Docker tiene el concepto de compilaciones de varias fases, una característica que permite crear una imagen de contenedor más pequeña con un mejor almacenamiento en caché y una superficie de seguridad más pequeña, lo que permite una mayor optimización y mantenimiento del Dockerfile a lo largo del tiempo. Por ejemplo, puede separar la etapa de creación del contenedor para compilar y construir la aplicación de la etapa para ejecutar la aplicación. Esta característica permite copiar solo los artefactos generados durante la compilación en la imagen de contenedor de producción, lo que reduce la superficie. Dado que las imágenes de contenedor se almacenan en caché, si no hay cambios, las imágenes almacenadas en caché se pueden reutilizar, lo que reduce el costo y el tiempo de descarga de la red.
Los servicios expuestos en el entorno de producción deben administrarse cuidadosamente para la seguridad. Por lo tanto, el entorno de producción usa y opera una imagen de contenedor segura. En el ejemplo se usa una imagen de Azure Linux proporcionada por Microsoft.
La fase final usa Compilación de Microsoft de OpenJDK 17 en Azure Linux 3.0. La etiqueta 17-azurelinux identifica explícitamente esta base. La etiqueta anterior 17-mariner sigue siendo un alias de compatibilidad que refleja la imagen de Azure Linux; no debe confundirse con las imágenes retiradas 17-mariner-cm2 y 17-mariner-cm1. Para obtener más información, consulte imágenes de contenedor de Compilación de Microsoft de OpenJDK.
Nota:
Microsoft proporciona imágenes de contenedor agrupadas con OpenJDK, incluidas Ubuntu, Azure Linux y distroless imágenes. La distroless imagen tiene el tamaño de imagen más pequeño, pero la ejecución de Tomcat en ella es difícil. Para lograr un diseño ligero, la distroless imagen quita muchos comandos y herramientas, incluido el shell, lo que significa que no se puede llamar catalina.sh para iniciar Tomcat. La distroless imagen es adecuada para ejecutar archivos JAR ejecutables, como los que se usan con Spring Boot o Quarkus.
En el ejemplo siguiente, se usa la misma versión de Microsoft Build de OpenJDK tanto en la fase de compilación como en la fase final. Este enfoque garantiza que compile el código fuente con la misma versión del JDK que usa la implementación del servicio Tomcat, lo que ayuda a evitar un comportamiento inesperado debido a errores de coincidencia de versiones.
En la imagen siguiente se muestra la compilación de varias fases y lo que ocurre en cada fase en función de los comandos especificados en el Dockerfile:
En la fase 0, Tomcat se descarga de un archivo apache específico de la versión, se comprueba con una suma de comprobación SHA-512 y se extrae en un directorio especificado por una variable de entorno en una imagen de Ubuntu. La TOMCAT_VERSION variable especifica la versión y el TOMCAT_SHA512 argumento build especifica su suma de comprobación esperada. Actualice ambos valores al seleccionar una versión compatible más reciente. Docker puede reutilizar la capa de instalación almacenada en caché mientras sus instrucciones y entradas no cambian. La instalación de Tomcat se copia en el entorno de fase final para su uso.
En la fase 1, Maven se instala en una imagen de Ubuntu y el código fuente y los archivos de configuración creados se copian antes de compilar el proyecto de Maven. Cada capa se almacena en caché, por lo que la imagen del sistema operativo y las capas de imagen de Maven reutilizan la memoria caché. Si se actualizan los archivos de configuración, los archivos de código fuente o el directorio web, se vuelven a generar las capas a partir de los cambios. Si la compilación se completa correctamente sin errores durante la compilación, se genera un artefacto denominado AirlinesReservationSample-0.0.1-SNAPSHOT.war en el directorio de destino . Este artefacto se copia en el entorno de fase final para su uso.
En la fase final, los artefactos de compilación de Tomcat y Java a partir de la fase 0 y la fase 1, respectivamente, se copian en una imagen de Compilación de Microsoft de OpenJDK 17 en Azure Linux 3.0. Un usuario denominado app posee todos los archivos usados en el proyecto y la aplicación también se ejecuta como el app usuario en lugar de tener root privilegios. Esta configuración garantiza que la imagen de contenedor se pueda operar de forma segura sin conceder permisos innecesarios. Por último, se expone el número de puerto 8080 y se ejecuta el script catalina.sh para iniciar Tomcat. Cuando se ejecuta en el escritorio de Docker local, puede acceder a él a través de la dirección URL http://localhost:8080/AirlinesReservationSample.
En containerize-and-deploy-Java-app-to-Azure/Project/Airlines, utilice un editor de texto instalado para crear un archivo denominado Dockerfile. Agregue el siguiente contenido y guarde el archivo:
Important
Antes de compilar una imagen que expondrá públicamente, compruebe la versión de Tomcat 10.1.x compatible actual en la página de descarga de Apache Tomcat y revise los avisos de seguridad. Actualice TOMCAT_VERSION y TOMCAT_SHA512 juntos con la información de la versión publicada de Apache. Un artefacto que queda disponible en el archivo de Apache no significa que su versión sea actual o recomendada.
############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the reviewed Tomcat version and checksum
ENV TOMCAT_VERSION=10.1.61
ARG TOMCAT_SHA512=35120a2b1182bac3d55438fbec0dc581d03b0f1c16ce692a930f3bb6a1097cf896b94b389152257071a87cb2928ac8c15f1bad08387c14acc7c17dbc1bb598fb
# Install Tomcat and required packages
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates && \
curl -fSL \
"https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" \
-o /tmp/tomcat.tar.gz && \
echo "${TOMCAT_SHA512} /tmp/tomcat.tar.gz" | sha512sum -c - && \
mkdir -p "${CATALINA_HOME}" && \
tar -xzf /tmp/tomcat.tar.gz --strip-components=1 -C "${CATALINA_HOME}" && \
rm /tmp/tomcat.tar.gz && \
apt-get remove --purge -y curl && \
apt-get autoremove -y && \
apt-get clean
############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
WORKDIR /build
# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version
# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web
# Build the project
RUN mvn clean package
############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-azurelinux
# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH
# This is the user that runs the Tomcat process
USER app
# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war
# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]
Nota:
Utiliza el Dockerfile que se muestra en esta unidad para este ejercicio. La aplicación debe copiarse a webapps/AirlinesReservationSample.war para que las direcciones URL de las unidades siguientes usen el contexto /AirlinesReservationSample/. Otro nombre de archivo WAR cambia el contexto de aplicación predeterminado de Tomcat.
El Dockerfile se divide en tres fases, que se describen en las tablas siguientes:
La fase de instalación de Tomcat:
Comando de Docker Descripción FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcatestablece la imagen base en Compilación de Microsoft de OpenJDK 17 on Ubuntu y asigna el nombre a esta fasetomcat. Aquí es donde está instalado Tomcat.ENVENV CATALINA_HOME=/usr/local/tomcatestablece una variable de entorno para el directorio de instalación de Tomcat.ENVENV TOMCAT_VERSION=10.1.61establece la versión de Tomcat anclada para este ejemplo. Revise las versiones admitidas y actualícelas junto conTOMCAT_SHA512según sea necesario.ARGARG TOMCAT_SHA512proporciona la suma de comprobación SHA-512 publicada de Apache para el archivo seleccionado como argumento de compilación.RUNInstala curly certificados de CA y, a continuación, usacurl -fSLpara descargar el archivo de Apache específico de la versión.-fhace que los errores HTTP fallen;-Lsigue los redireccionamientos. El archivo se verifica consha512sum -c -antes de la extracción enCATALINA_HOMEcon--strip-components=1. La&&cadena se detiene en caso de error en lugar de enmascarar errores durante la limpieza. Por último, quita el archivo comprimido y los paquetes innecesarios y limpia la memoria caché del paquete.La fase de compilación, que se compila con Java 17:
Comando de Docker Descripción FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS buildestablece la imagen base en Compilación de Microsoft de OpenJDK 17 on Ubuntu y asigna el nombre a esta fasebuild. Esta fase se usa para compilar la aplicación Java.WORKDIRWORKDIR /buildestablece el directorio de trabajo dentro del contenedor/builden , donde se copia y compila el código fuente.RUNRUN apt-get update && apt-get install -y maven && mvn --versioninstala Maven, una herramienta de automatización de compilación que se usa para proyectos de Java y comprueba su instalación.COPYCOPY pom.xml .copia el archivo de configuración de Maven en el directorio de trabajo. Este archivo es esencial para compilar el proyecto.COPYCOPY src ./srccopia el directorio de código fuente en el contenedor. Aquí es donde reside el código de la aplicación Java.COPYCOPY web ./webcopia el directorio de recursos web en el contenedor. Esto incluye los recursos de aplicación web necesarios para la compilación.RUNRUN mvn clean packageejecuta el proceso de compilación de Maven, que compila la aplicación Java y la empaqueta en un archivo WAR.La fase final del paquete:
Comando de Docker Descripción FROMFROM mcr.microsoft.com/openjdk/jdk:17-azurelinuxestablece la imagen base como Compilación de Microsoft de OpenJDK 17 en Azure Linux 3.0, que se usa para la implementación final de la aplicación.ENVENV CATALINA_HOME=/usr/local/tomcatestablece la variable de entorno para el directorio de instalación de Tomcat, similar a la fase de instalación.ENVENV PATH=$CATALINA_HOME/bin:$PATHagrega el directorio bin de Tomcat al sistemaPATH, lo que permite que los comandos de Tomcat se ejecuten fácilmente.USERUSER appespecifica el usuario en el que se ejecuta el proceso de Tomcat, lo que mejora la seguridad al no ejecutarse como usuario raíz.COPYCOPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}copia la instalación de Tomcat desde la fasetomcat, estableciendo la propiedad en el usuarioapp.COPYCOPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/confcopia el archivo de configuración de usuario de Tomcat en el contenedor, asignando la propiedad al usuarioapp.COPYCOPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.warcopia el archivo WAR compilado de la fasebuilden el directorio webapps de Tomcat, estableciendo la propiedad en el usuarioapp.EXPOSEEXPOSE 8080documenta que se espera que la aplicación escuche en el puerto TCP 8080. No publica ningún puerto del host. La siguiente unidad publica el puerto con la-p 8080:8080opción endocker run.CMDCMD ["catalina.sh", "run"]especifica el comando para iniciar Tomcat cuando se ejecuta el contenedor.
Para obtener más información sobre la construcción de Dockerfile, consulte la referencia de Dockerfile.