Compartir a través de


Introducción a JavaScript en Azure Container Apps

Azure Container Apps puede ejecutar cualquier aplicación javaScript en contenedor en la nube, a la vez que proporciona opciones flexibles para implementar las aplicaciones.

Configuración

Azure Container Apps le permite simplificar la implementación de las aplicaciones de JavaScript a través de una contenedorización eficaz, incluida la configuración de variables de entorno, el diseño de dockerfiles eficaces y la organización del proceso de compilación de la aplicación.

Variables de entorno

Las variables de entorno son cruciales para configurar la aplicación. Use un .env archivo para administrar estas variables localmente y asegurarse de que se administran de forma segura en producción con un servicio como Azure Key Vault.

En el ejemplo siguiente se muestra cómo crear variables para la aplicación.

# .env
NODE_ENV=production
PORT=3000
AZURE_COSMOS_DB_ENDPOINT=https://<YOUR_COSMOSDB_RESOURCE_NAME>.documents.azure.com:443/

Contenedores

Un Dockerfile bien configurado es esencial para incluir en contenedores la aplicación:

  • Use un Dockerfile base: si varios proyectos comparten una configuración común, puede crear un Dockerfile base que incluya estos pasos comunes. A continuación, el Dockerfile de cada proyecto puede empezar con FROM esta imagen base y agregar configuraciones específicas del proyecto.

  • Parametrización de argumentos de compilación: puede usar argumentos de compilación (ARG) en el Dockerfile para que sea más flexible. De este modo, puede pasar valores diferentes para estos argumentos al compilar para desarrollo, almacenamiento provisional o producción.

  • Imagen base de Node.js optimizada: asegúrese de que usa una imagen base deNode.js adecuada. Considere usar imágenes más pequeñas y optimizadas, como las variantes Alpine, para reducir la sobrecarga.

  • Archivos mínimos: copie solo los esenciales: céntrese en copiar solo los archivos necesarios en el contenedor. Cree un archivo .dockerignore para asegurarse de que archivos de desarrollo como .env y node_modules no se copien. Este archivo ayuda a acelerar las compilaciones en los casos en los que los desarrolladores copiaron en archivos innecesarios.

  • Separar la compilación y el tiempo de ejecución con compilaciones de varias fases: use compilaciones de varias fases para crear una imagen final ajustada separando el entorno de compilación del entorno en tiempo de ejecución.

  • Crear artefactos previamente mediante la compilación y la agrupación: precompilar los artefactos de la aplicación (por ejemplo, compilar TypeScript o agrupar JavaScript) antes de copiarlos en la fase de tiempo de ejecución puede minimizar el tamaño de la imagen, acelerar la implementación del contenedor y mejorar el rendimiento del arranque en frío. El orden cuidadoso de las instrucciones de Dockerfile también optimiza el almacenamiento en caché y los tiempos de recompilación.

  • Docker Compose para entornos de desarrollo: Docker Compose permite definir y ejecutar aplicaciones de Docker de varios contenedores. Este enfoque de varios contenedores es útil para configurar entornos de desarrollo. Puede incluir el contexto de compilación y el Dockerfile en el archivo de composición. Este nivel de encapsulación permite usar diferentes dockerfiles para distintos servicios cuando sea necesario.

Base Dockerfile

Este archivo sirve como punto de partida común para las imágenes de Node.js. Puede usarlo con una FROM directiva en Dockerfiles que hagan referencia a esta imagen base. Use un número de versión o una confirmación para admitir la versión reciente y segura de la imagen.

# Dockerfile.base

FROM node:22-alpine

# Set the working directory
WORKDIR /usr/src/app

# Define build arguments with default values
ARG PORT_DEFAULT=3000
ARG ENABLE_DEBUG_DEFAULT=false

# Set environment variables using the build arguments
ENV PORT=${PORT_DEFAULT}
ENV ENABLE_DEBUG=${ENABLE_DEBUG_DEFAULT}

# Copy package manifests and install dependencies
COPY package*.json ./
RUN npm install

# Expose the application and debugging ports
EXPOSE $PORT
EXPOSE 9229

# This image focuses on common steps; project-specific Dockerfiles can extend this.

