资讯详情

资讯详情

Kubernetes Namespaces 实战:资源隔离与多租户管理指南

1. 为什么 Namespaces 是 K8s 里最被低估的隔离手段很多人刚接触 Kubernetes 的时候对 Namespaces 的理解就停留在“建个命名空间把 Pod 丢进去”这个层面。我一开始也这样觉得这东西不就是个文件夹嘛把资源分分类而已。直到有一次在测试环境里一个同事误删了某个 Namespace 下的 Service结果整个测试链路全挂了而生产环境因为 Namespace 隔离得当毫发无损。那次之后我才真正意识到Namespaces 远不止“分类”这么简单它是 K8s 里做逻辑隔离最轻量、最灵活、也最容易被忽视的一把利器。这篇文章我想跟你聊的就是怎么在实际项目里把 Namespaces 用好、用对、用出价值。不管你是刚上手 K8s 的新手还是已经跑过几个集群的老手我相信下面这些从实战里摸出来的经验多少能帮你少踩几个坑。我会从设计思路讲到具体操作从资源配额讲到网络策略从常见问题讲到排查技巧尽量把每个环节的“为什么”和“怎么做”都说透。先明确一下Namespaces 解决的核心问题是在同一个物理集群内为不同的团队、项目、环境提供逻辑上的资源分组和隔离。它不是网络隔离也不是安全隔离的银弹但它是一切上层隔离策略的基础。你后面要做的 ResourceQuota、LimitRange、NetworkPolicy、RBAC几乎都要挂在 Namespace 这个维度上。所以把 Namespaces 设计好后面的路会顺很多。2. 集群 Namespace 的整体设计与划分思路2.1 按团队划分还是按环境划分这是我在实际项目里被问得最多的一个问题。我的经验是优先按环境划分再按团队细分。为什么因为环境之间的隔离需求通常比团队之间的隔离需求更刚性。开发环境的东西绝对不能跑到生产环境去这是底线。而团队之间的隔离很多时候可以通过 RBAC 和 ResourceQuota 在同一个 Namespace 内做更细粒度的控制。具体来说我一般会这样设计dev开发环境资源配额宽松允许频繁变更staging预发布环境配置尽量贴近生产但资源规模可以缩小prod生产环境资源配额严格变更需要审批monitoring监控组件专用独立出来避免被业务资源挤占ingress-nginx入口控制器专用同样独立kube-system系统组件绝对不要动如果团队规模比较大可以在每个环境下面再按团队细分比如dev-teamA、dev-teamB、prod-teamA这样。但我不建议一上来就分得太细Namespace 太多管理成本会急剧上升。我见过一个集群里有一百多个 Namespace最后连谁负责哪个都搞不清楚了。2.2 哪些资源是 Namespace 级别的哪些不是这个问题非常关键搞错了会导致很多莫名其妙的错误。我整理了一个表格你可以直接对照着看资源类型是否属于 Namespace说明Pod是最常见的 Namespace 级资源Service是服务发现的范围限定在 Namespace 内ConfigMap是配置隔离的重要载体Secret是敏感信息按 Namespace 隔离PVC是存储卷声明是 Namespace 级的Deployment是工作负载控制器StatefulSet是有状态工作负载DaemonSet是但 Pod 会调度到所有节点Node否集群级资源PV否存储卷是集群级的StorageClass否集群级资源ClusterRole否集群级 RBACNamespace 本身否它是集群级资源注意DaemonSet 虽然定义在某个 Namespace 里但它创建的 Pod 会跑在所有节点上所以它并不受 Namespace 的资源配额限制。这一点很多人会搞混。2.3 命名规范与标签策略命名规范这件事看起来小实际上影响很大。我建议采用环境-团队-用途的三段式命名比如prod-payment-api、dev-search-cache。全小写用连字符分隔不要用下划线或大写字母因为 K8s 的 DNS 命名规则对大小写敏感混用容易出问题。标签策略同样重要。我一般会给每个 Namespace 打上这几个标签apiVersion: v1 kind: Namespace metadata: name: prod-payment-api labels: environment: prod team: payment cost-center: 12345 managed-by: platform-team这些标签后面可以用来做成本分摊、策略匹配、监控分组非常有用。特别是cost-center这个标签财务那边做成本核算的时候会感谢你的。3. 核心隔离机制与实操配置详解3.1 ResourceQuota给 Namespace 戴上紧箍咒ResourceQuota 是我认为 Namespace 体系里最重要的一个东西。没有它一个 Namespace 里的 Pod 可以把整个集群的资源吃光其他 Namespace 全部遭殃。我亲身经历过一次某个开发在 dev 环境里跑了一个死循环的 Job创建了几百个 Pod把节点资源全部占满导致 prod 环境的 Pod 被驱逐。从那以后我给每个 Namespace 都配了 ResourceQuota。一个典型的 ResourceQuota 配置长这样apiVersion: v1 kind: ResourceQuota metadata: name: prod-quota namespace: prod-payment-api spec: hard: requests.cpu: 20 requests.memory: 40Gi limits.cpu: 40 limits.memory: 80Gi pods: 100 services: 20 persistentvolumeclaims: 10 requests.storage: 500Gi这里有几个参数需要解释一下。requests.cpu和requests.memory是所有 Pod 的 requests 总和的上限limits.cpu和limits.memory是 limits 总和的上限。为什么两个都要设因为 K8s 调度器看的是 requests而实际运行时受 limits 约束。只设 requests 不设 limits可能导致节点过载只设 limits 不设 requests调度器可能把 Pod 调度到资源不足的节点上。pods这个参数限制的是 Namespace 内 Pod 的总数。这个值不要设得太大否则一个 Namespace 可以创建大量小 Pod把节点的 PID 或 IP 资源耗尽。我一般会根据节点规模和业务需求来估算比如 3 个节点、每个节点最多跑 110 个 Pod那总容量大概 330 个给单个 Namespace 设 100 个已经算宽松了。实操心得ResourceQuota 设置之后如果 Namespace 内已有的资源已经超过了配额新的资源创建会被拒绝但已有的资源不会被删除。所以最好在 Namespace 创建初期就把配额设好避免后期调整时出现“存量超限”的尴尬局面。3.2 LimitRange给单个 Pod 设默认值和上下限ResourceQuota 管的是总量LimitRange 管的是单个 Pod 或 Container 的粒度。这两个配合使用效果最好。LimitRange 可以做三件事设置默认的 requests 和 limits、设置最小和最大的 requests/limits、限制 PVC 的大小范围。apiVersion: v1 kind: LimitRange metadata: name: prod-limits namespace: prod-payment-api spec: limits: - type: Container default: cpu: 500m memory: 512Mi defaultRequest: cpu: 200m memory: 256Mi max: cpu: 4 memory: 8Gi min: cpu: 50m memory: 64Mi - type: PersistentVolumeClaim max: storage: 100Gi min: storage: 1Gidefault和defaultRequest的作用是如果用户在创建 Pod 时没有指定 requests 和 limits就自动使用这些默认值。这个功能非常实用因为很多开发人员写 YAML 的时候会忘记写 resources 字段有了 LimitRange至少不会出现“裸奔”的 Pod。max和min则是硬性约束。比如max.cpu: 4意味着任何单个 Container 的 CPU limit 不能超过 4 核。这个限制可以防止某个 Pod 申请过大的资源导致调度失败或者影响其他 Pod。注意LimitRange 的max和 ResourceQuota 的hard是两套独立的约束。LimitRange 管单个 PodResourceQuota 管总量。两者都要配才能形成完整的资源管控体系。3.3 NetworkPolicyNamespace 间的网络隔离默认情况下K8s 集群内所有 Pod 之间是可以互相通信的不管它们在哪个 Namespace。这在多租户场景下是个安全隐患。NetworkPolicy 就是用来解决这个问题的。不过要注意NetworkPolicy 需要 CNI 插件支持。Calico、Cilium、Weave Net 这些主流 CNI 都支持但 Flannel 默认不支持。如果你用的是 Flannel需要额外配置才能启用 NetworkPolicy。一个典型的场景是只允许prod-payment-api这个 Namespace 的 Pod 接收来自ingress-nginxNamespace 的流量拒绝其他所有入站流量。apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-only namespace: prod-payment-api spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: ingress-nginx ports: - protocol: TCP port: 8080这个配置的意思是选中prod-payment-api下所有 Pod只允许来自带有name: ingress-nginx标签的 Namespace 的流量进入且只允许访问 8080 端口。其他所有入站流量一律拒绝。实操心得NetworkPolicy 是白名单机制一旦某个 Pod 被任何 NetworkPolicy 选中它就会拒绝所有未被明确允许的流量。所以配置的时候要特别小心别把自己需要用的流量也给拒了。我建议先在测试环境验证确认没问题再上生产。3.4 RBAC把权限锁在 Namespace 里RBAC 和 Namespace 的结合是权限隔离的关键。通过 Role 和 RoleBinding你可以让某个用户或 ServiceAccount 只在特定的 Namespace 里有权限出了这个 Namespace 就什么也干不了。apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: dev-teamA name: developer rules: - apiGroups: [, apps, batch] resources: [pods, deployments, services, jobs] verbs: [get, list, watch, create, update, patch, delete] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: developer-binding namespace: dev-teamA subjects: - kind: User name: dev-user-a apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: developer apiGroup: rbac.authorization.k8s.io这个配置让dev-user-a这个用户在dev-teamA这个 Namespace 里拥有对 Pod、Deployment、Service、Job 的完整操作权限但在其他 Namespace 里没有任何权限。注意Role 和 RoleBinding 是 Namespace 级的ClusterRole 和 ClusterRoleBinding 是集群级的。如果你想让某个用户在所有 Namespace 里都有只读权限可以用 ClusterRole 加 RoleBinding 的组合这样权限只在绑定的 Namespace 生效。4. 完整实操流程从零搭建一个多租户 Namespace 体系4.1 创建 Namespace 并打标签第一步当然是创建 Namespace。我习惯用 YAML 文件而不是kubectl create namespace命令因为 YAML 可以版本化管理方便追溯和回滚。apiVersion: v1 kind: Namespace metadata: name: prod-payment-api labels: environment: prod team: payment cost-center: 12345创建命令kubectl apply -f namespace.yaml创建完之后验证一下kubectl get namespace prod-payment-api --show-labels4.2 配置 ResourceQuota 和 LimitRange接着把前面提到的 ResourceQuota 和 LimitRange 都应用上去kubectl apply -f resourcequota.yaml kubectl apply -f limitrange.yaml验证配置是否生效kubectl describe resourcequota prod-quota -n prod-payment-api kubectl describe limitrange prod-limits -n prod-payment-api这里有个小技巧kubectl describe resourcequota会显示当前已使用的资源和剩余配额非常直观。我一般会把这个命令加到日常巡检脚本里定期检查有没有 Namespace 快超限了。4.3 配置网络策略网络策略的配置需要根据实际业务流量来定。我一般会先梳理清楚哪些 Namespace 需要互相通信然后按最小权限原则配置。kubectl apply -f networkpolicy.yaml验证网络策略是否生效可以用一个临时 Pod 来测试kubectl run test-pod --imagebusybox -n default -- sleep 3600 kubectl exec -it test-pod -n default -- wget -qO- --timeout3 http://payment-service.prod-payment-api:8080如果网络策略配置正确这个请求应该超时或被拒绝。如果返回了内容说明策略没生效需要检查 CNI 插件是否支持 NetworkPolicy。4.4 配置 RBAC 权限RBAC 的配置要根据团队的组织结构来。我一般会为每个团队创建一个 Role然后绑定到对应的用户或 ServiceAccount。kubectl apply -f role.yaml kubectl apply -f rolebinding.yaml验证权限kubectl auth can-i create pods -n prod-payment-api --asdev-user-a kubectl auth can-i create pods -n dev-teamA --asdev-user-a第一条命令应该返回yes第二条应该返回no。如果结果不符合预期检查 RoleBinding 的 subjects 和 roleRef 是否正确。4.5 设置默认 Namespace 和上下文切换日常操作中频繁指定-n参数很麻烦可以用kubectl config set-context来设置默认 Namespacekubectl config set-context --current --namespaceprod-payment-api这样后续的kubectl命令就不需要每次都加-n了。不过要小心切换上下文之后容易忘记当前在哪个 Namespace建议在 shell 提示符里显示当前 Namespace或者用kubens这类工具来管理。实操心得我强烈建议安装kubectx和kubens这两个工具。kubectx用来快速切换集群kubens用来快速切换 Namespace。它们能极大提升日常操作效率减少因为搞错 Namespace 而导致的误操作。5. 常见问题与排查技巧实录5.1 Pod 创建失败提示“exceeded quota”这是最常见的 ResourceQuota 相关问题。排查步骤查看 ResourceQuota 的当前使用情况kubectl describe resourcequota -n namespace检查 Pod 的 requests 和 limits 是否超过了剩余配额如果确实超了要么调整配额要么优化 Pod 的资源申请避坑技巧ResourceQuota 的hard值调整之后不会自动重新计算已有资源。如果之前已经超限调整配额后需要等一段时间或者手动触发重新计算。我一般会先删除一些不必要的资源再调整配额。5.2 Pod 创建失败提示“no limit range”这个错误通常是因为 LimitRange 配置了max但 Pod 没有指定 limits或者 Pod 的 limits 超过了max。解决方法检查 LimitRange 配置kubectl describe limitrange -n namespace确保 Pod 的 resources 字段符合 LimitRange 的要求如果 LimitRange 配置了defaultPod 不指定 resources 也可以但建议还是显式指定5.3 跨 Namespace 服务访问不通Service 的 DNS 名称是service.namespace.svc.cluster.local跨 Namespace 访问必须带上 Namespace 名称。比如从dev-teamA访问prod-payment-api的payment-service需要用payment-service.prod-payment-api.svc.cluster.local。如果 DNS 解析没问题但连接不通检查 NetworkPolicy 是否阻止了流量。可以用kubectl describe networkpolicy -n namespace查看策略详情。5.4 删除 Namespace 卡在 Terminating 状态这是 K8s 里非常经典的问题。Namespace 删除时会清理里面的所有资源如果有资源无法删除比如 Finalizer 没处理完Namespace 就会一直卡在 Terminating。排查方法kubectl get namespace name -o json | jq .status.conditions查看是哪些资源卡住了kubectl api-resources --verbslist --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n name如果确认资源已经不需要了可以手动移除 Finalizerkubectl get namespace name -o json | jq .spec.finalizers [] | kubectl replace --raw /api/v1/namespaces/name/finalize -f -注意手动移除 Finalizer 是有风险的操作可能导致资源泄漏。只有在确认资源已经清理干净或者不再需要的情况下才这么做。5.5 常见问题速查表问题现象可能原因排查命令解决方法Pod 创建失败提示 exceeded quotaResourceQuota 超限kubectl describe resourcequota -n ns调整配额或清理资源Pod 创建失败提示 no limit rangeLimitRange 约束不满足kubectl describe limitrange -n ns调整 Pod resources 或 LimitRange跨 Namespace 访问不通DNS 名称不对或 NetworkPolicy 阻止kubectl exec测试连通性使用完整 DNS 名称或调整 NetworkPolicyNamespace 卡在 Terminating资源 Finalizer 未处理kubectl get namespace -o json清理资源或手动移除 FinalizerService 无法解析CoreDNS 问题或 Service 不存在kubectl get svc -n ns检查 Service 和 CoreDNSRBAC 权限不足RoleBinding 配置错误kubectl auth can-i检查 Role 和 RoleBinding6. 一些容易被忽视的细节和进阶用法6.1 Namespace 的自动清理K8s 从 1.20 版本开始支持自动清理已删除 Namespace 中的资源。这个功能默认是关闭的需要在 kube-controller-manager 中启用NamespaceDefaultDeletionPolicy特性门控。启用后Namespace 删除时会自动清理里面的资源不需要手动处理 Finalizer。不过这个功能在生产环境要慎用因为自动清理可能会误删一些还在使用的资源。我一般只在测试环境启用生产环境还是手动确认后再删除。6.2 用 Namespace 做成本分摊结合前面提到的cost-center标签可以用 Prometheus 和 Grafana 做成本分摊看板。具体做法是用 kube-state-metrics 采集 Namespace 级别的资源使用数据在 Prometheus 里按 Namespace 聚合 CPU 和内存使用量在 Grafana 里按cost-center标签分组展示这样每个团队用了多少资源、该分摊多少成本一目了然。我在上一家公司就是这么做的财务那边非常满意。6.3 Namespace 与 GitOps 的结合如果你在用 ArgoCD 或 Flux 这类 GitOps 工具可以把 Namespace 的定义也纳入 Git 管理。每个 Namespace 对应一个目录里面放 Namespace 本身、ResourceQuota、LimitRange、NetworkPolicy、RBAC 等配置。这样 Namespace 的创建和变更都有版本记录方便审计和回滚。namespaces/ ├── prod-payment-api/ │ ├── namespace.yaml │ ├── resourcequota.yaml │ ├── limitrange.yaml │ ├── networkpolicy.yaml │ └── rbac.yaml ├── dev-teamA/ │ ├── namespace.yaml │ ├── resourcequota.yaml │ └── limitrange.yaml └── monitoring/ ├── namespace.yaml └── resourcequota.yaml这种结构清晰、可维护新人接手也能快速理解。6.4 监控 Namespace 级别的资源使用除了 ResourceQuota 的硬限制我还建议配置监控告警。当某个 Namespace 的资源使用率达到配额的 80% 时提前告警避免真的超限了才发现。Prometheus 告警规则示例groups: - name: namespace-quota rules: - alert: NamespaceQuotaNearLimit expr: | kube_resourcequota{typeused} / kube_resourcequota{typehard} 0.8 for: 5m labels: severity: warning annotations: summary: Namespace {{ $labels.namespace }} 的 {{ $labels.resource }} 使用率超过 80%这个告警规则会监控所有 Namespace 的所有资源配额使用率超过 80% 持续 5 分钟就触发告警。非常实用。6.5 关于默认 Namespace 的那些坑default这个 Namespace 是 K8s 自带的很多人图省事就把东西往里面丢。我强烈建议不要这么做。原因有三第一defaultNamespace 没有 ResourceQuota 和 LimitRange资源不受控。第二defaultNamespace 的 RBAC 权限通常比较宽松容易出安全问题。第三所有东西混在一起排查问题的时候非常痛苦。我的做法是把defaultNamespace 当成一个“禁止使用”的 Namespace通过 ResourceQuota 把它限制到最小比如只允许创建 1 个 Pod、100m CPU、128Mi 内存。这样即使有人不小心往里面创建资源也造不成什么影响。实操心得我见过一个集群defaultNamespace 里跑了上百个 Pod包括一些关键的业务服务。后来做安全审计的时候发现这些 Pod 的 ServiceAccount 权限过大存在严重的安全隐患。清理的时候花了整整两天时间。所以从一开始就养成好习惯别用default。7. 我个人在实际操作中的几点体会Namespace 这个东西说简单也简单说复杂也复杂。简单在于它的概念很直观就是逻辑分组复杂在于它牵扯到资源管控、网络隔离、权限管理、成本分摊等方方面面。我做了这么多集群最大的体会就是Namespace 的设计一定要在集群搭建初期就做好后期再改成本极高。我踩过最大的一个坑就是早期没有给 Namespace 配 ResourceQuota结果一个测试环境的 Namespace 把整个集群的资源吃光了导致生产环境受影响。从那以后我给每个 Namespace 都配了配额而且把“创建 Namespace 必须同时创建 ResourceQuota 和 LimitRange”写进了团队的操作规范里。另一个体会是Namespace 的命名和标签一定要规范。我见过太多集群Namespace 名字五花八门有的用下划线有的用大写有的用中文最后做自动化的时候各种兼容问题。统一用环境-团队-用途的小写连字符格式能省掉很多麻烦。最后再分享一个小技巧如果你经常需要在多个 Namespace 之间切换可以在 shell 里定义一个函数把当前 Namespace 显示在提示符里。这样你随时都知道自己在哪个 Namespace避免误操作。我用的是 zsh在.zshrc里加了这么一段kube_ns() { local ns$(kubectl config view --minify -o jsonpath{..namespace} 2/dev/null) echo ${ns:-default} } PROMPT%F{blue}[$(kube_ns)]%f $PROMPT这样每次打开终端提示符里都会显示当前 Namespace非常直观。如果你用的是 bash原理类似改一下PS1就行。这个内容后续还可以这样扩展结合 Open Policy Agent 做 Namespace 级别的策略校验确保所有创建的资源都符合团队的规范或者结合 Kyverno 做自动化的 Namespace 配置注入新建 Namespace 时自动带上 ResourceQuota、LimitRange 和 NetworkPolicy。这些进阶玩法等有机会再单独聊。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →