
在 K8s 里部署 Sentinel到底该把 Dashboard 塞进业务 Pod 当 Sidecar 模式用还是独立组件部署这个问题我最近被问了很多次尤其在微服务改造接近尾声、流量治理开始提上日程的时候。很多人照着教程把 Sentinel Dashboard 跑起来了但一聊到生产环境怎么落、规则怎么持久化、Dashboard 挂了怎么办答案就开始含糊。这篇文章就把两种部署模式讲透从 Sentinel 的组件架构说起拆解 Sidecar 模式和独立组件模式各自的适用边界和真实代价再给出一套基于 Kubernetes v1.26 集群的完整实操流程最后附上我踩过的坑和排查套路。适合正在做微服务流量治理、或者准备把 Sentinel 迁到 K8s 的开发和运维同学参考。1. 先看清楚部署的本质控制面与数据面分开聊1.1 Sentinel 不是一个单体而是控制面加数据面Sentinel 的项目结构容易让人误以为它是一个服务部署好就完事了。实际上它有完全不同的两个部分。sentinel-dashboard 是控制台负责机器发现、规则管理、监控展示它是一个独立的 Java Web 应用默认端口 8858。sentinel-core以及 Go、Python 等其他语言实现是数据面组件它嵌入在你的业务进程里真正执行流量统计、限流判断、熔断降级。数据面的核心是一个 Slot Chain 调用链每一个请求经过资源入口时会依次经过 NodeSelectorSlot、FlowSlot、DegradeSlot、SystemSlot 这些节点流量在哪一步被拦截规则就在哪一步生效。这里最关键的一点是真正干活的是客户端 SDKDashboard 只是遥控器。你通过 Dashboard 改一条规则本质上是 Dashboard 把规则推送给所有已注册的客户端由客户端在本地维护这份规则快照并在每次请求时做判断。所以就算 Dashboard 整个挂掉已经下发过的规则依然在客户端内存里生效。理解了这个机制后面所有关于部署模式的判断都会顺很多因为你不会再把部署 Sentinel误当成部署一个流量网关而是清楚你部署的其实是一个控制面入口。1.2 在 K8s 里真正要决策的是控制面放哪既然数据面 SDK 是嵌入业务进程的那在 Kubernetes 里所谓部署 Sentinel严格来说只包含两件事部署 Dashboard 控制面以及把 SDK 接进业务应用。SDK 的接入方式Java 应用最简单直接在 JVM 参数里加 -Dcsp.sentinel.dashboard.server 指定 Dashboard 地址就行非 Java 应用则需要额外进程代理这块放到 2.3 节讲。而大家反复讨论的 Sidecar 模式和独立组件模式争的核心问题只有一个Dashboard 控制面放在哪里。Sidecar 模式就是把 Dashboard 作为业务 Pod 里的第二个容器和业务容器共享网络栈、共享生命周期独立组件模式则是把 Dashboard 做成一个标准的 Deployment Service放到专门命名空间里业务应用通过网络访问。注意一个容易混淆的点两种模式下业务 Pod 里的 SDK 始终存在它天然是进程内的。所以问题的本质不是要不要 Sidecar而是控制面是跟随业务摊到每个 Pod还是集中独立部署。这个选择会连锁影响资源开销、规则管理方式、故障域大小还有扩容时的运维体验。别小看这个决策我见过有团队因为选了 Sidecar 模式业务一扩容整个节点池都被 Dashboard 的 JVM 内存吃满。2. Sidecar 模式特定场景下它是更优解2.1 三个真正适合 Sidecar 模式的场景先给结论生产环境我的默认推荐是独立组件但 Sidecar 模式绝不是鸡肋至少有三个场景它是实打实的更优解。第一个是开发测试环境。你只想在本地集群或者测试环境验证某条限流规则是否生效不想为了看个面板专门搭一套独立的 Dashboard 服务那直接把 Dashboard 塞进业务 Pod 是最省事的。第二个是团队强隔离。多个团队共享一个集群时独立组件会把所有应用的监控数据和规则堆在一个 Dashboard 上规则互相可见权限管理又不够细这时候把 Dashboard 作为各自业务的 Sidecar隔离边界非常清晰。第三个是集群规模极小的情况。总共就两三个应用、每应用单副本上一个独立组件意味着多维护一份 Deployment、Service 和网络策略反而得不偿失。2.2 Sidecar 模式的 YAML 长什么样Sidecar 在 K8s 里实现方式很直白在业务 Deployment 的容器列表里追加一个 Dashboard 容器。下面这个示例order-service 业务容器旁边就挂了 sentinel-dashboardapiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 1 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: app image: registry.example.com/order-service:1.0.0 ports: - containerPort: 8080 - containerPort: 8719 env: - name: JAVA_OPTS value: -Dcsp.sentinel.dashboard.server127.0.0.1:8858 -Dproject.nameorder-service -Dcsp.sentinel.api.port8719 - name: sentinel-dashboard image: sentinel-dashboard:1.8.8 ports: - containerPort: 8858 env: - name: JAVA_OPTS value: -Dserver.port8858 -Dcsp.sentinel.dashboard.server127.0.0.1:8858 -Dcsp.sentinel.api.port8719核心细节是 Dashboard 地址写 127.0.0.1:8858因为同 Pod 内容器共享网络栈业务容器直接访问本地端口即可。同时业务容器必须暴露 8719 端口因为 Dashboard 需要反向连接这个端口拉取应用的监控指标。这两个端口配置有任何一个不对表现都是应用注册上了但监控数据空白。另外一个你必须提前算清的账每增加一个业务副本就多一份 Dashboard 的 JVM 开销。一个 Dashboard 默认堆内存 512M 起步加上元空间和线程栈实际占用接近 1G。十个副本就是额外 10G 内存集群规模一上来这个成本根本兜不住。所以 Sidecar 模式天然适合小环境和临时验证不适合规模化。2.3 非 Java 应用的另一种 Sidecar 玩法除了把 Dashboard 当 Sidecar还有一种针对非 Java 应用的 Sidecar 用法。Node.js、Python、PHP 这些服务没有 Java SDK 那样成熟的进程内接入方案想对它们做统一限流一个可行的做法是在业务 Pod 里塞一个跑着 Sentinel 内核的代理容器业务容器把流量决策请求发给 SidecarSidecar 里完成限流判断后返回 allow/reject业务侧按结果放行或者拦截。这个方案的思路类似 Service Mesh只是治理能力限定在 Sentinel 自己的协议里。好处是侵入性小业务代码不需要逐个埋点代价是每一次请求都多一跳代理调用P99 延迟会多个位数毫秒还要自己维护协议协商逻辑。实测下来性能损耗可接受但架构复杂度是真的涨。我的建议是核心 Java 服务直接用进程内 SDK非 Java 服务如果不是特别核心前期可以暂不接入等流量治理体系跑顺了再上代理方案别一上来就给所有服务都塞一个 Sidecar 代理。3. 独立组件模式生产环境的默认答案3.1 独立命名空间的完整部署清单独立部署的含义是给 Dashboard 一个正经的岗位独立命名空间、独立 Deployment、独立 Service。收益也很明确——集中管理、故障域与业务隔离、升级扩缩容完全不碰业务 Pod。下面这份清单我按生产可用的标准写包含命名空间、带探针的 Deployment 和 ClusterIP Service。如果需要外网访问再在 Service 前面挂一层 Ingress 或者把 type 改成 LoadBalancer但默认不建议直接暴露公网。镜像名我这里用 sentinel-dashboard:1.8.8 表示实际部署时按你们内网镜像仓库的地址替换。apiVersion: v1 kind: Namespace metadata: name: sentinel --- 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: securityContext: runAsNonRoot: true runAsUser: 1000 containers: - name: sentinel-dashboard image: sentinel-dashboard:1.8.8 imagePullPolicy: IfNotPresent ports: - containerPort: 8858 name: http - containerPort: 8719 name: api env: - name: JAVA_OPTS value: -Dserver.port8858 -Dcsp.sentinel.dashboard.serverlocalhost:8858 -Dcsp.sentinel.api.port8719 - name: AUTH_USERNAME value: sentinel - name: AUTH_PASSWORD value: sentinel123 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1 memory: 1Gi readinessProbe: httpGet: path: /auth/login port: 8858 initialDelaySeconds: 30 periodSeconds: 5 livenessProbe: httpGet: path: /auth/login port: 8858 initialDelaySeconds: 60 periodSeconds: 15 --- apiVersion: v1 kind: Service metadata: name: sentinel-dashboard namespace: sentinel spec: selector: app: sentinel-dashboard ports: - name: http port: 8858 targetPort: 8858 - name: api port: 8719 targetPort: 8719这里有几个细节值得展开。第一securityContext 里配置 runAsNonRoot 和 runAsUser: 1000是为了兼容 Kubernetes 1.25 之后默认启用的 Pod Security Admission。如果你所在的命名空间被标成 restricted镜像以 root 启动会被直接拒绝。第二探针路径用 /auth/login 而不是 TCP 探测因为这个接口返回的是真实 HTTP 状态码能更准确地反映 Dashboard 是否已经能对外提供服务。第三Service 同时暴露 8858 和 8719 两个端口虽然 Dashboard 反向连接客户端用的是客户端自己的 8719不需要客户端访问 Dashboard 的 8719但暴露出来方便调试也方便后续接集群流控的 token server。访问方式上集群内直接用服务名 sentinel-dashboard.sentinel.svc.cluster.local:8858 即可。运维要看面板kubectl port-forward -n sentinel svc/sentinel-dashboard 8858:8858 是最快的办法长期用建议配内网 Ingress 加域名省得每次敲端口转发命令。3.2 客户端接入的配置细节业务应用接入独立组件很简单JVM 参数指到 Dashboard 的 Service 域名就行。我推荐用 Deployment 的 env 注入 JAVA_OPTS而不是把参数写死在 Dockerfile 里这样不同环境切地址不用重新打镜像。apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: business spec: replicas: 2 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: app image: registry.example.com/order-service:1.0.0 ports: - containerPort: 8080 - containerPort: 8719 env: - name: JAVA_OPTS value: -Dcsp.sentinel.dashboard.serversentinel-dashboard.sentinel.svc.cluster.local:8858 -Dproject.nameorder-service -Dcsp.sentinel.api.port8719project.name 是最容易被漏掉却又最关键的一个参数。它决定了应用在 Dashboard 上的显示名字也是规则匹配的应用维度。如果所有服务都不配置 project.nameDashboard 上会堆一堆同名机器规则也会串到别的服务排查时非常痛苦。还有一个隐藏很深的坑业务应用的 Service 必须把 8719 端口暴露出来。Dashboard 拿到客户端的机器注册表后是反向连接每个 Pod 的内网 IP:8719 去拉监控数据的。如果 Service 只暴露了 8080规则下发不受影响但控制台的监控图表永远空白。你查配置查三天都未必能想到是 Service 端口漏了。3.3 规则持久化和高可用的真实做法独立组件模式下最容易被忽视的问题是规则放哪里。默认情况下Dashboard 把规则保存在自己的内存里下发给客户端后客户端也放在本地内存。于是诞生两个经典故障场景第一个场景Dashboard 重启规则的母本丢了。虽然已下发到客户端的规则还在客户端内存里但新扩容的 Pod 从 Dashboard 拿不到历史规则等于新实例是裸奔的。第二个场景客户端 Pod 重启本地内存的规则清空去 Dashboard 拉取如果 Dashboard 也重启过规则就彻底消失了。所以生产环境必须给规则接数据源。常见方案是 Nacos、Apollo、Redis。我倾向推荐 Redis因为大多数公司 Redis 集群是现成的不用额外引入配置中心。Sentinel 客户端通过 Redis 的 Pub/Sub 通道订阅规则消息发布后所有订阅实例几乎同时收到更新变更延迟在毫秒级。关于这个方案的实操细节我会在 5.3 节完整展示包括规则 JSON 的字段含义。至于 Dashboard 本身的高可用我的经验是保持单副本。规则都在 Redis 里之后Dashboard 的角色变成了查看和操作入口它挂了最多是暂时看不了监控、改不了规则业务限流完全不受影响。如果硬要扩多副本多副本之间如果没有分布式锁规则写入可能互相覆盖反而引入新的不一致问题。单副本 Dashboard 加外部规则数据源是投入产出比最高的组合。4. 正面 PK六个维度看清两种模式4.1 一张对比表快速定位差异把 Sidecar 模式和独立组件的差异放到一张表里决策会直观很多对比维度Sidecar 模式独立组件模式资源开销每个业务 Pod 额外约 1G 内存全局 1~2 个 Pod 即可部署与升级要改每个业务 Deployment升级逐应用推进独立升级业务零改动故障域Sidecar 异常只影响所在 Pod 的监控Dashboard 异常影响所有应用的规则维护和监控数据隔离天然按 Pod 隔离所有应用共享依赖账号和后续权限能力新应用接入必须追加容器配置只改应用自身 env不动基础设施适用场景开发测试、小集群、强隔离生产环境、多团队、规模化资源开销这个维度是决定性的。Dashboard 是 Java Web 应用加元空间和线程栈一个实例实际占用接近 1G 内存。二十个业务副本的集群Sidecar 模式要多花二十个 G独立组件模式只需要一两个 G这是数量级的差距。4.2 什么团队选什么别纠结我的选型建议很直接只有开发或测试环境、几台机器、应用数不超过五个用 Sidecar省事。生产集群、应用数量超过五个用独立组件并且必须接 Redis 或者 Nacos 做规则数据源。已经用配置中心的团队直接把规则数据源接到配置中心链路更顺。非 Java 服务占多数的时候先治理核心 Java 服务非 Java 服务如果是强需求再上 Sidecar 代理不要一上来全量铺开。还有一个容易被忽视的考量团队规模。如果你们只有一两个后端同学兼职维护基础设施独立组件的维护成本虽然低但前期搭建设计的工作量仍在反过来如果你们有专门的平台组那独立组件毫无疑问是正确方向后续还可以在这个组件上继续叠加限流规则管理、告警联动等能力。5. 实操v1.26 集群从零部署全流程5.1 应用 Dashboard 并验证下面操作基于 Kubernetes v1.26.0 集群假设你已经配好 kubectl 并具备 deploy 权限。把 3.1 节那份 YAML 保存为 sentinel.yaml直接 apply。$ kubectl apply -f sentinel.yaml namespace/sentinel created deployment.apps/sentinel-dashboard created service/sentinel-dashboard created然后观察启动状态。Dashboard 是 Java 应用首次启动到就绪需要二三十秒看日志比较靠谱$ kubectl get pods -n sentinel -w $ kubectl logs -n sentinel deploy/sentinel-dashboard -f看到 Tomcat started on port(s) 8858 这类日志说明已经起来了。随后用端口转发打开面板$ kubectl port-forward -n sentinel svc/sentinel-dashboard 8858:8858浏览器访问 http://127.0.0.1:8858用 YAML 里 AUTH_USERNAME 和 AUTH_PASSWORD 配置的账号登录能看到空控制台就说明部署成功。5.2 接入 Spring Cloud 应用并开通 8719 数据通道我以一个 order-service 为例Spring Cloud Alibaba 版本假设是 2021.0.5.0对应 Sentinel 1.8.4。业务 Deployment 需要增加 JAVA_OPTS 环境变量和 8719 端口apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: business spec: replicas: 2 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: app image: registry.example.com/order-service:1.0.0 ports: - containerPort: 8080 - containerPort: 8719 env: - name: JAVA_OPTS value: -Dcsp.sentinel.dashboard.serversentinel-dashboard.sentinel.svc.cluster.local:8858 -Dproject.nameorder-service -Dcsp.sentinel.api.port8719 --- apiVersion: v1 kind: Service metadata: name: order-service namespace: business spec: selector: app: order-service ports: - name: http port: 8080 targetPort: 8080 - name: sentinel port: 8719 targetPort: 8719apply 之后先看客户端日志确认注册成功正常会出现类似 Register to sentinel dashboard success: sentinel-dashboard.sentinel.svc.cluster.local:8858 的记录。然后打开 Dashboard 的机器列表页能看到两个 order-service 实例各自带 IP 和 API 端口。这时候就可以建一条规则验证资源名填你在代码里 SentinelResource 注解的 value或者 HTTP URL 路径阈值类型选 QPS阈值填 10保存后立刻生效。多打点请求超过 10 QPS 就会出现 Blocked by Sentinel 的返回。5.3 接 Redis 集群做规则数据源彻底告别丢规则规则持久化是生产环境最后一块拼图。假设你的 Redis 集群在 middleware 命名空间服务名 redis-cluster-headless端口 6379。在 order-service 的 application.yml 里加spring: cloud: sentinel: datasource: flow: redis: server-address: redis-cluster-headless.middleware.svc.cluster.local:6379 database: 0 channel: sentinel:order-service:flow这套机制的核心是 Redis 的发布订阅。Sentinel 客户端启动后订阅指定 channel同时规则 JSON 通过这个 channel 广播出去所有订阅了同一个 channel 的应用实例会实时更新本地规则。换句话说规则的大本营从 Dashboard 内存挪到了 RedisDashboard 变成纯粹的操作入口。规则 JSON 的格式大概长这样[ { resource: /api/v1/order/create, grade: 1, count: 50, limitApp: default, strategy: 0, controlBehavior: 0 } ]几个字段的含义说清楚resource 是资源名grade 为 1 表示按 QPS 限流、0 表示按并发线程数count 是阈值strategy 为 0 表示直接限流、1 表示关联、2 表示链路controlBehavior 为 0 是快速失败、1 是 Warm Up、2 是排队等待。更新规则的时候你可以通过内部工具往 channel 发这条 JSON客户端会实时收到并生效。这个机制解释了为什么用 Redis 做数据源比定时轮询优雅变更延迟在毫秒级。有一点必须提醒具体到你用的 Spring Cloud Alibaba 版本的 RedisDataSource 实现有的版本支持启动时从 Redis 读取初始规则有的只监听 channel 更新。上线前一定要做一次重启 Pod 后规则是否自动恢复的验证这一步能避免生产环境扩容时新实例没有规则的尴尬。我见过团队在这上面栽跟头扩容的瞬间接口被流量打穿。5.4 一套完整的验收链路部署完成别急着收工按下面四条走一遍验收在 Dashboard 上加一条 QPS1 的限流规则用压测工具或者循环请求打流量确认出现 Blocked 的返回证明规则链路通。重启 order-service 的一个 Pod等 Pod 起来后检查限流规则是否自动恢复证明 Redis 数据源生效。手动杀掉 Dashboard Pod再打流量确认限流依然生效证明业务不依赖 Dashboard 存活。把 order-service 扩容到三个副本回 Dashboard 机器列表确认三个实例都注册成功并且监控图表有数据证明 8719 数据通道没问题。把这四条走通Sentinel 在 K8s 里才算是真正站稳了后面再谈 Warm Up、排队等待这些高级规则才有基础。6. 常见问题与排查实录6.1 应用在 Dashboard 上不出现或突然消失这种问题十有八九是注册链路断了。按顺序查先确认进程参数里有没有 -Dcsp.sentinel.dashboard.server再确认客户端日志有没有 register success 的输出最后进 Pod 里试一下本机 8719 是否正常响应。$ kubectl exec -it deploy/order-service -- ps aux | grep java | grep sentinel $ kubectl logs deploy/order-service | grep -i sentinel $ kubectl exec -it deploy/order-service -- curl -s http://127.0.0.1:8719/api?typemetric第三步如果返回一堆 JSON说明本机 Sentinel 数据面活着问题大概率在 Pod 间的网络策略或者 Dashboard 到业务 Pod 的连接上。如果 8719 没响应多半是端口被占用自动递增到了别的端口或者 SDK 压根没加载。6.2 Dashboard 重启后规则全没了不用怀疑就是规则存在内存里没做持久化。解决方案是接数据源前面 5.3 节已经给了 Redis 的配置。这里补充一个反直觉的细节一旦接了 Redis 数据源你要养成规则以 Redis 为准的习惯不要在 Dashboard 上只改内存规则就不管了不然下次 Dashboard 一重启你刚在页面上调出来的新规则又会消失。规则变更应该通过 channel 发布到 Redis再让客户端订阅生效。6.3 限流规则不生效先核对资源名。SentinelResource(order:create) 注解的 value 和规则里的 resource 必须完全一致差一个冒号、差一个字母都匹配不上。第二个常见原因是 Web 应用默认的 SentinelWebInterceptor 会把 URL 作为资源名如果配置了 context path资源名会带上前缀规则里也要对应写。还有一个被我踩过的坑BlockException 被捕获后没有打日志限流其实触发了但业务表现为接口响应异常地快或者返回默认兜底数据看起来像没生效。排查时直接在 Block 分支里打日志或者加计数器就能立刻确认。6.4 K8s 1.26 下镜像被 Pod Security 拦截Kubernetes 1.25 之后 Pod Security Admission 默认启用。如果命名空间打了 restricted 标签而 sentinel-dashboard 镜像以 root 启动Pod 会被拒绝创建kubectl describe 里会看到 runAsUser 0 之类的报错。两种解法要么在 Deployment 里加 securityContextrunAsNonRoot: true、runAsUser: 1000要么把命名空间的 Pod Security 级别调到 baseline。我的建议是优先加 securityContext不要为了一个组件放宽整个命名空间的准入策略。6.5 8719 端口冲突多个 Sentinel 客户端跑在同一个节点上时如果都写死 8719后启动的客户端会自动尝试 8720、8721 递增。这在功能上没问题但会在 Dashboard 机器列表里看到同一应用的不同实例 API 端口不一样排查监控数据缺失时容易误导思路。更稳妥的做法是每个应用固定一个端口段或者直接给每个应用分配独立的 API 端口避免自动递增带来的不确定性。最后分享一点个人体会。我最早也图省事开发环境把 Dashboard 塞在业务 Pod 里确实怎么玩都行。但生产环境一扩容就原形毕露二十个副本吃掉了二十个 G 的内存全是被 Dashboard 的 JVM 吃的。后来切换到独立组件加 Redis 数据源的组合才彻底稳定下来。现在我的默认配置就是独立命名空间、单副本 Dashboard、Redis Pub/Sub 数据源、客户端固定 8719、规则以 Redis 为准。这套组合不花哨但每一层都是踩坑踩出来的。如果你正在纠结部署模式记住一句话在 K8s 里部署的是控制面控制面就该集中独立存在数据面才跟着业务走别把两个维度混在一起做选择题。