Azure Functions
概览
Azure Functions 允许在 Microsoft Azure 上进行无服务器计算。构建事件驱动的应用程序,自动缩放,灵活绑定到各种 Azure 服务,并集成通过 Application Insights 进行监控。
何时使用
- HTTP APIs 和 webhooks
- 消息驱动处理(Service Bus,Event Hub)
- 计划作业和 CRON 表达式
- 文件和 blob 处理
- 基于队列的工作流
- 实时数据处理
- 微服务和后端逻辑
- 与 Azure 生态系统服务集成
实施示例
1. 使用 Azure CLI 创建 Azure 函数
# 安装 Azure Functions Core Tools
curl https://aka.ms/install-artifacts-ubuntu.sh | bash
# 登录到 Azure
az login
# 创建资源组
az group create --name myapp-rg --location eastus
# 创建存储账户(函数必需)
az storage account create \
--name myappstore \
--location eastus \
--resource-group myapp-rg \
--sku Standard_LRS
# 创建函数应用
az functionapp create \
--resource-group myapp-rg \
--consumption-plan-location eastus \
--runtime node \
--runtime-version 18 \
--functions-version 4 \
--name myapp-function \
--storage-account myappstore
# 在应用中创建函数
func new --name HttpTrigger --template "HTTP trigger"
# 配置认证
az functionapp auth update \
--resource-group myapp-rg \
--name myapp-function \
--enabled true \
--action RedirectToLoginPage \
--default-provider AzureActiveDirectory
# 部署函数
func azure functionapp publish myapp-function
# 检查部署
az functionapp list --output table
# 获取函数详情
az functionapp function show \
--resource-group myapp-rg \
--name myapp-function \
--function-name HttpTrigger
2. Azure 函数实现(Node.js)
// HttpTrigger/index.js
module.exports = async function (context, req) {
context.log('HTTP trigger function processed request.');
// 提取请求数据
const name = req.query.name || (req.body && req.body.name);
const requestId = context.traceContext.traceparent;
try {
// 验证输入
if (!name) {
return {
status: 400,
body: { error: 'Name parameter is required' }
};
}
// 业务逻辑
const response = {
message: `Hello ${name}!`,
timestamp: new Date().toISOString(),
requestId: requestId
};
// 记录到 Application Insights
context.log({
level: 'info',
message: 'Request processed successfully',
name: name,
requestId: requestId
});
return {
status: 200,
headers: {
'Content-Type': 'application/json',
'X-Request-ID': requestId
},
body: response
};
} catch (error) {
context.log.error('Error processing request:', error);
return {
status: 500,
body: { error: 'Internal server error' }
};
}
};
// TimerTrigger/index.js
module.exports = async function (context, myTimer) {
const timeStamp = new Date().toISOString();
if (myTimer.isPastDue) {
context.log('Timer function is running late!');
}
// 处理计划作业
context.log(`Timer trigger function ran at ${timeStamp}`);
context.log('Processing batch job...');
// 模拟工作
await new Promise(resolve => setTimeout(resolve, 1000));
context.log('Batch job completed');
};
// ServiceBusQueueTrigger/index.js
module.exports = async function (context, mySbMsg) {
context.log('ServiceBus queue trigger function processed message:', mySbMsg);
try {
const messageBody = typeof mySbMsg === 'string' ? JSON.parse(mySbMsg) : mySbMsg;
// 处理消息
await processMessage(messageBody);
context.log('Message processed successfully');
} catch (error) {
context.log.error('Error processing message:', error);
throw error; // 重新排队消息
}
};
async function processMessage(messageBody) {
// 业务逻辑在这里
return true;
}
3. 使用 Terraform 的 Azure Functions
# functions.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {
virtual_machine {
delete_os_disk_on_delete = true
graceful_shutdown = false
skip_shutdown_and_force_delete = false
}
}
}
variable "environment" {
default = "dev"
}
variable "location" {
default = "eastus"
}
# 资源组
resource "azurerm_resource_group" "main" {
name = "myapp-rg-${var.environment}"
location = var.location
}
# 函数应用的存储账户
resource "azurerm_storage_account" "function_storage" {
name = "myappstore${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
identity {
type = "SystemAssigned"
}
tags = {
environment = var.environment
}
}
# 应用程序洞察
resource "azurerm_application_insights" "main" {
name = "myapp-insights-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
application_type = "web"
retention_in_days = 30
}
# 应用服务计划
resource "azurerm_service_plan" "function_plan" {
name = "myapp-plan-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
os_type = "Linux"
sku_name = "Y1" # 消费计划
tags = {
environment = var.environment
}
}
# 函数应用
resource "azurerm_linux_function_app" "main" {
name = "myapp-function-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
service_plan_id = azurerm_service_plan.function_plan.id
storage_account_name = azurerm_storage_account.function_storage.name
storage_account_access_key = azurerm_storage_account.function_storage.primary_access_key
app_settings = {
APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.main.instrumentation_key
APPLICATIONINSIGHTS_CONNECTION_STRING = azurerm_application_insights.main.connection_string
AzureWebJobsStorage = azurerm_storage_account.function_storage.primary_blob_connection_string
WEBSITE_NODE_DEFAULT_VERSION = "~18"
FUNCTIONS_EXTENSION_VERSION = "~4"
FUNCTIONS_WORKER_RUNTIME = "node"
ENABLE_INIT_LOGGING = true
WEBSITE_RUN_FROM_PACKAGE = 1
}
site_config {
application_insights_key = azurerm_application_insights.main.instrumentation_key
application_insights_connection_string = azurerm_application_insights.main.connection_string
cors {
allowed_origins = ["https://example.com"]
}
http2_enabled = true
}
https_only = true
identity {
type = "SystemAssigned"
}
tags = {
environment = var.environment
}
}
# 密钥库用于密钥
resource "azurerm_key_vault" "function_secrets" {
name = "myappkv${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_linux_function_app.main.identity[0].principal_id
secret_permissions = [
"Get",
"List"
]
}
tags = {
environment = var.environment
}
}
# 将数据库密码存储在密钥库中
resource "azurerm_key_vault_secret" "db_password" {
name = "db-password"
value = "MySecurePassword123!"
key_vault_id = azurerm_key_vault.function_secrets.id
}
# 诊断设置
resource "azurerm_monitor_diagnostic_setting" "function_logs" {
name = "function-logs"
target_resource_id = azurerm_linux_function_app.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log {
category = "FunctionAppLogs"
}
metric {
category = "AllMetrics"
}
}
# 日志分析工作区
resource "azurerm_log_analytics_workspace" "main" {
name = "myapp-logs-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
sku = "PerGB2018"
retention_in_days = 30
}
data "azurerm_client_config" "current" {}
output "function_app_url" {
value = "https://${azurerm_linux_function_app.main.default_hostname}"
}
output "app_insights_key" {
value = azurerm_application_insights.main.instrumentation_key
sensitive = true
}
4. 函数绑定配置
{
"scriptFile": "index.js",
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get", "post"],
"route": "api/{*route}"
},
{
"type": "http",
"direction": "out",
"name": "$return"
},
{
"type": "queue",
"direction": "out",
"name": "myQueueItem",
"queueName": "myqueue",
"connection": "AzureWebJobsStorage"
},
{
"type": "serviceBus",
"direction": "in",
"name": "mySbMsg",
"queueName": "myqueue",
"connection": "ServiceBusConnection",
"cardinality": "one"
},
{
"type": "blob",
"direction": "in",
"name": "inputBlob",
"path": "input/{name}",
"connection": "AzureWebJobsStorage"
},
{
"type": "blob",
"direction": "out",
"name": "outputBlob",
"path": "output/{name}",
"connection": "AzureWebJobsStorage"
}
]
}
最佳实践
✅ DO
- 使用 Azure 服务的托管身份
- 在 Key Vault 中存储密钥
- 启用 Application Insights
- 实现幂等函数
- 使用持久函数进行长时间运行的操作
- 处理异常和失败
- 监控函数执行
- 使用绑定而不是 SDK 调用
❌ DON’T
- 在代码或配置中存储密钥
- 忽略 Application Insights
- 创建没有错误处理的函数
- 使用阻塞操作
- 创建没有持久函数的长时间运行函数
- 忽略监控和日志记录
监控
- Application Insights 用于跟踪和指标
- Azure Monitor 用于整体健康
- Log Analytics 用于日志分析
- 函数指标(执行次数,持续时间)
- 自定义遥测和事件