你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。

使用自定义容器将自定义软件迁移到 Azure 应用服务

Azure 应用服务 在 Windows 上提供预定义的应用程序堆栈,例如 ASP.NET 或 Node.js。 这些应用程序堆栈在 IIS 上运行。 预配置的 Windows 环境锁定了操作系统,不允许对其进行以下操作:

  • 管理权限。
  • 软件安装。
  • 全局程序集缓存更改。

有关详细信息,请参阅应用服务上的操作系统功能。

可以从 Visual Studio 部署自定义配置的 Windows 映像,以进行应用所需的 OS 更改。 这样做可以轻松迁移需要自定义 OS 和软件配置的本地应用。 本教程演示如何将使用 Windows 字体库中安装的自定义字体的 ASP.NET 应用迁移到应用服务。 你将自定义配置的 Windows 映像从 Visual Studio 部署到 Azure 容器注册表,然后在应用服务中运行它。

显示 Windows 容器中运行的 Web 应用的屏幕截图。

先决条件

在本地设置应用

下载示例

在此步骤中,你将设置本地 .NET 项目。

示例项目包括一个简单的 ASP.NET 应用程序,该应用程序使用安装在 Windows 字体库中的自定义字体。 不需要安装字体。 但是,此示例是一个与基础 OS 集成的应用示例。 要将此类应用迁移到应用服务,可以选择重新构建代码以移除集成,或者通过自定义 Windows 容器将其按原样迁移。

安装字体

在 Windows 资源管理器中,转到 custom-font-win-container-master/CustomFontSample,右键单击 FrederickatheGreat-Regular.ttf,然后选择“ 安装”。

该字体在 Google Fonts 中公开发布。

运行应用

在 Visual Studio 中打开 custom-font-win-container-master/CustomFontSample.sln 文件。

选择 Ctrl+F5 在不调试的情况下运行应用。 该应用将显示在默认浏览器中。

显示在默认浏览器中显示的应用的屏幕截图。

由于应用使用已安装的字体,因此应用无法在应用服务沙盒中运行。 但是,你可以改用 Windows 容器来部署它,因为你可以在 Windows 容器中安装字体。

配置 Windows 容器

在解决方案资源管理器中,右键单击 CustomFontSample 项目,选择添加>容器业务流程支持。

解决方案资源管理器窗口中的 CustomFontSample 项目的屏幕截图。其中突出显示了“添加”和“容器业务流程协调程序支持”菜单项。

选择Docker ComposeOK。

现在,项目设置为在 Windows 容器中运行。 Dockerfile 添加到 CustomFontSample 项目,docker-compose 项目添加到解决方案。

在解决方案资源管理器中,打开 Dockerfile。

需要使用受支持的父映像。 通过将 FROM 行替换为以下代码,更改父映像:

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-ltsc2019

将以下行添加到文件末尾并保存该文件:

RUN ${source:-obj/Docker/publish/InstallFont.ps1}

可以在 CustomFontSample 项目中找到 InstallFont.ps1。 它是一个安装该字体的简单脚本。 可在 PowerShell 库 中找到更复杂的脚本版本。

注意

若要在本地测试 Windows 容器,请确保在本地计算机上启动 Docker。

发布到 Azure 容器注册表

Azure 容器注册表可以存储用于容器部署的映像。 可以将应用服务配置为使用容器注册表中托管的映像。

打开“发布”窗口

在解决方案资源管理器中,右键单击 CustomFontSample 项目,然后选择“ 发布”。

解决方案资源管理器中 CustomFontSample 项目的屏幕截图。突出显示了“发布”菜单项。

创建并发布注册表

  1. 在 “发布 ”窗口中,选择 “Azure”,然后选择“ 下一步”。

  2. 选择 Azure 容器注册表,然后选择“ 下一步”。

  3. 选择要在其中发布注册表的订阅,然后选择新建。

显示“发布”窗口的屏幕截图。突出显示了“创建新”按钮。

使用 Azure 帐户登录