Cuando se pasan valores mediante la --build-arg marca durante el proceso de compilación, los valores pasados invalidan los valores predeterminados codificados de forma rígida en el Dockerfile.

Por ejemplo:

docker build \
  --build-arg PORT_DEFAULT=4000 \
  --build-arg ENABLE_DEBUG_DEFAULT=true \
  --tag <IMAGE>:<TAG> \
  --file Dockerfile.base .

En este ejemplo, las variables PORT de entorno y ENABLE_DEBUG se establecen en valores explícitos, en lugar de sus valores predeterminados.

Las convenciones de etiquetado de imágenes de contenedor como el uso de latest son una convención. Obtenga más información sobre las recomendaciones para el etiquetado y el versionado de imágenes de contenedores.

Configuración del entorno de desarrollo con Docker Compose

En la siguiente configuración de ejemplo se usa un Dockerfile de desarrollo dedicado (Dockerfile.dev) junto con montajes de volúmenes para la recarga en vivo y la sincronización de origen local.

version: "3.8"
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.base
      args:
        PORT_DEFAULT: ${PORT:-3000}
        ENABLE_DEBUG_DEFAULT: ${ENABLE_DEBUG:-false}
    ports:
      - "${PORT:-3000}:3000"
      - "9229:9229"  # Expose debug port if needed
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
    environment:
      - NODE_ENV=development
      - PORT=${PORT:-3000}
      - ENABLE_DEBUG=${ENABLE_DEBUG:-false}

Para iniciar Docker Compose con valores personalizados, puede exportar las variables de entorno en la línea de comandos. Por ejemplo:

PORT=4000 ENABLE_DEBUG=true docker compose up

Dockerfile de producción

Este Dockerfile de varias fases construye tu aplicación y genera una imagen de runtime optimizada. Asegúrese de tener el .dockerignore archivo ya en el código fuente para que el COPY . . comando no copie ningún archivo específico del entorno de desarrollo que no necesite en producción.

# Stage 1: Builder
FROM node:22 AS build

WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .

# Build your project (e.g., compile TypeScript or bundle JavaScript)
RUN npm run build

# Stage 2: Runtime
FROM my-base-image:latest AS runtime

WORKDIR /usr/src/app

# Copy only the compiled output and essential files from the build stage
COPY --from=build /usr/src/app/dist ./dist
COPY --from=build /usr/src/app/package*.json ./

# Install only production dependencies
RUN npm ci --omit=dev

# Copy the entrypoint script for remote debugging
COPY entrypoint.sh /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh

# Expose the application port (using the PORT environment variable) and the debug port (9229)
EXPOSE $PORT
EXPOSE 9229

# Use the entrypoint script to conditionally enable debugging
ENTRYPOINT ["sh", "/usr/src/app/entrypoint.sh"]

El script de punto de entrada permite conectarse a la aplicación contenedora para la depuración remota.

Para ejecutar un contenedor desde la imagen de producción compilada con variables de entorno personalizadas, ejecute:

docker run \
  --env PORT=4000 \
  --env ENABLE_DEBUG=true \
  --publish 4000:4000 \
  --publish 9229:9229 \
  <IMAGE>:<TAG>

Para las compilaciones de producción, asegúrese de usar la etiqueta de versión correcta, que puede no ser latest. Las convenciones de etiquetado de imágenes de contenedor como el uso de latest son una convención. Obtenga más información sobre las recomendaciones para el etiquetado y la versionado de imágenes de contenedor.

Despliegue

Para admitir la integración continua o la implementación continua (CI/CD), configure una canalización de CI/CD mediante Acciones de GitHub, Azure DevOps u otra herramienta de CI/CD para automatizar el proceso de implementación.

# .github/workflows/deploy.yml
name: Deploy to Azure

on:
push:
    branches:
    - main

jobs:
build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Set up Node.js
      uses: actions/setup-node@v4
      with:
          node-version: '22'

    - name: Install dependencies
      run: npm ci

    - name: Build the app
      run: npm run build

    - name: Log in to Azure
      uses: azure/login@v2
      with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Deploy to Azure Container Apps
      run: |
          az containerapp up \
          --name my-container-app \
          --resource-group my-resource-group \
          --image my-image:my_tag \
          --environment my-environment \
          --cpu 1 --memory 2Gi \
          --env-vars NODE_ENV=production PORT=3000

