多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

K8s容器化改造:Sentinel限流组件Sidecar与独立部署选型指南

K8s容器化改造:Sentinel限流组件Sidecar与独立部署选型指南 在做K8s容器化改造时团队里有个问题争论了很久限流组件Sentinel到底应该是Sidecar方式陪在业务Pod旁边还是把它作为一个独立组件部署在集群里这问题看起来像架构洁癖但实际牵涉到规则下发、监控上报、故障隔离和扩容方式。这篇文章从两种模式的本质差异入手结合我在集群里实际部署的配置和踩过的坑给你一份可以直接参考的选型笔记。无论你马上要上Sentinel还是已经在用但准备迁到K8s都能找到可复用的配置和决策思路。1. 为什么“把 Sentinel 放进 K8s”会变成一个选择题1.1 Sentinel 的传统部署假设在传统虚拟机/物理机时代Sentinel一般不是作为一个单独服务存在的。它的核心是Java库常见做法是直接以SDK方式嵌进业务应用比如Spring Cloud Alibaba项目里引入spring-cloud-starter-alibaba-sentinel。应用进程内部做限流判断规则数据存在本地内存里监控数据定时上报到独立的Dashboard控制台。这个模式下限流的决策点就是业务进程本身部署时只需要多启动一个Dashboard实例应用本身不产生额外部署负担。到了K8s业务被打散成多副本Pod部署的基本单元从“进程”变成了“PodDNS网络策略存储”。应用本身依然可以继续用SDK方式启动但问题随之而来规则配置写死在JVM内存里Pod重新调度后就丢失监控上报的地址从固定的IP变成了需要跟服务发现联动想给不同业务做隔离又得考虑每个Pod的额外开销。这些问题让“Sentinel怎么放”第一次成为一个需要专门设计的架构问题。1.2 K8s 部署位置变化引发的四类诉求我在实际做改造时发现团队争论的其实是四件事规则管理限流规则需要统一管理、动态推送、持久化不能重启就丢。监控链路Sentinel统计数据需要稳定地汇聚到Dashboard或监控系统不能因为Pod漂移断链。业务侵入有些团队希望不改代码就把限流能力加进去或者最少程度改动。资源隔离一个Pod内多容器意味着CPU、内存、磁盘配额要重新划分也影响故障半径。这四类诉求在传统部署里分散在不同系统解决到了K8s就聚焦成一个问题Sentinel是不是应该成为一个独立组件如果是它跟业务Pod的关系是什么1.3 “Sidecar”这个词在 Sentinel 场景里指的不是同一个东西很多人在一个群里聊Sidecar说的根本不是一回事。第一种说法源自服务网格Istio的Sidecar模式在业务Pod里注入一个网络代理容器接管进出流量Sentinel可以作为这个代理的插件或过滤器存在。第二种说法是在业务Pod里多跑一个专门负责规则同步或独立限流判断的容器业务SDK通过共享存储或本地接口调用它。第三种说法则是把Sentinel Dashboard打包成容器放到每个Pod里实际上是不合理的资源浪费。所以讨论之前先把“Sidecar模式”定义为业务Pod中额外包含一个功能是Sentinel相关能力的容器可以是规则同步、限流判断代理、上报Agent中的一种而“独立组件模式”定义为Sentinel Dashboard、规则配置中心、限流服务以独立Deployment/StatefulSet部署在集群中业务Pod直接通过网络访问它们。这样后面才有得聊。2. Sidecar 模式把 Sentinel 塞进业务 Pod 的三种姿势与落地细节2.1 姿势一规则同步 SidecarSDK 读本地文件这是我在K8s环境里最先尝试的降低业务侵入方案。思路很简单业务应用里继续集成Sentinel核心SDK但规则来源不是写死在代码里而是从共享目录读取。Sidecar容器负责监听Nacos或其他配置源把变化后的规则文件同步到Pod内的emptyDir卷中。SDK通过FileDataSource监听该目录一旦文件变化就加载新规则。相关的基础配置片段如下apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: template: spec: containers: - name: order-service image: registry.example.com/order-service:1.0.0 volumeMounts: - name: sentinel-rules mountPath: /opt/rules - name: sentinel-rule-sync image: registry.example.com/sentinel-sync:1.8.6 env: - name: NACOS_ADDR value: nacos-headless:8848 - name: RULE_DATA_ID value: order-service-flow-rules - name: RULE_TYPE value: flow volumeMounts: - name: sentinel-rules mountPath: /opt/rules volumes: - name: sentinel-rules emptyDir: {}这段配置的核心是emptyDir卷。它让两个容器可以共享同一个目录Sidecar写规则文件业务进程监听并重新加载。emptyDir的生命周期和Pod一致所以Pod重建后Sidecar会重新从Nacos拉取最新规则解决了规则丢失问题。实际跑下来这个方案的资源开销很低规则同步容器只需几十MB内存。它的主要问题是监控上报依然由业务SDK直连Dashboard如果Pod分布在不同节点还得确保网络策略放行另外如果规则文件过大、更新频繁文件监听容易出现重复加载甚至短暂解析失败需要在Sidecar里设计好版本号避免抖动。2.2 姿势二旁路限流进程业务通过本地接口检查如果业务完全不想集成Sentinel SDK就需要把限流决策点从应用进程里拆出去。这种模式下我会在业务Pod里附带一个独立的限流容器业务代码在每次请求进来时通过gRPC或HTTP调用它进行一次“是否放行”的判定。限流容器内部用Sentinel维护规则和计数器并把判定结果返回。为了减少网络开销这个Sidecar容器和业务容器共享Pod网络端口监听在本机回环上。业务调用链路的时延增加但通常仍在毫秒级以内。它的编排方式和第一种几乎一样只是把容器镜像换成限流服务本身- name: sentinel-sidecar image: registry.example.com/sentinel-rate-limit:1.0.0 ports: - containerPort: 8789 protocol: TCP resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi我给资源设置了request和limit是因为旁路限流最容易出现的问题就是高并发下CPU被打满反而拖慢主业务。如果不限制Sidecar容器会跟业务容器争抢节点资源限制设太低又可能成为新的瓶颈。所以建议先按每个请求的判定耗时估算比如单次判定0.1msQPS 1万大约用1个CPU核心再除以容器可分配的限额。这种姿势的适用场景很明确业务是多语言异构不想为每个语言各写一套Sentinel适配或者团队想在未来把限流从业务进程里彻底剥离开。它的代价也很明显业务代码每增加一次本地调用都有失败兜底、超时和降级的处理成本复杂度上升不少。2.3 姿势三接入层 Sidecar与网格方案结合在一些使用Istio或类似服务网格的集群里Sentinel能力可以出现在接入层代理中。代理拦截进入Pod的流量在转发前把请求特征发给Sentinel规则引擎做限流。这种方案最接近大众对“Sidecar模式”的认知但它不是开箱即用的需要开发代理与Sentinel的适配层也需要把代理、业务容器、限流容器三者的启动顺序和健康检查理顺。我自己的结论是这条路线适合那种已经把网格全面铺开、愿意投入平台团队做二次开发的场景。如果只是为了给几个服务加限流专门引入网格和Sidecar网络代理收益会被运维成本抵消。2.4 Sidecar 模式的共性注意点无论用哪种姿势下面几项是我在各个项目里反复踩过坑后形成的强制要求给Sidecar容器配置独立的resources不要跟业务容器混用限制不要用restartPolicy让Sidecar无限重启必要时设置backoffLimit和terminationGracePeriodSeconds保证Pod退出时Sidecar能清理信号如果侧车容器执行写卷操作要控制权限避免非Root用户写不了目录监控数据如果从业务SDK直推Dashboard需要保证所有节点都能访问Dashboard Service特别是跨namespace的场景。3. 独立组件模式中心化部署的架构、清单与高可用思路3.1 独立组件到底要把什么独立出去“独立组件模式”这个词在团队讨论里也很容易跑偏。它至少包含两种独立第一把Sentinel Dashboard独立部署到集群里作为一个控制台服务业务SDK照常嵌入应用。这是很多Spring Cloud Alibaba项目实际在使用的形态。第二把限流判断本身做成一个独立集群所有业务的请求都先经过这个集群做统一限流业务应用完全不感知Sentinel。这个形态其实更接近“限流中心”或“网关限流”是独立组件模式的完全形态。从投入产出比看大多数K8s环境适合先做第一种业务嵌入SDKDashboard和配置中心独立部署。真正有必要建设第二种的通常已经走到了多语言、多团队、统一接入层的规模阶段。3.2 把 Dashboard 放到 K8s 的部署清单先看我的一个最小部署样例。用Deployment保证副本数用Service暴露内部访问Ingress负责外部访问另加PVC持久化Dashboard自己的配置和监控数据。apiVersion: apps/v1 kind: Deployment metadata: name: sentinel-dashboard namespace: sentinel spec: replicas: 1 selector: matchLabels: app: sentinel-dashboard template: metadata: labels: app: sentinel-dashboard spec: containers: - name: sentinel-dashboard image: sentinel-dashboard:1.8.6 ports: - containerPort: 8858 env: - name: AUTH_USERNAME valueFrom: { secretKeyRef: { name: sentinel-auth, key: username } } - name: AUTH_PASSWORD valueFrom: { secretKeyRef: { name: sentinel-auth, key: password } } volumeMounts: - name: data mountPath: /data resources: requests: { cpu: 200m, memory: 512Mi } limits: { cpu: 1, memory: 1Gi } volumes: - name: data persistentVolumeClaim: claimName: sentinel-dashboard-pvc --- apiVersion: v1 kind: Service metadata: name: sentinel-dashboard-svc namespace: sentinel spec: selector: app: sentinel-dashboard ports: - port: 8858 targetPort: 8858官方原版Dashboard默认没有鉴权这是个大问题。把它暴露到公司内网后谁都能改规则。我通常在Ingress层挂一层Basic Auth或者给镜像加上AUTH_USERNAME/AUTH_PASSWORD这类环境变量支持。上面的配置使用了Secret引用Controller不会把密码直接暴露在YAML里。另外一个关键点是持久化。Dashboard自身的数据如果只放在容器内Pod重启监控历史就没了。所以我给/data挂PVC。如果集群支持StorageClass建议用动态供给没有的话先建一个NFS或Local PV也可以。3.3 业务应用接入独立组件的经典链路业务Pod里继续使用Sentinel SDK但规则不再来自本地而是通过数据源组件从Nacos拉取。监控上报则通过spring.cloud.sentinel.transport.dashboard指向Dashboard的Service地址。典型配置spring: cloud: sentinel: transport: dashboard: sentinel-dashboard-svc.sentinel.svc.cluster.local:8858 port: 8719 datasource: flow: nacos: server-addr: ${NACOS_ADDR} >[ { resource: GET:/api/order, count: 1000, grade: 1, limitApp: default, strategy: 0, controlBehavior: 0 }, { resource: POST:/api/order, count: 500, grade: 1, limitApp: default, strategy: 0, controlBehavior: 0 } ]规则字段grade1表示QPS维度count是阈值strategy0默认按资源名直接限流controlBehavior0表示快速失败。这些是原生格式不同版本字段略有差别我在线上会先用文档版本确认后再写配置。应用端接入时如果是Spring Cloud Alibaba项目配置较简单spring: cloud: sentinel: datasource: flow-nacos: nacos: server-addr: nacos-headless.sentinel.svc.cluster.local:8848 >spring: cloud: sentinel: datasource: flow-redis: redis: server-addr: ${REDIS_ADDR} password: ${REDIS_PASSWORD} rule-key: order-service:flow:rules rule-type: flow这里rule-key就是Redis中存储规则的Key。变更时先更新Redis中的规则内容再向一个固定的Channel通常是以rule-key命名的Channel发布更新事件客户端收到事件后就会去重新加载。Redis方案的优点是基础设施成熟、几乎每个团队都有缺点是没有图形化界面管理规则很多操作要靠脚本或自研配置页面来完成。如果团队缺少MIS系统支持我更推荐用Nacos因为它的控制台天然适合管理JSON配置。4.5 从配置变更到规则生效的链路配置中心推送的生效时间Nacos客户端监听配置变更解析JSON后重新加载到FlowRuleManager这个链路在大多数情况下不超过1秒。Redis Pub/Sub方案会稍微快一些因为事件通知触发的是一次主动拉取而不是每次长轮询。但如果走“Dashboard推送”链路时间略长Dashboard把规则发给应用客户端HTTP端口客户端写入内存。这个链路在Pod多了以后推送成功率会受网络波动影响。更严重的是这种方式没有“重拉”机制某次推送失败后续不会自动补推只能手动再推一次。用了数据源后这个问题就不存在了。5. 实测对比与故障演练三种形态在真实集群里的表现5.1 测试环境与方法为了把话说得有数据支撑我搭了一套测试环境。集群是K8s 1.243个Worker节点每节点4C8G。被测应用是一个Spring Boot 2.7接口模拟下单操作默认QPS约5000。Sentinel版本1.8.6。对比对象分别是SDK直接嵌入、规则同步Sidecar、旁路限流进程、独立DashboardNacos数据源这四种实际可部署形态。压测工具用wrk分两轮一轮是恒定QPS看资源占用一轮是瞬时打满看限流是否生效。5.2 对比结果我用一张表把关键维度列出来都是我实测环境里的值不同机器会有浮动但相对差距是稳定的部署形态部署复杂度额外时延额外内存/实例规则持久化动态更新链路业务侵入SDK直接嵌入低无客户端本身即有约20-50MB需要数据源配合Dashboard或数据源改代码规则同步Sidecar中可忽略Sidecar约30MB依赖Nacos文件监听数据源低旁路限流进程较高0.5-2msSidecar约150-300MB依赖后端存储数据源或API极低独立DashboardNacos中无Dashboard约500MB依赖Nacos数据源直连改代码“额外时延”这一列旁路限流进程的数值来自本地回环HTTP调用。如果Sidecar容器与业务容器共享Pod网络这个时延相对稳定如果误用了跨Pod访问时延很容易翻倍到3-5ms我在测试中故意试了一次结果非常明显。5.3 故障场景下的表现差异我做了三组故障演练第一组Dashboard宕机。独立DashboardNacos模式下SDK的限流规则继续生效监控曲线断流但业务限流不中断。规则同步Sidecar的监控上报也是业务SDK直连Dashboard同样断流但规则不受影响。旁路限流进程如果自身集成SDK并向Dashboard上报也会出现同样的现象。第二组Nacos暂时不可用。开始时业务已加载规则Nacos挂掉后已有规则继续在内存中生效但新规则无法下发。等Nacos恢复后客户端数据源会自动重新建立监听规则会补拉。这一轮两种跟Nacos配合的模式表现一致。第三组突发流量超过阈值。Sidecar规则同步模式与独立DashboardNacos模式都能准确限流但旁路限流进程在QPS超过其容器CPU上限时本身会先出现延迟升高。因为判定的调用发生在业务进程之外一旦请求堆积在sidecar队列反而会拖垮接口吞吐。所以在旁路方案里容器配额不只是资源控制还是配套的容量规划要素。5.4 日志和监控上的差异这轮对比给我最直接的感受是独立DashboardNacos模式的日志最集中规则变更、监控上报都在业务Pod内完成后由SDK直接推送到Dashboard排障简单。Sidecar规则同步模式下业务日志和Sidecar日志混在一个Pod要看规则同步是否正常必须同时打开两个容器的日志。我用Loki做集中日志Sidecar容器打出的sync success和业务报错的时间要对齐排查节奏明显慢一点。6. 选型判断与迁移建议到底该走哪条路6.1 一个可落地执行的决策清单结合前面的对比我给团队定了这样一套决策逻辑你也可以照着自己画一遍如果应用已经是Spring Cloud Alibaba体系直接选独立DashboardNacos数据源这是收益最高、成本最低的方案如果是纯网关或接入层要做全局限流独立限流集群或旁路进程更合适不建议把每个网关代理都塞一个SDK副本如果是多语言异构、不想侵入业务代码且团队有平台研发能力再考虑旁路限流进程或接入层Sidecar如果团队连Nacos都还没上且只有两三个服务先用SDK直接嵌入PVC保存简单配置过渡等规模上来再补数据源。6.2 从传统架构迁到 K8s 的过渡路线迁移的第一步不是换部署方式而是统一规则的存储位置。我建议先在传统环境里把规则的持久化从Dashboard迁移到Nacos应用端改成客户端数据源。这一步完成后的规则行为就和K8s里的要求一致了。第二步再动部署拓扑。把业务改成多个无状态Pod接入数据源自动监听。此时Dashboard可以暂时放在集群外部或独立机以保持监控链路不变。第三步才把Dashboard迁进集群做成独立DeploymentServicePVC。整个过程分成三阶段每阶段可回滚不会出现“迁移当天限流规则全丢”的问题。6.3 踩过坑后我想特别提醒的配置项几个我反复提醒团队的点Dashboard和Nacos的地址都要配置成Service域名不要写Pod IP。Pod重建后IP会变写IP等于自埋雷客户端上报端口8719需要固定且被网络策略放行。如果多个客户端Pod调度到同一节点需要关注端口自动偏移对监控上报的影响规则变更不要太频繁。Nacos每次发布配置客户端都会全量解析一次如果规则上千条可能触发短暂CPU尖刺。原则上让规则粒度到“接口集群”而不是每条URL一条规则如果用了PVCDashboard数据目录要定期备份。否则一次误删历史监控数据比规则更难恢复。6.4 我最终怎么选在我们团队的实际情况里我最终选的是“独立DashboardNacos数据源”的组合没有用Sidecar方案。原因很直接团队已经有Nacos业务大多是JavaSDK集成不是障碍Sidecar带来的额外运维复杂度完全没有必要。但我把旁路限流进程的方案留给了将来的接入层网关等流量入口统一以后全局限流更适合放在那个位置。如果你正在做同样的选择我的建议很简单先想规则从哪来、监控往哪去再决定Sentinel在哪。部署形态是被这两条链路推着走的不是靠“Sidecar更时髦”或者“独立组件更正规”来定的。
返回列表