为 Azure 应用服务配置自定义容器

本文说明如何将自定义容器配置为在 Azure App Service 上运行。

本指南提供有关应用服务中的 Windows 应用的容器化的重要概念和说明。 若从未使用过 Azure 应用服务,则首先按照自定义容器快速入门教程进行操作。

本指南提供有关应用服务中的 Linux 应用的容器化的重要概念和说明。 若从未使用过 Azure 应用服务,则首先按照自定义容器快速入门教程进行操作。

支持的父映像

对于自定义 Windows 映像,必须为所需框架选择适当的父映像(基础映像)

  • 若要部署 .NET Framework 应用,请使用基于 Windows Server 2019 Core 长期服务渠道 (LTSC) 发行版的父映像。
  • 若要部署 .NET Core 应用,请使用基于 Windows Server 2019 Nano 半年服务渠道 (SAC) 发行版的父映像。

在应用启动期间,下载父映像需要一些时间。 不过,可以使用已缓存在 Azure 应用服务中的下述父映像之一,缩短启动时间:

更改自定义容器的 Docker 映像

若要将现有的自定义容器从当前 Docker 映像更改为新映像,请使用以下命令:

az webapp config container set --name <app-name> --resource-group <group-name> --docker-custom-image-name <docker-hub-repo>/<image>

使用专用注册表中的映像

使用专用注册表(如 Azure 容器注册表)中的映像,请运行以下命令:

az webapp config container set --name <app-name> --resource-group <group-name> --docker-custom-image-name <image-name> --docker-registry-server-url <private-repo-url> --docker-registry-server-user <username> --docker-registry-server-password <password>

对于 <username> 和 <password>,请提供专用注册表帐户的登录凭据。

使用托管标识从 Azure 容器注册表拉取映像

使用以下步骤配置 Web 应用,以使用托管标识从 ACR 进行拉取。 这些步骤将使用系统分配的托管标识,但你也可以使用用户分配的托管标识。

  1. 使用 az webapp identity assign 命令为 Web 应用启用系统分配的托管标识

    az webapp identity assign --resource-group <group-name> --name <app-name> --query principalId --output tsv
    

    <app-name> 替换为上一步使用的名称。 命令的输出(由 --query 和 --output 参数筛选)是所分配标识的服务主体 ID,你很快就会用到它。

  2. 获取 Azure 容器注册表的资源 ID:

    az acr show --resource-group <group-name> --name <registry-name> --query id --output tsv
    

    <registry-name> 替换为注册表的名称。 命令的输出(由 --query 和 --output 参数筛选)是 Azure 容器注册表的资源 ID。

  3. 授予托管标识访问容器注册表的权限:

    az role assignment create --assignee <principal-id> --scope <registry-resource-id> --role "AcrPull"
    

    请替换以下值:

    • <principal-id> 替换为 az webapp identity assign 命令中的服务主体 ID
    • <registry-resource-id> 替换为 az acr show 命令中的容器注册表 ID

    有关这些权限的详细信息,请参阅什么是 Azure 基于角色的访问控制

  4. 配置应用,以使用托管标识从 Azure 容器注册表进行拉取。

    az webapp config set --resource-group <group-name> --name <app-name> --generic-configurations '{"acrUseManagedIdentityCreds": true}'
    

    请替换以下值:

    • <app-name> 替换为 Web 应用的名称。

    提示

    如果使用 PowerShell 控制台运行命令,则需要在本步骤和下一步中将 --generic-configurations 参数中的字符串进行转义。 例如:--generic-configurations '{\"acrUseManagedIdentityCreds\": true'

  5. (可选)如果应用使用用户分配的托管标识,请确保在 Web 应用中已进行了相应配置,然后设置一个额外的 acrUserManagedIdentityID 属性来指定其客户端 ID:

    az identity show --resource-group <group-name> --name <identity-name> --query clientId --output tsv
    

    替换用户分配的托管标识的 <identity-name>,并使用输出 <client-id> 来配置用户分配的托管标识 ID。

    az  webapp config set --resource-group <group-name> --name <app-name> --generic-configurations '{"acrUserManagedIdentityID": "<client-id>"}'
    

全部设置完毕后,Web 应用现将使用托管标识从 Azure 容器注册表中进行拉取。

使用受网络保护的注册表中的映像

若要从虚拟网络或本地的注册表进行连接和拉取,需使用 VNet 集成功能将应用连接到虚拟网络。 对于具有专用终结点的 Azure 容器注册表,也需要这样做。 配置好网络和 DNS 解析后,可通过设置应用设置 WEBSITE_PULL_IMAGE_OVER_VNET=true 来启用通过 VNet 拉取映像的路由:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITE_PULL_IMAGE_OVER_VNET=true