Cuando use Docker Registry, inicie sesión en el registro y, a continuación, inserte las imágenes de Docker en un registro de contenedor como Azure Container Registry (ACR) o Docker Hub.

# Tag the image
docker tag \
  <IMAGE>:<TAG> \
  <AZURE_REGISTRY>.azurecr.io/<IMAGE>:<TAG>

# Push the image
docker push <AZURE_REGISTRY>.azurecr.io/<IMAGE>:<TAG>

Arranques en frío

Optimice la compilación de producción incluyendo solo el código y las dependencias esenciales. Para asegurarse de que la carga útil sea lo más ajustada posible, use uno de los siguientes métodos:

  • Compilaciones o agrupaciones de Docker de varias fases: use herramientas de compilación y agrupación como Webpack o Rollup para ayudarle a crear la carga más pequeña posible para el contenedor. Al compilar y agrupar solo lo que se necesita para producción, ayuda a minimizar el tamaño del contenedor y ayuda a mejorar los tiempos de inicio en frío.

  • Administre las dependencias cuidadosamente: Mantenga la node_modules carpeta optimizada incluyendo solo los paquetes necesarios para ejecutar el código de producción. No enumere las dependencias de desarrollo ni de prueba en la sección dependencies de package.json. Quite las dependencias sin usar y asegúrese de que el package.json archivo y el archivo de bloqueo sigan siendo coherentes.

Seguridad

Las consideraciones de seguridad para los desarrolladores de JavaScript que usan Azure Container Apps incluyen la protección de variables de entorno (como el uso de Azure Key Vault), asegurar HTTPS con una gestión adecuada de certificados, mantener las dependencias actualizadas con auditorías periódicas e implementar un registro y una supervisión sólidos para detectar y responder rápidamente a las amenazas.

Protección de variables de entorno

Asegúrese de que la información confidencial, como las cadenas de conexión de base de datos y las claves de API, se almacenan de forma segura. Use Azure Key Vault para administrar secretos y variables de entorno de forma segura.

Antes de ejecutar este comando, asegúrese de reemplazar los marcadores de posición rodeados por <> con sus valores.

az keyvault secret set \
  --vault-name <KEY_VAULT_APP> \
  --name "<SECRET_NAME>" \
  --value "<CONNECTION_STRING>"

HTTPS y certificados

Asegúrese de que la aplicación se proporciona a través de HTTPS. Azure Container Apps puede administrar certificados por usted. Configure el dominio y el certificado personalizados en Azure Portal.

Administración de dependencias

Actualice periódicamente las dependencias para evitar vulnerabilidades de seguridad. Use herramientas como npm audit para comprobar si hay vulnerabilidades.

npm audit

Control de errores

Implemente un control sólido de errores en la aplicación Node.js. Use middleware en Express o Fastify para manejar los errores de forma elegante.

// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';

export function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
}

Cierres elegantes

El apagado correcto de tu aplicación es crucial para asegurarse de que las solicitudes en vuelo se completan y se liberan correctamente los recursos. Esto ayuda a evitar la pérdida de datos y mantiene una experiencia de usuario fluida durante las implementaciones o eventos de escalado horizontal. En el ejemplo siguiente se muestra un enfoque mediante Node.js y Express para controlar las señales de apagado correctamente.

import express from 'express';
import healthRouter from './health.js';

const app = express();

app.use(healthRouter);

const server = app.listen(process.env.PORT || 3000);

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });

  // Force close after 30s
  setTimeout(() => {
    console.error('Could not close connections in time, forcing shutdown');
    process.exit(1);
  }, 30000);
});

Registro

En Azure Container Apps, las llamadas console.log y console.error se capturan y registran automáticamente. Azure Container Apps captura los flujos de salida estándar (stdout) y de error estándar (stderr) de la aplicación y los pone a disposición de Azure Monitor y Log Analytics.

Configuración del registro en Azure Container Apps

