:K8s环境下的授权锚定实战)
文章目录方案Init Container 动态注册——我的最终方案实例授权 vs VCPU授权——容器场景下的选型建议PVC挂载与授权文件的持久化LAC服务端的部署位置生产环境跑了一年半的监控配置容器环境下心跳参数的特殊考量幽灵授权的清理容器化部署的完整清单回过头来想想兼容是对前人努力的尊重是确保业务平稳过渡的基石然而这仅仅是故事的起点上篇把问题讲清楚了——Pod IP会变、MAC是虚拟的、资源弹性跟授权刚性有冲突。这篇聊怎么在实际项目里把这些坑填上。先声明我试过的方案有好几个有些能用有些不能用有些能凑合用但得加一堆补丁。没有一个方案是完美的但有至少一条路能让你在生产环境里睡着觉。方案Init Container 动态注册——我的最终方案折腾了一圈之后我最后用的是一个曲线救国的方案——让lac_agent启动前先搞清楚自己该叫什么名字而不是依赖自动获取的IP。思路是这样的用StatefulSet的序号ordinal index作为客户端的唯一标识启动时通过一个Init Container把自己的逻辑身份注册到LAC服务端的一个映射表里然后把这个逻辑身份对应的某个固定IP比如Headless Service解析到的IP写进lac_agent.conf。apiVersion:apps/v1kind:StatefulSetmetadata:name:kes-dbspec:serviceName:kes-db-headlessreplicas:5template:spec:initContainers:-name:lac-registerimage:busybox:1.36command:-sh--c-|# 获取Pod的序号作为稳定标识 ORDINAL${HOSTNAME##*-} # 生成逻辑IP用10.99.x.x段作为标识段不参与实际网络通信 LOGIC_IP10.99.1.${ORDINAL}# 写lac_agent.confcat/lac-config/lac_agent.conf EOF lac_host lac-server.lac-system.svc.cluster.local lac_port 11234 lac_type Ent local_ip ${LOGIC_IP}lac_interval 10 use_vcpu_limit 0 enable_auto_refresh 1 db_paths /data/kingbase/data EOF echo Registered kes-db-${ORDINAL}with logic_ip${LOGIC_IP}volumeMounts:-name:lac-configmountPath:/lac-configcontainers:-name:kesimage:kingbase/kes:v9r2c14volumeMounts:-name:lac-configmountPath:/opt/Kingbase/ES/V9/Server/share-name:datamountPath:/data/kingbasevolumes:-name:lac-configemptyDir:{}-name:datapersistentVolumeClaim:claimName:data-kes-db-0# 每个Pod用自己的PVC这里的关键点是local_ip 10.99.1.${ORDINAL}。这个IP不是Pod的真实网络IP而是一个逻辑标识。LAC服务端心跳来的时候看到的就是10.99.1.0、10.99.1.1这种格式的地址它们跟Pod的实际网络位置无关只跟StatefulSet的序号绑定。Pod重建了没关系序号没变local_ip还是同一个值。Pod漂移到另一个节点了也没关系逻辑IP跟节点无关。但这个方案有个前提——LAC服务端得认这个逻辑IP。也就是说服务端那边的路由表里不需要真的能路由到10.99.1.x因为lac_agent和服务端之间的通信用的是K8s的Service DNSlac-server.lac-system.svc.cluster.local跟local_ip无关。local_ip只是lac_agent上报给服务端的我叫什么服务端不会尝试回访这个IP。我反复验证过这一点——lac_agent.conf里的local_ip确实只是个身份标识字段不是通信地址。通信地址是lac_host和lac_port。实例授权 vs VCPU授权——容器场景下的选型建议回到授权类型选择的问题。经过几个项目的实践我的建议是如果你的K8s集群规模不大30个KES Pod以内而且实例数量比较固定选实例授权。理由很简单实例授权的逻辑最清晰一个Pod对应一个授权跟裸机时代的思维一致。PVC挂载与授权文件的持久化不管选哪种方案PVC的配置都有讲究。KES的数据目录必须挂PVC这没商量。但lac_agent.conf和license.dat放在哪我的做法是分开挂——数据用PVC持久化配置文件用ConfigMapemptyDir# ConfigMap存放lac_agent.confapiVersion:v1kind:ConfigMapmetadata:name:kes-lac-configdata:lac_agent.conf:|lac_host lac-server.lac-system.svc.cluster.local lac_port 11234 lac_type Ent lac_interval 10 use_vcpu_limit 0 enable_auto_refresh 1 log_level INFO---# Secret存放license.dat如果有的话apiVersion:v1kind:Secretmetadata:name:kes-licensetype:Opaquedata:license.dat:base64-encoded-license-content---# StatefulSet里的挂载spec:template:spec:containers:-name:kesvolumeMounts:-name:datamountPath:/data/kingbase-name:lac-configmountPath:/opt/Kingbase/ES/V9/Server/share/lac_agent.confsubPath:lac_agent.conf-name:licensemountPath:/data/kingbase/license.datsubPath:license.datvolumes:-name:datapersistentVolumeClaim:claimName:data-{{.PodName}}-name:lac-configconfigMap:name:kes-lac-config-name:licensesecret:secretName:kes-license这样lac_agent.conf通过ConfigMap管理改配置只需要kubectl edit configmap然后重启Pod。license.dat放在PVC的数据目录下这样即使Pod重建文件也在。但要注意——如果你用的是上面方案二的逻辑IP方案ConfigMap就不能写死local_ip了得用Init Container动态生成。所以实际上ConfigMap里只放通用配置local_ip由Init Container追加# Init Container里的逻辑cat/config-map/lac_agent.conf/runtime-config/lac_agent.confecholocal_ip 10.99.1.${ORDINAL}/runtime-config/lac_agent.confLAC服务端的部署位置这个也很重要——LAC服务端放哪放在K8s集群内放在集群外的裸机上放在另一个K8s集群里我的建议是放在K8s集群外最好是一台稳定的裸机或虚拟机。原因有三第一LAC服务端是基础设施级别的服务它的稳定性不应该依赖业务集群。如果K8s集群出了问题比如etcd挂了、控制面故障LAC也跟着挂了那所有KES客户端都拿不到授权、心跳也续不上整个就死锁了。第二LAC服务端的授权文件绑MAC地址。如果服务端也跑在K8s里你用的是虚拟MAC前面说过了这东西不稳定。第三运维职责分离。LAC是授权系统归安全或运维团队管K8s是业务平台归开发或平台团队管。两个系统放在同一个集群里权限边界就模糊了。# LAC服务端部署在独立虚拟机上# /opt/KingbaseLAC/bin/lac_server.confport11234max_workers16# 大规模客户端场景调大receive_timeout15# 容器网络延迟可能更大给宽一点heart_offline_times5# 配合lac_interval10判定窗口50分钟K8s集群内的Pod通过NodePort或LoadBalancer Service访问集群外的LAC服务端apiVersion:v1kind:Servicemetadata:name:lac-server-externalspec:type:ExternalNameexternalName:lac-server.internal.company.com# LAC服务端的可达地址---# 或者直接用IP# lac_agent.conf里写lac_host 192.168.1.100# LAC服务端物理机IPlac_port 11234生产环境跑了一年半的监控配置最后说说监控。LAC在容器环境里跑你不能只靠肉眼看WEB管理界面。必须把监控接进去。我的做法是写个sidecar容器定期调lac_agent的status命令把结果暴露成Prometheus metrics#!/bin/bash# lac-metrics-exporter.shwhiletrue;doSTATUS$(lac_agent status21)# 解析状态ifecho$STATUS|grep-qrunning;thenLAC_RUNNING1elseLAC_RUNNING0fi# 检查license有效性VALID_DAYS$(ksql-Usystem-dtest-cselect get_license_validdays();-t2/dev/null|head-1)# 输出Prometheus格式cat/metrics/lac_metrics.promEOF # HELP lac_agent_running Whether lac_agent is running (1running, 0stopped) # TYPE lac_agent_running gauge lac_agent_running${LAC_RUNNING}# HELP lac_license_valid_days Remaining valid days of license # TYPE lac_license_valid_days gauge lac_license_valid_days${VALID_DAYS:-0}EOFsleep60done# Sidecar容器配置-name:lac-metricsimage:busybox:1.36command:[/bin/sh,/scripts/lac-metrics-exporter.sh]ports:-containerPort:9100name:metricsvolumeMounts:-name:metricsmountPath:/metrics-name:scriptsmountPath:/scripts然后在Grafana里配几个关键告警lac_agent_running 0lac_agent挂了立刻告警lac_license_valid_days 30授权快到期了提前一个月告警lac_license_valid_days 7授权即将到期紧急告警心跳中断持续时间 15分钟可能掉授权了这套东西搞完之后至少我不用再半夜被叫起来看授权有没有掉了。虽然不能说100%无忧但至少能睡个囫囵觉。容器环境下心跳参数的特殊考量容器网络跟裸机网络有个本质区别——延迟和抖动更大。裸机上两台服务器在同一个VLAN里ping一下0.2ms。容器里跨节点通信经过veth pair、iptables、overlay网络如果是Flannel之类的隧道模式一个来回可能要2~5ms网络抖动的时候能到50ms以上。这对LAC的心跳机制有什么影响呢首先receive_timeout不能卡太死。默认5秒在裸机上绰绰有余但容器里如果赶上节点负载高或者overlay网络拥堵5秒可能不够lac_agent把一个心跳包发完整。特别是当多个Pod同时向服务端发心跳的时候服务端的max_workers如果不够大处理队列会积压某些请求就会因为超时而失败。我在一个20个KES Pod的项目里调过这个参数。开始用的默认值max_workers8结果每隔几个小时就会有1~2个Pod的心跳超时。后来把max_workers调到16同时把receive_timeout从5调到10问题就消失了。# 容器环境推荐配置LAC服务端 lac_server.conf max_workers 16 # 至少是Pod数量的50%~80% receive_timeout 10 # 容器网络延迟补偿 heart_offline_times 5 # 配合lac_interval1050分钟判定窗口客户端那边的lac_interval也要考虑容器的资源限制。如果Pod的CPU request只有500mlac_agent本身要占一点CPUKES主进程也要占CPU在资源紧张的时候lac_agent可能被cgroup限流导致心跳延迟。这时候lac_interval5就太紧了因为5分钟一次心跳留给延迟恢复的窗口太小。调到10~15分钟给lac_agent更多的喘息空间。# 容器环境推荐配置客户端 lac_agent.conf lac_interval 10 # 别太激进容器里CPU资源可能紧张 enable_auto_refresh 1 # 这个永远是1不要动幽灵授权的清理用了一年多容器化LAC之后我发现一个很烦人的问题——幽灵授权会慢慢累积。什么是幽灵授权就是一个Pod被删了、重建了、IP变了老IP上的授权没有被及时回收一直挂在服务端的已发放列表里。虽然心跳超时后服务端会把状态标成offline但offline的授权并不会自动从池子里删掉。它在WEB管理界面上一直显示着只是标了个灰色的离线。时间一长服务端可能积累了上百条offline记录而激活文件里的授权总数是有限的。这些幽灵记录虽然不直接占license配额offline的授权理论上可以被重新分配但它们会干扰你的判断——你在WEB界面上看100个客户端里只有30个在线你以为授权池有70个富余但其实那70个offline记录对应的Pod可能随时重新上线比如之前只是网络抖动Pod进程其实还在。# 通过lac_manager命令行清理offline的授权记录# 先查看所有客户端状态./lac_manager list-clients# 手动回收某个offline客户端的授权./lac_manager revoke --client-ip10.244.2.15# 批量清理写个脚本foripin$(./lac_manager list-clients--statusoffline|awk{print $1});doechoRevoking$ip..../lac_manager revoke --client-ip$ipdone我后来写了个定时清理脚本每周跑一次把所有offline超过7天的记录清掉。这个脚本放在LAC服务端的crontab里# /opt/KingbaseLAC/scripts/cleanup-ghost.sh#!/bin/bashLAC_BIN/opt/KingbaseLAC/binLOG/opt/KingbaseLAC/log/cleanup.logecho[$(date)] Starting ghost license cleanup...$LOG# 获取offline超过7天的客户端IP列表OFFLINE_CLIENTS$($LAC_BIN/lac_manager list-clients--statusoffline --days-since-heartbeat7|awkNR2 {print $1})foripin$OFFLINE_CLIENTS;do$LAC_BIN/lac_manager revoke --client-ip$ip2$LOGecho[$(date)] Revoked$ip$LOGdoneecho[$(date)] Cleanup done. Processed$(echo$OFFLINE_CLIENTS|wc-l)clients.$LOG# 每周日凌晨3点清理 0 3 * * 0 /opt/KingbaseLAC/scripts/cleanup-ghost.sh这个不算是多高级的方案但确实帮我避免了授权池被幽灵记录占满的问题。如果LAC能加一个自动回收offline超过N天的授权的配置项就好了但目前没有。容器化部署的完整清单搞了这么久我把容器化环境下部署LAC的checklist整理一下给后面的人省点时间□ LAC服务端部署在K8s集群外的物理机或虚拟机上 □ LAC服务端授权文件绑定的是物理机MAC不是虚拟MAC □ lac_server.conf中max_workers≥Pod数量×60% □ lac_server.conf中receive_timeout≥10 □ lac_server.conf中heart_offline_times≥3根据网络质量调整 □ 客户端lac_agent.conf中local_ip使用逻辑标识而非Pod IP □ 客户端lac_agent.conf中enable_auto_refresh1 □ lac_agent通过Init Container动态注入local_ip □ license.dat通过Secret或PVC持久化 □ lac_agent.conf通过ConfigMap管理支持热更新 □ 配置Prometheus监控lac_agent运行状态 □ 配置授权到期提前30天告警 □ 配置offline授权定期清理每周 □ 预留授权池余量≥实际需求的30%应对Pod重建期间的双重占用 □ HPA的最大副本数×单实例授权数 激活文件总授权数 □ 确认CNI插件类型评估MAC地址变化对服务端的影响这份清单不完美但能覆盖大部分坑了。上面这些脚本和配置说难听点都是在打补丁。我其实一直想写个更完善的LAC容器化运维工具——自动处理Pod漂移的授权迁移、自动清理幽灵记录、自动适配CNI插件的MAC变化……但一直没抽出时间。前两天看到金仓社区在办2026智能运维工具开发大赛感觉这个方向还挺对路的正好把我这一年多踩的坑沉淀成工具代码交上去。不管参不参赛这个方向确实值得有人去做传送门https://bbs.kingbase.com.cn/forumDetail?articleId2394013b19f3ef84a43edb994692b88ehttps://bbs.kingbase.com.cn/forumDetail?articleId6152608d769b472397ccfbd29879c0bd回过头来想想容器化环境下搞授权管理本质上是在两种完全不同的设计哲学之间找平衡。K8s说一切都是临时的LAC说一切都是固定的。你不可能让其中一个完全迁就另一个只能在中间找到一条勉强能走的路。我的方案二Init Container逻辑IP不是最优解但它是在不改LAC底层代码的前提下能做到的最稳的方案了。如果LAC团队未来能支持用自定义标识比如hostname或UUID替代IP作为客户端身份那容器化授权的问题就能从根本上解决。但在那之前我们只能在夹缝中求生存。