我看不到更新的容器

如果将 Docker 容器的设置更改为指向新的容器,则可能需要几分钟,应用才能从新容器处理 HTTP 请求。 在拉取和启动新容器时,应用服务会继续处理来自旧容器的请求。 只有新容器已启动并准备好接收请求后,应用服务才会开始向新容器发送请求。

容器映像的存储方式

首次在应用服务中运行自定义 Docker 映像时,应用服务会执行 docker pull 并拉取所有映像层。 这些层存储在磁盘上,就像使用 Docker 本地一样。 应用服务每次重新启动时,都将执行 docker pull,但只拉取已更改的层。 如果没有更改,应用服务将使用本地磁盘上的现有层。

如果应用由于某种原因(例如向上和向下扩展定价层)更改了计算实例,应用服务必须再次拉取所有层。 如果横向扩展以添加更多实例,则也是这种情况。 在极少数情况下,应用实例可能会在无扩展操作的情况下发生变化。

配置端口号

默认情况下,应用服务假定自定义容器在端口 80 或端口 8080 上进行侦听。 如果容器侦听其他端口,请在应用服务应用中设置 WEBSITES_PORT 应用设置。 可以通过 Azure CLI 进行设置。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_PORT=8000

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITES_PORT"="8000"}

应用服务目前允许容器只为 HTTP 请求提供一个端口。

配置环境变量

自定义容器可能会使用需要在外部提供的环境变量。 可以通过 Azure CLI 传入它们。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings DB_HOST="myownserver.mysql.database.chinacloudapi.cn"

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"DB_HOST"="myownserver.mysql.database.chinacloudapi.cn"}

当应用运行时,应用服务应用设置会自动作为环境变量注入到进程中。 可以通过 URL https://<app-name>.scm.chinacloudsites.cn/Env 来验证容器环境变量。

如果应用使用专用注册表或 Docker Hub 中的映像,则用于访问存储库的凭据保存在环境变量 DOCKER_REGISTRY_SERVER_URLDOCKER_REGISTRY_SERVER_USERNAMEDOCKER_REGISTRY_SERVER_PASSWORD 中。 由于存在安全风险,这些保留变量名都不会向应用程序公开。

对于基于 IIS 或 .NET Framework(4.0 或更高版本)的容器,应用服务会将它们作为 .NET 应用设置和连接字符串自动注入到 System.ConfigurationManager 中。 对于所有其他语言或框架,它们作为该进程的环境变量提供,其中包含以下相应的前缀之一:

  • APPSETTING_
  • SQLCONTR_
  • MYSQLCONTR_
  • SQLAZURECOSTR_
  • POSTGRESQLCONTR_
  • CUSTOMCONNSTR_

此方法适用于单容器应用或多容器应用,其中环境变量是在 docker-compose.yml. docker-compose.override.yml 文件中指定的。

添加持久共享存储

你可以在你的自定义容器文件系统中使用 C:\home 目录,以便在重新启动时持续保存文件,并在不同的实例中共享它们。 提供 C:\home 目录是为了使自定义容器能够访问持久存储。

禁用持久存储后,写入 C:\home 目录的所有内容不会在应用重启或多个实例之间持久保存。 启用持久存储时,将保留写入到 C:\home 目录的所有内容,可由横向扩展的应用的所有实例访问。 此外,当容器启动时,容器的 C:\home 目录内的任何内容都将被持久存储上已存在的任何现有文件覆盖。

唯一的例外是 C:\home\LogFiles 目录,它用于存储容器和应用程序日志。 如果使用“文件系统”选项启用应用程序日志记录,此文件夹将始终在应用重启时保留,与启用或禁用持久存储无关。 换句话说,启用或禁用持久存储不会影响应用程序的日志记录行为。

默认在 Windows 自定义容器上禁用永久性存储。 若要启用,请通过 Azure CLI 或 Azure PowerShell 将 WEBSITES_ENABLE_APP_SERVICE_STORAGE 应用设置值设置为 true。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITES_ENABLE_APP_SERVICE_STORAGE"=true}

你可以在你的自定义容器文件系统中使用 /home 目录,以便在重新启动时持续保存文件,并在不同的实例中共享它们。 提供 /home 目录是为了使自定义容器能够访问持久存储。 在 /home 中保存数据将计入应用服务计划中包含的存储空间配额

