ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Gemini Enterprise 群组许可自动化管理实战:基于 Cloud Run Job 的批量许可证生命周期编排

2026/9/14 10:52:55 拓冰建站 浏览量
Gemini Enterprise 群组许可自动化管理实战:基于 Cloud Run Job 的批量许可证生命周期编排 Gemini Enterprise 群组许可自动化管理实战基于 Cloud Run Job 的批量许可证生命周期编排【免费下载链接】generative-aiSample code and notebooks for Generative AI on Google Cloud, with Gemini Enterprise Agent Platform项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai导读在 Gemini EnterpriseGemini Enterprise Agent Platform的规模化落地中许可证license的授予与回收往往依赖人工操作随着组织成员变动、群组结构调整很容易出现该有权限的没开通、已离职/停用的仍占用席位的错配问题。本项目 group-licensing 以Cloud Run Job Cloud Scheduler为载体实现了一个自动化的 Gemini Enterprise 许可证生命周期管理方案通过定时批量对账reconciliation在 Google Cloud Identity 群组成员身份与 Discovery Engine 许可证分配之间建立桥梁提供基于群组的 SKU 映射、自动开通provisioning与过期许可证清理cleanup能力。阅读本文后你将掌握该方案的整体架构、配置格式、两个核心工作流的源码级原理以及构建、测试、部署的完整命令。方案概览一个镜像两个 Job各自调度整个方案的核心是一个 Go 语言编写的 Cloud Run Job 程序从同一个容器镜像部署出两个 Job 定义分别由 Cloud Scheduler 按各自的时间表触发JobJOB_TYPE调度频率职责gemini-box-office-joinerjoiner每日24 小时分页遍历所有配置的群组成员为缺失许可的用户授予许可证若用户同时属于多个映射了 SKU 的群组则按最高优先级 SKU 授予gemini-box-office-gcgarbage_collection每 6 小时分页遍历所有已授权用户回收过期超过配置阈值未登录或已不属于任何有权限群组的用户的许可证这种一镜像双 Job的设计让两类对账逻辑共享同一套配置解析、适配器、日志与领域模型同时又能独立调度、独立设置环境变量、独立扩缩容通过--tasks N并行分片。环境变量两个工作流共享的运行时开关Job 的行为通过环境变量控制完整清单如下对应源码 job_settings.go 中的LoadJobSettings解析逻辑变量说明默认值JOB_TYPE选择要运行的工作流joiner或garbage_collection必填—DRY_RUN为true时完整执行评估逻辑但不发起任何写 API 调用可通过 Cloud Scheduler 请求体按次覆盖falseGC_SKIP_GROUP_EVAL为true时垃圾回收 Job 跳过对已授权用户的群组成员身份评估仅依据许可过期情况回收仅适用于 garbage collection 工作流falseCLOUD_RUN_TASK_INDEXCloud Run 注入当前任务实例的 0 基索引0CLOUD_RUN_TASK_COUNTCloud Run 注入并发任务实例总数1从源码看LoadJobSettings会对每个变量做严格校验JOB_TYPE必须是joiner或garbage_collection对应 enums.go 中的WorkflowJoiner/WorkflowGarbageCollection三个布尔变量经strconv.ParseBool解析非法值直接报错CLOUD_RUN_TASK_INDEX必须是非负整数、CLOUD_RUN_TASK_COUNT必须是 ≥1 的整数且强制TASK_INDEX TASK_COUNT防止任务索引越界。SKU 名称与优先级多群组重叠时的裁决规则当用户同时符合多个 SKU 的资格时系统为其分配排名最高的那个 SKU。该优先级硬编码在 enums.go 的skuPrecedence映射中数值越大优先级越高自上而下依次为SUBSCRIPTION_TIER_SEARCH_AND_ASSISTANTGemini Enterprise PlusSUBSCRIPTION_TIER_ENTERPRISEGemini Enterprise StandardSUBSCRIPTION_TIER_SEARCHSearch NotebookLMSUBSCRIPTION_TIER_NOTEBOOK_LM仅 NotebookLMSUBSCRIPTION_TIER_AGENTSPACE_BUSINESSGemini BusinessSUBSCRIPTION_TIER_AGENTSPACE_STARTERGemini Business StarterSUBSCRIPTION_TIER_FRONTLINE_WORKERSUBSCRIPTION_TIER_FRONTLINE_STARTERSUBSCRIPTION_TIER_ENTERPRISE_EMERGING新兴市场 Enterprise StandardSUBSCRIPTION_TIER_EDU_PROSUBSCRIPTION_TIER_EDUSUBSCRIPTION_TIER_EDU_PRO_EMERGINGSUBSCRIPTION_TIER_EDU_EMERGING在 joiner 工作流中collectGroupMembers为每个用户记录当前遍历到的最高优先级条目HasHigherPrecedenceThan比较确保同一用户最终只会落在一个最优 SKU/位置的许可配置上未识别或SUBSCRIPTION_TIER_UNSPECIFIED的值优先级为 0同时IsValid()校验会在配置加载阶段就拦截非法 SKU。配置Secret Manager 中的entitlements.json配置存放在GCP Secret Manager中以文件卷的方式挂载到 Job 内的固定路径/run/secrets/entitlements.json路径常量定义于 constants.go。{ billing_account_id: ABCDE-12345-FGHIJ, projects: { customer-project-alpha: [ { subscription_tier: SUBSCRIPTION_TIER_ENTERPRISE, location: global, groups: [ group-data-scientistsexample.com, group-senior-devsexample.com ] }, { subscription_tier: SUBSCRIPTION_TIER_AGENTSPACE_BUSINESS, location: global, groups: [ group-marketingexample.com, group-general-staffexample.com ] } ] }, settings: { staleness_threshold_days: 30 } }字段说明如下字段说明billing_account_id与被管理项目关联的 GCP 结算账号 ID必填projectsGCP 项目 ID → 授权条目列表每个 SKU/location 组合一条的映射projects[].subscription_tier该条目的 Gemini SKU合法值见上文 SKU 优先级列表projects[].location许可证管理的地理区域仅可为global、us、eu之一projects[].groups拥有该 SKU 权限的 Google 群组邮箱列表settings.staleness_threshold_days超过该天数未活跃的用户将被回收设为0或省略则完全禁用过期检查仅评估授权资格单个 Job 可通过在projects下追加条目管理多个项目。对于非常大的项目组合可在 Job 执行时设置--tasks N将项目列表分片到 N 个并行任务实例上。配置校验加载即拦截错误配置解析与校验逻辑集中在 config.go 的Load/validate中任何一项不满足都会返回带哨兵错误的失败信息哨兵错误定义于 errors.gobilling_account_id非空projects非空否则报ErrConfigNoProjects项目 ID 必须匹配^[a-z][a-z0-9\-]{4,28}[a-z0-9]$6–30 字符、小写字母开头、仅含[a-z0-9-]、以字母或数字结尾每个项目至少一个条目、每个条目至少一个群组邮箱否则报ErrConfigNoGroupsSKU 必须是skuPrecedence中已识别的值否则ErrInvalidSKUlocation 必须是global/us/eu否则ErrInvalidLocation群组邮箱须匹配^[^\s][^\s]\.[^\s]$恰好一个、域名含点staleness_threshold_days必须 ≥ 0。值得一提的是配置结构里还预留了可选字段subscription_id当启用DIRECT_LAW直连模式环境变量时每条授权记录要求显式提供subscription_id用于直接指定licenseConfigs/{uuid}中的{uuid}而不再依赖按 SKU 自动解析配置路径见 config.go。IAM、OAuth 与 API 要求Job 的服务账号需要以下权限IAM 角色roles/discoveryengine.admin— 列出与更新用户许可证roles/cloudidentity.groups.viewer— 列出群组成员并校验成员关系roles/secretmanager.secretAccessor— 读取挂载的配置 Secretroles/billing.viewer— 读取已购买的 Gemini Enterprise 订阅配置roles/run.builder可选— 使用 Cloud Build 构建产物时roles/storage.admin可选— 使用 Cloud Build 上传源码、暂存构建roles/artifactregistry.createOnPushWriter可选— 使用 Cloud Build 推送镜像到 Artifact Registryroles/logging.logWriter可选— 使用 Cloud Build 写构建日志OAuth 范围https://www.googleapis.com/auth/cloud-platformhttps://www.googleapis.com/auth/admin.directory.group.member.readonlyAPI 要求Discovery Engine APIGCPResource Manager APIGCPAdmin SDK APICloud Identity / WorkspaceCloud Run Admin API可选Cloud Build API可选Compute Engine API可选Secret Manager API可选Cloud Scheduler API可选Job 使用Application Default CredentialsADC无需任何服务账号密钥文件。触发器授权Cloud Scheduler 的服务账号必须被授予对每个 Cloud Run Job 资源的roles/run.invoker权限。从 main.go 可以看到实际初始化过程Admin SDK 客户端使用admin.AdminDirectoryGroupMemberReadonlyScope范围Resource Manager 客户端使用cloudresourcemanager.CloudPlatformReadOnlyScope范围随后分别组装为cloudidentity.New(adminSvc)、discoveryengine.New()与resourcemanager.New(crmSvc)三个适配器。项目结构与构建测试cmd/job/ # Job 入口main.go internal/ adapters/ cloudidentity/ # Cloud Identity Admin API 适配器 discoveryengine/ # Discovery Engine API 适配器 resourcemanager/ # Resource Manager API 适配器解析项目号 config/ # 配置加载与校验授权配置 Job 设置 middleware/ # 结构化日志中间件 models/ # 领域类型、DTO、错误、常量、枚举 ports/ # 接口定义IdpClient、GeminiClient、ResourceManagerClient services/ # 业务逻辑JoinerService、GCService docs/ PRD.md # 产品需求文档 TDD.md # 技术设计文档 Dockerfile # 多阶段构建 → distroless 运行时镜像构建镜像docker build -t gemini-box-office:latest .运行单元测试go test ./...所有测试均为单元测试不依赖任何外部服务或凭据。测试覆盖了配置解析/校验config_test.go、SKU 优先级与枚举校验enums_test.go、joiner 与 GC 工作流joiner_test.go、gc_test.go以及两个适配器cloudidentity、discoveryengine。部署一条命令部署为 Cloud Run Job以下示例命令可直接将当前目录源码构建并部署为 Cloud Run Jobgcloud run jobs deploy [name_of_job] \ --source . \ --region [desired_cloud_run_region] \ --update-secrets/run/secrets/entitlements.json[name_of_secret_in_secret_manager]:latest \ --set-env-vars JOB_TYPE[joiner_or_garbage_collection],DRY_RUNfalse,GC_SKIP_GROUP_EVALfalse \ --service-account [desired_service_account_email_address]构建到 Artifact Registry 再部署如果需要先把容器镜像构建并保存到 Artifact Registry、稍后再部署可参考以下 Cloud Build 命令gcloud builds submit . \ --tag [desired_gcp_region]-docker.pkg.dev/[gcp_project_id]/[artifact_registry_repository]/[artifact_registry_package_name]:latest \ --service-accountprojects/[gcp_project_id]/serviceAccounts/[desired_build_service_account_email_address] \ --default-buckets-behaviorregional-user-owned-bucket \ --region[desired_gcp_region]构建成功后使用 Artifact Registry 中的镜像部署为 Cloud Run Jobgcloud run jobs deploy [name_of_job] \ --image [desired_gcp_region]-docker.pkg.dev/[gcp_project_id]/[artifact_registry_repository]/[artifact_registry_package_name]:latest \ --region [desired_cloud_run_region] \ --update-secrets/run/secrets/entitlements.json[name_of_secret_in_secret_manager]:latest \ --set-env-vars JOB_TYPE[joiner_or_garbage_collection],DRY_RUNfalse,GC_SKIP_GROUP_EVALfalse \ --service-account [desired_service_account_email_address]Job 输出结构化 JSON 日志Job 完成时以退出码0成功或1失败结束。结果以结构化 JSON 日志条目输出到 stdout由 Cloud Logging 自动采集{time:...,level:INFO,workflow:joiner,task_index:0,msg:joiner workflow complete,duration_ms:4821,licenses_granted:42,licenses_soft_failed:0,groups_processed:5,dry_run:false}如果某个 SKU 的许可证池在运行中途耗尽系统会为每个耗尽的池发出一条 WARN 日志并继续执行{time:...,level:WARN,workflow:joiner,task_index:0,msg:license pool exhausted, soft-failing remaining users,project_id:customer-project-alpha,license_config_path:projects/123/locations/global/licenseConfigs/ent-config,available:3,soft_failed:17}如果结算账号对同一 SKU 有多个有效订阅无法在一个池中入座的用户会被自动顺延到下一个池每个耗尽的池都会触发 WARN但最终汇总中的licenses_soft_failed只统计所有池尝试后仍未入座的用户数。{time:...,level:INFO,workflow:garbage_collection,task_index:0,msg:garbage collection workflow complete,duration_ms:9134,licenses_revoked:12,users_evaluated:500,dry_run:false}日志规范所有日志输出均为结构化 JSON 并写入 stdout便于 Cloud Logging 自动采集每条日志都包含workflow和task_index字段。PII用户邮箱地址永不写入日志。从 main.go 可见日志系统将 slog 的level键重写为 Cloud Logging 期望的severity键并在加载配置后为默认 logger 附加workflow与task_index属性使服务层通过middleware.LoggerFromContext发出的每一条日志都自动携带这些字段。源码级原理joiner 工作流的对账链条joiner 的完整执行链路在 joiner.go 中清晰可见可归纳为五步获取许可证配置索引通过FetchLicenseConfigIndex读取结算账号下的全部 licenseConfig 资源构建(SKU, 项目号, location) → licenseConfig 路径列表的索引LicenseConfigIndex。解析项目号由于 Discovery Engine API 的 licenseConfig 资源路径使用数字项目号如projects/415104041262/...而非人类可读的项目 IDjoiner 会通过 Resource Manager 适配器把每个项目 ID 解析为项目号见 types.go 的说明。枚举群组成员并裁决最高优先级对每个项目的每条配置逐页调用 Cloud Identity 的members.list每页 200 人最多 500 页见 constants.go适配器以includeDerivedMembershiptrue展平嵌套群组见 idp.go只保留USER类型成员用优先级比较为每个用户记录最优 (SKU, location)。按 key 分组待授权项默认按(SKU, 项目号, location)分组直连DIRECT_LAW模式下则按subscription_id分组。批量授予按MaxBatchSize 100分块调用BatchUpdateUserLicenses。若池耗尽grantBatch会先查询FetchLicenseUsageStats计算剩余席位用余量重试超出部分作为 soft-failed 顺延到下一个同 key 的池。源码级原理garbage collection 工作流的回收规则GC 工作流在 gc.go 中实现核心是shouldRevoke的判定逻辑用户满足过期 OR 无资格即被回收过期判定staleness仅当staleness_threshold_days 0时执行。参考时间优先取LastLoginTime若该时间为零用户从未登录则回退到AssignmentTime许可证创建时间避免刚开通、尚未登录的用户被立即回收两者皆为零时视为立即过期。资格判定entitlement用户须至少属于项目配置中任意群组members.hasMember校验支持直接/间接成员。若启用GC_SKIP_GROUP_EVALtrue则跳过该检查、仅按过期情况回收过期判定通过时会短路避免昂贵的成员关系查询。GC 的processProject按去重后的 location 分别分页ListUserLicenses每页内就地分块回收保证任意时刻内存中只保留一页候选而不是先累积全量再写。值得注意的是GC 工作流只依赖 IdP 与 Gemini 两个端口无需 Resource Manager见 main.go。任务分片多任务并行处理大项目组合Cloud Run Job 支持并行任务本项目据此实现了项目列表的分片机制。在 main.go 中启动流程会对cfg.Projects的键做确定性排序按i % TaskCount TaskIndex将项目分配到当前任务若当前任务未分到任何项目打印no projects assigned to this task; nothing to do并以0退出。这样无论是 joiner 还是 GC都可以通过--tasks N将数千个项目的对账工作横向拆分到 N 个并发实例且每个实例的分配结果与任务数量无关地保持稳定便于观察和重放。总结与适用前提gemini-box-office以定时对账的思路用极小的运维面两个 Cloud Run Job 一份 Secret 配置覆盖了 Gemini Enterprise 许可证生命周期的三个关键诉求按群组自动开通、按 SKU 优先级仲裁、按活跃度与资格自动回收。使用前请注意以下前提本文所述命令、变量与行为均以当前仓库代码为准SKU 清单以 Google 官方 Gemini Enterprise SubscriptionTier 参考为准部署需具备文中列出的 IAM 角色与 API 启用条件首次上线强烈建议保持DRY_RUNtrue通过结构化日志观察将执行的授予/回收动作后再切换为真实写入产品需求与技术设计细节可进一步阅读 docs/PRD.md 与 docs/TDD.md。【免费下载链接】generative-aiSample code and notebooks for Generative AI on Google Cloud, with Gemini Enterprise Agent Platform项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考