你已经使用Docker将Azure Function部署到生产环境,一切看起来都很好,但当你尝试调用你的函数端点时,你会收到一个令人沮丧的401 Unauthorized错误:
curl -X POST \ "https://my-prod-functions.azurewebsites.net/api/MyFunction?code=my-function-key" \ -H "Content-Type: application/json" # Response: 401 Unauthorized
同时,你的非Docker化开发环境使用完全相同的代码和密钥却能完美运行。这是怎么回事?
首先,我检查了所有常见嫌疑:
AuthorizationLevel.Function。Application Insights揭示了一些有趣的信息:
Request successfully matched the route with name 'MyFunction' and template 'api/MyFunction' Executing StatusCodeResult, setting HTTP status code 401
请求已到达函数并匹配路由,但Azure在代码执行之前就返回了401。这意味着问题出在Azure Functions运行时身份验证层,而不是我的代码中。
我通过Kudu控制台(https://my-functions.scm.azurewebsites.net)检查了环境变量,发现:
AzureWebJobsSecretStorageType = files WEBSITES_ENABLE_APP_SERVICE_STORAGE = false # I THOUGHT THIS IS THE PROBLEM!
这是发生的情况:
Azure Functions可以通过两种方式存储身份验证密钥:
AzureWebJobsSecretStorageType = files)/home/data/Functions/secrets/AzureWebJobsSecretStorageType = blob)当你设置WEBSITES_ENABLE_APP_SERVICE_STORAGE = false(无状态Docker容器的常见设置)时,Azure不会挂载持久存储到你的容器。
这意味着:
让我在容器中验证这一点:
# SSH into container or via Kudu ls -la /home/data/ # Result: No such file or directory ls -la /azure-functions-host/Secrets/ #Result: No such file or directory
secrets目录不存在,因为存储没有被挂载!
当我最初尝试通过设置WEBSITES_ENABLE_APP_SERVICE_STORAGE = true来修复这个问题时,身份验证工作了,但我却得到了404 Not Found!
\ 为什么?因为在/home/site/wwwroot/挂载Azure的持久存储覆盖了我的Docker容器的应用程序文件。挂载的目录只有host.json但没有编译的DLL(在docker容器中,而不是主机容器,记住这一点很重要,这里有两个容器:主机容器和应用docker容器),所以Azure Functions运行时找到0个函数加载,因此当请求进来时,身份验证有效(blob中的密钥),但找不到函数,所以我们得到404。简而言之,这个选项使主机能找到函数密钥文件(所以身份验证有效),但丢失了函数本身。我们不能使用它,WEBSITES_ENABLE_APP_SERVICE_STORAGE需要设为false。
Docker化Azure Functions的正确修复方法是使用基于blob的密钥存储:
在Azure Portal中:
AzureWebJobsSecretStorageTypeblobWEBSITES_ENABLE_APP_SERVICE_STORAGE为false点击Save,然后重启function app。Azure将自动:
azure-webjobs-secrets容器转到Portal → Function App → Functions → [你的函数] → Function Keys
从Portal复制密钥(这是解密版本)。
curl -X POST \ "https://my-prod-functions.azurewebsites.net/api/MyFunction?code=<KEY_FROM_PORTAL>" \ -H "Content-Type: application/json" \ -d '{"test": "data"}' # Response: 200 OK
理解密钥加密
当你检查blob存储时,你会看到密钥存储如下:
{ "keys": [ { "name": "default", "value": "CfDJ8AAAAAAA...encrypted-value...", "encrypted": true } ] }
重要:你不能直接使用这个加密值!Azure自动:
始终从Azure Portal UI获取你的密钥,它显示解密版本。
FROM mcr.microsoft.com/azure-functions/dotnet:4 AS base WORKDIR /home/site/wwwroot EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyFunction/MyFunction.csproj", "MyFunction/"] RUN dotnet restore "MyFunction/MyFunction.csproj" COPY . . WORKDIR "/src/MyFunction" RUN dotnet build "MyFunction.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyFunction.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /home/site/wwwroot COPY --from=publish /app/publish . ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabled=true
\
# Secret storage configuration AzureWebJobsSecretStorageType = blob AzureWebJobsStorage = <your-storage-connection-string> # Docker configuration WEBSITES_ENABLE_APP_SERVICE_STORAGE = false WEBSITE_RUN_FROM_PACKAGE = 0 # Functions runtime FUNCTIONS_WORKER_RUNTIME = dotnet FUNCTIONS_EXTENSION_VERSION = ~4
这个问题特定于Docker + Azure Functions,因为:
非Docker化的function apps没有这个问题,因为它们自然可以访问App Service文件系统。
问题:在没有挂载卷的情况下使用基于文件的密钥存储时,Docker化的Azure Functions返回401。
解决方案:使用基于blob的密钥存储,它是无状态的且对Docker友好。
关键设置:
AzureWebJobsSecretStorageType = blob WEBSITES_ENABLE_APP_SERVICE_STORAGE = false
这种配置允许你的Docker容器保持无状态,同时仍然能够从Azure Blob Storage安全地访问身份验证密钥。
遇到类似问题?欢迎在下方评论中联系我!