在“Azure 容器注册表”窗口中,选择“添加帐户”,然后登录到 Azure 订阅。 如果已登录,请从下拉列表中选择包含所需订阅的帐户。

配置注册表

使用下表中的建议值作为指南配置新的容器注册表。 完成后,选择创建。

设置 建议值
DNS 前缀 保留生成的注册表名称,或将其更改为另一个唯一的名称。
资源组 选择“新建”,输入 myResourceGroup,然后选择“确定”。
SKU 基本。 有关详细信息,请参阅定价层。
注册表位置 西欧

Azure 容器注册表窗口的屏幕截图。

选择完成。

终端窗口随即打开并显示映像部署进度。 等待部署完成。

登录 Azure

登录到 Azure 门户。

创建 Web 应用

选择“创建资源”,然后在“Web 应用”下选择“创建”。

配置应用基本信息

在“ 基本信息 ”选项卡上,使用下表作为指南配置设置。

设置 建议值
订阅 确保选择正确的订阅。
资源组 选择 “新建”,输入 myResourceGroup,然后选择 “确定”。
名称 输入唯一名称。 Web 应用的 URL 为 https://<app-name>.azurewebsites.net,其中 <app-name> 是应用名称。
发布 容器
操作系统 Windows
区域 西欧
Windows 计划 选择 “新建”,输入 myAppServicePlan,然后选择 “确定”。

“ 基本信息 ”选项卡应如下所示:

用于配置 Web 应用的“基本信息”选项卡的屏幕截图。

配置 Windows 容器

在“ 容器 ”选项卡上,配置自定义 Windows 容器,如下表所示,然后选择“ 查看 + 创建”。

设置 建议值
图像来源 Azure 容器注册表
注册表 选择前面创建的镜像仓库。
图像 customfontsample
标记 最新

完成应用创建

选择“创建”,等待 Azure 创建所需的资源。

浏览到 Web 应用

部署完成后,会显示一条通知消息:

显示通知消息的屏幕截图。

  1. 选择“转到资源”。

  2. 在应用页中,选择 URL 下的链接。

如果会打开一个如以下页面所示的新浏览器页面:

显示 Web 应用的新浏览器页面的屏幕截图。

等待几分钟,然后重试,直到主页显示你所需的字体:

使用了你配置的字体的主页的屏幕截图。

恭喜! 已将 ASP.NET 应用程序迁移到 Windows 容器中的 App Service。

查看容器启动日志

加载 Windows 容器可能需要一些时间。 若要查看进度,请转到以下 URL。 (请将 <app-name> 替换为你的应用名称。)

https://<app-name>.scm.azurewebsites.net/api/logstream

流式传输的日志如下所示:

14/09/2018 23:16:19.889 INFO - Site: fonts-win-container - Creating container for image: customfontsample20180914115836.azurecr.io/customfontsample:latest.
14/09/2018 23:16:19.928 INFO - Site: fonts-win-container - Create container for image: customfontsample20180914115836.azurecr.io/customfontsample:latest succeeded. Container Id 329ecfedbe370f1d99857da7352a7633366b878607994ff1334461e44e6f5418
14/09/2018 23:17:23.405 INFO - Site: fonts-win-container - Start container succeeded. Container: 329ecfedbe370f1d99857da7352a7633366b878607994ff1334461e44e6f5418
14/09/2018 23:17:28.637 INFO - Site: fonts-win-container - Container ready
14/09/2018 23:17:28.637 INFO - Site: fonts-win-container - Configuring container
14/09/2018 23:18:03.823 INFO - Site: fonts-win-container - Container ready
14/09/2018 23:18:03.823 INFO - Site: fonts-win-container - Container start-up and configuration completed successfully

Azure 应用服务使用 Docker 容器技术来托管内置映像和自定义映像。 若要查看内置映像的列表,请运行 Azure CLI 命令 az webapp list-runtimes --os linux。 如果这些映像无法满足需要,可以生成并部署自定义映像。

注意

