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
相关产品推荐
相关产品推荐