Para asegurarse de que los registros se capturan correctamente y son accesibles, debe configurar las opciones de diagnóstico de la aplicación contenedora de Azure. La configuración es un proceso de dos pasos.

  1. Habilitar configuración de diagnóstico: use la CLI de Azure para habilitar la configuración de diagnóstico para la aplicación contenedora de Azure.

    Antes de ejecutar este comando, asegúrese de reemplazar los marcadores de posición delimitados por <> con sus valores.

    az monitor diagnostic-settings create \
    --resource /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.Web/containerApps/<CONTAINER_APP_NAME> \
    --name "containerapp-logs" \
    --workspace <LOG_ANALYTICS_WORKSPACE_ID> \
    --logs '[{"category": "ContainerAppConsoleLogs","enabled": true}]'
    
  2. Acceda a los registros en el portal; para ello, vaya al área de trabajo de Log Analytics y consulte los registros.

Uso de librerías de logs

Aunque console.log y console.error se capturan automáticamente, el uso de una biblioteca de registro como Winston proporciona más flexibilidad y control sobre el registro. Esta flexibilidad le permite dar formato a registros, establecer niveles de registro y registros de salida en varios destinos, como archivos o servicios de registro externos.

En el ejemplo siguiente se muestra cómo configurar Winston para almacenar registros de alta fidelidad.

// src/logger.ts
import { createLogger, transports, format } from 'winston';

const logger = createLogger({
  level: 'info',
  format: format.combine(
    format.timestamp(),
    format.json()
  ),
  transports: [
    new transports.Console(),
    new transports.File({ filename: 'app.log' })
  ]
});

export default logger;

Para usar el registrador, use la sintaxis siguiente en la aplicación:

import logger from './logger';

logger.info('This is an info message');
logger.error('This is an error message');

Depuración remota

Para habilitar la depuración remota, puede usar el inspector integrado de Node. En lugar de codificar la configuración de depuración en la CMD de Dockerfile, puede habilitar dinámicamente la depuración remota mediante un script de shell como punto de entrada del contenedor.

El script siguiente comprueba una variable de entorno (por ejemplo, ENABLE_DEBUG) cuando se inicia el contenedor. Si la variable se establece en true, el script arranca Node.js en modo depuración (mediante --inspect o --inspect-brk). De lo contrario, el contenedor inicia normalmente la aplicación.

Puede implementar la depuración remota con los pasos siguientes:

  1. Cree un script de punto de entrada en un archivo denominado entrypoint.sh en la raíz del proyecto con el siguiente contenido:

    #!/bin/sh
    # If ENABLE_DEBUG is set to "true", start Node with debugging enabled
    if [ "$ENABLE_DEBUG" = "true" ]; then
      echo "Debug mode enabled: starting Node with inspector"
      exec node --inspect=0.0.0.0:9229 dist/index.js
    else
      echo "Starting Node without debug mode"
      exec node dist/index.js
    fi
    
  2. Modifique el Dockerfile para copiar el entrypoint.sh script en el contenedor y establézcalo como punto de entrada. Además, exponga el puerto de depuración si es necesario:

    # Copy the entrypoint script to the container
    COPY entrypoint.sh /usr/src/app/entrypoint.sh
    
    # Ensure the script is executable
    RUN chmod +x /usr/src/app/entrypoint.sh
    
    # Expose the debugging port (if using debug mode)
    EXPOSE 9229
    
    # Set the shell script as the container’s entrypoint
    ENTRYPOINT ["sh", "/usr/src/app/entrypoint.sh"]
    
  3. Active el modo de depuración estableciendo la variable de entorno ENABLE_DEBUG en true. Por ejemplo, con la CLI de Azure:

    az containerapp update \
      --name <CONTAINER_APP> \
      --env-vars ENABLE_DEBUG=true
    

Antes de ejecutar este comando, asegúrese de reemplazar los marcadores de posición rodeados de <> con sus valores.

Este enfoque ofrece una solución flexible que permite reiniciar el contenedor en modo de depuración mediante la actualización de una variable de entorno en el inicio. Evita la necesidad de crear una nueva revisión con diferentes CMD configuraciones cada vez que necesite depurar la aplicación.

Consideraciones de mantenimiento y rendimiento