你的容器应面向 x86-64 体系结构。 ARM64 不受支持。

本教程介绍如何执行下列操作:

  • 将自定义 Docker 映像推送到 Azure 容器注册表。
  • 将自定义映像部署到应用服务。
  • 配置环境变量。
  • 使用托管标识将映像拉取到应用服务中。
  • 访问诊断日志。
  • 启用从容器注册表到应用服务的 CI/CD。
  • 使用 SSH 连接到容器。

完成本教程会导致 Azure 帐户中产生一点容器注册表费用,如果容器托管时间超过一个月,还会产生其他费用。

设置初始环境

本教程需要 2.0.80 或更高版本 Azure CLI。 如果你使用的是 Azure Cloud Shell,则已安装最新版本。

  • 安装 Docker,用于构建 Docker 映像。 安装 Docker 可能需要重启计算机。

安装 Docker 后,请打开终端窗口,验证是否已安装 Docker:

docker --version

克隆或下载示例应用

可以通过 Git 克隆或下载获取本教程的示例。

使用 Git Bash 克隆存储库

克隆示例存储库:

git clone https://github.com/Azure-Samples/docker-django-webapp-linux.git --config core.autocrlf=input

请确保包括 --config core.autocrlf=input 参数,以确保 Linux 容器中使用的文件中的行尾正确。

然后,导航到以下文件夹:

cd docker-django-webapp-linux

从 GitHub 下载 ZIP

可以转到 https://github.com/Azure-Samples/docker-django-webapp-linux 并选择 “代码>本地>下载 ZIP”,而不是使用 Git 克隆。

将 ZIP 文件解压缩到名为 docker-django-webapp-linux 的文件夹中。

然后在 docker-django-webapp-linux 文件夹中打开终端窗口。

准备样品容器

较新的浏览器终端和 az webapp exec 可提供对运行中的 Linux 容器进行认证访问,而无需 SSH 服务器或在镜像中嵌入 SSH 密码。 遗留的 Kudu WebSSH 和 az webapp ssh 访问可能仍然需要 SSH 服务器。 示例代码仓库包含旧版 SSH 配置。 在构建之前,将其 Dockerfile 的内容替换为以下仅适用于应用的配置:

FROM tiangolo/uwsgi-nginx-flask:python3.6

RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt --no-cache-dir
ADD . /code/

COPY init.sh /usr/local/bin/

RUN chmod u+x /usr/local/bin/init.sh
EXPOSE 8000

ENTRYPOINT ["init.sh"]

用以下脚本替换 init.sh 的内容,保留Linux行尾:

#!/bin/bash
set -e

exec python /code/manage.py runserver 0.0.0.0:8000

把遗留 的sshd_config 文件从本地样本文件夹里移除,这样 ADD . /code/ 就不会把它复制到镜像里。

Dockerfile 安装应用的依赖,复制应用和启动脚本,并公开应用的端口。 入口点只启动 Python 应用程序。 这些更改会移除样本的SSH安装和凭证。 映像包含较新的浏览器终端的 /bin/bash 以及默认的 az webapp exec shell。

在本地生成和测试映像

注意

Docker Hub 对每个 IP 的匿名拉取数和每个免费用户经过身份验证的拉取数施加配额。 如果发现来自 Docker Hub 的请求受到限制,请尝试运行 docker login(如果尚未登录)。

  1. 运行以下命令生成映像:

    docker build --tag appsvc-tutorial-custom-image .
    
  2. 通过在本地运行 Docker 容器来测试构建是否正常工作:

    docker run -it -p 8000:8000 appsvc-tutorial-custom-image
    

    此 docker run 命令使用 -p 参数指定端口并包括映像名称。 -it 允许你使用 Ctrl+C 停止它。

    提示

    如果在 Windows 上运行时显示错误“*standard_init_linux.go:211: exec 用户进程导致‘无此类文件或目录’”,则表示 init.sh 文件包含 CRLF 行尾而不是预期的 LF 行尾。 如果使用 Git 来克隆示例存储库,但省略了 --config core.autocrlf=input 参数,就会发生此错误。 在这种情况下,请使用 --config 参数再次克隆存储库。 如果已编辑 init.sh 并将其保存为 CRLF 行尾格式,也可能会发生此错误。 在这种情况下,请再次保存文件,并且仅使用 LF 行尾。

  3. 浏览到 http://localhost:8000,验证 Web 应用和容器是否正常运行。

    测试结果的屏幕截图。

