Docker部署的Azure Web App无法通过托管身份连接Azure SQL
问题概述
将Python应用以Docker方式部署到Azure Web App后,应用无法连接仅允许托管身份认证的Azure SQL数据库。本地非Docker环境下执行az login可正常连接,但Docker部署后报错。应用及本人的托管身份均已加入my-group安全组,该组已配置为SQL Server外部管理员,并在数据库中创建了对应用户及角色权限。
报错详情
- 初始报错:
Login timeout expired - 添加
AZURE_CLIENT_ID和AZURE_TENANT_ID环境变量后,报错变为:
azure.core.exceptions.ClientAuthenticationError: DefaultAzureCredential failed to retrieve a token from the included credentials. Attempted credentials: EnvironmentCredential: EnvironmentCredential authentication unavailable. Environment variables are not fully configured. ManagedIdentityCredential: (None) No User Assigned or Delegated Managed Identity found for specified ClientId/ResourceId/PrincipalId. Code: None Message: No User Assigned or Delegated Managed Identity found for specified ClientId/ResourceId/PrincipalId.
注:环境变量值曾尝试使用应用的principalId、Application ID及安全组Object Id,均无效。
相关配置与代码
Python连接代码
from azure.identity import DefaultAzureCredential import struct import pyodbc def create_token_struct(databaseToken): exptoken = b'' for i in bytes(databaseToken[0], "UTF-8"): exptoken += bytes({i}) exptoken += bytes(1) return struct.pack("=i", len(exptoken)) + exptoken # 当前初始化方式 credential = DefaultAzureCredential() pyodbc.connect(AZURE_SQL_CONNECTION_STRING, attrs_before = {1256: create_token_struct(credential.get_token('https://database.windows.net/'))})
连接字符串:
Driver={ODBC Driver 18 for SQL Server};Server=tcp:my-server-name.database.windows.net,1433;Database=my_database_name;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30
SQL配置
CREATE USER [my-group] FROM EXTERNAL PROVIDER; ALTER ROLE db_datareader ADD MEMBER [my-group]; ALTER ROLE db_datawriter ADD MEMBER [my-group]; ALTER ROLE db_ddladmin ADD MEMBER [my-group];
SQL Server创建命令:az sql server create ... --external-admin-name my-group
Dockerfile
FROM python:3.10 COPY requirements.txt requirements.txt RUN pip install -r requirements.txt # 安装pyodbc依赖 RUN apt-get update && apt-get -y install unixodbc-dev RUN sh -c "curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -" \ && apt-get update \ && sh -c "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 msodbcsql18 \ && ACCEPT_EULA=Y apt-get install -y mssql-tools18 # 安装Azure CLI RUN sh -c "curl -sL https://aka.ms/InstallAzureCLIDeb | bash" WORKDIR /app COPY . /app EXPOSE 8501
解决方案与问题分析
1. 移除冲突的环境变量
设置AZURE_CLIENT_ID和AZURE_TENANT_ID后,DefaultAzureCredential会优先尝试EnvironmentCredential,但仅设置这两个变量不足以完成环境认证(还需AZURE_CLIENT_SECRET或其他凭据),同时会干扰系统托管身份的自动检测。
操作:删除Web App中配置的AZURE_CLIENT_ID和AZURE_TENANT_ID环境变量。
2. 修正DefaultAzureCredential初始化
按照要求显式初始化凭据(默认exclude_environment_credential为False,明确配置可避免歧义):
credential = DefaultAzureCredential(exclude_environment_credential=False)
3. 验证系统托管身份的可用性
- 确认Web App已启用系统分配托管身份:执行
az webapp identity show,确保返回包含principalId和tenantId的有效结果。 - 系统分配托管身份无需指定
Client ID,ManagedIdentityCredential会自动从Azure实例元数据服务获取身份信息。
4. 确认SQL权限配置有效性
- 验证
my-group安全组确实包含应用的系统托管身份:在Azure AD中检查安全组成员。 - 确认SQL Server的外部管理员配置正确:执行
az sql server ad-admin list --resource-group <rg-name> --server-name <server-name>,确保返回my-group。
5. 容器网络与元数据端点访问
Azure Web App会自动将实例元数据服务端点(http://169.254.169.254)映射到Docker容器内,无需额外配置。若存在网络隔离,需确保容器可访问该端点(无需公网,为内部地址)。
内容的提问来源于stack exchange,提问作者limitIntegral314

