K8S微服务部署实战:双区隔离、多租户与CI/CD避坑指南
发布时间:2026/10/9 3:00:30 锦皓数字建站

简介这份文档面向正在推进微服务架构落地的架构师、运维工程师与技术决策者围绕基于K8S容器云平台的微服务部署方案展开重点解决服务依赖、服务发现、负载均衡、集群管理与有状态数据管理等落地难题。内容涵盖容器云部署框架、权限管理、多租户管理以及日志与监控四大关键维度并具体说明DMZ与内网两套Openshift环境彼此隔离的部署思路、OCP基于OAuth的认证与细粒度鉴权、project租户隔离机制以及EFK日志方案与Heapster、Hawkular、Cassandra监控链路。资源包为1个docx文档约417KB结构紧凑适合作为企业级容器云部署的参考方案。目前已有497人学习可帮助读者快速理解K8S与Openshift在微服务场景下的部署要点与设计取舍。1. 从一份社区问答整理稿说起K8S 微服务部署到底在解决什么如果你手上正拿着一份叫《基于K8S容器云平台的微服务部署方案》的文档大概率会先愣一下——它不像官方手册那样从架构图讲起也不像博客那样只贴几段 YAML而是一份由社区专家顾文俊根据线上交流活动整理、多位会员贡献的问答合集。这种“问答体”文档的价值恰恰在于它记录的是真实落地时被反复追问的问题而不是产品宣传页上那些漂亮话。K8S 是第一个把“一切以服务为中心一切围绕服务运转”当指导思想做出来的容器编排产品构建在它上面的系统可以跑在物理机、虚拟机集群或企业私有云上也能托管在公有云里。微服务架构把一个巨大的单体应用拆成很多小的、互相连接的服务一个服务背后可能有多个实例副本在支撑服务之间必然产生依赖关系。发布时如果每个服务都单独启动登录服务、支付服务一个个手动拉起来那运维基本不用干别的了编排动作必不可少。这份文档要解决的就是“基于 K8S 的容器云平台到底怎么部署微服务”这个从选型到落地的完整链路问题适合正在做容器化改造的运维和架构人员也适合想搞清楚 OCP 与原生 K8S 差异的开发。2. 部署框架与权限底座DMZ/内网双区隔离怎么落地2.1 双 Openshift 集群的物理隔离逻辑文档里给出的部署框架很明确在 DMZ 和内网分别部署彼此独立的 2 套 Openshift分别对应内网和 DMZ 区两个网段两套环境彼此隔离。DMZ 区的 Openshift 部署对外发布的应用负责处理外网访问内网的 Openshift 部署针对内网的应用仅负责处理内网访问。这种做法的核心动机是安全边界——对外暴露的服务和内网核心服务不共享同一个集群的控制面即使 DMZ 区被攻破内网集群的 API Server、etcd 和业务 Pod 仍然独立。常见做法是两套集群各自维护独立的镜像仓库和存储后端DMZ 区集群的节点不挂载内网数据库的直连权限所有跨区数据访问走防火墙白名单。落地时第一步是给计算节点打标签让应用部署时能精确落到指定节点。比如在 DMZ 网段对某应用使用的 2 台计算节点打上标签部署时 nodeSelector 指明使用的节点标签。命令如下# 给 DMZ 区的两台计算节点打标签 oc label node dmz-node-01 zonedmz appxxx oc label node dmz-node-02 zonedmz appxxx # 查看标签是否生效 oc get nodes --show-labels | grep dmz逻辑说明oc label node是 OpenShift 对 K8Skubectl label node的封装zonedmz用于区域标识appxxx用于应用级绑定。参数上标签键值对一旦写入节点对象调度器就会在 Pod 的 nodeSelector 匹配时使用。失败时先看oc describe node的 Labels 字段是否包含目标键值再检查节点是否处于 Ready 状态。注意标签是覆盖式操作重复执行同一键会更新值不会报错。2.2 认证、鉴权与 SCC 三层权限模型企业级平台会有来自内外不同角色的用户灵活的、细粒度的、可扩展的权限管理必不可少。OCP 从设计初期就集成了标准化的认证服务器定义了详细的权限策略和角色。认证层面OCP 平台的用户是基于对 OCP API 的调用权限来定义的所有操作都基于 API用户可以是一个开发人员或者管理员和 OCP 进行交互。OCP 内置了一个基于 OAuth 的通用身份认证规范的服务器可以通过多种不同类型的认证源对用户进行认证。鉴权层面权限策略决定了一个用户是否具有对某个对象的操作权限管理员可以设置不同规则和角色对用户或用户组赋予一定角色角色包含一系列操作规则。除了传统的认证和鉴权OCP 还提供了针对 Pod 的细粒度权限控制 SCCSecurity Context Constraints可以限制 Pod 具备何种类型的权限比如容器是否可以运行在特权模式下、是否可以挂载宿主机的目录、是否可以使用宿主机的端口、是否可以以 root 用户运行等。配置 SCC 的常见做法是# 创建一个限制性 SCC禁止特权模式和宿主机目录挂载 apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-custom allowPrivilegedContainer: false allowHostDirVolumePlugin: false allowHostPorts: false runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs逻辑说明allowPrivilegedContainer: false禁止特权容器allowHostDirVolumePlugin: false禁止挂载宿主机目录runAsUser设为MustRunAsRange强制容器以指定范围内的非 root 用户运行。参数上MustRunAsRange需要配合 namespace 的openshift.io/sa.scc.uid-range注解使用。失败时看 Pod 事件里的unable to validate against any security context constraint说明 SCC 与 Pod 的 securityContext 冲突需要调整 SCC 或 Pod 的 runAsUser。注意 SCC 是集群级资源绑定到 ServiceAccount 才生效直接改 default SCC 会影响所有未指定 SCC 的 Pod。2.3 多租户隔离的四个层面租户是指多组不同的应用或者用户同时运行在一个基础资源池之上实现软件、硬件资源的共享为了安全需求平台需要提供资源隔离的能力。在 OCP 中project 是一个进行租户隔离的概念来源于 K8S 的 namespace 并做了功能扩展。利用 ProjectOCP 从多个层面提供多租户支持。权限控制上管理员可以对不同的用户和组设置不同 project 的权限不同用户登录后只能操作和管理特定的 project。网络隔离上OCP 使用 openvswitch 管理内部容器网络提供两种网络模式一种是集群范围内互通的平面网络另一种是 project 级别隔离的网络。每个 project 都有一个虚拟网络 IDVNID不同 VNID 的流量被 openvswitch 自动隔离不同项目之间的服务在网络层不能互通。Router 隔离上OCP 提供 Router 分组功能不同 project 可以使用独立的 Router不互相干扰避免某些应用流量过大时对其他应用造成干扰。物理资源池隔离上OCP 利用 nodeSelector 功能将基础设施资源池划分给特定 project 独享实现从物理层面的隔离。安装时启用多租户插件的参数是os_sdn_network_plugin_nameredhat/openshift-ovs-multitenant这样 OpenShift 将使用 ovs-multitenant 多租户插件实现租户之间的安全隔离。在 OpenShift 的多租户和容器中心化日志实现中每个租户都只能查看属于自己项目的日志。除了 OVS 插件OpenShift 完全支持 CNI 标准符合 CNI 标准的三方 SDN 插件都可以在 OpenShift 中使用目前支持的包括 Cisco Contiv、Juniper Contrail、Nokia Nuage、Tigera Calico、VMware NSX-T。如果使用 OVS 插件且 OpenShift 部署在已有公有云或私有云上可能出现 overlay on overlay 的情况此时借助三方 SDN 插件是不错的选择比如 flannelhostgw 在性能上优于默认的 ovs-multitenant。3. 日志监控与负载均衡EFK、Heapster 和 Router 的配合3.1 传统应用日志与新应用日志的分治传统应用日志有别于当前流行的容器应用传统应用同时一个中间件会运行多个应用且应用通过 log4j 等机制保存在文件中方便查看和排错。因为容器运行的特性这部分日志需要持久化到外置存储中。日志分类包括中间件日志、dump 文件、应用日志日志保存在计算节点上挂载的 NFS 存储按 OCP 平台中的 namespace 建立目录进行划分。新应用日志面对分布式环境下日志分散的问题解决办法是收集日志集中到一个地方收集到的海量日志经过结构化处理交给需要的人员分析。不同人员对日志的需求不一样运营人员关注访问日志运维人员关注系统日志开发人员关注应用日志。这就需要一种足够开放、灵活的方法让所有关心日志的人在日志收集过程中对其定义、分割、过滤、索引、查询。OpenShift 使用 EFK 来实现日志管理平台EFK 是 Elasticsearch Fluentd Kibana 的简称。ES 负责数据的存储和索引Fluentd 负责数据的调整、过滤、传输Kibana 负责数据的展示。Fluentd 无论在性能上还是在功能上都表现突出尤其在收集容器日志领域更是独树一帜成为众多 PaaS 平台日志收集的标准方案。部署 EFK 时常见做法是通过 Ansible playbook 自动化完成而不是手工逐个组件安装。手工部署 ELK 在任何环境下都不推荐通过 Ansible 可以自动实现。至于分布式存储、本地存储还是集中存储没有既定答案可以参考行业实现。不建议 Elasticsearch 采用分布式存储日志量大的情况下分布式存储 ES 写会成为瓶颈。ES 的后端存储选择上集中存储目前用得最多不管是 FCSAN 还是 IPSAN其稳定性和安全性都能满足要求但在性价比和可扩展性方面存在很大问题。分布式存储随着云计算兴起优势是无中心节点、弹性伸缩适合云应用但还处于发展阶段技术有待成熟。本地存储一般使用较少主要是数据复制同步方面的问题。3.2 监控技术栈的选型与演进PaaS 平台的监控包括系统监控、容器监控等监控流程由信息收集、信息汇总和信息展示几个部分组成。在 OpenShift 中默认使用 K8S 的监控信息收集机制在每个节点上部署 cadvisor 的代理负责收集容器级别的监控信息然后将所有信息汇总到 heapsterheapster 后台的数据持久化平台是 Cassandra最后由 hawkular 从 Cassandra 获取信息进行统一展示。组件说明上Heapster 用于监控数据的采集Hawkular Metrics 属于开源监控解决方案 Hawkular基于 JSON 格式管理、展示监控数据Cassandra 是 Apache 的开源分布式数据库专门用于处理大数据量业务。K8S 节点的 kubelet 服务自带 cadvisor 用来收集各节点容器相关监控信息然后通过 heapster 收集这样在 dashboard 上可以看到容器使用 CPU 和 Memory。为了长期监控可以采用 Prometheus 监控方案nodeExporter 收集主机监控信息cadvisor 收集容器监控信息。K8S 中需要给 kubelet 配合 kube-reserved 和 system-reserved 相关参数给系统预留内存。Prometheus 作为一个时间序列数据收集、处理、存储的服务能够监控的对象必须直接或间接提供 Prometheus 认可的数据模型通过 HTTP API 的形式发出来。cAdvisor 支持 Prometheus同样包含了 cAdvisor 的 kubelet 也支持 Prometheus每个节点都提供了供 Prometheus 调用的 API。Prometheus 获取监控端点的方式有很多其中就包括 K8SPrometheus 会通过调用 master 的 apiserver 获取到节点信息然后去调取每个节点的数据。就目前来看Prometheus 应该是最具前景的监控工具在 OpenShift 3.12 里面 heapster 将由 Prometheus 替换。3.3 负载均衡与高可用的四个层次高可用主要分为几个层面。外部镜像仓库高可用方面外部镜像仓库独立于 OCP 平台之外用于存储平台构建过程中所使用的系统组件镜像。因为外部无法直接访问 OCP 平台的内部镜像仓库所以由 QA 环境 CD 推送到生产环境的镜像也是先复制到外部镜像仓库再由平台导入至内部镜像仓库。为了保证外部镜像仓库的高可用使用了 2 台服务器前端使用 F5 进行负载均衡所有请求均发至 F5 的虚拟地址由 F5 进行转发后端镜像仓库通过挂载 NFS 共享存储。Master 主控节点高可用方面OpenShift 的 Master 主控节点承担了集群的管理工作。计算节点高可用方面一个计算节点异常停机后其上的容器将会被逐步迁移到其他节点上从而保证高可用。同时可以通过标签的方式管理计算节点在不同的计算节点划分为不同的可用区或组在部署应用时使用节点选择器将应用部署至带有指定标签的目标计算节点上。为了保证高可用标签组合的目标计算节点数要大于 1这样可以避免一台目标节点宕机后调度器还能找到满足条件的计算节点进行容器部署。应用高可用方面基于软件 HAproxy 负载均衡服务容器服务弹性伸缩时无需人工对负载均衡设备进行配置干预即可保证容器化应用的持续、正常访问可通过图形界面自定义负载均衡会话保持策略。由于平台内部通过软件定义网络为每个应用容器分配了 IP 地址而此地址是内网地址因此外部客户无法直接访问到该地址所以平台使用路由器转发外部的流量到集群内部具体的应用容器上如果应用有多个容器实例路由器也可实现负载均衡的功能。路由器会动态检测平台的元数据仓库当有新的应用部署或者应用实例发生变化时路由器会自动根据变化更新路由信息从而实现动态负载均衡的能力。简单来说内部服务的动态发现、负载均衡、高可用和外部访问的路由通过 Service 解耦动态变化的 IP 地址Pod 可以随意关停IP 可以任意变只要 DNS 正常服务访问不受影响但这里面要随时保证有个可用的 Pod这个时候就需要 LB 了。内部服务之间访问通过 Service 解决外部访问集群内服务则通过 Router 解决外网访问要不要负载均衡大规模高并发情况下是肯定的外部负载均衡通常需要用户自己搞定F5 或者开源的 HAproxy 都行。4. 微服务拆分与 CI/CD从 SVN 到镜像仓库的流水线4.1 按业务能力拆分的粒度控制微服务架构按照什么细粒度拆分这个问题没有标准答案。既然理解微服务是用来重构业务应用的那就以业务应用为核心构建业务服务。业务服务需要数据服务、计算服务、搜索服务、算法服务以及基本的日志、监控、配置、注册发现、网关、任务调度等组件。至于数据服务怎么实现看团队能力这才涉及数据分拆、模型重构。服务通信可以考虑事件驱动机制也是后期业务数据处理、态势感知、智能风控、智能营销、智能运维等的基础。如何拆、按什么套路来拆回答这两个问题的基础是一定要十分熟悉业务逻辑才行。微服务这东西尤其是那种已经运行多年的老系统一不小心就能拆出问题。如果对云计算、对 OpenStack 有了解建议以 OpenStack 中的 Kolla 项目为微服务入门学习对象。Kolla 干的事情就是把 OpenStack 服务拆分成微服务的形式跑在容器中OpenStack 号称全球最大开源 Python 项目由几十个开源子项目组成如果能把这样复杂的集群项目都拆分成微服务那么一定会得到很多别人给不了的心得体会。以 OpenStack 为例Kolla 这个项目对 OpenStack 的拆分大概如下先按服务功能划分得到粗粒度如计算服务、网络服务、存储服务这些粗粒度模块通常会共享同一个 base 镜像这个 base 镜像中预置了服务模块的共性依赖然后基于服务模块的“原子性”拆分如把计算服务 Nova 拆分为 nova-api、nova-scheduler、nova-compute、nova-libvirt 等等所谓原子性拆分就是拆分到不能再往下拆为止原子拆分后通常就是彼此独立的单进程了也可以把它们称为叶子节点它们的镜像都是针对自己依赖的“个人”镜像不能被其他进程共享了。从镜像的角度来看继承关系是 centos-base - centos-openstack-base - centos-nova-base - centos-nova-api。先将系统模块化解耦别的微服务还是一体都只是部署的问题。常见的耦合方式有逻辑耦合、功能耦合、时间耦合等从码农的角度来分析解决耦合是基于微服务还是 SOA 化的最大区别。SOA 化的系统更多的是业务系统、领域模型级别的在分布式系统中远远不够需要考虑性能、安全、事务等最起码的 CAP 原则还是要把控的。码农解耦的角度有接口化、动静分离查询和修改等、元数据抽取等等更多的是代码上、设计模式上的真功夫。4.2 SVN 环境下的 CI/CD 流水线搭建SVN 环境下实现 CI/CD可以使用 hookpost commit的方式来实现但是需要编写 hook 脚本灵活度存在问题这在 svn-repo 的粒度较细的情况下还可行如果一个大的 repo管理起来较复杂不建议使用。建议使用 Jenkins 轮询 SCM 的方式触发 pipeline/job。能不能实现 CI/CD 与 SVN 无关关键是如何构建 pipeline微服务理念下大致流程是gitlab/svn - Jenkins - build images - push images - docker-registry - pull images - containers。具体落地时Jenkins 侧配置轮询触发// Jenkinsfile 片段轮询 SVN 并构建镜像 pipeline { agent any triggers { pollSCM(H/5 * * * *) // 每 5 分钟轮询一次 SVN } stages { stage(Checkout) { steps { checkout([$class: SubversionSCM, locations: [[remote: svn://svn.example.com/repo/app, local: .]]]) } } stage(Build Image) { steps { sh docker build -t registry.example.com/app:${BUILD_NUMBER} . } } stage(Push Image) { steps { sh docker push registry.example.com/app:${BUILD_NUMBER} } } } }逻辑说明pollSCM(H/5 * * * *)让 Jenkins 每 5 分钟检查一次 SVN 是否有新提交H表示哈希散列避免整点并发。checkout步骤拉取 SVN 代码docker build和docker push完成镜像构建与推送。参数上BUILD_NUMBER作为镜像 tag 保证每次构建唯一。失败时先看 Jenkins 的 SVN 轮询日志常见问题是 SVN 凭据未配置或仓库 URL 变更。注意轮询频率不宜过高否则 SVN 服务器压力大生产环境建议改用 webhook 或 post-commit 触发。4.3 K8S DNS 与服务发布的配合配置 K8S DNSDNSDomain Name System提供域名解析服务解决了难于记忆的 IP 地址问题以更人性可读可记忆可标识的方式映射对应 IP 地址。Cluster DNS 扩展插件用于支持 K8S 集群系统中各服务之间发现与调用。组件包括 SkyDNS 提供 DNS 解析服务Etcd 存储 DNS 信息Kube2sky 监听 Kubernetes当有 Service 创建时生成相应的记录到 SkyDNS。如访问外部 DNS可以设置 external_dns 到 configmap 实现。K8S 分配给 Service 一个固定 IP这是一个虚拟 IP也称为 ClusterIP并不是一个真实存在的 IP而是由 K8S 虚拟出来的。虚拟 IP 的范围通过 K8S API Server 的启动参数--service-cluster-ip-range19.254.0.0/16配置虚拟 IP 属于 K8S 内部的虚拟网络外部是寻址不到的。在 K8S 系统中实际上是由 K8S Proxy 组件负责实现虚拟 IP 路由和转发的所以 K8S Node 中都必须运行了 K8S Proxy从而在容器覆盖网络之上又实现了 K8S 层级的虚拟转发网络。服务代理在逻辑层面上Service 被认为是真实应用的抽象每一个 Service 关联着一系列的 Pod。在物理层面上Service 是真实应用的代理服务器对外表现为一个单一访问入口通过 K8S Proxy 转发请求到 Service 关联的 Pod。Service 同样是根据 Label Selector 来筛选 Pod 进行关联的实际上 K8S 在 Service 和 Pod 之间通过 Endpoint 衔接Endpoints 同 Service 关联的 Pod 相对应可以认为是 Service 的服务代理后端K8S 会根据 Service 关联到 Pod 的 PodIP 信息组合成一个 Endpoints。Service 不仅可以代理 Pod还可以代理任意其他后端比如运行在 K8S 外部的服务。假设现在要使用一个 Service 代理外部 MySQL 服务不用设置 Service 的 Label Selector。微服务化应用的每一个组件都以 Service 进行抽象组件与组件之间只需要访问 Service 即可以互相通信而无须感知组件的集群变化这就是服务发现。K8S 提供了 NodePort Service、LoadBalancer Service 和 Ingress 可以发布 Service。NodePort Service 是类型为 NodePort 的 ServiceK8S 除了会分配给 NodePort Service 一个内部的虚拟 IP另外会在每一个 Node 上暴露端口 NodePort外部网络可以通过 [NodeIP]:[NodePort] 访问到 Service。LoadBalancer Service 需要底层云平台支持创建负载均衡器比如 GCE它是建立在 NodePort Service 集群基础上的K8S 会分配给 LoadBalancer Service 一个内部的虚拟 IP并且暴露 NodePort除此之外K8S 请求底层云平台创建一个负载均衡器将每个 Node 作为后端负载均衡器将转发请求到 [NodeIP]:[NodePort]。5. 避坑与排查双区部署、多租户和数据库容器化的血泪经验5.1 DMZ 区计算节点访问数据库的两种方案怎么选现象DMZ 区计算节点访问内网数据库时直接开通防火墙后仍然连接超时。原因DMZ 区节点和数据库不在同一网段且没有配置 Outbound 路由流量默认走默认网关而非内网防火墙。解决内网计算节点可以直接访问数据库DMZ 区计算节点访问数据库有 2 种方案。方案一是计算节点直接通过内网防火墙访问该应用数据库内网防火墙仅开通应用所在节点访问内部数据库的端口例如本期项目某应用仅使用 2 个节点则防火墙仅开通这 2 个节点访问该数据库的权限。方案二是计算节点经 Outbound 路由通过内网防火墙访问内网数据Outbound 路由在 OpenShift 中称之为 Egress Router因此内网防火墙仅开通应用所在节点访问内部数据库的端口例如应用 A 仅通过路由节点 A 和 B 访问内部数据库则防火墙仅开通这 2 个节点访问 A 数据库的权限。选择时看节点数量和防火墙策略复杂度节点少用方案一节点多且需要统一出口用方案二。5.2 多租户网络隔离后服务间调用失败现象两个不同 project 的微服务互相调用时DNS 能解析但 TCP 连接被拒绝。原因启用了 ovs-multitenant 插件后不同 VNID 的流量被 openvswitch 自动隔离不同项目之间的服务在网络层不能互通。解决如果确实需要跨 project 通信常见做法是使用oc adm pod-network join-projects将两个 project 的网络合并或者通过 Router 暴露服务后走外部路由。注意合并网络会削弱租户隔离生产环境慎用。排查时先用oc get netnamespace查看各 project 的 VNID再用oc exec进入 Pod 测试curl目标 Service 的 ClusterIP确认是网络层不通还是应用层拒绝。5.3 Elasticsearch 在 K8S 中部署的存储选型翻车现象ES Pod 频繁重启日志显示写入超时集群状态 yellow 或 red。原因ES 采用了分布式存储作为后端日志量大的情况下分布式存储 ES 写成为瓶颈。解决不建议 Elasticsearch 采用分布式存储日志量大的情况下分布式存储 ES 写会是瓶颈。集中存储目前用得最多不管是 FCSAN 还是 IPSAN其稳定性和安全性都能满足要求。如果必须用分布式存储需要评估 IOPS 和延迟Ceph 等开源分布式存储需要调优后才能承载 ES 写入。排查时看 ES 的cluster.stat和nodes.stats中的fs.total和write指标确认磁盘 IO 是否饱和。5.4 Dubbo Zookeeper 环境下 K8S Provider 注册地址问题现象K8S 上的应用作为 Provider 注册到 Zookeeper 时注册的是容器地址Consumer 拿到后无法连接。原因容器 IP 是集群内部地址外部 Consumer 或不同网络的 Consumer 无法路由到该地址。解决如果 K8S 上的应用仅仅是 Consumer应该是没问题的不管 Provider 是在 K8S 集群内部还是外部。如果 K8S 上的应用是 Provider注册到 ZK 时是容器地址这时如果 Consumer 不在同一集群网络内就会失败。常见做法是让 Provider 注册宿主机 IP 或 NodePort 地址或者使用 Service 的 ClusterIP 并确保 Consumer 也在集群内。排查时先在 ZK 中get /dubbo/com.example.Service/providers看注册的 URL再确认 Consumer 能否telnet该地址和端口。5.5 监控数据持久化后查询缓慢现象Heapster Cassandra 方案运行一段时间后Hawkular 查询监控数据越来越慢。原因Cassandra 作为 Heapster 的后端存储数据量增长后未做 compaction 和 TTL 调优导致查询扫描过多 SSTable。解决定期检查 Cassandra 的nodetool tablestats确认SSTable count和read latency。如果延迟高调整compaction策略为LeveledCompactionStrategy并设置合理的 TTL 让过期监控数据自动清理。长期方案是迁移到 PrometheusPrometheus 的本地 TSDB 在监控场景下查询性能更好且 OpenShift 3.12 已计划用 Prometheus 替换 Heapster。6. 进阶技巧用 nodeSelector 标签把高可用真正做扎实高可用这件事文档里讲了很多层面但真正落地时最容易翻车的是计算节点标签和目标节点数量的配合。计算节点高可用指计算节点上运行的容器应用的高可用一个计算节点异常停机后其上的容器将会被逐步迁移到其他节点上从而保证了高可用。同时可以通过标签的方式管理计算节点在不同的计算节点划分为不同的可用区或组在部署应用时使用节点选择器将应用部署至带有指定标签的目标计算节点上。为了保证高可用标签组合的目标计算节点数要大于 1这样可以避免一台目标节点宕机后调度器还能找到满足条件的计算节点进行容器部署。我一般会强制走一遍这个检查先确认目标标签对应的节点数再确认这些节点分布在不同的物理机或可用区最后用反亲和性把同一应用的多个副本打散。# Deployment 中同时使用 nodeSelector 和 podAntiAffinity apiVersion: apps/v1 kind: Deployment metadata: name: payment-service spec: replicas: 3 selector: matchLabels: app: payment template: metadata: labels: app: payment spec: nodeSelector: zone: dmz app: payment affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - payment topologyKey: kubernetes.io/hostname containers: - name: payment image: registry.example.com/payment:1.0 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1 memory: 1Gi逻辑说明nodeSelector把 Pod 限定在zonedmz且apppayment的节点上podAntiAffinity的requiredDuringSchedulingIgnoredDuringExecution确保同一apppayment的 Pod 不会调度到同一台主机topologyKey: kubernetes.io/hostname。参数上replicas: 3配合反亲和性要求至少 3 台满足 nodeSelector 的节点否则会有 Pod 处于 Pending。resources的 requests 和 limits 用于调度和限制requests 影响调度决策limits 影响运行时上限。失败时先看oc describe pod的 Events如果出现0/8 nodes are available: 3 node(s) didnt match node selector, 5 node(s) didnt match pod anti-affinity rules说明节点数不够或标签不匹配。注意反亲和性用required时是硬约束节点不足会直接 Pending生产环境可以先用preferred软约束过渡。另一个容易忽略的点是 Router 隔离和物理资源池隔离的配合。Router 是 OCP 平台一个重要软件资源它提供了外部请求导入 OCP 集群内部的能力。OCP 提供了 Router 分组的功能不同的 project 可以使用独立的 Router不互相干扰这样就避免了由于某些应用流量过大时对其他应用造成干扰。物理资源池隔离方面在多租户的环境中为了提高资源的利用率一般情况下物理资源池是共享的但是有些用户也会提供独占资源池的需求针对这种类型的需求OCP 平台利用 nodeSelector 的功能可以将基础设施资源池划分给特定的 project 独享实现从物理层面的隔离。我一般会在给 project 分配独占节点后再给这些节点打上projectxxx的标签并在 project 的 ResourceQuota 中限制 CPU 和内存总量防止单个租户耗尽节点资源。从那以后我每次做多租户交付都强制走一遍“标签数 副本数、反亲和性拓扑键正确、ResourceQuota 已设置”这三步检查希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。