Azure 容器应用上的 JavaScript 概述

Azure 容器应用可以在云中运行任何容器化的 JavaScript 应用程序,同时提供灵活选项来部署应用程序。

配置

借助 Azure 容器应用,可以通过有效的容器化简化 JavaScript 应用程序的部署,包括设置环境变量、设计高效的 Dockerfiles 以及组织应用程序的生成过程。

环境变量

环境变量对于配置应用程序至关重要。 .env使用文件在本地管理这些变量,并确保在生产环境中使用 Azure Key Vault 等服务安全地管理这些变量。

以下示例演示如何为应用程序创建变量。

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

容器

配置良好的 Dockerfile 对于容器化应用程序至关重要:

  • 使用基本 Dockerfile:如果多个项目共享一个通用设置,则可以创建包含这些常见步骤的基本 Dockerfile。 然后,每个项目的 Dockerfile 都可以以此基本映像 FROM 开始,并添加特定于项目的配置。

  • 生成参数的参数化:可以使用 Dockerfile 中的生成参数(ARG)使其更加灵活。 这样,您可以在构建开发环境、测试环境或生产环境时为这些参数传入不同的值。

  • 优化 Node.js 基础映像:确保使用适当的 Node.js 基础映像。 为了减少开销,请考虑使用更小的经过优化的映像(例如 Alpine 变体)。

  • 最小文件 - 仅复制基本文件:专注于仅将必要的文件复制到容器中。 创建一个.dockerignore文件,以确保开发文件不会被复制到例如.envnode_modules中。 此文件有助于加快编译速度,尤其是在开发人员复制不必要的文件的情况下。

  • 使用多阶段构建分离构建和运行时:通过将构建环境与运行时环境分开来创建精简的最终映像。

  • 通过编译和捆绑预构建项目:在将项目复制到运行时阶段之前,预构建应用程序项目(例如编译 TypeScript 或捆绑 JavaScript),可以最大程度地减小映像大小、加快容器部署并提高冷启动性能。 在 Dockerfile 中仔细排序说明也会优化缓存和重新生成时间。

  • Docker Compose 用于开发环境:Docker Compose 允许您定义和运行多容器的 Docker 应用程序。 此多容器方法可用于设置开发环境。 可以在 Compose 文件中包括构建上下文和 Dockerfile。 这种封装级别允许在必要时为不同的服务使用不同的 Dockerfile。

基本 Dockerfile

此文件用作 Node.js 映像的常见起点。 可以在引用此基本映像的 Dockerfiles 中将它与 FROM 指令一起使用。 可以使用版本号或提交记录来指定映像的最新安全版本。

# 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.

在生成过程中使用 --build-arg 标志传入值时,传入的值将覆盖 Dockerfile 中的硬编码默认值。

例如:

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

在此示例中,环境变量 PORT 设置为 ENABLE_DEBUG 显式值,而不是默认值。

容器映像标记的惯例,例如使用 latest,是一种常规。 详细了解 有关标记和版本化容器镜像的建议

使用 Docker Compose 设置开发环境

以下示例配置使用专用开发 Dockerfile (Dockerfile.dev),并通过卷挂载实现实时重新加载和本地源同步。

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}

若要使用自定义值启动 Docker Compose,可以在命令行上导出环境变量。 例如:

PORT=4000 ENABLE_DEBUG=true docker compose up

生产 Dockerfile

这个多阶段 Dockerfile 用于构建你的应用程序,并生成轻量级的运行时映像。 请确保 .dockerignore 文件已在源代码中,以便 COPY . . 命令不会复制任何特定于开发环境且在生产环境中不需要的文件。

# 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"]

使用入口点脚本可以连接到容器应用进行 远程调试

若要使用自定义环境变量从生成的生产映像运行容器,请运行:

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

对于生产版本,请确保使用正确的版本标记(可能不是 latest)。 容器镜像标记规则(如使用latest)是一种惯例。 详细了解有关对容器映像进行标记和版本控制的建议

部署

若要支持持续集成/持续部署(CI/CD),请使用 GitHub Actions、Azure DevOps 或其他 CI/CD 工具设置 CI/CD 管道,以自动执行部署过程。

# .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

使用 Docker 注册表时,登录到注册表,然后将 Docker 映像推送到容器注册表,例如 Azure 容器注册表(ACR)或 Docker 中心。

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

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

冷启动

通过只包含必要的代码和依赖项来优化生产构建。 若要确保有效负载尽可能精简,请使用以下方法之一:

  • 多阶段 Docker 生成或捆绑程序:使用生成和捆绑工具(如 Webpack 或汇总)来帮助为容器创建最小的有效负载。 仅编译和捆绑生产所需的内容,有助于最大程度地减少容器大小,并帮助改善冷启动时间。

  • 仔细管理依赖项:node_modules仅包括运行生产代码所需的包,使文件夹保持精简。 请勿在 package.jsondependencies 部分中列出开发或测试依赖项。 删除任何未使用的依赖项,并确保你的 package.json 和锁定文件保持一致。

安全

使用 Azure 容器应用的 JavaScript 开发人员的安全注意事项包括保护环境变量(例如使用 Azure Key Vault)、确保 HTTPS 与适当的证书管理、使用定期审核维护 up-to日期依赖项,以及实现可靠的日志记录和监视,以快速检测和响应威胁。

保护环境变量

确保安全地存储数据库连接字符串和 API 密钥等敏感信息。 使用 Azure Key Vault 安全地管理机密和环境变量。

在运行此命令之前,请确保将用 <> 括起来的占位符替换为你的值。

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

HTTPS 和证书

确保通过 HTTPS 提供应用程序。 Azure 容器应用可以为你管理 证书 。 在 Azure 门户中配置 自定义域 和证书。

依赖项管理

定期更新依赖项以避免安全漏洞。 使用像 npm audit 这样的工具来检查漏洞。

npm audit

错误处理

在 Node.js 应用程序中实现可靠的错误处理。 使用 Express 或 Fastify 中的中间件正常处理错误。

// 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!');
}

正常关闭

要确保正在处理的请求完成且正确发布资源,正常关闭应用程序至关重要。 这有助于防止数据丢失,并在部署或横向扩展事件期间保持流畅的用户体验。 以下示例演示了使用 Node.js 和 Express 正常处理关机信号的一种方法。

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);
});

伐木业

在 Azure 容器应用中,调用的 console.logconsole.error 会被自动捕获和记录。 Azure 容器应用从应用程序捕获标准输出(stdout)和标准错误(stderr)流,并在 Azure Monitor 和 Log Analytics 中提供它们。

在 Azure 容器应用中设置日志记录

若要确保正确捕获和访问日志,需要为 Azure 容器应用配置诊断设置。 设置是一个两个步骤的过程。

  1. 启用诊断设置:使用 Azure CLI 为 Azure 容器应用启用诊断设置。

    在运行此命令之前,请确保将用 <> 括起来的占位符替换为你的值。

    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. 在门户中访问日志,可以转到 Log Analytics 工作区并查询日志。

使用日志记录库

console.logconsole.error 会被自动捕获,而使用像 Winston 这样的日志库可以更灵活地控制您的日志记录。 这种灵活性使你可以将日志、设置日志级别和输出日志的格式设置为多个目标,例如文件或外部日志记录服务。

以下示例演示如何将 Winston 配置为存储高保真日志。

// 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;

若要使用记录器,请在应用程序中使用以下语法:

import logger from './logger';

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

远程调试

若要启用远程调试,可以使用 Node 的内置检查器。 可以使用 shell 脚本作为容器的入口点来动态启用远程调试,而不是将调试设置硬编码到 Dockerfile CMD中。

以下脚本在容器启动时检查环境变量(例如 ENABLE_DEBUG)。 如果变量设置为 true,脚本将在调试模式下启动 Node.js(使用 --inspect--inspect-brk)。 否则,容器通常会启动应用程序。

可以使用以下步骤实现远程调试:

  1. 在项目根目录下命名 entrypoint.sh 的文件中创建入口点脚本,内容如下:

    #!/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. 修改 Dockerfile 以将 entrypoint.sh 脚本复制到容器中,并将其设置为入口点。 此外,如果需要,请公开调试端口:

    # 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. 通过将环境变量 ENABLE_DEBUG 设置为 true 来触发调试模式。 例如,使用 Azure CLI:

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

在运行此命令之前,请确保用您的值替换 <> 所围绕的占位符。

此方法提供了一种灵活的解决方案,通过更新启动时的环境变量,可以在调试模式下重启容器。 这避免了每次需要调试应用程序时,都要通过不同的 CMD 设置创建新的修订。

维护和性能注意事项

若要在一段时间内维护和优化应用程序的性能,请确保有效地管理环境变量更改、监视资源、保持依赖项 up-to日期、正确配置缩放以及设置监视警报。

环境变量更改

由于对环境变量的每次更改都需要一个新部署的修订版本,请一次性更改应用程序的所有密钥。 更改完成后,将机密链接到修订的环境变量。 此方法可最大程度地减少修订数,并帮助维护干净的部署历史记录。

资源分配

根据应用程序的性能和使用模式监视和调整容器的 CPU 和内存分配。 过度预配可能会导致不必要的成本,而预配不足可能会导致性能问题。

依赖项更新

定期更新依赖项,以受益于性能改进和安全修补程序。 使用类似于 npm-check-updates 自动化此过程的工具。

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

扩展

根据应用程序的负载配置自动缩放。 Azure 容器应用支持水平缩放,这会自动根据 CPU 或内存使用情况调整容器实例的数量。

以下示例演示如何设置基于 CPU 的规模规则。 在运行此命令之前,请确保用你的值替换由<>包围的占位符。

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

监视警报

设置监视和警报以跟踪应用程序的性能和运行状况。 使用 Azure Monitor 为特定指标(例如 CPU 使用率、内存使用情况和响应时间)创建警报。

在运行此命令之前,请确保用您的值替换被 <> 包围的占位符。

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%"

资源管理

使用适用于 Visual Studio Code 的 Azure 容器应用 扩展直接从 Visual Studio Code 快速创建、编辑和部署容器化应用。

故障排除

当应用程序在 Azure 容器应用中遇到运行时问题时,可以使用日志记录、远程调试和运行状况检查警报来查找和解决问题。

伐木业

启用和配置日志记录以捕获应用程序日志。 使用 Azure Monitor 和 Log Analytics 收集和分析日志。 在运行这些命令之前,请确保将用 <> 括起来的占位符替换为你的值。

  1. 创建新工作区。

    az monitor log-analytics workspace create \
        --resource-group <RESOURCE_GROUP> \
        --workspace-name <WORKSPACE_NAME>
    
  2. 然后创建新的工作区设置。

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

调试

使用 远程调试 工具连接到正在运行的容器。 确保 Dockerfile 公开调试所需的端口。

# Expose the debugging port
EXPOSE 9229

健康检查

配置运行状况检查以监视应用程序的运行状况。 此功能可确保 Azure 容器应用在无响应时可以重启容器。

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