Kafbat UI(Kafka UI):从只读观察到受控集群入口
页面能打开,只说明 HTTP 进程活着
Kafbat UI 是目前持续维护的 Kafka UI 发行线。浏览器连接它的 HTTP 端口,应用再以 Kafka 客户端身份连接 broker、Schema Registry、Kafka Connect 或其他扩展服务。这里有两套身份:操作者登录 UI 的身份决定能看到哪些页面和按钮;应用持有的 Kafka 服务身份决定后台请求最终能做什么。
这两层经常被误当成一层。给页面接上 OIDC,却让应用继续使用集群管理员账号,泄漏的服务凭据仍能绕过 UI 写入 Kafka。反过来,Kafka 服务账号只有只读 ACL,但 UI 没有认证,任何能访问 HTTP 端口的人都能查看获准范围内的元数据和消息。共享入口必须同时收紧浏览器身份、UI RBAC、Kafka ACL、网络入口和消息正文。
官方 Getting Started 提供使用动态配置和 latest 镜像的演示命令。它适合快速看界面,不适合直接成为团队基线。长期实例应锁定发行标签或镜像 digest,使用可评审的静态配置,并把动态配置是否开启当作明确的架构选择。
先用静态配置跑通一个本地集群
准备 compose.yaml 和 config.yml。下面的镜像值由部署环境提供,避免把漂移的 latest 写进仓库:
services:
kafka-ui:
image: ${KAFBAT_UI_IMAGE:?set approved Kafbat UI tag or digest}
ports:
- "127.0.0.1:18080:8080"
environment:
SPRING_CONFIG_ADDITIONAL_LOCATION: /etc/kafkaui/config.yml
DYNAMIC_CONFIG_ENABLED: "false"
SWAGGER_UI_ENABLED: "false"
volumes:
- ./config.yml:/etc/kafkaui/config.yml:ro
networks:
- dev-messaging
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://127.0.0.1:8080/actuator/health"]
interval: 10s
timeout: 3s
retries: 12
networks:
dev-messaging:
external: trueconfig.yml 只接入一个开发集群:
kafka:
clusters:
- name: dev-local
bootstrap-servers: kafka:19092
read-only: true
polling-throttle-rate: 2
properties:
security.protocol: PLAINTEXTread-only 会阻止 UI 发起写操作,但不会修改 Kafka ACL。polling-throttle-rate 给页面轮询留出间隔,避免多人打开控制台后持续枚举集群。完整字段以目标发行线的配置文件说明为准,尤其要复核 Kafka client properties、消息分页与 AdminClient timeout。
启动后依次看进程、日志和健康端点:
docker compose up -d
docker compose ps
docker compose logs --tail=120 kafka-ui
curl -fsS http://127.0.0.1:18080/actuator/health健康返回 UP 仍未证明 Kafka 可用。打开页面,看见 broker、Topic 和 consumer group,并用 Kafka CLI 对照同一集群的元数据,才算第一轮跑通。容器端口绑定回环地址,是为了防止这个无 UI 认证的本地实例被局域网直接访问。
bootstrap 成功后仍报错,通常要查 advertised listener
Kafbat UI 先连接 bootstrap-servers,拿到集群元数据后再访问元数据中的 broker 地址。容器里的 127.0.0.1 指向控制台自己,不是宿主机 Kafka;bootstrap 可达而 broker 列表持续超时,常见原因是 Kafka 向容器客户端公布了宿主机视角的 listener。
先从 Kafbat UI 所在网络验证 DNS 与端口:
docker run --rm --network dev-messaging busybox:1.37 \
sh -c "nslookup kafka && nc -vz kafka 19092"DNS 和 TCP 成功,只能证明入口可达。若日志随后尝试 localhost:9092 或另一个不可解析地址,应修正 Kafka 的 advertised.listeners,让容器客户端获得 kafka:19092 之类的内部地址。宿主机 CLI 可以继续走单独的外部 listener。更换 UI 镜像、提高连接超时都不会修复错误元数据。
故障可以按第一条异常分层。UnknownHostException 和 timeout 先查 DNS、路由、listener 与防火墙;SSLHandshakeException 查信任链、主机名与协议;SaslAuthenticationException 查机制、JAAS、用户名和密码。页面显示空集群时先读应用日志,不要直接把服务账号换成管理员。
SASL/SSL 与 Registry 是两条独立连接
共享 Kafka 通常需要 SASL/SSL。Kafka 客户端属性和证书路径进入集群配置,真实密码由运行环境注入:
kafka:
clusters:
- name: shared-dev
bootstrap-servers: broker-a.dev.example:9093,broker-b.dev.example:9093
read-only: true
ssl:
truststore-location: /run/kafka-secrets/client.truststore.jks
truststore-password: ${KAFKA_TRUSTSTORE_PASSWORD}
verify-ssl: true
properties:
security.protocol: SASL_SSL
sasl.mechanism: SCRAM-SHA-512
sasl.jaas.config: >-
org.apache.kafka.common.security.scram.ScramLoginModule required
username="${KAFKA_UI_USERNAME}"
password="${KAFKA_UI_PASSWORD}";
schemaRegistry: https://schema.dev.example
schema-registry-auth:
username: ${SCHEMA_REGISTRY_USERNAME}
password: ${SCHEMA_REGISTRY_PASSWORD}信任库用只读 Secret 挂载,密码不提交。Kafka 认证成功并不表示 Schema Registry 可用:Registry 是另一条 HTTP、TLS 和身份链。Topic 可见而 Avro、JSON Schema 或 Protobuf 无法解码时,检查 subject 命名策略、Registry 读取权限、证书和消息格式;不要为了显示正文关闭 TLS 校验。
消息探针只使用虚构数据。选择一个专用 Topic,写入 key 为 ui-probe-001 的小消息,再按分区、offset 或 key 查找。这样能区分“控制台读不到消息”和“业务数据恰好不在当前窗口”,也不会拿真实客户数据做调试样本。
UI 认证、RBAC 与 Kafka ACL 要分别验收
Kafbat UI 支持 Basic、OAuth2、LDAP/AD 等页面认证。共享实例更适合使用 OAuth2/OIDC,把身份提供方中的 group 或 role 映射到应用 RBAC。回调地址、issuer 与反向代理外部地址必须一致,否则常见现象是登录成功后循环跳转或回到错误协议。
下面的角色只允许查看特定项目 Topic 和消费组:
rbac:
roles:
- name: project-a-observers
clusters: ["shared-dev"]
subjects:
- provider: oauth_team_oidc
type: group
value: project-a-observers
permissions:
- resource: clusterconfig
actions: ["view"]
- resource: topic
value: "project-a\\..*"
actions: ["view", "messages_read"]
- resource: consumer
value: "project-a\\..*"
actions: ["view"]资源名和 action 必须按目标版本的 RBAC 文档核对。不要给只读角色使用 all,也不要把 Topic 的 view 与 messages_read 当成同一权限:查看元数据不必天然获得消息正文。
验收先用只读用户查看目标 Topic 配置、分区、副本和 group lag。然后尝试创建 permission_probe_should_fail、修改 retention 或重置测试 group offset。UI 应隐藏或拒绝,Kafka 服务身份本身也应收到 TopicAuthorizationException、GroupAuthorizationException 或等价拒绝。只有把服务凭据拿到 CLI 后仍不能写,才能证明安全不依赖按钮。
消息遮罩比“只读”更接近真实风险
只读用户仍可能看到手机号、地址、token 或业务密钥。Kafbat UI 的消息遮罩支持 REMOVE、REPLACE 和 MASK,可按 Topic key/value 模式与字段配置。敏感 Topic 可以先整段遮罩,再为确有需要的结构化字段设计更细规则:
kafka:
clusters:
- name: shared-dev
masking:
- type: MASK
topic-values-pattern: "project-a\\.private-.*"
- type: REMOVE
fields: ["accessToken", "phone"]
topic-values-pattern: "project-a\\.events"遮罩规则要用虚构消息做正反测试:敏感字段必须不可恢复,正常诊断字段仍可见,非 JSON 内容不能意外绕过规则。截图、浏览器缓存、访问日志和工单附件也属于消息正文的复制面。控制台不应默认承担生产消息浏览;能从 headers、时间、partition、offset 和 trace 标识定位问题时,就不开放 value。
用 lag 和 offset 缩小问题,不用控制台猜业务语义
消费延迟上升时,先看每个分区的 current offset、log end offset 和 lag。单个分区持续上升通常指向热点 key、分区分配或单条慢消息;所有分区一起上升,再查消费者实例、下游依赖和生产速率。页面值要与 kafka-consumer-groups.sh --describe 交叉验证,先排除连错集群、缓存刷新和同名 group。
offset reset 是写操作。向前重置会重放,向后跳转会跳过未处理消息;业务幂等也不能自动证明这次操作安全。日常观察角色不应拥有 reset_offsets。临时提权前保存原 offset、目标 offset、分区、业务暂停状态和回退命令,执行后再用 CLI 核对。Kafbat UI 只是入口,不知道一次重放会不会重复扣款或覆盖下游状态。
大集群要限制页面抓取。Topic、partition、group、并发用户、消息大小、刷新周期和副本数共同决定控制台对 broker 与自身堆内存的压力。出现 UI 堆增长或 Kafka 管理请求突增时,先降低分页、消息条数和轮询频率,收窄接入集群,再决定是否扩容。两个副本会重复轮询,不会天然把读取成本减半。
动态配置决定谁是事实源
官方的配置向导说明,动态配置默认关闭;开启后,运行时修改会写入容器文件系统中的 dynamic_config.yaml。这对个人演示很方便,对共享实例则会产生第二个配置事实源:Git 里的静态 YAML 与页面保存的动态配置可能不同。
团队必须二选一。静态模式由 Git、Secret 系统和部署流水线负责变更;动态模式则要持久化文件、限制编辑权限、备份并纳入审计。无状态多副本若各自保存动态文件,会得到不同集群清单。升级前不仅要保存镜像 digest,还要保存静态配置、动态配置和 Kafka ACL 基线。
退出时先关闭 HTTP 入口,再撤销 OIDC client secret、Kafka/Registry 服务身份和临时 ACL,清理动态文件、信任库副本、日志与测试消息,最后删除容器。docker compose down --remove-orphans 不会替你撤销集群权限。新电脑仅凭批准镜像、静态契约和访问申请入口能够重建只读实例,才算这套工具真正可迁移。
