首页 / 资讯中心 / 文章详情

Kubernetes集群运维核心:Service、Ingress与RBAC权限管控实践

Kubernetes集群运维核心:Service、Ingress与RBAC权限管控实践 ★ FEATURED ARTICLE
1. 从流量到权限集群管理的四条主线聊 Kubernetes 集群运维绕不开四个关键词Service 管理、Ingress 管理、Dashboard 管理、以及 ServiceAccount 和 RBAC 角色鉴权。第一次接触集群的人容易把它们当成各自独立的模块实际上这是一条从“流量接入”到“安全管控”的完整链路。Service 解决 Pod 之间怎么稳定访问Ingress 解决外部流量怎么进集群Dashboard 解决你用什么视角观察集群ServiceAccount 和 RBAC 解决谁有权限做什么操作。我见过不少团队Service 和 Ingress 配得挺熟但权限体系一团糟——要么所有人共用 admin 账号要么业务方要个只读权限结果直接绑了 cluster-admin。这些问题一旦上了生产环境排查成本非常高。这篇文章我用实际集群里常见的配置方法和踩坑记录把这条链路完整串一遍。内容偏实操不绕理论适合刚接手集群运维的同学也适合已经在用但想补全权限体系的平台工程师。1.1 Service 是集群内部流量的“总机”Service 在集群里起到的角色可以理解成公司内部的总机。Pod 的 IP 是随时会变的副本一扩一缩、节点一重启IP 就换了你不能让业务代码去记 Pod IP。Service 提供一个稳定的虚拟 IP通过标签选择器把背后的一组 Pod 聚合起来客户端只要访问这个 Service 的名字或 IP流量就会被转发到某个健康的 Pod 上。实际部署时Service 的 type 决定它的暴露范围。ClusterIP 只在集群内部可达NodePort 会在每个节点上开一个端口LoadBalancer 依赖云厂商的负载均衡器ExternalName 则是把一个 Service 映射到集群外部的域名。大部分内部服务用 ClusterIP 就够了只有需要被集群外部访问时才考虑 NodePort 或 LoadBalancer。这个选择直接影响流量路径和排障思路后面我会逐个展开说。1.2 Ingress 是南北流量的“大厅前台”Service 解决了内部访问问题但外部流量要进来总不能每个服务都开一个 NodePort那样端口管理会乱成一锅粥。Ingress 就是这层“大厅前台”——你告诉它哪个域名对应哪个 Service它负责按规则把请求转发到对应的后端。Ingress 工作在七层可以做域名路由、路径路由、TLS 终止比单纯的 NodePort 灵活得多。很多新手把 Ingress 理解成“另一个 Service”这是不对的。Ingress 只是一个资源对象真正干活的是背后的 Ingress Controller比如社区最常用的 nginx-ingress。创建 Ingress 资源之前你得先把 Controller 部署好。Controller 本身是一个 Pod通过监控集群里的 Ingress 资源动态更新它的反向代理配置。这个前后关系理清了后面排查 404、503 才有方向。1.3 Dashboard 是集群可观测性的“驾驶舱”Kubernetes Dashboard 是官方提供的 Web 管理界面可以查看工作负载、Service、Pod 日志、存储卷等资源是排查问题时最直观的工具。我在命令行下面敲 kubectl 能做的事在 Dashboard 上基本都能点出来尤其适合不习惯命令行的人或者给对端业务同学开一个只读的运维视图。不过 Dashboard 有一个必须重视的点默认部署完后它本身没有对外安全暴露的能力很多教程里图省事直接提供管理员账号这是隐患。更合理的做法是把它放到 Ingress 后面配上 TLS再用独立的 ServiceAccount 控制权限。标题里说的“Dashboard 管理插件”其实可以理解为你把 Dashboard 作为一个可插拔管理组件接入到自己的运维平台里而不是让每个人裸访问一个公网地址。1.4 ServiceAccount 与 RBAC 是权限体系的“门禁卡”ServiceAccount 和 RBAC 是集群安全的最底层。ServiceAccount 是 Pod 内应用访问 API Server 时使用的身份可以理解成一张门禁卡RBAC 就是门禁规则决定这张卡能进哪些门、能在里面做什么。Kubernetes 的 API 请求会经过一个固定流程先认证也就是确认你是什么身份再鉴权确认你有没有权限执行这个操作。认证阶段解决“你是谁”的问题鉴权阶段解决“你能干什么”的问题。ServiceAccount 负责认证Role、ClusterRole、RoleBinding、ClusterRoleBinding 这四类 RBAC 对象负责鉴权。把这套机制理清楚之后给业务方开权限、给 CI/CD 系统建账号、给不同团队划分命名空间都能做到最小权限管理而不是一把梭给 admin。2. Service 管理从类型选择到故障定位2.1 四种 Service 类型怎么选Service 类型的选择本质上是在问一个问题谁需要访问这个服务我整理了一个比较直观的对照类型访问范围适用场景注意事项ClusterIP集群内部微服务之间的调用、后端服务默认类型外部不可直接访问NodePort节点 IP 固定端口临时对外、自建集群无负载均衡时端口范围默认 30000-32767端口容易冲突LoadBalancer公网/内网负载均衡器云上生产环境对外服务会绑定云厂商LB有计费成本ExternalName外部域名别名把外部服务封装成集群内服务只做DNS CNAME不做转发我自己在云上集群里对外服务普遍用 LoadBalancer内网中间件用 ClusterIP。NodePort 更多是应急调试或测试环境用生产环境不建议大面积铺开因为节点数量多了之后端口管理很痛苦。ExternalName 用得最少但有一个场景很合适——你在把某个老系统的服务迁到 K8s 时先用 ExternalName 指向旧的物理机服务切流时再改成真正在集群内的 Service。这是平滑迁移的省力技巧。实战配置一个最基础的 ClusterIP Service假设后端有一组带app: myapp标签的 PodapiVersion: v1 kind: Service metadata: name: myapp-svc spec: type: ClusterIP selector: app: myapp ports: - port: 80 targetPort: 8080这里port是 Service 对外暴露的端口targetPort是 Pod 里容器实际监听的端口。很多新手把这两个写反导致 Service 创建成功但不通而且不报错很坑。2.2 选择器与 Endpoint 的联动关系Service 是怎么知道要把流量转给哪些 Pod 的答案是标签选择器。Service 通过spec.selector匹配 Pod 的标签一旦匹配成功Kubernetes 会自动维护对应的 Endpoint 列表。你可以用一条命令验证这个联动关系kubectl get endpoints myapp-svc -n your-namespace如果 Endpoints 里有 IP 列表说明选择器匹配成功如果显示none那说明后端 Pod 没被选中。这个命令是排查 Service 问题的第一工具。选择器配置最常见的三个坑一是标签不一致。写 selector 时少写了一个标签或者 Pod 模板里标签拼写错了都会导致 Endpoint 为空。二是 Pod 没就绪。Service 只会把流量转发给 readinessProbe 探测成功的 Pod如果 Pod 一直处于 NotReadyEndpoint 里也不会有它。三是命名空间搞混。Service 和 Pod 必须在同一个命名空间才能通过 selector 联动跨命名空间访问要靠服务名.命名空间.svc.cluster.local这种完整域名。2.3 排查 Service 不通的执行顺序我在排查 Service 不通的问题时已经形成了一套固定的执行顺序推荐给大家# 第一步确认 Service 是否存在 kubectl get svc myapp-svc -n your-namespace # 第二步查看 Endpoint 是否有后端地址 kubectl get endpoints myapp-svc -n your-namespace # 第三步查看 Service 详情里的端口配置和 Selector kubectl describe svc myapp-svc -n your-namespace # 第四步找一个集群内 Pod 做连通性测试 kubectl exec -it pod-name -n your-namespace -- curl http://myapp-svc:80为什么第一步要看 Service 存在有些人创建完发现不通结果kubectl get svc直接没输出说明 YAML 根本没生效可能是命名空间写错也可能是 apply 时报错没注意到。第二步看 Endpoints能定位到 selector 和 Pod 就绪的问题。第三步看 describe可以捕捉到事件信息比如拉起负载时有没有异常。第四步是终极验证在集群内部直接 curl Service 域名能区分问题是出在 DNS 解析、Service 转发还是后端应用本身。这套流程实测下来能解决九成以上的 Service 不通问题。如果到第四步发现 curl 到了 Service IP 但没响应再去检查后端 Pod 的日志和容器进程基本就是应用层的问题了。3. Ingress 管理路由接入与排障实录3.1 控制器才是流量入口的核心Ingress 要生效前提是集群里已经运行了 Ingress Controller。Controller 类型很多nginx-ingress 最常用traefik 也有不少人在用云厂商还会提供托管的 ingress controller。我自己线上环境用的是 nginx-ingress理由很简单功能成熟、文档多、社区踩坑资料丰富遇到问题基本都能搜到现成案例。部署完成以后先确认 Controller 的运行状态kubectl get pods -n ingress-nginx kubectl get svc -n ingress-nginxnginx-ingress 的 Service 一般会暴露成一个 LoadBalancer流量路径是外部流量 → LB → nginx-ingress Pod → 目标 Service → 目标 Pod。这里要注意一个常见误区Ingress 资源里的spec.rules[].http.paths[].backend.service写的是集群内部的 Service 名字和端口而不是直接写 Pod IP。Controller 会监听这些 Service 对应的 Endpoint 变化把流量转发到当前健康的 Pod。3.2 Ingress 资源与常用注解配置部署好 Controller 之后再创建 Ingress 资源才会有实际路由效果。这里给一个带 TLS 和路径重写的完整示例apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress namespace: your-namespace annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx tls: - hosts: - app.example.com secretName: app-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: myapp-svc port: number: 80TLS 部分要求app-tls这个 Secret 已经存在于同一命名空间内容格式是标准的 Kubernetes TLS Secret通过kubectl create secret tls生成即可。pathType: Prefix表示路径前缀匹配比如/api可以同时匹配/api/v1、/api/v2。如果你只想精确匹配一个路径用Exact。nginx-ingress 的注解是控制路由行为的关键。我常用的几个注解nginx.ingress.kubernetes.io/rewrite-target: /把请求路径重写后再转发给后端适合后端服务本身不带子路径的情况。nginx.ingress.kubernetes.io/proxy-body-size: 10m调整上传文件大小限制默认 1m很容易踩坑。nginx.ingress.kubernetes.io/ssl-redirect: trueHTTP 请求强制跳转到 HTTPS。nginx.ingress.kubernetes.io/backend-protocol: HTTPS当后端 Service 本身是 HTTPS 服务时必须加这个注解否则 Controller 会用 HTTP 去访问后端的 HTTPS 端口报 502 或 504。最后一个注解尤其重要Dashboard 这类服务默认就是 HTTPS后面接入 Ingress 时一定要用。3.3 503/404 的现场排查经验Ingress 的报错形态很典型大部分集中在 503 和 404。503 的报错信息最常见的是类似503 Service Unavailable: no server is available to handle this request。这个报错的根源通常是请求已经进入 nginx-ingress但 Controller 从它维护的上游列表里找不到可用后端。说白了就是目标 Service 的 Endpoint 为空或者所有 Pod 都处于未就绪状态。排查路径是回到第 2 节的思路先看 Service 的 Endpoints再看 Pod 状态和 readinessProbe。404 则分两种情况。第一种是 DNS/域名没匹配上 Ingress rule请求访问的 host 和 Ingress 里写的 host 不一致第二种是路径匹配不到规则pathType写得太严格或路径前缀没对上。此时先检查kubectl get ingress里的规则再检查 Controller 的访问日志确认实际请求 URL 匹配到了哪条规则。日志一般通过查看 ingress-nginx-controller Pod 的标准输出得到里面每一行都会记录 host、path、upstream 和状态码信息量很大。4. Dashboard 管理安全暴露与插件化接入4.1 部署 Dashboard 并获取管理员 TokenKubernetes Dashboard 的部署很简单官方给了统一的一键安装包但版本会持续更新建议直接去官方仓库的 recommended.yaml 获取最新版本。部署命令一般是kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/version/aio/deploy/recommended.yaml部署完成以后Dashboard 默认跑在kubernetes-dashboard命名空间下Service 类型是 ClusterIP外部还不能直接访问。访问 Dashboard 需要先有 Token 或 kubeconfig。我自己平时调试会先建一个临时管理员账号步骤如下。先创建管理员 ServiceAccountapiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard然后生成 Tokenkubectl -n kubernetes-dashboard create token admin-user这里我要专门提醒一句管理员账号只建议在调试阶段使用不能给真实用户直接用。真实用户应该按照第 5 节的 RBAC 思路给每个人或每个角色独立的最小权限账号。这是集群使用习惯的问题从一开始就要立规矩。新版 Dashboard 登录时选择 Token 方式粘贴上面命令输出的 Token 即可。4.2 用 Ingress 给 Dashboard 做安全入口Dashboard 默认监听 443 端口直接用 NodePort 暴露当然能访问但裸奔不安全。更稳妥的做法是用 Ingress 把 Dashboard 接到域名上同时加上 TLS。这里用前面提到过的backend-protocol: HTTPS注解apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: dashboard-ingress namespace: kubernetes-dashboard annotations: nginx.ingress.kubernetes.io/backend-protocol: HTTPS spec: ingressClassName: nginx rules: - host: k8s-dashboard.example.com http: paths: - path: / pathType: Prefix backend: service: name: kubernetes-dashboard port: number: 443这里最容易踩的坑就是忘记backend-protocol注解导致 Ingress 用 HTTP 访问 Dashboard 的 HTTPS 端口浏览器里会出现反复 502 或重定向循环。另外如果前面配置了全局 TLS SecretDashboard 的 Ingress 同样可以复用浏览器端和 Controller 到后端这两段链路都是加密的。4.3 把 Dashboard 集成进自有平台的思路标题里的“Dashboard 管理插件”我理解不只是指用官方的 Web 界面而是希望把集群管理能力整合进公司已有的运维平台上。我实践过一种比较顺的方案。第一种方式是 iframe 嵌入。把 Dashboard 通过 Ingress 暴露到一个内部域名后直接在你自己的平台上用 iframe 内嵌。但要注意iframe 跨域时如果你在顶层页面已经登录过 SSO里面的 Dashboard 还是要再输入一次 Token。解决方案是在生成隧道页面时通过后端调用 K8s 的 TokenRequest API获取一个短时有效的 ServiceAccount Token然后把它塞到 iframe 的 URL 或者初始化参数里实现免登录。第二种方式是直接用 client-go 调用 API Server把资源列表、Pod 状态、事件等数据在前端自己渲染成仪表盘。Dashboard 本身是个参考实现它底层也是调用 API Server。如果你所在团队有前端能力我更推荐这种灵活度和安全边界都可控不依赖官方 Dashboard 的交互逻辑。所谓插件化就是把 K8s 的展示层和你的平台打通不要让用户去记一长串集群地址和 Token。5. ServiceAccount 与 RBAC 角色鉴权5.1 ServiceAccount 的底层逻辑ServiceAccount 的本质是一个身份对象。默认情况下每个命名空间都有一个defaultServiceAccountPod 创建时如果没有显式指定就会自动挂载这个 default SA 的凭证。你可以进到任意一个 Pod 里查看kubectl exec -it pod-name -- ls /var/run/secrets/kubernetes.io/serviceaccount/这个目录下一般有三个文件ca.crt、namespace、token。应用通过读取 token 文件把它放到 API Server 请求的Authorization头里API Server 就能识别这个请求来自哪个 ServiceAccount。这个就是认证过程。新版 Kubernetes 对 token 的处理发生了变化不再建议长期依赖自动生成的 Secret token而是推荐使用 TokenRequest API 获取短期 token过期自动轮换。所以你会看到创建 ServiceAccount 之后不再自动创建对应的 Secret这是正常现象不用慌。创建 ServiceAccount 很简单kubectl create serviceaccount dev-readonly -n gwl30但只有 ServiceAccount 还不够它只是一个身份没有任何权限。要让这个身份能读取资源必须通过 RBAC 绑定角色。5.2 RBAC 四类对象的适用边界RBAC 有四个核心对象新手经常混淆我列了一个对照表对象作用范围用于绑定谁典型用途Role单个命名空间ServiceAccount、用户、组给某个业务团队分配某命名空间操作权限ClusterRole整个集群ServiceAccount、用户、组查看所有命名空间的资源、节点信息RoleBinding命名空间内绑定 Role 或 ClusterRole把权限限定在单个命名空间内ClusterRoleBinding整个集群绑定 ClusterRole把集群级权限授给某个主体这里有一个很实用的点RoleBinding 不只可以绑定 Role也可以绑定 ClusterRole。比如你希望业务方能看到集群所有命名空间的 Pod但只能在gwl30这个命名空间里创建 Deployment那就可以用一个 ClusterRolelist pods加一个 Role管理 deployment然后分别通过不同方式绑定。另一个内置的坑Kubernetes 带了一批以system:开头的内置 ClusterRole比如cluster-admin、view、edit、admin。view是只读edit能读写一个命名空间内的大部分资源但不能改角色权限admin在 edit 基础上还能管理角色绑定。给普通业务方开权限时优先考虑在这些内置角色基础上做裁剪而不是直接给 cluster-admin。5.3 最小权限配置实例给业务方只读权限下面是一个实际可用的最小权限配置案例。假设你在gwl30命名空间里有一个业务团队需要它能够查看 Pod、Service、Endpoint、ConfigMap 以及 Pod 日志但不需要修改资源更不应该拥有删除权限。可以在 YAML 里一次性创建 ServiceAccount、Role、RoleBindingapiVersion: v1 kind: ServiceAccount metadata: name: dev-readonly namespace: gwl30 --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: gwl30 name: readonly rules: - apiGroups: [] resources: [pods, pods/log, services, endpoints, configmaps] verbs: [get, list, watch] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: namespace: gwl30 name: dev-readonly-binding subjects: - kind: ServiceAccount name: dev-readonly namespace: gwl30 roleRef: kind: Role name: readonly apiGroup: rbac.authorization.k8s.io注意pods/log也被写进了 resources否则即便能列出 Pod也无法查看日志。日志权限是业务方很常用的需求但很多人只给pods不给pods/log结果前端 Dashboard 里日志按钮一点就是 Forbidden。配好之后验证一下当前 ServiceAccount 的实际权限kubectl auth can-i list pods -n gwl30 --assystem:serviceaccount:gwl30:dev-readonly返回yes说明权限生效。把同样的命令换成delete pods应该返回no。5.4 现场还原一次 Forbidden 报错的排查有时候你会看到这样的报错Error from server (Forbidden): user system:serviceaccount:gwl30:default cannot get resource services in API group in the namespace gwl30这个报错信息其实已经把原因讲清楚了发起请求的身份是gwl30命名空间里的defaultServiceAccount它想读取gwl30命名空间下的 services 资源但它没有被赋予这个权限。这是典型的“身份已认证但授权不通过”。排查思路分三步。第一步确认当前请求用的身份是什么。如果是 Pod 里发起的看 Pod 的spec.serviceAccountName是不是没有指定默认就用 default。有时候你明明创建了dev-readonly但 Deployment 里忘了写serviceAccountNamePod 仍然用 default权限自然不够。第二步确认这个身份绑定了哪些角色kubectl get rolebindings -n gwl30 -o yaml看有没有包含system:serviceaccount:gwl30:dev-readonly的 subjects。第三步如果确实没有绑定创建对应的 RoleBinding 即可。如果绑定了但还是 Forbidden检查 Role 的 rules 里是否包含了请求中的 resource 和 verb。get、list、watch是三个不同的 verb只给get不给list页面上能看详情但列表页会报错这种情况比较隐蔽。6. 常见问题速查与实用习惯6.1 问题现象与处理动作速查表把前面提到的典型问题整理成一张速查表方便遇到现象时直接对照现象可能原因优先处理动作Service 创建成功但 Pod 间访问不通Endpoints 为空 / 端口配置错误kubectl get endpoints查后端检查 selector 和 targetPortNodePort 外网访问不通安全组未放行 / 节点防火墙检查云安全组入方向是否放行节点端口Ingress 返回 503后端 Service 无可用 Endpoint / Pod 未就绪查看 Endpoints、Pod 状态、readinessProbeIngress 返回 404host 或 path 未匹配规则查看 Ingress 规则和 Controller 访问日志Ingress 返回 502/504后端协议不匹配 / 后端应用超时检查backend-protocol注解和后端应用响应时间Dashboard 无法登录Token 未生成 / SA 未绑定权限kubectl -n kubernetes-dashboard create token admin-userDashboard 通过 Ingress 访问反复 502忘了加backend-protocol: HTTPS给 Ingress 加对应注解Forbidden 报错身份没有对应 RBAC 权限定位请求身份补齐 RoleBinding核对 Role 规则 verbs业务 Pod 报 403ServiceAccount 未绑定权限Deployment 中显式指定 serviceAccountName这张表是我在生产环境里踩过的最多的几类问题基本覆盖了大多数故障场景。6.2 几个值得长期坚持的操作习惯最后说几个我自己的操作习惯都是在多集群环境里长期验证过有效的事。第一个习惯每个业务命名空间都单独建 ServiceAccount明确写上serviceAccountName不要依赖 default。default SA 是历史包袱权限边界模糊出了问题你很难定位到人。第二个习惯凡是牵扯到跨资源权限的配置都用kubectl auth can-i先验证再交付不要等用户报错才知道没权限。第三个习惯对 Ingress 和 Dashboard 这类对外暴露的入口统一走内部域名 TLS不要开裸 NodePort安全审查会省很多事。第四个习惯升级集群或安装新组件之前先看一眼有没有引入 admission webhook这类组件如果版本不兼容可能会拦截所有请求导致集群看起来一切正常但什么操作都执行不了。我在这几年的集群运维里最大的体会是Service 和 Ingress 决定业务跑不跑得通RBAC 和 ServiceAccount 决定你有没有能力控制这个系统。前者是效率问题后者是安全问题两者都值得在集群刚建好时一次性规划到位不要等出了问题再补救。
阅读完成 · 觉得有帮助?
咨询建站