在使用Azure云过程中,经常会用到Blob存储,这里记录一下,再业务使用过程中常用的几种场景:

一、通过Microsoft Entra ID应用程序账户访问Blob

优点:

  • 可用于非 Azure 环境(如本地服务器、AWS EC2)
  • 支持跨订阅、跨租户访问

缺点:

  • 密钥必须安全存储(如 Key Vault)
  • 密钥到期需手动轮换,否则应用中断
  • 存在凭据泄露风险(尤其在 CI/CD 中硬编码)

具体配置如下:

1、配置应用程序专用账户

在Entra ID中注册应用,创建服务主体,生成客户端密钥或证书,将应用分配到Storage Blob Data Contributor角色(读写),同时记录如下信息:

Tenant ID(租户 ID)
Client ID(客户端 ID)
客户端密钥(Client secrets),有过期时间,到期后需要手动更新。

2、赋予账户Azure Blob存储权限

(1)在Azure门户,转到存储账户(StorageAccount),选择你的存储账户(mdblob001);

(2)在左侧菜单,选择访问控制(IAM)(Access Control),点击 + 添加(Add)→添加角色分配(Add role assignment);选择角色:Storage Blob Data Contributor(允许读取 & 写入 Blob);

(3)分配访问权限给:用户、组或服务主体(User, group, or service principal),选择成员:搜索 你的应用名称(MyBlobApp),点击 选择(Select),点击 下一步 → 分配。

3、客户端调用Blob

应用代码中使用ClientSecretCredential或ClientCertificateCredential获取令牌访问Blob,这里拿Python SDK举例:

需要提前安装:

1
pip install azure-identity azure-storage-blob
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from azure.identity import ClientSecretCredential
from azure.storage.blob import BlobServiceClient

# 配置 Entra ID 应用信息
TENANT_ID = "3c4e99f3-4831-4352-ae86-4d8f59512865"
CLIENT_ID = "3357a2b1-9895-484d-8754-42c162f2c9eb"      # 应用程序(客户端)ID
CLIENT_SECRET = "DoB8Q~4Gwl6sdzV_ocfUvXA3BtHYJLgrimwwMcj1"

# 存储账户和本地文件
ACCOUNT_NAME = "mdblob001"
LOCAL_FILE_PATH = "/mnt/d/file.txt"
BLOB_NAME = "uploaded-file.txt"          # 上传后在 Blob 中的名称

# 获取 Token 凭据
credential = ClientSecretCredential(
    tenant_id=TENANT_ID,
    client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET
)

# 创建 Blob 客户端
blob_service_client = BlobServiceClient(
    account_url=f"https://{ACCOUNT_NAME}.blob.core.windows.net",
    credential=credential
)

# 列出容器(测试权限)
try:
    containers = blob_service_client.list_containers()
    for container in containers:
        print("Container:", container.name)
except Exception as e:
    print("Error:", e)

# === 上传文件 ===
try:
    blob_client = blob_service_client.get_blob_client(
        container=CONTAINER_NAME,
        blob=BLOB_NAME
    )

    with open(LOCAL_FILE_PATH, "rb") as data:
        blob_client.upload_blob(data, overwrite=True)  # overwrite=True 允许覆盖

    print(f"✅ 文件 '{LOCAL_FILE_PATH}' 已成功上传为 '{BLOB_NAME}'")

except FileNotFoundError:
    print("❌ 本地文件未找到")
except Exception as e:
    print("❌ 上传失败:", e)

二、通过AKS-系统托管标识访问Blob

系统托管标识(System-assigned Managed Identity)是Azure 提供的一种自动管理的服务主体(Service Principal),可为支持的 Azure 资源(如 VM、AKS、App Service 等)提供安全的身份认证能力,AKS默认已经启用。

优点:

  • 自动管理生命周期,随着AKS集群创建、销毁等
  • 无需管理凭证,完全无秘钥访问
  • 更高安全,凭据不会暴露在代码、配置文件或环境变量中
  • 可以和Azure的RBAC深度集成
  • 开发SDK只需配置DefaultAzureCredential即可自动获取令牌

缺点:

  • 不能跨资源复用,比如如多个workload使用不同权限,无法细化
  • 标识自动管理,但是RBAC需要手动配置
  • 无法跨云进行标记
  • 无法自定义命名