禁用持久存储后,写入 /home 目录的所有内容不会在应用重启或多个实例之间持久保存。 启用持久存储时,将保留写入到 /home 目录的所有内容,可由横向扩展的应用的所有实例访问。 此外,当容器启动时,容器的 /home 目录内的任何内容都将被持久存储上已存在的任何现有文件覆盖。

唯一的例外是 /home/LogFiles 目录,它用于存储容器和应用程序日志。 如果使用“文件系统”选项启用应用程序日志记录,此文件夹将始终在应用重启时保留,与启用或禁用持久存储无关。 换句话说,启用或禁用持久存储不会影响应用程序的日志记录行为。

建议将数据写入 /home装载的 Azure 存储路径。 在重启期间,在这些路径外部写入的数据不会持久的,将会保存到独立于应用服务计划文件存储配额的平台托管主机磁盘空间。

默认在在 Linux 自定义容器上启用永久性存储。 若要禁用它,请通过 Azure CLI 或 Azure PowerShell 将 WEBSITES_ENABLE_APP_SERVICE_STORAGE 应用设置值设置为 false。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=false

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITES_ENABLE_APP_SERVICE_STORAGE"=false}

注意

你还可以配置自己的持久存储

检测 HTTPS 会话

应用服务在前端终止 TLS/SSL。 也就是说,TLS/SSL 请求永远不会进入你的应用程序。 不需要,也不应在应用程序中实现对 TLS/SSL 的任何支持。

前端位于 Azure 数据中心内。 如果对应用程序使用 TLS/SSL,则始终会安全加密 Internet 上的流量。

自定义 ASP.NET 计算机密钥注入

在容器启动过程中,会将自动生成的密钥作为 ASP.NET 加密例程的计算机密钥注入到容器中。 可以通过以下环境变量在容器中找到这些密钥MACHINEKEY_DecryptionMACHINEKEY_DecryptionKeyMACHINEKEY_ValidationKeyMACHINEKEY_Validation

如果你的应用程序依赖于这些密钥,则每次重启时的新密钥可能会重置 ASP.NET 窗体身份验证和视图状态。 要阻止自动重新生成密钥,请手动将其设置为应用服务应用设置

连接到容器

通过导航到 https://<app-name>.scm.chinacloudsites.cn/DebugConsole,可以直接连接到 Windows 容器以执行诊断任务。 工作原理如下:

  • 调试控制台允许执行交互式命令,如启动 PowerShell 会话、检查注册表项,以及导航整个容器文件系统。
  • 它独立于其上方的图形浏览器运行,只显示共享存储中的文件。
  • 在向外扩展的应用程序中,调试控制台连接到一个容器实例。 您可以从顶部菜单中的“实例”下拉列表中选择其他实例。
  • 重新启动应用后,你控制台内对容器所做的任何更改都不会保留(共享存储中的更改除外),因为它不是 Docker 映像的一部分。 要保存更改,如注册表设置和软件安装,请将其设为 Dockerfile 的一部分。

访问诊断日志

应用服务通过 Docker 主机以及容器中的活动记录操作。 默认情况下,会附带 Docker 主机中的日志(平台日志),但容器中的应用程序日志或 web 服务器日志则需要手动启用。 有关详细信息,请参阅启用应用程序日志记录启用 Web 服务器日志记录

有多种方法可访问 Docker 日志:

在 Azure 门户中配置

Docker 日志显示在门户中应用的“容器设置”页面中。 日志被截断,但可以单击“下载”来下载所有日志。

从 Kudu 控制台中

导航到 https://<app-name>.scm.chinacloudsites.cn/DebugConsole 并单击 LogFiles 文件夹以查看各个日志文件。 要下载整个 LogFiles 目录,请单击目录名称左侧的“下载”图标。 还可以使用 FTP 客户端访问此文件夹。

在控制台终端中,默认情况下无法访问 C:\home\LogFiles 文件夹,因为未启用持久共享存储。 要在控制台终端中启用此行为,请启用持久共享存储

如果尝试使用 FTP 客户端下载当前正在使用的 Docker 日志,可能会由于文件锁定而出现错误。

使用 Kudu API

直接导航到 https://<app-name>.scm.chinacloudsites.cn/api/logs/docker 以查看 Docker 日志的元数据。 你可能会看到列出了多个日志文件,而 href 属性允许你直接下载日志文件。

要将所有日志一起下载到一个 ZIP 文件中,请访问 https://<app-name>.scm.chinacloudsites.cn/api/logs/docker/zip

自定义容器内存

默认情况下,部署在 Azure 应用服务中的所有 Windows 容器均限制为 1 GB RAM。 可以通过 Azure CLI 或 Azure PowerShell 提供 WEBSITE_MEMORY_LIMIT_MB 应用设置来更改此值。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITE_MEMORY_LIMIT_MB=2000

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITE_MEMORY_LIMIT_MB"=2000}

