在本文中,你将创建一个在 Linux 容器中运行的函数应用,并将其从容器注册表部署到 Azure 容器应用环境。 通过部署到容器应用,可以将函数应用集成到云原生微服务中。 有关详细信息,请参阅 Azure Functions 的 Azure 容器应用托管。
重要
Azure 容器应用中直接运行 Azure Functions 的新托管方法现已推出(请参阅 公告),建议用于大多数新工作负载。 通过此集成,您可以充分利用 Azure 容器应用的全部特性和功能,同时受益于 Azure Functions 提供的函数编程模型和自动扩展的简单性。
有关详细信息,请参阅 Azure 容器应用中的本机 Azure Functions 支持
本文介绍如何创建在 Linux 容器中运行的函数,并将容器部署到容器应用环境。
完成本快速入门会在你的 Azure 帐户中产生几美分甚至更少的费用,在完成后,可以通过清理资源来最大程度地减少该费用。
选择开发语言
首先,使用 Azure Functions 工具,借助特定于语言的 Linux 基础映像在 Docker 容器中将项目代码创建为函数应用。 确保在文章顶部选择所选语言。
Core Tools 会为项目自动生成一个 Dockerfile,它使用最新版本的、适用于你的函数语言的正确基础映像。 应定期通过最新基础映像更新容器,并通过容器的更新版本重新部署。 有关详细信息,请参阅创建容器化函数应用。
先决条件
在开始之前,必须满足以下要求:
安装 .NET 8.0 SDK。
安装 Azure Functions Core Tools 版本 4.0.5198 或更高版本。
- 安装 Azure Functions Core Tools 版本 4.x。
- 安装 Azure Functions 支持的Node.js 版本。
- 安装 Azure Functions 支持的 Python 版本。
- 安装 .NET 6 SDK。
安装 Apache Maven 版本 3.0 或更高版本。
- Azure CLI 2.4 或更高版本。
如果没有 Azure 订阅,请在开始之前创建一个 Azure 免费帐户。
若要将创建的容器化函数应用映像发布到容器注册表,需要 Docker ID 和 Docker(在本地计算机上运行)。 如果没有 Docker ID,可以创建 Docker 帐户。
还需要完成容器注册表快速入门中的创建容器注册表部分,才能创建注册表实例。 记下完全限定的登录服务器名称。
创建并激活虚拟环境
在适当的文件夹中,运行以下命令以创建并激活一个名为 .venv
的虚拟环境。 确保使用 Azure Functions 支持的 Python 版本之一。
python -m venv .venv
source .venv/bin/activate
如果 Python 未在 Linux 分发版中安装 venv 包,请运行以下命令:
sudo apt-get install python3-venv
所有后续命令将在这个已激活的虚拟环境中运行。
创建并测试本地 Functions 项目
在终端或命令提示符下,根据所选的语言运行以下命令,以便在当前文件夹中创建一个函数应用项目:
func init --worker-runtime dotnet-isolated --docker
func init --worker-runtime node --language javascript --docker
func init --worker-runtime powershell --docker
func init --worker-runtime python --docker
func init --worker-runtime node --language typescript --docker
在空的文件夹中,运行以下命令以从 Maven 原型生成 Functions 项目:
mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=8 -Ddocker
-DjavaVersion
参数告诉 Functions 运行时要使用哪个 Java 版本。 如果希望函数在 Java 11 上运行,请使用 -DjavaVersion=11
。 如果不指定 -DjavaVersion
,则 Maven 默认使用 Java 8。 有关详细信息,请参阅 Java 版本。
重要
要完成本文中的步骤,JAVA_HOME
环境变量必须设置为正确版本的 JDK 的安装位置。
Maven 会请求你提供所需的值,以在部署上完成项目的生成。 按照提示提供以下信息:
提示 | 值 | 说明 |
---|---|---|
groupId | com.fabrikam |
一个值,用于按照 Java 的包命名规则在所有项目中标识你的项目。 |
artifactId | fabrikam-functions |
一个值,该值是 jar 的名称,没有版本号。 |
版本 | 1.0-SNAPSHOT |
选择默认值。 |
包 | com.fabrikam.functions |
一个值,该值是所生成函数代码的 Java 包。 使用默认值。 |
键入 Y
或按 Enter 进行确认。
Maven 在名为 artifactId 的新文件夹(在此示例中为 )中创建项目文件。
--docker
选项生成该项目的 Dockerfile,其中定义了适合用于 Azure Functions 和所选运行时的容器。
导航到项目文件夹:
cd fabrikam-functions
使用以下命令将一个函数添加到项目,其中,--name
参数是该函数的唯一名称,--template
参数指定该函数的触发器。 func new
将在项目中创建一个 C# 代码文件。
func new --name HttpExample --template "HTTP trigger"
使用以下命令将一个函数添加到项目,其中,--name
参数是该函数的唯一名称,--template
参数指定该函数的触发器。 func new
创建一个与函数名称匹配的子文件夹,该子文件夹包含一个名为 function.json 的配置文件。
func new --name HttpExample --template "HTTP trigger"
若要在本地测试函数,请启动项目文件夹的根目录中的本地 Azure Functions 运行时主机。
func start
func start
npm install
npm start
mvn clean package
mvn azure-functions:run
看到 HttpExample
终结点写入输出后,导航到该终结点。 响应输出中应该可看到欢迎消息。
看到 HttpExample
终结点写入输出后,导航到 http://localhost:7071/api/HttpExample?name=Functions
。 浏览器一定会显示一条“hello”消息,该消息回显 Functions
(提供给 name
查询参数的值)。
按 Ctrl+C(在 macOS 上按 Command+C)停止主机。
生成容器映像并在本地验证
(可选)检查项目文件夹的根目录中的“Dockerfile”。 Dockerfile 描述了在 Linux 上运行函数应用所需的环境。 Azure Functions 支持的基础映像的完整列表可以在 Azure Functions 基础映像页中找到。
在项目根文件夹中运行 docker build 命令,并提供一个名称作为 azurefunctionsimage
,提供一个标记作为 v1.0.0
。 将 <DOCKER_ID>
替换为 Docker 中心帐户 ID。 此命令为容器生成 Docker 映像。
docker build --tag <DOCKER_ID>/azurefunctionsimage:v1.0.0 .
该命令完成后,可在本地运行新容器。
若要验证生成,请使用 docker run 命令运行本地容器中的映像,再次将 <DOCKER_ID>
替换为你的 Docker Hub 帐户 ID,并将端口参数添加为 -p 8080:80
:
docker run -p 8080:80 -it <DOCKER_ID>/azurefunctionsimage:v1.0.0
映像在本地容器中启动后,请浏览到 http://localhost:8080/api/HttpExample
,其中一定会显示与先前相同的问候消息。 由于你创建的 HTTP 触发函数使用匿名授权,因此你无需获取访问密钥即可调用容器中运行的函数。 有关详细信息,请参阅授权密钥。
映像在本地容器中启动后,请浏览到 http://localhost:8080/api/HttpExample?name=Functions
,其中一定会显示与先前相同的“hello”消息。 由于你创建的 HTTP 触发函数使用匿名授权,因此你无需获取访问密钥即可调用容器中运行的函数。 有关详细信息,请参阅授权密钥。
验证容器中的函数应用后,按 Ctrl+C(在 macOS 上按 Command+C)停止执行。
将容器映像发布到注册表
若要使容器映像可用于部署到托管环境,必须将其推送到容器注册表。 作为安全最佳做法,应使用 Azure 容器注册表实例并强制实施基于托管标识的连接。 Docker Hub 要求你密钥进行身份验证,这使得部署更容易受到攻击。
Azure 容器注册表是用于生成、存储和管理容器映像和相关项目的专用注册表服务。 应使用专用注册表服务将容器发布到 Azure 服务。
运行以下命令,使用你的当前 Azure 凭据登录到注册表实例:
az acr login --name <REGISTRY_NAME>
在上一个命令中,将
<REGISTRY_NAME>
替换为容器注册表实例的名称。运行以下命令,使用你的注册表登录服务器的完全限定名称标记映像:
docker tag <DOCKER_ID>/azurefunctionsimage:v1.0.0 <LOGIN_SERVER>/azurefunctionsimage:v1.0.0
将
<LOGIN_SERVER>
替换为注册表登录服务器的完全限定名称,并将<DOCKER_ID>
替换为 Docker ID。使用以下命令将容器推送到注册表实例:
docker push <LOGIN_SERVER>/azurefunctionsimage:v1.0.0
创建函数的支持性 Azure 资源
需要先创建三个资源,然后才可将容器部署到 Azure:
- 一个资源组:相关资源的逻辑容器。
- 一个存储帐户:用于维护有关函数的状态和其他信息。
- 创建具有 Log Analytics 工作区的 Azure 容器应用环境。
- 用户分配的托管标识,使函数应用能够在不使用共享密钥的情况下安全地连接到 Azure 资源。 相反,与 Azure 存储帐户以及 Azure 容器注册表实例的连接,是通过使用带有该标识的 Microsoft Entra 身份验证来实现的,此场景推荐这种做法。
注意
Docker Hub 不支持托管标识。
使用以下命令创建所需的 Azure 资源:
如有必要,请登录到 Azure:
使用
az login
命令登录到 Azure 帐户。 如果你有多个订阅与帐户相关联,请使用az account set
。运行以下命令,将 Azure CLI 更新到最新版本:
az upgrade
如果你的 Azure CLI 版本不是最新版本,则会开始安装。 升级方式取决于操作系统。 升级完成后,可以继续。
运行以下命令以升级 Azure 容器应用扩展并注册容器应用所需的命名空间:
az extension add --name containerapp --upgrade -y az provider register --namespace Microsoft.Web az provider register --namespace Microsoft.App az provider register --namespace Microsoft.OperationalInsights
创建名为
AzureFunctionsContainers-rg
的资源组。az group create --name AzureFunctionsContainers-rg --___location eastus
此
az group create
命令在美国东部区域创建一个资源组。 如果你想要使用附近的区域,请使用 az account list-locations 命令返回的可用区域代码。 必须将后续命令修改为使用自定义区域而不是eastus
。创建启用了工作负荷配置文件的 Azure 容器应用环境。
az containerapp env create --name MyContainerappEnvironment --enable-workload-profiles --resource-group AzureFunctionsContainers-rg --___location eastus
此命令可能需要几分钟才能完成。
在资源组和区域中创建常规用途存储帐户,而无需共享密钥访问。
az storage account create --name <STORAGE_NAME> --___location eastus --resource-group AzureFunctionsContainers-rg --sku Standard_LRS --allow-blob-public-access false --allow-shared-key-access false
az storage account create
命令创建的存储帐户,只能由获得特定资源访问权限、通过 Microsoft Entra 身份验证的标识来访问。在上一个示例中,将
<STORAGE_NAME>
替换为适合你且在 Azure 存储中唯一的名称。 存储名称只能包含 3 到 24 个数字和小写字母字符。Standard_LRS
指定 Functions 支持的常规用途帐户。创建一个托管标识,并使用返回的
principalId
为其授予对存储帐户的访问权限以及在注册表实例中的拉取权限。principalId=$(az identity create --name <USER_IDENTITY_NAME> --resource-group AzureFunctionsContainers-rg --___location eastus --query principalId -o tsv) acrId=$(az acr show --name <REGISTRY_NAME> --query id --output tsv) az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal --role acrpull --scope $acrId storageId=$(az storage account show --resource-group AzureFunctionsContainers-rg --name <STORAGE_NAME> --query 'id' -o tsv) az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal --role "Storage Blob Data Owner" --scope $storageId
az identity create
命令创建用户分配的托管标识,az role assignment create
命令则会将标识添加到所需的角色。 将<REGISTRY_NAME>
、<USER_IDENTITY_NAME>
和<STORAGE_NAME>
分别替换为现有容器注册表的名称、托管标识名称和存储帐户名称。 现在,应用可以使用托管标识来访问存储帐户和 Azure 容器注册表,而无需使用共享密钥。
使用映像在 Azure 上创建并配置函数应用
Azure 上的函数应用将管理函数在 Azure 容器应用环境中的执行。 在本部分,你将使用上一部分所述的 Azure 资源,基于容器应用环境中容器注册表内的某个映像创建一个函数应用。 还可以使用所需 Azure 存储帐户的连接字符串配置新环境。
使用 az functionapp create
命令在 Azure 容器应用支持的新托管环境中创建函数应用。 在 az functionapp create
中,参数 --environment
指定容器应用环境。
提示
为确保你的函数应用使用基于托管标识的连接访问注册表实例,请不要在 --image
中设置 az functionapp create
参数。 在存储库中为 --image
设置映像的完全限定名称时,系统将从注册表获取共享密钥凭据,并存储在应用设置中。
首先,必须获取用户分配的托管标识的完全限定 ID 值,并拥有对注册表的拉取访问权限,然后通过 az functionapp create
命令使用默认映像创建函数应用,并向其分配此标识。
UAMI_RESOURCE_ID=$(az identity show --name $uami_name --resource-group $group --query id -o tsv)
az functionapp create --name <APP_NAME> --storage-account <STORAGE_NAME> --environment MyContainerappEnvironment --workload-profile-name "Consumption" --resource-group AzureFunctionsContainers-rg --functions-version 4 --assign-identity $UAMI_RESOURCE_ID
在 az functionapp create
中,将 --assign-identity
托管标识分配给新应用。 由于未在 --image
中设置 az functionapp create
参数,因此系统将使用占位符映像创建应用程序。
在此示例中,请将 <APP_NAME>
、<STORAGE_NAME>
和 <USER_IDENTITY_NAME>
替换为你新函数应用的名称,以及存储帐户和标识的名称。
最后,你必须将 linuxFxVersion
站点设置更新为存储库中映像的完全限定名称。 还必须更新和 acrUseManagedIdentityCreds
和 acrUserManagedIdentityID
站点设置,以便在从注册表获取映像时使用托管标识。
UAMI_RESOURCE_ID=$(az identity show --name <USER_IDENTITY_NAME> --resource-group AzureFunctionsContainers-rg --query id -o tsv)
az resource patch --resource-group AzureFunctionsContainers-rg --name <APP_NAME> --resource-type "Microsoft.Web/sites" --properties "{ \"siteConfig\": { \"linuxFxVersion\": \"DOCKER|<REGISTRY_NAME>.azurecr.io/azurefunctionsimage:v1.0.0\", \"acrUseManagedIdentityCreds\": true, \"acrUserManagedIdentityID\":\"$UAMI_RESOURCE_ID\", \"appSettings\": [{\"name\": \"DOCKER_REGISTRY_SERVER_URL\", \"value\": \"<REGISTRY_NAME>.azurecr.io\"}]}}"
除了所需的站点设置,az resource patch
命令还会将 DOCKER_REGISTRY_SERVER_URL
应用设置更新为注册表服务器的 URL。
在此示例中,分别替换 <APP_NAME>
<REGISTRY_NAME>
函数 <USER_IDENTITY_NAME>
应用、容器注册表和标识的名称。
指定 --workload-profile-name "Consumption"
会在一个使用默认 Consumption
工作负荷配置文件的环境中创建应用,其成本与在容器应用消耗计划中运行的成本相同。 首次创建函数应用时,它会从注册表中拉取初始映像。
更新应用程序设置
若要使 Functions 主机能够使用共享密钥连接到默认存储帐户,你必须将 AzureWebJobsStorage
连接字符串设置替换为使用用户分配的托管标识的等效设置,以连接到存储帐户。
删除现有的
AzureWebJobsStorage
连接字符串设置:az functionapp config appsettings delete --name <APP_NAME> --resource-group AzureFunctionsContainers-rg --setting-names AzureWebJobsStorage
az functionapp config appsettings delete 命令会从应用中删除该设置。 将
<APP_NAME>
替换为你的函数应用的名称。添加包含
AzureWebJobsStorage__
前缀的等效设置,用于定义默认存储帐户的用户分配的托管标识连接:clientId=$(az identity show --name <USER_IDENTITY_NAME> --resource-group AzureFunctionsContainers-rg --query 'clientId' -o tsv) az functionapp config appsettings set --name <APP_NAME> --resource-group AzureFunctionsContainers-rg --settings AzureWebJobsStorage__accountName=<STORAGE_NAME> AzureWebJobsStorage__credential=managedidentity AzureWebJobsStorage__clientId=$clientId
在此示例中,将
<APP_NAME>
、<USER_IDENTITY_NAME>
和<STORAGE_NAME>
分别替换为函数应用名称、标识名称和存储帐户名称。
此时,你的函数正在容器应用环境中运行,其中已添加所需的应用程序设置。 如果需要,可以使用 Functions 的标准方式在函数应用中添加其他设置。 有关详细信息,请参阅使用应用程序设置。
提示
对函数代码进行后续更改时,需要重新生成容器,将映像重新发布到注册表,并使用新的映像版本更新函数应用。 有关详细信息,请参阅更新注册表中的映像
在 Azure 上验证函数
将映像部署到 Azure 中的函数应用后,现在可以通过 HTTP 请求调用函数。
请运行以下
az functionapp function show
命令以获取新函数的 URL:az functionapp function show --resource-group AzureFunctionsContainers-rg --name <APP_NAME> --function-name HttpExample --query invokeUrlTemplate
将
<APP_NAME>
替换为你的函数应用的名称。
- 使用刚刚获取的 URL 调用
HttpExample
函数终结点,追加查询字符串?name=Functions
。
- 使用刚刚获取的 URL 调用
HttpExample
函数终结点。
导航到此 URL 时,浏览器显示的输出一定与在本地运行函数时显示的输出类似。
请求 URL 应如下所示:
https://myacafunctionapp.kindtree-796af82b.eastus.azurecontainerapps.io/api/httpexample?name=functions
https://myacafunctionapp.kindtree-796af82b.eastus.azurecontainerapps.io/api/httpexample
清理资源
若要继续借助在本文中创建的资源来使用 Azure Functions,可以保留所有这些资源。
此函数应用部署完成后,请删除 AzureFunctionsContainers-rg
资源组以清理该组中的所有资源:
az group delete --name AzureFunctionsContainers-rg