You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带自定义Docker容器的Azure Durable Functions应用部署策略咨询

问题场景与部署策略咨询

我正在开发一个C# .NET Azure Functions应用,采用Http启动器、Durable编排器、活动函数的经典模式。其中活动函数需要访问Simba Spark ODBC驱动,从Azure Databricks SQL Warehouse获取数据,因此必须部署在预配置该驱动的自定义Docker容器中。现在针对应用打包与部署有以下两个问题:

  1. 仅活动函数需要自定义容器,是否应该将其单独部署为一个Functions应用,其余两个函数部署在另一个独立应用中?如果采用这个方案,Durable编排器该如何调用跨应用的活动函数?
  2. 如果把三个函数打包到同一个自定义Docker容器并部署为单个应用,扩缩容时会复制全部三个函数而非仅活动函数,这种情况下该如何实现水平扩缩容?

简言之,这个场景下最适配的打包与部署策略是什么?


最优部署策略推荐

优先选择将三个函数打包到同一个自定义Docker容器,部署为单个Azure Functions应用,原因及优化方案如下:

为什么不拆分部署?

拆分部署(活动函数单独一个应用)会引入额外复杂度:

  • Durable Functions原生不支持跨应用直接调用活动函数,你需要手动实现跨应用通信(比如让编排器通过HTTP调用独立的活动函数,或者用Azure Service Bus等消息队列中转),这会打破Durable的原生重试、跟踪、状态管理机制,增加开发和维护成本。
  • 拆分后需要维护两个应用的配置、权限、监控,运维复杂度翻倍。

单容器应用的扩缩容优化方案

虽然单容器会包含所有函数,但可以通过以下方式实现精准扩缩容:

  • 函数级弹性缩放:在Azure Functions应用中,为活动函数单独设置缩放规则,基于活动函数的队列长度、CPU使用率等指标触发扩缩容,编排器和启动器函数由于资源消耗低,无需频繁扩缩,不会浪费资源。
  • 资源隔离配置:在应用宿主配置中,限制非活动函数的资源使用率(如CPU/内存配额),确保活动函数在高负载时能获取足够资源。
  • 选择合适的托管计划:使用Premium计划或Dedicated(App Service)计划,这两种计划支持自定义容器,且提供更灵活的扩缩容控制,比Consumption计划更适配自定义容器的资源需求。

备选拆分方案(仅当单容器资源消耗过高时考虑)

如果活动函数的资源需求极高,单容器的资源占用影响到编排器和启动器的性能,可以考虑拆分,但需要做以下改造:

  • 将活动函数改为普通HTTP触发函数,部署到自定义容器应用中。
  • 在编排器中使用CallHttpAsync方法调用这个独立的活动函数,同时自行实现重试、超时、状态跟踪逻辑。
  • 为活动函数应用配置专用的缩放规则,确保其能独立扩缩。

相关代码实现

OrchestratorStarter.cs

using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Logging;

namespace DurableOrchestratorDotnet8Linux
{
    public static class OrchestratorStarter
    {
        [Function("Orchestrator_HttpStart")]
        public static async Task<HttpResponseData> HttpStart(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
            [DurableClient] DurableTaskClient client,
            FunctionContext executionContext)
        {
            ILogger logger = executionContext.GetLogger("Orchestrator_HttpStart");

            // 函数输入来自请求内容
            string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
                nameof(Orchestrator));

            logger.LogInformation("已启动编排实例,ID = '{instanceId}'.", instanceId);

            // 返回包含实例管理信息的HTTP 202响应
            return await client.CreateCheckStatusResponseAsync(req, instanceId);
        }
    }
}

Orchestrator.cs

using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace DurableOrchestratorDotnet8Linux
{
    public static class Orchestrator
    {
        [Function(nameof(Orchestrator))]
        public static async Task<List<string>> RunOrchestrator(
            [OrchestrationTrigger] TaskOrchestrationContext context)
        {
            ILogger logger = context.CreateReplaySafeLogger(nameof(Orchestrator));
            logger.LogInformation("正在调用活动函数.");
            var outputs = new List<string>();
            outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFunction), "Databricks"));
        
            return outputs;
        }
    }
}

Activity.cs(需自定义容器支持ODBC驱动)

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Data.Odbc;

namespace DurableOrchestratorDotnet8Linux
{
    public static class ActivityFunction
    {
        [Function(nameof(ActivityFunction))]
        public static string RetrieveDatabricksData([ActivityTrigger] string name, FunctionContext executionContext)
        {
            ILogger logger = executionContext.GetLogger("SayHello");
            logger.LogInformation("DB Warehouse 正在向 {name} 打招呼.", name);

            var connectionString = "DSN=DatabricksWarehouse";
            var query = "SELECT c.customer_id, c.customer_name, c.customer_email, o.order_id, o.product, o.amount" +
                     " FROM Customers c" +
                     " JOIN Orders o ON c.customer_id = o.customer_id";

            List<string> records = new List<string>();

            try {
                    using (OdbcConnection connection = new OdbcConnection(connectionString))
                    {
                        try
                        {
                            connection.Open();

                            using (OdbcCommand command = new OdbcCommand(query, connection))
                            {
                                using (OdbcDataReader reader = command.ExecuteReader())
                                {
                                    while (reader.Read())
                                    {
                                        string record = reader.GetString(0) + " " + reader.GetString(1) +
                                                        " " + reader.GetString(2) + " " + reader.GetString(3) +
                                                        " " + reader.GetString(4) + " " + reader.GetString(5);
                                        records.Add(record);
                                    }
                                }
                            }
                        }
                        catch (System.Exception ex)
                        {
                            logger.LogError($"发生错误: {ex.Message}");
                            name = ex.Message;
                        }
                    }
            }
            catch (System.Exception ex)
            {
                logger.LogError($"发生错误: {ex.Message}");
                name = ex.Message;
            }
            return $"Hello {name}!";
        }
    }
}

Dockerfile(预安装ODBC驱动)

# 使用官方Microsoft Azure Functions基础镜像
FROM mcr.microsoft.com/azure-functions/dotnet:4.0-appservice

# 安装依赖
RUN apt-get update && \
    apt-get install -y curl apt-transport-https unixodbc-dev libsasl2-modules-gssapi-mit && \
    curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \
    curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/mssql-release.list && \
    apt-get update && \
    ACCEPT_EULA=Y apt-get install -y msodbcsql17 && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
 
# 将本地Simba Spark ODBC驱动的.deb文件复制到镜像中
COPY simbaspark_2.8.0.1002-2_amd64.deb /tmp/simbaspark_2.8.0.1002-2_amd64.deb
COPY ./ini/odbc/* /usr/local/odbc/
COPY ./ini/simba.sparkodbc.ini /etc/
 
# 安装Simba Spark ODBC驱动所需的依赖
RUN apt-get update && \
    apt-get install -y libsasl2-modules-gssapi-mit
 
# 安装Simba Spark ODBC驱动
RUN dpkg -i /tmp/simbaspark_2.8.0.1002-2_amd64.deb && \
    apt-get install -f -y && \
    rm /tmp/simbaspark_2.8.0.1002-2_amd64.deb
 
# 设置ODBC相关环境变量
ENV ODBCINI=/usr/local/odbc/odbc.ini
ENV ODBCSYSINI=/usr/local/odbc/
ENV SPARKINI=/etc/simba.sparkodbc.ini
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
 
# 将函数应用文件复制到镜像中
COPY . /home/site/wwwroot
 
# 暴露Azure Functions运行时监听的端口
EXPOSE 80

内容的提问来源于stack exchange,提问作者Eugene Goldberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:37:33