该值以 MB 为单位定义,并且必须小于或等于主机的总物理内存。 例如,在具有 8 GB RAM 的应用服务计划中,所有应用的累积总数 WEBSITE_MEMORY_LIMIT_MB 不得超过 8 GB。 有关每个定价层可用内存的信息,可以在高级 v3 服务计划章节中的应用服务定价中找到。

自定义计算核心数

默认情况下,Windows 容器将运行,其中包含所选定价层的所有可用内核。 例如,你可能想要减少过渡槽使用的内核数。 要减少容器使用的内核数,请将 WEBSITE_CPU_CORES_LIMIT 应用设置设置为首选的内核数。 可以通过 Azure CLI 进行设置。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --slot staging --settings WEBSITE_CPU_CORES_LIMIT=1

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"WEBSITE_CPU_CORES_LIMIT"=1}

注意

更新应用设置会触发自动重新启动,从而使停机时间最短。 对于生产应用,请考虑将其交换到过渡槽,在过渡槽中更改应用设置,然后将其交换回生产。

转到 Kudu 控制台 (https://<app-name>.scm.chinacloudsites.cn) 并使用 PowerShell 键入以下命令,以验证调整后的数字。 每个命令输出一个数字。

Get-ComputerInfo | ft CsNumberOfLogicalProcessors # Total number of enabled logical processors. Disabled processors are excluded.
Get-ComputerInfo | ft CsNumberOfProcessors # Number of physical processors.

处理器可能是多核处理器或超线程处理器。 有关每个定价层可用内核数的信息,可以在高级 v3 服务计划章节中的应用服务定价中找到。

自定义运行状况 ping 行为

应用服务认为容器在容器启动时成功启动并响应 HTTP ping。 运行状况 ping 请求包含标头 User-Agent= "App Service Hyper-V Container Availability Check"。 如果容器在一段时间后启动但不响应 ping,应用服务会在 Docker 日志中记录一个事件,指出容器未启动。

如果你的应用程序占用大量资源,则容器可能无法及时响应 HTTP ping。 要控制 HTTP ping 失败时的操作,请设置 CONTAINER_AVAILABILITY_CHECK_MODE 应用设置。 可以通过 Azure CLI 进行设置。 在 Bash 中:

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings CONTAINER_AVAILABILITY_CHECK_MODE="ReportOnly"

在 PowerShell 中运行:

Set-AzWebApp -ResourceGroupName <group-name> -Name <app-name> -AppSettings @{"CONTAINER_AVAILABILITY_CHECK_MODE"="ReportOnly"}

下表列出了可能的值:

“值” 说明
Repair 三次连续可用性检查后重启容器
ReportOnly 默认值。 请不要重启容器,但在三次连续的可用性检查后,会在容器的 Docker 日志中报告。
关闭 不检查可用性。

支持组托管服务帐户

应用服务中的 Windows 容器当前不支持组托管服务帐户 (gMSA)。

启用 SSH

SSH 实现容器和客户端之间的安全通信。 为了使自定义容器支持 SSH,你必须将其添加到 Docker 映像本身。

提示

应用服务中的所有内置 Linux 容器已将 SSH 说明添加到了其映像存储库中。 你可以使用 Node.js 10.14 存储库完成以下说明,以了解如何在其中启用。 Node.js 内置映像中的配置略有不同,但原理相同。

  • sshd_config 文件 添加到存储库,如以下示例中所示。

    Port 			2222
    ListenAddress 		0.0.0.0
    LoginGraceTime 		180
    X11Forwarding 		yes
    Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr
    MACs hmac-sha1,hmac-sha1-96
    StrictModes 		yes
    SyslogFacility 		DAEMON
    PasswordAuthentication 	yes
    PermitEmptyPasswords 	no
    PermitRootLogin 	yes
    Subsystem sftp internal-sftp
    

    注意

    此文件配置 OpenSSH 并且必须包括以下项:

    • Port 必须设置为 2222。
    • Ciphers 必须至少包含此列表中的一项:aes128-cbc,3des-cbc,aes256-cbc
    • MACs 必须至少包含此列表中的一项:hmac-sha1,hmac-sha1-96
  • 向存储库添加 ssh_setup 脚本文件,以使用 ssh-keygen 创建 SSH 密钥。

    #!/bin/sh
    
    ssh-keygen -A
    
    #prepare run dir
    if [ ! -d "/var/run/sshd" ]; then
        mkdir -p /var/run/sshd
    fi
    
  • 在 Dockerfile 中,添加以下命令:

    # Install OpenSSH and set the password for root to "Docker!". In this example, "apk add" is the install instruction for an Alpine Linux-based image.
    RUN apk add openssh \
         && echo "root:Docker!" | chpasswd 
    
    # Copy the sshd_config file to the /etc/ssh/ directory
    COPY sshd_config /etc/ssh/
    
    # Copy and configure the ssh_setup file
    RUN mkdir -p /tmp
    COPY ssh_setup.sh /tmp
    RUN chmod +x /tmp/ssh_setup.sh \
        && (sleep 1;/tmp/ssh_setup.sh 2>&1 > /dev/null)
    
    # Open port 2222 for SSH access
    EXPOSE 80 2222
    

    注意

    根密码必须恰好是 Docker!,因为应用服务使用它以使你能够使用容器访问 SSH 会话。 此配置不允许从外部建立到容器的连接。 容器的端口 2222 只能在专用虚拟网络的桥网络中访问,Internet 上的攻击者无法访问该端口。

  • 在容器的启动脚本中启动 SSH 服务器。

    /usr/sbin/sshd
    

访问诊断日志

可以访问在容器中生成的控制台日志。

首先,请运行以下命令,以便启用容器日志记录功能:

az webapp log config --name <app-name> --resource-group <resource-group-name> --docker-container-logging filesystem

<app-name><resource-group-name> 替换为适合 Web 应用的名称。

启用容器日志记录功能以后,请运行以下命令来查看日志流:

az webapp log tail --name <app-name> --resource-group <resource-group-name>

如果没有立即看到控制台日志,请在 30 秒后重新查看。

若要随时停止日志流式处理,可键入 CtrlC。

也可通过浏览器在 https://<app-name>.scm.chinacloudsites.cn/api/logs/docker 中检查日志文件。

配置多容器应用

在 Docker Compose 中使用持久性存储

多容器应用(如 WordPress)需要持久存储才能正常工作。 要启用持久存储,你的 Docker Compose 配置必须指向容器外部的存储位置。 在应用重新启动后,容器中的存储位置不会保存更改。

通过在 Azure CLI 中使用 az webapp config appsettings set 命令来设置 WEBSITES_ENABLE_APP_SERVICE_STORAGE 应用设置,以此启用持久存储。

az webapp config appsettings set --resource-group <group-name> --name <app-name> --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=TRUE

docker-compose.yml 文件中,将 volumes 选项映射到 ${WEBAPP_STORAGE_HOME}

WEBAPP_STORAGE_HOME 是应用服务中已映射到应用持续性存储的环境变量。 例如:

wordpress:
  image: <image name:tag>
  volumes:
  - ${WEBAPP_STORAGE_HOME}/site/wwwroot:/var/www/html
  - ${WEBAPP_STORAGE_HOME}/phpmyadmin:/var/www/phpmyadmin
  - ${WEBAPP_STORAGE_HOME}/LogFiles:/var/log

预览版限制

多容器目前以预览版提供。 不支持以下应用服务平台功能:

  • 身份验证/授权
  • 托管标识
  • CORS
  • Docker Compose 方案不支持 VNET 集成
  • 目前,Azure 应用服务上的 Docker Compose 的限制为 4,000 个字符。

Docker Compose 选项

以下列表显示了支持和不支持的 Docker Compose 配置选项:

支持的选项

  • 命令
  • entrypoint
  • 环境
  • image
  • ports
  • restart
  • services
  • volumes

不支持的选项

  • build(不允许)
  • depends_on(已忽略)
  • networks(忽略)
  • secrets(忽略)
  • 除了 80 和 8080 之外的端口(忽略)
  • 默认环境变量(如 $variable and ${variable})与 docker 中的不同

语法限制

  • “version x.x”始终需要是文件中的第一个 YAML 语句
  • 端口部分必须使用带引号的数字
  • 图像和卷部分必须带引号,并且不能具有权限定义
  • 卷部分在卷名后不得有空的大括号

注意

未显式指明的任何其他选项在公共预览版中将被忽略。

日志中的 robots933456

你可能会在容器日志中看到以下消息:

2019-04-08T14:07:56.641002476Z "-" - - [08/Apr/2019:14:07:56 +0000] "GET /robots933456.txt HTTP/1.1" 404 415 "-" "-"

可以放心忽略此消息。 /robots933456.txt 是一个虚拟 URL 路径,应用服务使用它来检查容器能否为请求提供服务。 404 响应只是指示该路径不存在,但它让应用服务知道容器处于正常状态并已准备就绪,可以响应请求。

后续步骤

或者参阅其他某些资源: