尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

K8S容器云平台部署实战:多租户隔离、日志监控与高可用设计

K8S容器云平台部署实战:多租户隔离、日志监控与高可用设计 简介一份面向企业级微服务容器化落地的解决方案文档围绕 Kubernetes 与 OpenShift 容器云平台系统讲解单体应用拆分为微服务后如何应对服务依赖、负载均衡、集群管理及有状态数据等核心挑战。资源为单个 docx 文件约 417KB内容涵盖容器云部署框架、权限管理、多租户隔离、日志监控四大模块适合架构师、运维开发以及正在规划容器云平台的团队参考。文档以问答与方案说明相结合给出 DMZ 和内网双 Openshift 隔离部署、基于 OAuth 的认证鉴权、SCC 细粒度控制、Project 租户网络及物理资源池隔离等具体做法并说明了 EFK 日志平台与 Heapster/Cadvisor 监控组件的落地路径。对于容器日志持久化、中间件日志分类、监控信息采集与展示等实操细节也给出可对照的组件选型思路。目前已有 497 人学习下载内容由社区专家顾文俊等整理可作为理解 K8S 微服务容器化部署路径的入门与选型参考。1. 从单体拆完就乱说起K8S 容器云平台到底解决了什么微服务架构的核心是把一个巨大的单体应用拆成许多小的互相连接的微服务但拆完之后问题才刚开始——服务之间有依赖关系发布时每个服务单独启动会非常痛苦登录服务、支付服务想一次全部启动必须要用到编排的动作。K8S 是第一个把“一切以服务为中心一切围绕服务运转”作为指导思想的编排产品调度、负载均衡、集群管理、有状态数据管理这些微服务面临的痛点它都提供了原生解法。这份方案资料来自社区专家顾文俊的线上交流整理覆盖容器云部署框架、权限与多租户、日志监控、服务发布、集群安全和微服务拆分落地等内容适合正准备做 K8S 容器云平台选型或已经把微服务拆完但不知道怎么部署上线的团队。2. 容器云部署框架DMZ/内网双环境与多租户隔离的四层实现2.1 双环境隔离为什么 DMZ 和内网要各建一套 OpenShift很多团队一上来就问“K8S 集群应该建多大”其实第一个该问的是“我要不要建两套”。资料里给出的方案是在 DMZ 和内网分别部署彼此独立的 2 套 OpenShift分别为内网和 DMZ 区两个网段两套环境彼此隔离。这个思路在企业环境里非常基础也非常关键。DMZ 区的 OpenShift 部署对外发布的应用负责处理外网的访问内网的 OpenShift 部署针对内网的应用仅负责处理内网的访问。这样做的直接好处是外网流量再大也不至于打穿内网数据库等敏感资源不必暴露给 DMZ 区。就算 DMZ 区某个应用被攻破攻击者拿到的也只是一套隔离环境里的容器想横向移动进内网中间还隔着防火墙和两套独立的集群。我见过不少团队为了省成本只建一套集群然后用 namespace 硬隔离内外网应用结果每次安全审计都提心吊胆。如果业务上有明确的内外网边界要求两套独立环境基本是没得商量的选项这不是技术洁癖是安全边界问题。2.2 权限管理认证、鉴权与 SCC 细粒度控制的落地企业级应用平台会有来自企业内外不同角色的用户所以灵活的、细粒度的、可扩展的权限管理是必不可少的。OCP 从设计初期就考虑到企业级用户的需求在平台内部集成了标准化的认证服务器并且定义了详细的权限策略和角色。认证层面OCP 平台的用户是基于对 OCP API 的调用权限来定义的。因为 OCP 所有的操作都是基于 API 的用户可以是开发人员或者管理员直接和 OCP 进行交互。OCP 内置了一个基于 OAuth 的通用身份认证服务器这个 OAuth 服务器可以通过多种不同类型的认证源对用户进行认证比如 LDAP、AD 或者 GitHub 这类外部身份源。鉴权层面权限策略决定了一个用户是否具有对某个对象的操作权限。管理员可以设置不同规则和角色对用户或者用户组赋予一定的角色角色包含了一系列的操作规则。这就是典型的 RBAC 模型。除了传统的认证和鉴权功能OCP 还提供了针对 Pod 的细粒度权限控制 SCCsecurity context constraints可以限制 Pod 具备何种类型的权限比如容器是否可以运行在特权模式下、是否可以挂载宿主机的目录、是否可以使用宿主机的端口、是否可以以 root 用户运行。这里有个常见的误解很多人觉得配好 RBAC 就万事大吉其实 SCC 才是容器安全里最容易漏的一环。我见过有团队为了让某个监控 Agent 能读宿主机指标直接把整个 namespace 的 SCC 放开成 privileged结果所有应用都能以 root 跑任意特权容器这在生产环境里等于把门锁拆了。2.3 多租户隔离从 namespace 到 Project 的四层隔离租户是指多组不同的应用或者用户同时运行在一个基础资源池之上实现软件、硬件资源的共享。为了安全需求平台需要提供资源隔离的能力。在 OCP 中project 是一个进行租户隔离的概念它来源于 Kubernetes 的 namespace并对其进行了功能扩展。利用 ProjectOCP 平台从多个层面提供了多租户的支持。第一层是权限控制。通过细粒度的权限管理机制管理员可以对不同的用户和组设置不同 project 的权限不同用户登录以后只能操作和管理特定的 project。这个和上一节讲的 RBAC 是配套的project 就是权限的作用域。第二层是网络隔离。OCP 平台使用 openvswitch 来管理内部的容器网络提供两种类型的网络模式一种是集群范围内互通的平面网络另一种是 project 级别隔离的网络。每个 project 都有一个虚拟网络 IDVNID不同 VNID 的流量被 openvswitch 自动隔离所以不同项目之间的服务在网络层不能互通。这里要特别提醒一下默认安装的 OpenShift 用的是 ovs-subnet 插件网络实现类似于 flat 网络所有 Pod 都能互通。要实现多租户隔离必须在安装时显式指定插件参数os_sdn_network_plugin_nameredhat/openshift-ovs-multitenant这个参数必须在安装阶段就定好装完再切网络插件是非常痛苦的过程基本等于重装集群。很多团队前期图省事用了默认的 flat 网络等业务方开始抱怨“我们的服务怎么被别人调用了”的时候才想起来要隔离这时候就陷入两难重装伤筋动骨不重装审计过不了。第三层是 Router 隔离。Router 是 OCP 平台的一个重要软件资源它提供了外部请求导入 OCP 集群内部的能力。OCP 提供了 Router 分组的功能不同的 project 可以使用独立的 Router不互相干扰这样就避免了由于某些应用流量过大时对其他应用造成干扰。这一点在外网访问量差异大的场景里很实用——流量大的业务用独立的 Router不会把其它业务的入口带宽吃掉。第四层是物理资源池隔离。在多租户的环境中为了提高资源的利用率一般情况下物理资源池是共享的但是有些用户也会提供独占资源池的需求。针对这种类型的需求OCP 平台利用 nodeSelector 的功能可以将基础设施资源池划分给特定的 project 独享实现从物理层面的隔离。部署应用时给节点打标签再在 Deployment 里指定 nodeSelectorapiVersion: apps/v1 kind: Deployment metadata: name: dmz-app spec: replicas: 2 template: spec: nodeSelector: zone: dmz这个 YAML 的意思是这个应用只会被调度到带有zonedmz标签的节点上。在双环境方案里DMZ 区的计算节点打上zonedmz的标签内网节点打上zoneintranet的标签应用部署时按安全级别指定目标节点从物理层面保证隔离。注意 nodeSelector 匹配不到节点时 Pod 会一直处于 Pending 状态排查调度问题时要先看节点标签是否打对了。3. 日志与监控EFK 落地方案和监控栈的选型对比3.1 日志分类与持久化传统应用日志怎么进 NFS传统应用日志有别于流行的容器应用传统应用同时一个中间件会运行多个应用且应用通过 log4j 等机制保存在文件中方便查看和排错。因为容器运行的特性对于这部分的日志需要持久化到外置存储中。日志分类大概有三类中间件日志、dump 文件、应用日志。日志保存在计算节点上挂载的 NFS 存储。为了规范和方便查找日志按 OCP 平台中的 namespace 建立目录进行划分。这样设计的好处是一个 namespace 对应一个业务线日志目录和业务对得上出了问题直接去对应目录捞日志不用在一堆容器目录里翻。这里有一个关于存储的细节值得注意传统应用日志恰恰不能只靠 stdout。很多人一上容器就习惯用docker logs看日志但传统中间件比如 WebLogic、MQ的日志是写文件的dump 文件更是要落盘的Pod 一重建文件就没了。所以这类日志必须挂 NFS 这类外置存储并且要规划好目录结构。我见过一套比较省心的目录划分方式日志类型存储位置目录划分中间件日志NFS/logs/ / /dump 文件NFS/dumps/ / /应用日志NFS/applogs/ / /新应用 stdout 日志EFK由 Fluentd 收集索引目录按 namespace 分的做法我比较认同因为 namespace 天然对应业务边界运维同学查日志时不需要知道具体 Pod 在哪台机器上只需要知道业务属于哪个 namespace路径就确定了。3.2 EFK 日志平台Fluentd 为什么能成为容器日志标准分布式环境下日志分散的解决办法是收集日志将其集中到一个地方。收集到的海量日志需要经过结构化处理进而交给需要的人员分析挖掘日志的价值信息。同时不同的人员对日志的需求是不一样的运营人员关注访问日志运维人员关注系统日志开发人员关注应用日志。这就需要有一种足够开放、灵活的方法让所有关心日志的人在日志收集过程中对其定义、分割、过滤、索引、查询。OpenShift 使用 EFK 来实现日志管理平台EFK 是 Elasticsearch Fluentd Kibana 的简称。ES 负责数据的存储和索引Fluentd 负责数据的调整、过滤、传输Kibana 负责数据的展示。这个平台具备以下能力日志采集将日志集中在一起索引日志内容快速返回查询结果具有伸缩性在各个环节都能够扩容强大的图形查询工具、报表产出工具。Fluentd 无论在性能上还是在功能上都表现突出尤其在收集容器日志领域更是独树一帜成为众多 PaaS 平台日志收集的标准方案。它之所以能成为标准核心原因是它对容器环境的适配做得很好Kubernetes 的 Pod 是动态创建销毁的Fluentd 通过监听 Docker 的元数据能够自动感知新起的容器并开始收集日志不需要人工配置日志源。这一点在微服务频繁发布扩容的场景里非常重要——你不可能每次发布都手动改一遍日志采集配置。在明确要上 EFK 的时候有个部署方式的问题需要提前想清楚ES 集群在 K8S 里面怎么跑。这里有两条路一条是用 ansible 之类的工具自动化部署 ES 集群另一条是直接跑在 K8S 里。社区里一个比较一致的建议是不建议 elasticsearch 采用分布式存储日志量大情况下如果是分布式存储ES 写会是瓶颈。也就是说ES 的数据卷宁可挂在本地盘或者传统集中存储上也不要图省事扔给 Ceph 这类分布式存储。分布式存储的延迟和写放大对 ES 这种写密集型的场景很不友好我们内部做过压测同样的写入量分布式存储在高峰期延迟能差出好几倍。3.3 监控技术栈从 heapster 到 Prometheus 的演进PaaS 平台的监控包括系统监控、容器监控等。监控流程由信息收集、信息汇总和信息展示等几个部分组成。在 OpenShift 中默认使用 Kubernetes 的监控信息收集机制在每个节点上部署 cAdvisor 的代理负责收集容器级别的监控信息。然后将所有信息汇总到 heapsterheapster 后台的数据持久化平台是 Cassandra最后由 hawkular 从 Cassandra 获取信息进行统一的展示。监控组件的作用是对 Pod 运行状态的 CPU、内存、网络进行实时监控和 Kubernetes 使用的监控技术栈一样包括三个部分组件作用备注Heapster监控数据的采集和汇总调各节点 kubelet 内置 cAdvisor 的接口Hawkular Metrics监控数据的存储与展示基于 JSON 格式管理、展示监控数据Cassandra监控数据的持久化专门用于处理大数据量业务这套监控方案在早期是主流但演进非常快。到了 OpenShift 3.12 之后heapster 被 Prometheus 替换掉这基本成了后续的事实标准。Prometheus 作为一个时间序列数据收集、处理、存储的服务能够监控的对象必须直接或间接提供 Prometheus 认可的数据模型通过 HTTP API 的形式发出来。cAdvisor 支持 Prometheus同样包含了 cAdvisor 的 kubelet 也支持 Prometheus每个节点都提供了供 Prometheus 调用的 API。Prometheus 支持通过调用 Master 的 API Server 获取节点信息然后去调取每个节点的数据。如果现在新起一个 K8S 集群我建议不要纠结 heapster 了直接上 Prometheus Grafana。采集层用 nodeExporter 收集主机监控信息cAdvisor 收集容器监控信息Prometheus 负责时序数据的存储和查询Grafana 做展示。这套组合是目前社区里最主流的开源监控方案社区资料多遇到问题能搜到大量现成的排错经验。4. 服务发布与负载均衡从 ClusterIP 到 Router 的完整链路4.1 Service 与服务发现ClusterIP、Endpoints 的工作原理K8S 中微服务化的应用每一个组件都以 Service 进行抽象组件与组件之间只需要访问 Service 即可互相通信而无须感知组件的集群变化这就是服务发现。Service 的核心价值是解耦动态变化的 Pod IP——Pod 可以随意关停IP 可以任意变只要 DNS 正常服务访问不受影响。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。K8S 分配给 Service 一个固定 IP这是一个虚拟 IP也称 ClusterIP并不是一个真实存在的 IP而是由 K8S 虚拟出来的。虚拟 IP 的范围通过 K8S API Server 的启动参数--service-cluster-ip-range19.254.0.0/16配置。虚拟 IP 属于 K8S 内部的虚拟网络外部是寻址不到的。在 K8S 系统中实际上是由 kube-proxy 组件负责实现虚拟 IP 路由和转发的所以每个 Node 中都必须运行 kube-proxy从而在容器覆盖网络之上又实现了 K8S 层级的虚拟转发网络。一个最基础的 Service 定义长这样apiVersion: v1 kind: Service metadata: name: payment-service spec: selector: app: payment ports: - protocol: TCP port: 8080 targetPort: 8080这个 Service 会把所有带apppayment标签的 Pod 的 8080 端口抽象成一个稳定的 ClusterIP:8080 入口。其它微服务访问支付服务时只需要访问payment-service这个 DNS 名K8S 的 DNS 组件会解析到 ClusterIP再经过 kube-proxy 转发到具体 Pod。这里要留意的坑是Service 的port是服务对外暴露的端口targetPort是容器内实际监听的端口两者可以不一样。如果容器内应用改过监听端口而 Service 没有同步改targetPort就会出现服务通但实际报错的玄学问题。另外Service 不仅可以代理 Pod还可以代理任意其他后端比如运行在 K8S 外部的服务。假设现在要使用一个 Service 代理外部 MySQL 服务不用设置 Service 的 Label Selector而是手动创建 Endpoints 指向外部数据库的 IP。这个技巧在数据库迁到容器外、或者微服务要访问遗留系统的场景里非常实用不用改代码就能把外部服务抽象成集群内的 Service。4.2 外部访问NodePort、LoadBalancer 与 Router 的取舍K8S 提供了 NodePort Service、LoadBalancer Service 和 Ingress 三种方式发布 Service。三者适用的场景差异非常大选错了后续维护会很痛苦。NodePort Service 是类型为 NodePort 的 ServiceK8S 除了会分配给 NodePort Service 一个内部的虚拟 IP另外会在每一个 Node 上暴露端口 NodePort外部网络可以通过 [NodeIP]:[NodePort] 访问到 Service。OpenShift 产品推荐通过 NodePort 类型的 Service 为某个应用对外暴露一个服务端口。NodePort 类型的 Service 会在集群中的所有节点上监听一个特定的端口访问任意一个计算机节点的端口即可访问内部容器中的服务。在集群的所有节点的这个端口都会预留给该应用所用。这里要解释一下为什么说“在集群的所有节点都会预留给该应用”NodePort 的端口分配是集群级的一旦 Service 占用了某个端口所有节点都会监听这个端口。这意味着 NodePort 的端口资源是有限的默认范围 30000-32767如果应用数量多端口会不够用。另外用户访问任意节点 IP 加端口都能访问到服务如果其中一台节点挂了用户访问这台节点的端口就会失败。所以生产环境一般不在 NodePort 上裸奔而是用 F5 这类负载均衡设备做前端入口。在 F5 VS 的 Pool Member 中配置所有节点通过 Keepalived 来实现 HA。外部流量先打到 F5 虚拟地址F5 转发到各节点 NodePort再进入集群内部的 Service。应用系统和用户不用改变现有的访问方式这对接入层的平滑过渡非常重要。LoadBalancer Service 是类型为 LoadBalancer 的 Service它是建立在 NodePort Service 集群基础上的K8S 会分配给 LoadBalancer Service 一个内部的虚拟 IP并且暴露 NodePort。除此之外K8S 请求底层云平台创建一个负载均衡器将每个 Node 作为后端负载均衡器将转发请求到 [NodeIP]:[NodePort]。这个类型需要底层云平台支持创建负载均衡器比如 GCE、AWS 这些公有云或者企业内部有对接好的 LB 插件否则创建出来的 LoadBalancer Service 会一直处于 Pending 状态。在 OpenShift 里外部访问的入口是 Router。Router 是平台的一个重要软件资源它提供了外部请求导入 OCP 集群内部的能力如果应用有多个容器实例Router 也可实现负载均衡的功能。Router 会动态地检测平台的元数据仓库当有新的应用部署或者应用实例发生变化时Router 会自动根据变化更新路由信息从而实现动态负载均衡的能力。这就是 Router 比 NodePort 更合适做微服务入口的原因Pod 伸缩、重建、迁移都不需要人工干预路由配置Router 自动感知并更新。4.3 高可用设计镜像仓库、Master、计算节点的三层保障高可用主要分为外部镜像仓库高可用、Master 主控节点高可用、计算节点容器应用高可用和应用高可用几个层面。群里讨论的时候有人问“K8S 的负载均衡策略和总体思想是什么”其实拆开看就是这四层。外部镜像仓库独立于 OCP 平台之外用于存储平台构建过程中所使用的系统组件镜像。因为外部无法直接访问 OCP 平台的内部镜像仓库所以由 QA 环境 CD 推送到生产环境的镜像也是先复制到外部镜像仓库再由平台导入至内部镜像仓库。为了保证外部镜像仓库的高可用使用了 2 台服务器前端使用 F5 进行负载均衡所有的请求均发至 F5 的虚拟地址由 F5 进行转发。后端镜像仓库通过挂载 NFS 共享存储。这个方案说白了就是“无状态服务 共享存储”的经典组合镜像仓库本身是应用层无状态的共享存储保证镜像数据不丢F5 保证入口不挂。Master 主控节点承担了集群的管理工作它的高可用是整个集群的命脉。Master 挂了调度、API 调用全部中断。生产环境一般至少三台 Master配合 etcd 集群做数据一致。这里有个经验之谈Master 节点千万不要省资源etcd 的磁盘 IO 性能直接影响整个集群的稳定性有条件上 SSD 就上 SSD。计算节点高可用指计算节点上运行的容器应用的高可用。一个计算节点异常停机后其上的容器将会被逐步迁移到其他节点上从而保证了高可用。同时可以通过标签的方式来管理计算节点在不同的计算节点划分为不同的可用区或组。在部署应用时使用节点选择器将应用部署至带有指定标签的目标计算节点上。为了保证高可用标签组合的目标计算节点数要大于 1这样可以避免一台目标节点宕机后调度器还能找到满足条件的计算节点进行容器部署。这个细节特别重要——很多人给应用只配了一个节点当时看起来没问题等节点宕机才发现调度器找不到第二个满足条件的节点应用直接停摆。应用高可用的核心是基于软件HAproxy负载均衡服务容器服务弹性伸缩时无需人工对负载均衡设备进行配置干预即可保证容器化应用的持续、正常访问。可通过图形界面自定义负载均衡会话保持策略。因为平台内部通过软件定义网络为每个应用容器分配了 IP 地址而此地址是内网地址外部客户无法直接访问到该地址所以平台使用路由器转发外部的流量到集群内部具体的应用容器上。业务方内部的访问则是 service 驱动的内部服务之间访问通过 Service 解决了外部访问集群内服务通过 Router 解决。大规模高并发情况下外部负载均衡是肯定的外部负载均衡通常需要用户自己搞定F5 或者开源的 HAproxy 都行。5. 部署避坑与排查认证、网络、存储和监控的常见问题这个章节把我在实际部署和运维过程中踩过的坑集中写一下每条按“现象 → 原因 → 解决”来拆。5.1 集群安全配置双向认证的流程与性能权衡现象集群组件之间通信走 HTTP安全扫描一问一个准但配了全链路 HTTPS 双向认证之后API Server 的响应延迟明显上升压测数据不好看。原因Kubernetes 系统提供了三种认证方式CA 认证、Token 认证和 Base 认证。安全功能是一把双刃剑它保护系统不被攻击但是也带来额外的性能损耗。集群内的各组件访问 API Server 时由于它们与 API Server 同时处于同一局域网内建议用非安全的方式访问 API Server效率更高。解决双向认证配置方式是最为严格和安全的集群安全配置方式主要配置流程分两步第一步生成根证书、API Server 服务端证书、服务端私钥、各个组件所用的客户端证书和客户端私钥第二步修改 Kubernetes 各个服务进程的启动参数启用双向认证模式。如果不想引入证书体系的复杂度可以退一步用基于 Token 和 HTTP Base 的简单认证方式通信方仍然采用 HTTPS但不使用数字证书。采用基于 Token 和 HTTP Base 的简单认证方式时API Server 对外暴露 HTTPS 端口客户端提供 Token 或用户名、密码来完成认证过程。我的建议是API Server 对外的管理面必须走 HTTPS 加认证集群内部组件之间的通信可以用 Token 方式代替双向证书性能和安全的平衡点在这里比较合适。有些团队一上来就全链路双向认证性能损耗先不说证书轮换本身就是个巨大的运维负担证书过期导致集群失联的事故我见过不止一次。5.2 网络隔离翻车VNID、防火墙与 overlay on overlay现象开启了 ovs-multitenant 多租户插件后某些跨 project 的服务调用变得不通或者把 OpenShift 部署在已有云平台上之后容器网络性能下降明显。原因多租户网络隔离是靠 VNID 实现的每个 project 有一个虚拟网络 ID不同 VNID 的流量被 openvswitch 自动隔离。如果两个 project 之间有服务调用需求比如公共组件默认策略下网络层直接不通。而且如果利用已有的公有云或私有云部署 OpenShift使用 OVS 插件时OpenShift 中的 SDN 可能出现 overlay on overlay 的情况——底层 IaaS 已经做了一层 overlay容器网络又是一层 overlay两层封装叠加导致网络性能下降。解决网络层面的多租户隔离要提前规划好哪些项目之间需要互通在开启多租户插件时预先配置好相应的网络策略。对于 overlay on overlay 的问题借助三方 SDN 插件是个不错的选择比如 flannel hostgw 在性能上就优于默认的 ovs-multitenant因为 hostgw 模式走的是宿主机路由不叠加封装。如果性能要求更高calico 基于 BGP 的方案更佳但基础设施改动大不是所有客户都能接受。原则就是尽量防止二次封装叠加致使网络性能下降过多。5.3 ES 存储选型踩坑分布式存储为什么可能是瓶颈现象EFK 日志平台上线后Kibana 查询响应慢ES 集群写入高峰期 CPU 打满日志出现积压Fluentd 上游不断重试。原因ES 是典型的写入密集型应用每次写入涉及索引、分片复制、刷新到磁盘一系列操作。如果后端用的分布式存储比如 Ceph每次写入都会产生多副本的网络传输和确认延迟被放大。社区里的共识是不建议 elasticsearch 采用分布式存储日志量大情况下如果是分布式存储ES 写会是瓶颈。解决ES 的数据卷尽量用本地盘或者传统集中存储。虽然分布式存储有弹性伸缩、无中心节点的优势但对于 ES 这种场景写放大太致命了。日志量大时本地盘的 SSD 会明显改善写入性能。要控制好日志保留策略索引按天滚动老索引定期删除或者归档到冷存储别让 ES 集群无限膨胀。ES 集群本身不要在容器里跑除非对 ES 容器化运维非常有把握否则独立部署更省心。5.4 节点调度的隐形坑nodeSelector、标签与资源预留现象应用部署后 Pod 一直处于 Pending 状态describe 看事件只有一句 “0/8 nodes are available”没有更详细的提示。原因大概率是 nodeSelector 指定的标签没有任何节点匹配或者匹配到的节点资源不足。物理资源池隔离是依赖 nodeSelector 实现的如果节点没有打上对应标签或者标签值写错比如大小写不一致调度器就找不到可用的节点。另外有些团队只给应用分配了一个目标节点节点宕机后调度器找不到第二个满足条件的节点应用直接停摆。解决排查步骤按顺序来先kubectl get nodes --show-labels确认节点标签再kubectl describe deployment name看 selector 和 template 是否一致最后用kubectl describe pod name看 Pending 原因。生产环境部署时标签组合的目标计算节点数要大于 1给应用至少留两个满足调度条件的节点避免单节点故障时无家可归。还有资源预留的问题K8S 节点上的 kubelet 需要配置kube-reserved和system-reserved参数给系统预留内存。如果不预留Pod 可以把节点内存吃满最后 kubelet 和系统组件因为资源不足开始 OOM表现为节点状态频繁 NotReadyDocker 容器批量被杀。这个坑在压测的时候最容易暴露一压测节点就挂查了半天才发现是系统内存没预留。5.5 监控数据正常但告警不触发cAdvisor 与告警规则配置现象Grafana 上看 CPU、内存曲线都正常但配置好的告警规则就是不触发或者触发之后重复报警值班同学被骚扰得不行。原因cAdvisor 收集的数据是实时的而告警判断针对的是连续时间窗。很多人在 Prometheus 里配置告警规则时把for字段设置得太短瞬时抖动就触发了或者把阈值设得太贴近基线正常波动就会来回触碰。还有一种是团队直接沿用了传统监控的思路只在主机层面配了告警没有覆盖容器层面Pod 频繁重启时主机指标看着完全正常但容器已经在大规模 CrashLoopBackOff 了。解决告警规则要设置合理的for持续时间和触发阈值。一般建议 CPU 使用率阈值在 85% 以上并持续 5 分钟才告警避免瞬时峰值干扰。同时把重复告警的 group_interval 调大比如 15 分钟防止同一问题反复刷屏。另外监控存储的数据要结合 Prometheus 的 nodeExporter 和 cAdvisor 一起看主机层面的指标磁盘 IO、网络带宽和容器层面的指标CPU、内存要分开配告警维度不同告警阈值也应该不一样。Pod 层面的重启次数、CrashLoopBackOff 状态这类 K8S 事件也要单独接一条告警链路别只盯着资源曲线。6. 微服务拆分与 CI/CD把单体拆成可独立发布的服务微服务架构按照什么细粒度拆分这是群里被问得最多的问题。老实说这没有标准答案拆多拆少都可能翻车。比较靠谱的切入点是先按业务功能划分得到粗粒度模块比如计算服务、网络服务、存储服务然后再基于服务模块的“原子性”拆分拆分到不能再往下拆为止拆完后通常就是彼此独立的单进程。想找参考案例的强烈推荐去看看 OpenStack 中的 Kolla 项目。Kolla 干的事情就是把 OpenStack 服务拆分成微服务的形式跑在容器中。OpenStack 号称全球最大开源 Python 项目由几十个开源子项目组成如果能把这样复杂的集群项目都拆分成微服务你一定会得到很多别人给不了的心得体会。Kolla 的拆分思路从镜像依赖上看得非常清楚父镜像 centos-base → 一级子镜像 centos-openstack-base → 二级子镜像 centos-nova-base → 叶子节点镜像 centos-nova-api。粗粒度模块共享 base 镜像原子化拆分后的进程使用自己专属的叶子镜像。这个思路拿到业务系统里照样成立先把系统模块化解耦接口化、动静分离查询和修改分开、元数据抽取这些都是拆分的真功夫而不是简单按代码目录切一刀。拆分完了CI/CD 就要跟上。SVN 环境下能不能做 CI/CD能但看你怎么触发。SVN 可以用 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 Pipeline 来描述这个流程大概是这样的结构pipeline { agent any stages { stage(Checkout) { steps { checkout scm } } stage(Build Image) { steps { sh docker build -t registry.example.com/order-service:${BUILD_NUMBER} . } } stage(Push Image) { steps { sh docker push registry.example.com/order-service:${BUILD_NUMBER} } } stage(Deploy) { steps { sh kubectl set image deployment/order-service order-serviceregistry.example.com/order-service:${BUILD_NUMBER} } } } }这个流水线把代码提交到镜像构建再到 K8S 滚动更新串起来了。注意最后一步用的kubectl set image会触发 Deployment 的滚动更新而不是直接删 Pod这样能保证发布过程中服务不中断。最后说一个 Dubbo 场景里非常典型的问题如果 K8S 上的应用是 provider注册到 ZooKeeper 时是容器地址这时如果 consumer 在集群外面根本访问不到容器 IP。只靠 K8S 的 Service 也没法直接解决因为 Service 的 ClusterIP 也是集群内虚拟地址外部同样寻址不到。我们对 Dubbo 这类需要服务注册中心的老服务上 K8S 的常规处理是provider 侧不注册容器 IP而是注册宿主机 IP 加 NodePort 端口或者引入 Dubbo 的 K8S 注册中心插件让 provider 注册 Service 的 ClusterIP 地址然后在集群内部消费。从那以后我每次做微服务容器化改造都强制走一遍这个清单服务注册地址是集群外可达的吗配置中心里有没有写死容器 IP下线旧实例时注册中心的数据清理干净了吗这几个问题过一遍能省掉后面一大半的联调时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表