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

Azure Portal中已部署的Durable Functions信息不显示问题求助

Azure Durable Functions部署后Portal不显示函数的排查与修复

已通过VS Code完成Python版Azure Durable Functions代码部署,本地运行功能正常,但Azure Portal中Function App未显示预期的URL及函数列表,可从以下方向排查修复:


1. 核对项目文件与依赖配置

  • 确认项目根目录包含function_app.py、host.json、requirements.txt核心文件:
    • host.json需正确配置Durable扩展:
      {
        "version": "2.0",
        "extensions": {
          "durableTask": {
            "hubName": "DurableFunctionsHub"
          }
        }
      }
      
    • requirements.txt必须完整列出所有依赖包,否则部署后函数无法加载:
      azure-functions
      azure-durable-functions
      scikit-learn
      torch
      torchvision
      pytorch-lightning
      torchmetrics
      azure-storage-blob
      

2. 修正代码中的硬编码配置

代码中AzureBlobLogger的连接字符串为硬编码,建议改为从应用设置读取,避免配置错误与敏感信息泄露:

from azure.functions import environ
# ...
azure_logger = AzureBlobLogger(
    container_name="newblob",
    blob_name_prefix="log",
    connection_string=environ.get("BlobStorageConnection")
)

同时在Azure Portal的Function App配置>应用程序设置中添加BlobStorageConnection项,填入完整的存储连接字符串。

3. 检查Function App运行状态与日志

  • 确认Portal中Function App概述页面状态为「运行中」,若已停止则启动后刷新页面。
  • 打开监控>日志流,查看启动日志是否存在依赖安装失败、语法错误等异常信息,这些问题会导致函数无法被Portal识别。

4. 验证Durable扩展安装状态

在Portal的Function App中进入资源>应用程序扩展,确认已安装Microsoft.Azure.WebJobs.Extensions.DurableTask扩展,版本需与本地azure-durable-functions包兼容。

5. 重新部署并刷新Portal

  • 在VS Code中重新执行部署流程,确认部署输出无错误提示。
  • 部署完成后,在Portal的函数页面点击「刷新」按钮,或重启Function App后等待数分钟,Portal可能存在缓存延迟。

用户提供的代码

import azure.functions as func
import azure.durable_functions as df
from sklearn.datasets import fetch_california_housing
import torch
import torch.nn as nn
import torch.nn.functional as F
import pytorch_lightning as pl
from torchvision import transforms, datasets
from torchmetrics.functional import accuracy
from azure.storage.blob import BlobServiceClient
from pytorch_lightning.loggers import Logger
from pytorch_lightning.utilities import rank_zero_only

class AzureBlobLogger(Logger):
    def __init__(self, container_name, blob_name_prefix, connection_string):
        super().__init__()
        self.container_name = container_name
        self.blob_name_prefix = blob_name_prefix
        self.blob_service_client = BlobServiceClient.from_connection_string(connection_string)
        self.container_client = self.blob_service_client.get_container_client(container_name)
        if not self.container_client.exists():
            self.container_client.create_container()

    @property
    def name(self):
        return 'AzureBlobLogger'

    @property
    def version(self):
        return '0.0.1'

    @rank_zero_only
    def log_metrics(self, metrics, step):
        blob_name = f"{self.blob_name_prefix}_step_{step}.csv"
        blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=blob_name)
        csv_content = ",".join([f"{k},{v}" for k, v in metrics.items()])
        blob_client.upload_blob(csv_content, overwrite=True)

    @rank_zero_only
    def log_hyperparams(self, params):
        # Implement if needed
        pass

class Net(pl.LightningModule):
    def __init__(self):
        super().__init__()
        self.conv = nn.Conv2d(in_channels=1, out_channels=3, kernel_size=3, padding=1)
        self.bn = nn.BatchNorm2d(3)
        self.fc = nn.Linear(588, 10) 

    def forward(self, x):
        h = self.conv(x)
        h = F.relu(h)
        h = self.bn(h)
        h = F.max_pool2d(h, kernel_size=2, stride=2)
        h = h.view(-1, 588)
        h = self.fc(h)
        return h
    
    def training_step(self, batch, batch_idx):
        x, t = batch
        y = self(x)
        loss = F.cross_entropy(y, t)
        train_acc = accuracy(y.argmax(dim=-1), t, task='multiclass', num_classes=10, top_k=1)
        self.log('train_loss', loss, on_step=True, on_epoch=True, prog_bar=True)
        self.log('train_acc', train_acc, on_step=False, on_epoch=True, prog_bar=True)
        return loss
    
    def validation_step(self, batch, batch_idx):
        x, t = batch
        y = self(x)
        loss = F.cross_entropy(y, t)
        val_acc = accuracy(y.argmax(dim=-1), t, task='multiclass', num_classes=10, top_k=1)
        self.log('val_loss', loss, on_step=False, on_epoch=True)
        self.log('val_acc', val_acc, on_step=False, on_epoch=True, prog_bar=True)
        return loss
    
    def test_step(self, batch, batch_idx):
        x, t = batch
        y = self(x)
        loss = F.cross_entropy(y, t)
        test_acc = accuracy(y.argmax(dim=-1), t, task='multiclass', num_classes=10, top_k=1)
        self.log('test_loss', loss, on_step=False, on_epoch=True)
        self.log('test_acc', test_acc, on_step=False, on_epoch=True, prog_bar=True)
        return loss
    
    def configure_optimizers(self):
        optimizer = torch.optim.Adam(self.parameters(), lr=0.01) 
        return optimizer

app = df.DFApp(http_auth_level=func.AuthLevel.ANONYMOUS)  
@app.route(route="orchestrators/client_function")
@app.durable_client_input(client_name="client")
async def client_function(req: func.HttpRequest, client: df.DurableOrchestrationClient) -> func.HttpResponse:
    instance_id = await client.start_new("orchestrator", None, {})
    await client.wait_for_completion_or_create_check_status_response(req, instance_id)
    return client.create_check_status_response(req, instance_id)

### orchestrator function ###
@app.orchestration_trigger(context_name="context")
def orchestrator(context: df.DurableOrchestrationContext) -> str:
    result = yield context.call_activity("image", "")
    return result

### activity function ###
@app.blob_output(arg_name="outputblob", path="newblob/test.txt", connection="BlobStorageConnection")
@app.activity_trigger(input_name="blank")
def image(blank: str, outputblob: func.Out[str]):

    transform = transforms.Compose([transforms.ToTensor()])
    train_val = datasets.MNIST('./', train=True, download=True, transform=transform)
    test = datasets.MNIST('./', train=False, download=True, transform=transform)

    n_train,n_val = 50000, 10000
    torch.manual_seed(0)
    train, val = torch.utils.data.random_split(train_val, [n_train, n_val])

    batch_size = 256
    train_loader = torch.utils.data.DataLoader(train, batch_size=batch_size, shuffle=True, drop_last=True)
    val_loader = torch.utils.data.DataLoader(val, batch_size=batch_size)
    test_loader = torch.utils.data.DataLoader(test, batch_size=batch_size)

    net = Net()
    azure_logger = AzureBlobLogger(
        container_name="newblob",
        blob_name_prefix="log",
        connection_string="DefaultEndpointsProtocol=..."
    )
    trainer = pl.Trainer(max_epochs=3, deterministic=True, logger=azure_logger)
    trainer.fit(net, train_loader, val_loader)
    results = trainer.test(dataloaders=test_loader)
    return str(results)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:17:04