Para mantener y optimizar el rendimiento de la aplicación con el tiempo, asegúrese de administrar de forma eficaz los cambios de variables de entorno, supervisar los recursos, mantener las dependencias up-to-date, configurar el escalado correctamente y configurar alertas de supervisión.

Cambios en las variables de entorno

Dado que cada cambio en las variables de entorno requiere una nueva revisión implementada, realice todos los cambios en los secretos de la aplicación a la vez. Cuando se completen los cambios, vincule los secretos a las variables de entorno de la revisión. Este enfoque minimiza el número de revisiones y ayuda a mantener un historial de implementación limpio.

Asignación de recursos

Supervise y ajuste la asignación de CPU y memoria para los contenedores en función de los patrones de uso y rendimiento de la aplicación. El aprovisionamiento excesivo puede provocar costos innecesarios, mientras que el aprovisionamiento inferior puede provocar problemas de rendimiento.

Actualizaciones de dependencias

Actualice periódicamente las dependencias para beneficiarse de las mejoras de rendimiento y las revisiones de seguridad. Use herramientas como npm-check-updates para automatizar este proceso.

npm install -g npm-check-updates
ncu -u
npm install

Scaling

Configure el escalado automático en función de la carga de la aplicación. Azure Container Apps admite el escalado horizontal, que ajusta automáticamente el número de instancias de contenedor en función del uso de CPU o memoria.

En el ejemplo siguiente se muestra cómo establecer una regla de escalado basada en CPU. Antes de ejecutar este comando, asegúrese de reemplazar los marcadores de posición rodeados por <> con sus valores.

az containerapp revision set-scale \
  --name <CONTAINER_APP> \
  --resource-group <RESOURCE_GROUP> \
  --min-replicas 1 \
  --max-replicas 10 \
  --cpu 80

Supervisión de alertas

Configure la supervisión y las alertas para realizar un seguimiento del rendimiento y el estado de la aplicación. Use Azure Monitor para crear alertas para métricas específicas, como el uso de CPU, el uso de memoria y los tiempos de respuesta.

Antes de ejecutar este comando, asegúrese de reemplazar los marcadores de posición delimitados por <> con sus valores.

az monitor metrics alert create \
  --name "HighCPUUsage" \
  --resource-group <RESOURCE_GROUP> \
  --scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.ContainerInstance/containerGroups/<CONTAINER_GROUP> \
  --condition "avg Percentage CPU > 80" \
  --description "Alert when CPU usage is above 80%"

Administración de recursos

Use la extensión Azure Container Apps para Visual Studio Code para crear, editar e implementar aplicaciones en contenedores rápidamente desde Visual Studio Code.

Solución de problemas

Cuando la aplicación tiene problemas en tiempo de ejecución en Azure Container Apps, puede usar el registro, la depuración remota y las alertas de comprobación de estado para buscar y resolver el problema.

Registro

Habilite y configure el registro para capturar registros de aplicación. Use Azure Monitor y Log Analytics para recopilar y analizar registros. Antes de ejecutar estos comandos, asegúrese de reemplazar los marcadores de posición rodeados por <> con sus valores.

  1. Cree un nuevo espacio de trabajo.

    az monitor log-analytics workspace create \
        --resource-group <RESOURCE_GROUP> \
        --workspace-name <WORKSPACE_NAME>
    
  2. A continuación, cree una nueva configuración de área de trabajo.

    az monitor diagnostic-settings create \
        --resource <CONTAINER_APP> \
        --workspace <WORKSPACE_NAME> \
        --logs '[{"category": "ContainerAppConsoleLogs","enabled": true}]'
    

Depuración

Las herramientas de depuración remota se usan para conectarse al contenedor en ejecución. Asegúrese de que Dockerfile expone los puertos necesarios para la depuración.

# Expose the debugging port
EXPOSE 9229

Exámenes de salud

Configure las comprobaciones de estado para supervisar el estado de la aplicación. Esta característica garantiza que Azure Container Apps pueda reiniciar el contenedor si deja de responder.

# Azure Container Apps YAML configuration
properties:
configuration:
    livenessProbe:
    httpGet:
        path: /health
        port: 3000
    initialDelaySeconds: 30
    periodSeconds: 10