深度解析 autoscaler 内置的 Gophercloud:OpenStack Go SDK 的认证、客户端与容器集群实战指南
【免费下载链接】autoscalerAutoscaling components for Kubernetes项目地址: https://gitcode.com/GitHub_Trending/au/autoscaler
本篇技术指南以 autoscaler 仓库中随 Magnum 云提供商 一起内置(vendor)的 Gophercloud 源码及其 README 为核心,系统讲解这个 OpenStack Go SDK 的安装方式、凭证准备、认证机制、ProviderClient / ServiceClient 双层客户端模型,以及如何调用 Compute API 创建云主机。在此基础上,我们会深入仓库源码,展示 Gophercloud 的 containerinfra 扩展包如何被 magnum_manager_impl.go 复用,实现集群节点组的自动扩缩容——读完后你将同时掌握 SDK 的基础用法与它在 Kubernetes Autoscaler 中的真实落地方式。
一、Gophercloud 是什么:为什么 autoscaler 仓库里会有一份 SDK 源码
Gophercloud 是一个用 Go 编写的 OpenStack SDK。它的设计目标是把 OpenStack 庞大的 REST API(Identity、Compute、Network、Object Storage、Container Infra 等)封装成类型安全的 Go 包,让开发者用几行代码就能完成认证、查询资源、创建虚拟机等操作,而无需直接拼接 HTTP 请求。
在 autoscaler 仓库中,Gophercloud 以 vendor 形式存放于 cluster-autoscaler/cloudprovider/magnum/gophercloud 目录,其作用是为 OpenStack Magnum 云提供商提供 OpenStack API 访问能力。从目录结构可以清晰看到本仓库裁剪后保留的子包:
- openstack/identity/v2、v3:Keystone 认证与 Token 获取;
- openstack/compute/v2/flavors:查询虚拟机规格(flavor);
- openstack/containerinfra/v1/clusters、nodegroups:Magnum 容器集群与节点组管理(自动扩缩容的核心依赖);
- openstack/orchestration/v1/stacks、stackresources:Heat 编排栈查询(用于跟踪节点组内每台服务器的创建/删除状态);
- pagination:通用分页框架;
- testhelper:SDK 自带测试辅助工具。
因此,阅读这份 README 不仅能学会使用 Gophercloud,还能顺带理解 Kubernetes Autoscaler 的 Magnum 扩展是怎么通过它跟 OpenStack 打交道的。
二、安装与依赖管理:GOPATH、go get 与 vendor 化
Gophercloud 官方推荐的做法是先将 GOPATH 指向一个合适的目录:
mkdir $HOME/go export GOPATH=$HOME/go由于 Gophercloud 对上游 API 的变更比较敏感,官方建议引入依赖管理方案(如 godep)来锁定版本,防止依赖漂移:
go get github.com/gophercloud/gophercloud # Edit your code to import relevant packages from "github.com/gophercloud/gophercloud" godep save ./...上述命令会把所需的全部源码安装进Godeps/_workspace目录,之后用godep go编译即可引用。而在 autoscaler 仓库中,Gophercloud 已经被直接内置(vendored)进cluster-autoscaler/cloudprovider/magnum/gophercloud,Magnum 云提供商通过k8s.io/autoscaler/cluster-autoscaler/cloudprovider/magnum/gophercloud/...的导入路径直接使用(见 magnum_manager_impl.go 的 import 段),无需额外执行go get,这正是"vendor 它并自己写测试覆盖所需部分"这一官方建议的落地。
三、准备凭证:OpenStack 访问凭据与 OS_* 环境变量
因为要调用 API,你首先需要拿到 OpenStack 的访问凭据,官方推荐将其保存为环境变量而不是写死在代码里,这样源码可以安全地提交到版本控制系统。最少需要以下三项:
- username(用户名)
- password(密码)
- 一个有效的 Keystone identity URL(认证端点)
如果你有 OpenStack 仪表盘(Horizon),可以直接访问project/access_and_security路径,点击右上角的 "Download OpenStack RC File" 按钮下载一个 bash 文件,它会把所有访问信息导出为环境变量。然后执行:
source admin-openrc.sh按提示输入密码即可完成环境变量注入。SDK 会读取的标准环境变量在 openstack/auth_env.go 的 AuthOptionsFromEnv 中有完整定义,主要包括:
| 环境变量 | 对应字段 | 说明 |
|---|---|---|
OS_AUTH_URL | IdentityEndpoint | Keystone 认证端点,必填 |
OS_USERNAME/OS_USERID | Username / UserID | 用户名或用户 ID,至少其一(应用凭证场景可豁免) |
OS_PASSWORD | Password | 密码,必填(应用凭证场景可豁免) |
OS_PROJECT_ID/OS_PROJECT_NAME | TenantID / TenantName | 项目(租户)信息,v3 认证下优先使用前者;OS_TENANT_ID/OS_TENANT_NAME为已弃用的旧名称 |
OS_DOMAIN_ID/OS_DOMAIN_NAME | DomainID / DomainName | 域信息,v3 用户名认证下必填其一 |
OS_APPLICATION_CREDENTIAL_ID/_NAME/_SECRET | ApplicationCredential* | 应用凭证认证(替代密码的更安全方式) |
从 auth_env.go 的实现可以看到 SDK 还会做一系列输入校验:缺少OS_AUTH_URL、缺少用户名/密码、应用凭证缺少 secret、只填OS_PROJECT_NAME却未提供域信息或项目 ID 等情况都会直接返回对应的ErrMissingEnvironmentVariable错误,帮助你尽早发现问题。
四、认证机制:AuthOptions、ProviderClient 与版本协商
拿到凭证后的第一步是认证,认证由底层的 Provider 结构负责。有两种方式构造认证选项:
import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/utils" ) // Option 1: Pass in the values yourself opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://openstack.example.com:5000/v2.0", Username: "{username}", Password: "{password}", } // Option 2: Use a utility function to retrieve all your environment variables opts, err := openstack.AuthOptionsFromEnv()gophercloud.AuthOptions是认证选项的聚合结构,完整字段定义见 auth_options.go,除 README 用到的三个字段外,还支持UserID、DomainID/DomainName(v3 必须)、TenantID/TenantName(项目作用域)、TokenID(直接用已有 Token 认证)、Scope(限定 Token 作用到某个项目/域)、AllowReauth(允许 SDK 缓存凭证并自动重新认证)以及三个应用凭证字段。它同时实现 v2 与 v3 两种 Token 请求体的构造逻辑:ToTokenV2CreateMap组装经典的passwordCredentials结构,而ToTokenV3CreateMap(见 auth_options.go)则支持password、token、application_credential三种认证方法,并对"用户名与用户 ID 同时给出"、"密码认证但缺域信息"等冲突组合返回明确错误。
拿到opts后,调用openstack.AuthenticatedClient(opts)得到ProviderClient:
provider, err := openstack.AuthenticatedClient(opts)其内部实现(openstack/client.go 的 AuthenticatedClient)先调用NewClient解析并规范化认证端点,再调用Authenticate完成实际认证。认证阶段最关键的是身份服务版本协商:Authenticate(openstack/client.go)声明了v2.0(优先级 20)与v3(优先级 30)两个候选版本,交给 utils.ChooseVersion 处理。该函数的逻辑是:
- 若你给的端点已带明确的版本后缀(如
/v3/),直接按后缀匹配,不再探测; - 否则向
IdentityBase发起GET,读取服务公布的版本列表(versions.values), - 在状态为
current、supported或stable(goodStatus白名单)的版本中,选择优先级最高的那个。
因此,只要你的OS_AUTH_URL不带版本后缀,SDK 会自动优先选用 Keystone v3。
认证成功后返回的ProviderClient是"顶层客户端"——所有 OpenStack 服务客户端都从它派生,它持有访问 API 所需的全部认证信息(基地址、Token ID、端点定位器等)。其结构定义见 provider_client.go,关键字段包括:
IdentityBase/IdentityEndpoint:身份服务根地址与端点;TokenID:最近一次签发的有效 Token;EndpointLocator:从服务目录(service catalog)发现各服务端点的函数;HTTPClient:可替换的http.Client,用于注入自定义传输行为;UserAgent:请求头中的 UA 字符串,默认值为gophercloud/2.0.0(见 provider_client.go);ReauthFunc:收到 401 时自动重新认证的函数。
需要特别注意的几点实现细节:
- Token 的线程安全访问:应用若并发使用 ProviderClient,应调用
UseTokenLock()(provider_client.go),此后通过Token()/SetTokenAndAuthResult()读写 Token 都会加锁;AuthenticatedHeaders 会把 Token 放进X-Auth-Token请求头,且会先等待正在进行的重认证完成。 - 自动重认证:
AllowReauth开启时,认证过程会创建一个"一次性客户端"(throwaway client,把 Token 和 ReauthFunc 清零)用于重试,ReauthFunc内通过CopyTokenFrom把新 Token 拷回主客户端(见 openstack/client.go 的 v3auth)。Request收到 401 且设置了 ReauthFunc 时会自动重认证并重放请求,但通过hasReauthenticated标志保证单次请求最多重试一次,避免死循环(provider_client.go 的 doRequest)。 - 默认成功状态码:
Request未显式指定OkCodes时,按 HTTP 方法取默认值:GET→200,POST/PUT→201/202,PATCH→200/202/204,DELETE→202/204(见 defaultOkCodes),并在响应码不符合预期时返回带响应体的ErrUnexpectedResponseCode。
五、服务客户端:把 Provider 注入 Compute 等具体服务
有了 Provider 之后,把它作为依赖注入到具体的 OpenStack 服务中。以 Compute API 为例,创建 Compute 服务客户端:
client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), })openstack.NewComputeV2 的内部实现(initClientOpts,见 openstack/client.go)会调用ProviderClient.EndpointLocator,根据认证时从服务目录中抽取的 catalog 和传入的EndpointOpts(如 Region)解析出该服务在当前区域的访问端点。本仓库的 openstack/client.go 还提供了NewIdentityV2/V3、NewBareMetalV1、NewNetworkV2、NewBlockStorageV2/V3、NewOrchestrationV1、NewContainerInfraV1(Magnum 容器基础设施服务,见 openstack/client.go)等一批构造函数。
所有服务客户端都基于gophercloud.ServiceClient(service_client.go),它内嵌了*ProviderClient,并额外持有:
Endpoint:服务 API 基地址(来自服务目录,必须以/结尾);ResourceBase:资源级基地址(可含 API 版本);Type:服务类型标识(如compute、container-infra),用于设置微版本请求头;Microversion:服务微版本号;MoreHeaders:作用域为整个服务客户端的额外请求头。
ServiceClient封装了Get/Post/Put/Patch/Delete/Head六个方法(service_client.go),统一处理 JSON 请求体编码、JSON 响应解码、微版本头注入(如 Compute 用X-OpenStack-Nova-API-Version,见 setMicroversionHeader),并最终委托给ProviderClient.Request执行真正的 HTTP 请求。分页数据则由 pagination.Pager 管理:AllPages()会把一个 List 操作的所有页合并成单页返回(pager.go),配合各资源包的Extract*方法即可取出结构化对象。
六、实战:用 Compute API 创建一台云主机
拿到服务客户端后,就可以执行任何 Compute API 操作了。例如创建一台新服务器,只需调用Create方法并传入 flavor ID(硬件规格)和 image ID(操作系统镜像):
import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" server, err := servers.Create(client, servers.CreateOpts{ Name: "My new server!", FlavorRef: "flavor_id", ImageRef: "image_id", }).Extract()上述代码创建了一台带指定参数的服务器,并把新资源封装进server变量(servers.Server结构体)。这里的Create会经由ServiceClient.Post向/servers端点发送请求体,Extract()负责把 JSON 响应解码为强类型的Server对象——这就是 Gophercloud "结果对象 + Extract" 的经典用法:每个 API 操作返回一个携带原始响应的Result类型,通过Extract/ExtractErr得到最终结果或错误。
七、仓库中的进阶用法:containerinfra 扩展与 Magnum 节点组自动扩缩容
README 是面向通用 OpenStack 场景的入门教程,而 autoscaler 仓库真正让 Gophercloud 发挥价值的地方,是它额外维护的 containerinfra/v1 扩展包——这正是 Magnum 容器集群管理 API 的 Go 封装。其中两个包与自动扩缩容直接相关:
nodegroups 包提供节点组的 CRUD:
Get:按 ID/名称获取某个集群下的节点组详情(只接受 200 成功码);List:列出节点组,ListOpts支持marker/limit分页、sort_key/sort_dir排序以及按role过滤(ListOpts);Create:创建节点组,CreateOpts支持Name、NodeCount、MinNodeCount、MaxNodeCount(不设置则无上限)、Role(默认worker)、ImageID、FlavorID等字段(CreateOpts);Update:通过 JSON Patch 语义(add/remove/replace)修改/min_node_count与/max_node_count(UpdateOpts);Delete:删除节点组。
节点组对象NodeGroup(results.go)包含NodeCount、MinNodeCount、MaxNodeCount(指针类型,未设置时为 nil)、Role、IsDefault、StackID、Status等字段。注意List返回的字段是不完整的(不含 min/max 节点数),所以需要再对每个节点组单独Get一次。
clusters 包提供集群级操作,其中Resize是实现扩缩容的关键:
type ResizeOpts struct { NodeCount *int `json:"node_count" required:"true"` NodesToRemove []string `json:"nodes_to_remove,omitempty"` NodeGroup string `json:"nodegroup,omitempty"` }(见 ResizeOpts 定义)——它允许对指定NodeGroup调整目标节点数NodeCount,并可通过NodesToRemove精确指定要删除的节点。
这些 API 在 magnum_manager_impl.go 中如何被使用:
- 节点组自动发现:
autoDiscoverNodeGroups(magnum_manager_impl.go)先用nodegroups.List分页拉取全部节点组,跳过master角色,再对每个组Get详情并校验MaxNodeCount是否已设置(未设置即不参与扩缩容),最后按自动发现配置里的角色列表做匹配。 - 查询当前规模:
nodeGroupSize(magnum_manager_impl.go)直接Get节点组并读取NodeCount。 - 扩容:
updateNodeCount(magnum_manager_impl.go)构造clusters.ResizeOpts{NodeCount, NodeGroup}后调用clusters.Resize。 - 缩容:
deleteNodes(magnum_manager_impl.go)把要删除的节点按 server ID(或 minion 索引,用于还在创建中/处于错误态的节点)填入NodesToRemove,连同缩减后的NodeCount一起Resize。 - 节点状态跟踪:
getNodes(magnum_manager_impl.go)利用 orchestration/v1/stacks、stackresources 读取 Heat 栈的refs_map输出和 minion 资源状态,把每个 minion 映射为openstack:///<server-id>形式的 ProviderID,并区分InstanceCreating/InstanceRunning/InstanceDeleting/ 失败(quota 超限被归类为OutOfResourcesErrorClass),从而支持对"即将创建/已失败"节点做背压处理。
而 Magnum 云提供商的部署与参数(--cluster-name、--nodes、--node-group-auto-discovery=magnum:role=worker,autoscaling、max_node_count的 openstack CLI 设置命令等),在 Magnum 云提供商 README 中有完整说明,其中 examples 目录 提供了 ServiceAccount、Secret、Deployment 和 Helm values 等可直接参考的配置样例。
八、向后兼容性保证与注意事项
Gophercloud 官方的立场非常明确:不提供向后兼容性保证——"None. Vendor it and write tests covering the parts you use." 也就是说,升级 SDK 前必须自行评估 API 变化,通过 vendor 锁定版本,并用测试覆盖你所依赖的接口。autoscaler 仓库正是这一策略的实践者:把 Gophercloud 整个内置进cluster-autoscaler/cloudprovider/magnum/gophercloud,由仓库自身维护版本;SDK 内部也自带了 testhelper 与各包下的测试代码(如 nodegroups、clusters 的 fixtures),便于在改动后进行验证。
九、延伸阅读
- 继续阅读同目录下的 CHANGELOG.md 了解该内置版本的演进记录,LICENSE 查看许可条款;
- 想了解 Gophercloud 在真实扩缩容场景中的完整调用链,可精读 magnum_manager_impl.go 与 Magnum 云提供商 README;
- 需要为 OpenStack 之外的云(AWS、Azure、GCE、阿里云、华为云等)编写类似扩缩容逻辑时,可对照 cloudprovider 目录 下的其他云提供商实现,体会不同 SDK 在"认证 → 服务客户端 → 节点组抽象"三层结构上的共性设计。
【免费下载链接】autoscalerAutoscaling components for Kubernetes项目地址: https://gitcode.com/GitHub_Trending/au/autoscaler
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考