创建用户分配的托管标识

应用服务可以使用默认托管标识或用户分配的托管标识向容器注册表进行身份验证。 在本教程中,你将使用用户分配的托管标识。

  1. 运行 az group create 命令创建资源组:

    az group create --name msdocs-custom-container-tutorial --location westeurope
    

    可以更改 --location 值以指定您附近的区域。

  2. 在资源组中创建托管标识:

    az identity create --name myID --resource-group msdocs-custom-container-tutorial
    

创建容器注册表

  1. 使用以下 az acr create 命令创建容器注册表。 用一个用于你的注册表的唯一名称替换 <registry-name>。 该名称只能包含字母和数字,并且必须在整个 Azure 中都是唯一的。

    az acr create --name <registry-name> --resource-group msdocs-custom-container-tutorial --sku Basic --admin-enabled true
    

    该 --admin-enabled 参数允许使用管理凭据将映像推送到注册表。

  2. 通过运行 az credential acr show 命令检索管理凭据:

    az acr credential show --resource-group msdocs-custom-container-tutorial --name <registry-name>
    

    此命令的 JSON 输出提供两个密码以及镜像仓库的用户名。

将示例映像推送到容器注册表

在本部分中,会将映像推送到 Container Registry,稍后由 App Service 使用。

  1. 在生成示例映像的本地终端中,使用 docker login 命令登录到容器注册表:

    docker login <registry-name>.azurecr.io --username <registry-username>
    

    将 <registry-name> 和 <registry-username> 替换为前面步骤中的值。 出现提示时,键入上一部分中的某个密码。

    在本部分的所有剩余步骤中使用相同的注册表名称。

  2. 成功登录后,为本地 Docker 映像打标签,以便将其推送到该注册表:

    docker tag appsvc-tutorial-custom-image <registry-name>.azurecr.io/appsvc-tutorial-custom-image:latest
    
  3. 使用 docker push 命令将映像推送到注册表:

    docker push <registry-name>.azurecr.io/appsvc-tutorial-custom-image:latest
    

    第一次上传映像可能需要几分钟时间,因为它包含基本映像。 后续上传速度通常较快。

    在等待期间,可以完成下一部分中的步骤,将应用服务配置为从注册表部署。

为注册表的托管标识授权

创建的托管标识尚未获得从容器注册表拉取的授权。 在此步骤中,你将启用授权。

  1. 检索托管标识的主体 ID:

    principalId=$(az identity show --resource-group msdocs-custom-container-tutorial --name myID --query principalId --output tsv)
    
  2. 检索容器注册表的资源 ID:

    registryId=$(az acr show --resource-group msdocs-custom-container-tutorial --name <registry-name> --query id --output tsv)
    
  3. 授予托管标识访问容器注册表的权限:

    az role assignment create --assignee $principalId --scope $registryId --role "AcrPull"
    

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