1、AKS集群启用系统分配的托管标识:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
az aks create --resource-group test --name md-test-southafrica --generate-ssh-keys #仅针对新集群启动系统托管标识
az aks update --resource-group test --name md-test-southafrica --enable-managed-identity # 针对已经存在集群
az aks show --resource-group test -n md-test-southafrica --query "identity" # 查看系统托管标识
{
"delegatedResources": null,
"principalId": "37e6bf32-b18d-4e3a-9f97-b86d0c9dc2cf",
"tenantId": "3c4e99f3-4831-4352-ae86-4d8f59512865",
"type": "SystemAssigned",
"userAssignedIdentities": null
}

2、为托管标识分配 Blob Storage 角色

可选角色:

  • Storage Blob Data Reader:只读
  • Storage Blob Data Contributor:读写
  • Storage Blob Data Owner:读写 + ACL 管理(用于 ADLS Gen2)

使用 Azure CLI 为该标识授予对 Blob Storage 的访问权限(例如 Storage Blob Data Contributor):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 获取存储账户 ID
STORAGE_ACCOUNT_ID=$(az storage account show -n mdblob001 -g test --query id -o tsv)
echo $STORAGE_ACCOUNT_ID
/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001
# 分配角色
AKS_PRINCIPAL_ID=$(az aks show --resource-group test --name md-test-southafrica --query "identity.principalId" --output tsv)
echo "AKS Principal ID: $AKS_PRINCIPAL_ID"
AKS Principal ID: 37e6bf32-b18d-4e3a-9f97-b86d0c9dc2cf
az role assignment create --assignee "37e6bf32-b18d-4e3a-9f97-b86d0c9dc2cf" --role "Storage Blob Data Contributor" --scope "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001" # 授权给AKS系统托管认证

{
"condition": null,
"conditionVersion": null,
"createdBy": "dc8278a7-78d6-4f6e-971c-8974d0ac4e2f",
"createdOn": "2025-02-19T04:31:42.582306+00:00",
"delegatedManagedIdentityResourceId": null,
"description": null,
"id": "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001/providers/Microsoft.Authorization/roleAssignments/81f9e7d1-b83c-4c4b-aab0-047fc4fb525f",
"name": "81f9e7d1-b83c-4c4b-aab0-047fc4fb525f",
"principalId": "37e6bf32-b18d-4e3a-9f97-b86d0c9dc2cf",
"principalName": "ad674b19-a3b1-47ed-9463-128289ae5e34",
"principalType": "ServicePrincipal",
"resourceGroup": "Test",
"roleDefinitionId": "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe",
"roleDefinitionName": "Storage Blob Data Contributor",
"scope": "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001",
"type": "Microsoft.Authorization/roleAssignments",
"updatedBy": "dc8278a7-78d6-4f6e-971c-8974d0ac4e2f",
"updatedOn": "2025-02-19T04:31:42.582306+00:00"
}
az role assignment list --scope "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001" --output table #列出存储账户下授权信息
Principal                             Role                           Scope
--------------------------------------------------------------------------------
ad674b19-a3b1-47ed-9463-128289ae5e34  Storage Blob Data Contributor  /subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001

3、验证

使用python SDK构建容器,验证访问:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from azure.storage.blob import BlobServiceClient
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
blob_service_client = BlobServiceClient(
    account_url="https://mdblob001.blob.core.windows.net",
    credential=credential
)

# 列出容器
containers = blob_service_client.list_containers()
for c in containers:
    print(c.name)

DefaultAzureCredential 会自动尝试多种身份验证方式,在 AKS 中会通过 IMDS(Instance Metadata Service) 获取令牌,前提是 Pod 运行在启用了托管标识的节点上。

三、通过AKS-用户托管标识访问Blob

1、创建用户分配的托管标识:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
az group create -n rg-aks-wi -l test # 创建资源组,已经已经存在请忽略
az identity create --name MD-Blob-Identity --resource-group test # 创建用户分配托管标识
{
  "clientId": "7d9b65f6-959d-4a74-ab3f-67d16adc8c37",
  "id": "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourcegroups/test/providers/Microsoft.ManagedIdentity/userAssignedIdentities/MD-Blob-Identity",
  "location": "southafricanorth",
  "name": "MD-Blob-Identity",
  "principalId": "bdd58864-063f-451d-b352-2dded3582b36",
  "resourceGroup": "test",
  "systemData": null,
  "tags": {},
  "tenantId": "3c4e99f3-4831-4352-ae86-4d8f59512865",
  "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
}
# 获取关键信息
export USER_ASSIGNED_CLIENT_ID="$(az identity show -g test -n MD-Blob-Identity --query clientId -o tsv)" 
export USER_ASSIGNED_PRINCIPAL_ID="$(az identity show -g test -n MD-Blob-Identity --query principalId -o tsv)"

2、为标识分配Blob Storage权限

1
2
3
4
5
6
7
STORAGE_ACCOUNT_ID=$(az storage account show -n mdblob001 -g test --query id -o tsv)
echo $STORAGE_ACCOUNT_ID
/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/mdblob001
az role assignment create \
  --role "Storage Blob Data Contributor" \
  --assignee $USER_ASSIGNED_PRINCIPAL_ID \
  --scope $STORAGE_ACCOUNT_ID

3、在 AKS 上启用 Workload Identity(如未启用)

Workload Identity基于OIDC,标准、安全、微软主推一种方式

1
2
3
4
5
# 启用 OIDC issuer(AKS 1.24+ 支持)
az aks update -g test -n md-test-southafrica --enable-oidc-issuer
# 获取 OIDC issuer URL
export AKS_OIDC_ISSUER=$(az aks show -g test -n md-test-southafrica --query "oidcIssuerProfile.issuerUrl" -o tsv)
echo $AKS_OIDC_ISSUER

4、将用户标识与 Kubernetes Service Account 联邦(Federate)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 获取用户标识的 clientId
export USER_ASSIGNED_CLIENT_ID=$(az identity show -g test -n MD-Blob-Identity --query clientId -o tsv)
# 创建联邦凭据(允许 AKS 中的特定 ServiceAccount 使用此标识)
az identity federated-credential list --resource-group test --name md-test-southafrica
az identity federated-credential create --name my-fed-cred  \
--identity-name MD-Blob-Identity \
--resource-group test \
--issuer $AKS_OIDC_ISSUER  \
--subject system:serviceaccount:demo:md-blob-sa
--audience api://AzureADTokenExchange
{
"audiences": [
"api://AzureADTokenExchange"
],
"id": "/subscriptions/cdab1cd0-0556-41bc-8905-33ce907e818a/resourcegroups/test/providers/Microsoft.ManagedIdentity/userAssignedIdentities/MD-Blob-Identity/federatedIdentityCredentials/my-fed-cred",
"issuer": "https://oidc.prod-aks.azure.com/3c4e99f3-4831-4352-ae86-4d8f59512865/",
"name": "my-fed-cred",
"resourceGroup": "test",
"subject": "system:serviceaccount:demo:md-blob-sa",
"systemData": null,
"type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
}

subject 格式:system:serviceaccount::

5、在 Kubernetes 中创建 ServiceAccount 并部署应用

创建 ServiceAccount

1
2
3
4
5
6
7
8
# blob-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: blob-access-sa
  namespace: demo
  annotations:
    azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"

kubectl aply -f blob-sa.yaml

6、部署应用

这里以python为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# python-blob.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: blob-client
  namespace: demo
  labels:
    azure.workload.identity/use: "true"  # 必须添加此标签!
spec:
  serviceAccountName: blob-access-sa
  containers:
    - name: app
      image: python:3.11-slim
      command: ["sh", "-c"]
      args:
        - |
          pip install azure-identity azure-storage-blob && \
          python -c "
from azure.storage.blob import BlobServiceClient
from azure.identity import DefaultAzureCredential
import os

account = 'mdblob001'
url = f'https://mdblob001.blob.core.windows.net'
client = BlobServiceClient(account_url=url, credential=DefaultAzureCredential())
print('Containers:', [c.name for c in client.list_containers()])
"
      env:
        - name: AZURE_CLIENT_ID
          value: "${USER_ASSIGNED_CLIENT_ID}"  # 可选,但推荐显式指定
  restartPolicy: Never

部署应用

kubectl apply -f python-blob.yaml 
kubectl logs blob-client # 可以正常列出blob里容器,应用使用令牌访问 Blob Storage已经全程无密钥!

至此,Azure云下,三种访问Blob存储的方式实践完成。