从azure-storage-blob-go迁移到新版azblobSDK:以 Grafana Tempo 的 Azure 后端为实战案例
【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo
Grafana Tempo 是一款高吞吐、低依赖的分布式链路追踪后端,可将链路数据持久化到 Azure Blob Storage。本文以仓库内随附的 Azure SDK for Go 迁移指南(vendor/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/migrationguide.md)为主体,系统讲解如何从旧版azure-storage-blob-go模块(以及azblob早期测试版)迁移到新版azblob模块,并穿插 Tempo 在tempodb/backend/azure下的真实用法作为佐证。读完本文,你将掌握新版 SDK 的客户端构造、认证方式、分页列举与 HTTP Pipeline 配置的完整迁移路径,并能在自己的 Go 项目中直接套用。
为什么需要迁移:简化后的 API 表面
旧版azure-storage-blob-go的公开 API 是"扁平"的——所有客户端和支持类型都集中在azblob一个包内,导致包面难以导航,用户在数千个导出符号中寻找自己需要的 API 十分费力。
新版azblob模块(github.com/Azure/azure-sdk-for-go/sdk/storage/azblob)对设计做了重构,将客户端按职责拆分到多个子包中。从当前仓库的 vendor 目录(vendor/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/)可以看到这套拆分后的包结构:
blob:所有 Blob 类型共有的 API(删除/恢复删除、设置元数据等);container:容器特有 API(设置访问策略、属性等);service:Blob 服务级 API(操作容器、获取账户信息等);appendblob/blockblob/pageblob:针对三种特定 Blob 类型的专用客户端;sas:共享访问签名(SAS)令牌的创建与操作工具;bloberror:存储错误码与错误处理辅助工具。
Tempo 的 Azure 后端正是按这套子包来组织代码的。在 tempodb/backend/azure/azure.go 的 import 块中,可以看到它同时引用了azblob、azblob/blob、azblob/bloberror、azblob/blockblob、azblob/container等多个子包——这正是新版 API 拆分后的典型用法。
客户端构造:从url.URL + Pipeline到string URL + Credential
旧版方式
在azure-storage-blob-go中,客户端构造函数总是要求传入一个url.URL和一个Pipeline:
// 旧代码(azure-storage-blob-go) u, _ := url.Parse("https://myaccount.blob.core.windows.net/") pipeline := azblob.NewPipeline(cred, azblob.PipelineOptions{}) client := azblob.NewServiceURL(*u, pipeline) // 以 url.URL 和 Pipeline 构造新版方式
在新版azblob中,客户端构造函数改为接收string类型的 URL、指定的凭证类型以及可选的*ClientOptions(传nil表示接受默认选项):
// 新代码 client, err := azblob.NewClient("<my storage account URL>", cred, nil)以 Tempo 的实际代码为例,在 tempodb/backend/azure/azure_helpers.go 中,客户端正是通过azblob.NewClient(u.String(), credential, &opts)或azblob.NewClientWithSharedKeyCredential(u.String(), credential, &opts)构造的,其中u由url.Parse生成后再取其字符串形式传入,凭证对象则直接作为第二个参数。
从 vendor 目录中azblob/client.go的源码(vendor/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/client.go#L33-L85)可以看到,新版模块实际上提供了四个构造函数,覆盖全部认证场景:
| 构造函数 | 适用场景 |
|---|---|
NewClient(serviceURL string, cred azcore.TokenCredential, options *ClientOptions) | Azure AD 令牌凭证(通常来自 azidentity 模块) |
NewClientWithNoCredential(serviceURL string, options *ClientOptions) | 匿名访问或带 SAS 令牌的 URL |
NewClientWithSharedKeyCredential(serviceURL string, cred *SharedKeyCredential, options *ClientOptions) | 共享密钥认证 |
NewClientFromConnectionString(connectionString string, options *ClientOptions) | 连接字符串 |
认证方式的迁移
Azure AD / OAuth 令牌认证
旧版azure-storage-blob-go通过NewTokenCredential提供有限的 OAuth 令牌认证支持。
新版azblob不再自带令牌凭证实现,而是统一改用 azidentity 模块提供的 Azure Identity 凭证。这也是 Azure SDK for Go 所有服务模块的通用做法。典型用法:
// 新代码。cred 是由 azidentity 模块创建的 AAD 令牌凭证 cred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { // 处理错误 } client, err := azblob.NewClient("<my storage account URL>", cred, nil)Tempo 对 azidentity 的运用可以印证这一点。在 tempodb/backend/azure/azure_helpers.go 中:
- 当配置
use_federated_token时,使用azidentity.NewWorkloadIdentityCredential(...)创建 Azure Workload Identity(联邦令牌)凭证; - 当配置
use_managed_identity时,使用azidentity.NewManagedIdentityCredential(...)创建托管身份凭证,并可通过azidentity.ClientID(cfg.UserAssignedID)指定用户分配身份的 Client ID(未指定时默认使用系统分配身份)。
共享密钥认证
通过NewSharedKeyCredential进行共享密钥认证的方式在新版中保持不变:
cred, err := azblob.NewSharedKeyCredential(accountName, accountKey) client, err := azblob.NewClientWithSharedKeyCredential(u.String(), cred, &opts)Tempo 的默认路径正是如此。在 tempodb/backend/azure/azure_helpers.go 中,注释明确写道:"如果未显式指定任何认证机制,则默认假定使用共享密钥凭证"("If no authentication mechanism has been explicitly specified, assume shared key credential"),随后用azblob.NewSharedKeyCredential加上azblob.NewClientWithSharedKeyCredential完成构造。账户名与密钥还可以分别从环境变量AZURE_STORAGE_ACCOUNT与AZURE_STORAGE_KEY读取(见 azure_helpers.go 的getStorageAccountName与getStorageAccountKey)。
匿名 / SAS 认证
旧版通过NewAnonymousCredential构造 Pipeline 来支持匿名或 SAS 认证。
新版改用专用构造函数NewClientWithNoCredential():
// 新代码 client, err := azblob.NewClientWithNoCredential("<public blob or blob with SAS URL>", nil)从client.go的注释可以看出,该构造函数专门用于匿名访问存储账户,或在服务 URL 中直接携带 SAS 令牌进行访问(vendor/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/client.go#L44-L57)。
列举 Blob / 容器:从显式Marker到*runtime.Pager[T]
旧版azure-storage-blob-go要求开发者显式创建Marker类型来对分页结果进行翻页,每次手动将返回的Marker回传以获取下一页。
新版azblob中,所有返回分页值的操作统一返回*runtime.Pager[T]。Pager 是azcore运行时提供的泛型分页器,自带More()与NextPage()两个方法,遍历逻辑因此变得简洁一致:
// 新代码 pager := client.NewListBlobsFlatPager("my-container", nil) for pager.More() { page, err := pager.NextPage(context.TODO()) if err != nil { // 处理错误 } // 处理本页结果 for _, blob := range page.Segment.BlobItems { fmt.Println(*blob.Name) } }Tempo 的 Azure 后端大量使用了 Pager 模式,可以作为最佳实践参考:
- tempodb/backend/azure/azure.go 的
List方法使用container.NewListBlobsHierarchyPager(dir, ...)(层级列举)遍历租户/目录下的 blob 前缀(BlobPrefixes),用于发现后端对象; - tempodb/backend/azure/azure.go 的
ListBlocks方法使用NewListBlobsFlatPager(扁平列举)配合Prefix过滤,遍历某租户下的meta.json/meta.compacted.json文件来收集 block ID 列表; - tempodb/backend/azure/azure.go 的
Find方法同样用NewListBlobsFlatPager迭代全部 blob,并通过b.Properties.LastModified向回调传递对象修改时间。
三处都遵循同一范式:for pager.More() { page, err := pager.NextPage(ctx) ... },可见 Pager 已成为新版 SDK 分页操作的统一抽象。
配置 HTTP Pipeline:从显式 Pipeline 到azcore.ClientOptions
旧版需要先显式构建带配置的 HTTP Pipeline,再把它作为参数传给客户端构造函数。
新版azblob中,HTTP Pipeline 在客户端构造期间自动创建,其配置通过azcore.ClientOptions类型注入:
// 新代码 client, err := azblob.NewClient(account, cred, &azblob.ClientOptions{ ClientOptions: azcore.ClientOptions{ // 在这里配置 HTTP Pipeline 选项 Transport: ..., Retry: ..., Telemetry: ..., }, })Tempo 在这一环节做了相当细致的配置,是理解azcore.ClientOptions各字段的绝佳样例(见 tempodb/backend/azure/azure_helpers.go):
Retry:设置policy.RetryOptions{MaxRetries: 1, TryTimeout: 1 * time.Minute, RetryDelay: 4 * time.Second, MaxRetryDelay: 120 * time.Second},这些重试参数从旧 SDKazure-storage-blob-go继承而来(源码注释给出了旧实现出处);如果上下文携带 deadline,还会把TryTimeout调整为距离截止时间的剩余时长;Transport:克隆http.DefaultTransport并把MaxIdleConnsPerHost提升到 100,减少连接周转;外层再包一层instrumentation.NewTransport用于指标采集;- 可选地,Tempo 还会用
hedgedhttp包一层带统计的 RoundTripper 实现对冲请求(hedged requests),以降低长尾延迟——对应配置项HedgeRequestsAt/HedgeRequestsUpTo; Telemetry:设置ApplicationID: "Tempo",让 Azure 侧能够识别请求来源。
在 Tempo 中配置新版 azblob 后端
迁移指南之外,Tempo 对 Azure 后端的完整配置也值得一并掌握(详见 docs/sources/tempo/configuration/hosted-storage/azure.md 与 tempodb/backend/azure/config.go)。
Tempo 支持三种认证方式:共享密钥(storage_account_key)、托管身份(use_managed_identity/user_assigned_id)与 Azure Workload Identity 联邦令牌(use_federated_token)。单机(monolithic)模式下最小配置如下:
storage: trace: backend: azure azure: container_name: container-name storage_account_name: storage-account-name storage_account_key: ${STORAGE_ACCOUNT_ACCESS_KEY}使用联邦令牌(Workload Identity)时:
storage: trace: backend: azure azure: container_name: container-name storage_account_name: storage-account-name use_federated_token: true配置项与默认值(config.go中的RegisterFlagsAndApplyDefaults):
| 配置项 | 默认值 | 说明 |
|---|---|---|
storage_account_name | 空(可回退到环境变量AZURE_STORAGE_ACCOUNT) | 存储账户名 |
storage_account_key | 空(可回退到环境变量AZURE_STORAGE_KEY) | 共享密钥 |
use_managed_identity | false | 使用 Azure 托管身份认证 |
use_federated_token | false | 使用 Azure Workload Identity 联邦令牌 |
user_assigned_id | 空 | 用户分配身份的 Client ID |
container_name | 空 | 存储 block 的容器名 |
prefix | 空 | 容器内存储对象的可选前缀 |
endpoint_suffix | blob.core.windows.net | 目标端点,用于公有云以外的区域/主权云 |
max_buffers | 4 | 同时上传的缓冲区数量 |
buffer_size | 3 * 1024 * 1024(3 MiB,代码内置默认) | 上传块大小 |
hedge_requests_at | 0(0 表示禁用) | 对冲请求触发延迟 |
hedge_requests_up_to | 2 | 对冲请求最大并发数 |
需要特别说明的是 Azurite 本地模拟:Tempo 会将任何不以blob.开头的endpoint_suffix判定为 Azurite,并自动切换到模拟器 URL 风格(http://<endpoint>/<accountName>),见 azure_helpers.go 的注释与实现。这意味着一份配置即可在本地开发与云端生产之间切换。
迁移检查清单
- 替换模块依赖:将
go.mod中的github.com/Azure/azure-storage-blob-go/azblob替换为github.com/Azure/azure-sdk-for-go/sdk/storage/azblob;如需 Azure AD 认证,再添加github.com/Azure/azure-sdk-for-go/sdk/azidentity。 - 重写客户端构造:把"手动构造 Pipeline +
url.URL传入构造函数"改为"直接传stringURL + 凭证 +*ClientOptions(可传nil)"。 - 替换凭证创建:用 azidentity 的
NewDefaultAzureCredential/NewManagedIdentityCredential/NewWorkloadIdentityCredential替代旧版NewTokenCredential;共享密钥的NewSharedKeyCredential保持不变;匿名/SAS 场景改用NewClientWithNoCredential。 - 改写分页逻辑:删除显式
Marker管理,改用*runtime.Pager[T]的More()/NextPage()循环。 - 迁移 Pipeline 配置:把旧 Pipeline 中的重试、传输、日志等配置平移到
azcore.ClientOptions(Retry、Transport、Telemetry、Logging等字段)。
完成以上步骤后,你的代码即可与 Tempo 一样,在统一、可导航、goroutine 安全的新版azblobAPI 之上构建存储层——Tempo 的全部相关实现位于 tempodb/backend/azure/,可作为迁移后的参考实现。
【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考