创建 Web 应用

  1. 使用 az appservice plan create 命令创建应用服务计划:

    az appservice plan create --name myAppServicePlan --resource-group msdocs-custom-container-tutorial --is-linux
    

    应用服务计划对应托管 Web 应用的虚拟机。 默认情况下,前面的命令使用平价的 B1 定价层,该定价层第一个月免费提供。 可以使用 --sku 参数指定层级。

  2. 使用 az webapp create 命令创建 Web 应用:

    az webapp create --resource-group msdocs-custom-container-tutorial --plan myAppServicePlan --name <app-name> --deployment-container-image-name <registry-name>.azurecr.io/appsvc-tutorial-custom-image:latest
    

    请将 <app-name> 替换为 Web 应用的名称。 该名称在全 Azure 中必须是唯一的。 同时将 <registry-name> 替换为上一部分中注册表的名称。

    提示

    可以随时使用命令 az webapp config container show --name <app-name> --resource-group msdocs-custom-container-tutorial 检索 Web 应用的容器设置。 该映像在 DOCKER_CUSTOM_IMAGE_NAME 属性中指定。 当通过 Azure DevOps 或 Azure 资源管理器模板部署 Web 应用时,映像还可能出现在名为 LinuxFxVersion 的属性中。 这两个属性的作用相同。 如果 Web 应用的配置中同时存在这两个属性,则优先使用 LinuxFxVersion。

配置 Web 应用

在此步骤中,按如下方式配置 Web 应用:

  • 将应用配置为将请求发送到端口 8000。 示例容器在端口 8000 上侦听 Web 请求。
  • 将应用配置为使用托管标识从容器注册表拉取映像。
  • 从容器注册表配置持续部署。 (每次将映像推送到注册表都会触发你的应用拉取新映像。)在将 Web 应用配置为从容器注册表拉取数据时,此步骤不是必需的,但它可以在有新映像被推送到注册表时通知 Web 应用。 如果未完成此步骤,则必须通过重启 Web 应用来手动触发映像拉取。
  1. 使用 az webapp config appsettings set 按应用代码的需要设置 WEBSITES_PORT 环境变量:

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

    将 <app-name> 替换为上一步使用的名称。

  2. 使用 az webapp identity assign 以下命令在 Web 应用中启用用户分配的托管标识:

    id=$(az identity show --resource-group msdocs-custom-container-tutorial --name myID --query id --output tsv)
    az webapp identity assign --resource-group msdocs-custom-container-tutorial --name <app-name> --identities $id
    

    将 <app-name> 替换为上一步使用的名称。

  3. 使用托管标识将应用配置为从 Container Registry 拉取。

    appConfig=$(az webapp config show --resource-group msdocs-custom-container-tutorial --name <app-name> --query id --output tsv)
    az resource update --ids $appConfig --set properties.acrUseManagedIdentityCreds=True
    

    将 <app-name> 替换为上一步使用的名称。

  4. 设置 Web 应用用于从容器注册表拉取的客户端 ID。 如果使用系统分配的托管标识,则不需要此步骤。

    clientId=$(az identity show --resource-group msdocs-custom-container-tutorial --name myID --query clientId --output tsv)
    az resource update --ids $appConfig --set properties.AcrUserManagedIdentityID=$clientId
    
  5. 在应用服务中启用 CI/CD。

    cicdUrl=$(az webapp deployment container config --enable-cd true --name <app-name> --resource-group msdocs-custom-container-tutorial --query CI_CD_URL --output tsv)
    

    CI_CD_URL 是应用服务为你生成的 URL。 注册表应使用此 URL 来通知应用服务发生了映像推送。 实际上,它并不会为你创建 Webhook。

  6. 在容器注册表中创建 Webhook。 使用上一步中获取的CI_CD_URL。

    az acr webhook create --name appserviceCD --registry <registry-name> --uri $cicdUrl --actions push --scope appsvc-tutorial-custom-image:latest
    
  7. 若要测试 Webhook 是否已正确配置,请 ping webhook 并查看是否获得 200 正常响应。

    eventId=$(az acr webhook ping --name appserviceCD --registry <registry-name> --query id --output tsv)
    az acr webhook list-events --name appserviceCD --registry <registry-name> --query "[?id=='$eventId'].eventResponseMessage"
    

    提示

    若要查看有关所有 Webhook 事件的所有信息,请删除 --query 参数。

    如果你正在流式查看容器日志,应在 Webhook ping 后看到 Starting container for site 消息,因为 Webhook 会触发应用重启。

转到 Web 应用

若要测试应用,请转到 https://<app-name>.azurewebsites.net。 将 <app-name> 替换为您的 Web 应用名称。

首次尝试访问应用时,应用可能需要一些时间来响应,因为 App Service 必须从镜像注册表中拉取整个映像。 如果浏览器超时,刷新页面即可。 拉取初始映像后,后续测试将运行得更快。

浏览器中运行的 Web 应用的屏幕截图。

访问诊断日志

等待应用服务拉取映像时,通过将容器日志流式传输到终端来确切了解应用服务执行的操作是非常有用的。

  1. 启用容器日志记录:

    az webapp log config --name <app-name> --resource-group msdocs-custom-container-tutorial --docker-container-logging filesystem
    
  2. 启用日志流:

    az webapp log tail --name <app-name> --resource-group msdocs-custom-container-tutorial
    

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

    还可以通过浏览器访问 https://<app-name>.scm.azurewebsites.net/api/logs/docker 来检查日志文件。

  3. 不论任何时候,想要停止日志流式处理,请选择 Ctrl+C。

修改应用程序代码并重新部署

在本部分中,你将对 Web 应用代码进行更改,重新生成映像,然后将其推送到容器注册表。 然后,应用服务会自动从容器注册表中拉取更新后的映像,以更新正在运行的 Web 应用。

  1. 在本地 docker-django-webapp-linux 文件夹中,打开文件 app/templates/app/index.html。

  2. 更改第一个 HTML 元素,使其与以下代码相匹配。

    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="#">Azure App Service - Updated Here!</a>
        </div>
      </div>
    </nav>
    
  3. 保存所做更改。

  4. 转到 docker-django-webapp-linux 文件夹并重新生成映像:

    docker build --tag appsvc-tutorial-custom-image .
    
  5. 将映像的标记更新为 latest:

    docker tag appsvc-tutorial-custom-image <registry-name>.azurecr.io/appsvc-tutorial-custom-image:latest
    

    将 <registry-name> 替换为注册表的名称。

  6. 将映像推送到镜像仓库:

    docker push <registry-name>.azurecr.io/appsvc-tutorial-custom-image:latest
    
  7. 当映像推送完成时,Webhook 会通知应用服务有关推送的信息,应用服务会尝试拉取更新后的映像。 等待几分钟,然后转到 https://<app-name>.azurewebsites.net,验证更新是否已部署。

使用 SSH 连接到容器

当容器运行时,你可以通过 App Service 的经过身份验证的 Kudu 代理,使用新版浏览器终端或 az webapp exec 访问它。 你不需要在镜像中安装或启动 SSH 服务器,即可使用这些选项。

关于访问先决条件以及应用或插槽控制,请参见 “打开容器 Shell (SSH) 并运行命令”。

打开与容器的 SSH 连接。

  1. 使用Azure CLI 2.90.0或更高版本,登录你的Azure账户。 在本地终端用预览 az webapp exec 命令打开交互式 shell:

    az webapp exec --resource-group msdocs-custom-container-tutorial --name <app-name>
    
  2. 如果图片中有,请使用使用 top,检查应用内正在运行的进程。

如需使用分离式命令执行而不是交互式会话,请参阅在没有交互式会话的情况下运行命令。 请求接受并不能确认命令是否成功完成。

清理资源

本文中创建的资源可能会持续产生费用。 若要清理资源,只需删除包含这些资源的资源组即可:

az group delete --name msdocs-custom-container-tutorial

后续步骤

你已了解:

  • 将自定义映像部署到专用容器注册表。
  • 将自定义映像部署到应用服务。
  • 更新并重新部署映像。
  • 访问诊断日志。
  • 使用 SSH 连接到容器。
  • 将自定义 Docker 映像推送到容器注册表。
  • 将自定义映像部署到应用服务。
  • 配置环境变量。
  • 使用托管标识将映像拉取到应用服务中。
  • 访问诊断日志。
  • 启用从容器注册表到应用服务的 CI/CD。
  • 使用 SSH 连接到容器。

前往下一个教程:

或者查看其他资源: