该分类包含众多DevOps落地所应用到的各类组件
global: #全局配置 resolve_timeout: 5m #全局配置，设置解析超时时间 http_config: #使用 WebHook 发送通知时，默认使用此处配置的 http 相关参数设置。如果不使用 WebHook，请忽略此处的参数。 follow_redirects: true #跟随重定向 smtp_from: 38867033@qq.com #你的邮箱账号 smtp_smarthost: smtp.qq.com:25 #你的邮箱SMTP服务器地址 smtp_auth_username: 38867033@qq.com #你的邮箱名，可以自定义的 smtp_auth_password: <secret> #你的邮箱授权码，如果不明白，那就网上搜 smtp_require_tls: false #你是否启用TLS发信，如果启用，上面的那个25改为465 route: #定义发信路由 receiver: Default #路由名称Default group_by: #将标签值相同的告警事件组合到一个告警消息中发送。 - namespace continue: false #看下面解释 routes: #子路由 - receiver: Watchdog #子路由名称 match: #标签匹配 alertname: Watchdog continue: false - receiver: Critical #子路由名称 match: #标签匹配 severity: critical continue: false group_wait: 30s #分组等候时间 group_interval: 5m #同一分组消息发送间隔时间 repeat_interval: 12h #一模一样的告警消息再次发送时间 inhibit_rules: #抑制规则，博主不懂 - source_match: severity: critical target_match: severity_re: warning|info equal: - namespace - alertname - source_match: severity: warning target_match: severity_re: info equal: - namespace - alertname receivers: #联系人组配置 - name: Default #default组 webhook_configs: #webhook配置 - send_resolved: true #发送已解决的告警消息 http_config: follow_redirects: true url: http://dingtalk:8060/dingtalk/Default-2t88s/send #你webhook的地址 max_alerts: 0 #不知道 - name: Watchdog #Watchdog组 email_configs: #email配置 - send_resolved: true to: ilinux@88.com #发送给谁 from: 38867033@qq.com hello: localhost #test smarthost: smtp.qq.com:25 #邮箱配置 auth_username: 38867033@qq.com auth_password: <secret> headers: From: 38867033@qq.com Subject: '{{ template "email.default.subject" . }}' To: ilinux@88.com html: '{{ template "email.default.html" . }}' require_tls: false webhook_configs: - send_resolved: true http_config: follow_redirects: true url: http://dingtalk:8060/dingtalk/Watchdog-5hi33/send max_alerts: 0 - name: Critical email_configs: - send_resolved: true to: yongheng.liu@nx-engine.com from: 38867033@qq.com hello: localhost smarthost: smtp.qq.com:25 auth_username: 38867033@qq.com auth_password: <secret> headers: From: 38867033@qq.com Subject: '{{ template "email.default.subject" . }}' To: yongheng.liu@nx-engine.com html: '{{ template "email.default.html" . }}' require_tls: false templates: - /etc/alertmanager/config/*.tmpl相关该配置文件解释说明如下告警路由告警路由可以通过条件判断，决定将告警信息发送给哪一个联系人组。作用告警路由可以将 告警标签匹配器 以树形的结构组织起来，使得您可以对告警事件进行逐级分类，将不同类的事件发送给不同的告警联系人。匹配办法当出现新的告警事件要发送给用户时，AlertManger 将以深度优先的原则遍历告警路由规则树，具体来说，对于每一个即将发送的告警信息，AlertManger 首先将 告警路由规则树 的根节点设置为 当前节点，然后1.遍历 当前节点 的子节点，对于遍历中的子节点，比对告警事件的标签是否符合 当前节点 的标签匹配器：1.1. 如果不符合，则考察该子节点的下一个兄弟节点1.2. 如果符合，则：1.2.1. 将该子节点设置为 当前节点，并递归执行步骤 1.1.2.2. 从 1.1.1. 的递归调用返回后，判断该子节点的 continue 是否为 true1.2.2.1. 如果 continue 为 false，则跳过该子节点的后续兄弟节点1.2.2.2. 如果 continue 为 true，则将该子节点的后续兄弟节点设置为 当前节点，并递归执行步骤 1.1.3. 如果没有子节点的标签匹配器与告警事件符合，则按照 当前节点 定义的处理方式处理该告警事件抑制规则抑制规则可以避免当某种问题告警产生之后用户接收到大量由此问题导致的一系列的其它告警通知。例如当集群不可用时，用户可能只希望接收到一条告警，告诉他这时候集群出现了问题，而不是大量的如集群中的应用异常、中间件服务异常的告警通知。匹配办法当新的告警事件（目标告警事件）产生时，逐条比对抑制规则列表中的规则，对于每一条规则：1.查找当前已产生的符合 源告警事件匹配器 的告警事件，得出 匹配的源告警事件列表；2.比对 匹配的源告警事件列表 每一条源告警事件与目标告警事件，如果参与比对的源告警事件与目标告警事件都存在 Equal 标签列表 中的所有标签，且对于每一个标签来说，源告警事件中的标签值与目标告警事件中的标签值相同，则执行第 3 步的比对；3.目标告警事件符合目标 目标告警事件匹配器 的条件；对于目标告警事件，如果有一条抑制规则的 3 个比对步骤都通过，则不发送该事件的告警消息给告警联系人。match标签选择器可以使用多个标签匹配器来匹配告警事件。只有当所有标签匹配器都通过时，才认为该事件与此告警路由规则匹配。group_by按标签分组将标签值相同的告警事件组合到一个告警消息中发送。例如：带有标签 cluster=A 以及 alertname=LatencyHigh 的告警事件放到同一个消息中发送。如果想要将每一个告警事件都通过一个独立的告警消息发送，则可以将此字段设置为你懂的group_wait在收到分组中的第一条告警事件之后，等候一定时间，以便尽量收集到更多属于同一个分组中的告警事件，超过等候时间后，再将该分组中的所有告警事件组合成一个告警消息发送。一般设置在 0秒 - 180秒 之间group_interval在发送分组的一条告警消息后，间隔一定时间才允许发送同分组的下一条告警消息，以避免用户过于频繁地接收到重复的告警消息。一般设置在 5 分钟以上repeat_interval为一个告警事件成功发送告警消息之后，间隔一定时间才重复发送告警消息。一般设置在 3 小时以上上面的配置文件的总体解释为声明三个Router路由，分别为Default,Watchdog,Critical，他们的父子关系如下DefaultWatchdogCritical当有告警时，Alert深度匹配路由，也就是说三个都匹配，从Default开始以此匹配子路由，如果没有匹配到相关的标签，那么就由Default路由发出，如果匹配到相关标签，则以匹配到的相关标签的该路由发出路由Default没有设置标签匹配，所以按理说，当子路由没有满足匹配标签相关的告警时，告警这件事由Default来发出Default设置了标签分组,分组等候时间,分组间隔时间,重复间隔时间。配置文件翻译过来也就是当有告警通过Default路由发出时，他会在收到第一个发送请求后等待30秒钟，等待同一个Namespace标签的相关告警，如果没有等到，则直接发送。在发送后五分钟内如果再收到相同的分组告警发送请求，不予发送，必须等五分钟后再发。当告警通知成功后，在12个小时后才能再次发送一模一样的告警。Watchdog设置了标签匹配器，配置文件翻译过来就是当有告警请求过来时，如果匹配到Watchdog路由上的标签匹配器alertname = Watchdog那么就等候30s发出（默认）Critical设置了标签选择器，配置文件翻译过来就是当有告警请求过来时，如果匹配到Critical路由上的标签匹配器serverity = critical那么就等候30s发出（默认）
如何选择最佳的 Kubernetes 集群自动伸缩策略
前言这篇内容篇幅比较长，如果不想深入探讨或时间有限，这是全文简述： 在默认设置下，扩展 Kubernetes 集群中的 pod 和节点可能需要几分钟时间。 了解如何调整集群节点的大小、配置水平和集群自动缩放器以及过度配置集群以加快扩展速度。目录当自动伸缩的 Pod 报错Kubernetes 的 Cluster Autoscaler 是如何工作的探索 Pod 自动伸缩提前期为 Kubernetes 节点选择最佳实例大小在 Kubernetes 集群中过度配置节点为 Pod 选择正确的内存和CPU资源关于集群的缩容为什么不基于内存或CPU进行自动伸缩在 Kubernetes 中, 自动伸缩功能包括:Pod水平自动伸缩（Horizontal Pod Autoscaler，HPA）Pod垂直自动伸缩（Vertical Pod Autoscaler，VPA）集群自动伸缩（Cluster Autoscaler，CA）这些自动伸缩组件属于不同的类别，关注点也不同。Horizontal Pod Autoscaler 负责增加 Pod 的副本数量。随着你的应用接收到的流量越来越多，你可以让自动伸缩组件调整副本数量来处理更多的请求。Vertical Pod Autoscaler 的使用场景是，当资源不足无法创建更多的 Pod 副本时，而又仍然需要处理更多的流量。 一个简单的例子，你无法通过简单地添加更多的 Pod 副本来扩容数据库。数据库可能需要进行数据分片或者配置只读节点。 但你可以通过增加内存和CPU资源来让数据库能够处理更多的连接数。 这正是 VPA 的目的，增加 Pod 的资源大小。最后，我们要说说集群自动伸缩组件了。 当你的集群资源不足时，Cluster Autoscaler 会配置一个新的计算单元并将其添加到集群中。如果空节点过多，会移除它们以降低成本。虽然这三个组件都 “自动伸缩” 了一些东西，但它们并不造成相互之间的干扰。它们各自都有自己使用场景，定义和工作机制。并且它们是在独立的项目中开发的，独立的使用。 然而，更重要的是，为了最好的 scaling 你的集群，你必须花些心思去设置好这些 Autoscaler，让我们看个例子。当自动伸缩的 Pod 报错想象一下，有一个应用程序始终需要并使用 1.5GB 内存和 0.25 个 vCPU。 你配置了一个具有 8GB 和 2 个 vCPU 的单个节点的集群 —— 它应该能够完美地容纳四个 pod（并且还有一点额外的空间）。现在，你部署了一个 Pod 并且配置如下：HPA 配置每 10 个请求进来就添加一个 Pod 副本（例如：如果有 40 个并发请求涌入，会扩容到 4 个 Pod 副本）。CA 配置在资源不足时，创建更多的 Node 节点。HPA 可以通过在 deployment 文件中使用 Custom Metrics（例如在 Ingress Controller 中的 queries per second（QPS）） 来扩容 Pod 副本数量。现在，你开始为集群增加 30 个并发请求，并观察一下情况：HPA 开始扩容 Pod。创建了两个 Pod 副本。CA 没有触发 - 没有新增集群 Node 节点。这很好理解，因为现在有足够的内存和 CPU 资源来支持更多的 Pod。你进一步将流量增加到 40 个并发请求，并再次观察：HPA 又创建了一个 Pod。这个 Pod 是 pending 状态并且无法被部署。CA 触发创建了一个新的 Node 节点。新 Node 节点启动 4 分钟后开始工作。之后，pending Pod 也成功被部署了。为什么第四个 Pod 没有部署在第一个 Node 节点上呢？Pod 部署在集群上需要消耗内存，CPU，硬盘空间等资源，在同一个 Node 上，操作系统和 kubelet 组件也需要消耗内存和 CPU 资源。Kubernetes 中一个 Worker Node 节点的内存和 CPU 等资源使用分布如下：需要运行操作系统和一些系统级的守护进程，例如 SSH，Systemd 等。需要运行 Kubernetes Agent 组件，例如 Kubelet，Container Runtime，Node Problem Detector 等。需要运行 Pod。需要保留一些资源用来驱逐阀值 之用。你猜的没错，所有这些配额都是可定制的，但你需要好好计算一下。在一个 8GB 内存和 2vCPU 的单个节点的，可以按如下估算：操作系统运行大概需要 100MB 内存和 0.1vCPU。kubelet 运行大概需要 1.8GB 内存和 0.07vCPU。驱逐阀值大概需要 100MB 内存。剩余的大约 6GB 内存空间和 1.83vCPU 是提供给 Pod 使用的。如果你的集群需要运行 DaemonSet 资源，像 kube-proxy，那么你应该进一步减少提供给 Pod 的资源。考虑到 kube-proxy 大概需要 128MB 内存和 0.1vCPU，那么剩余大约 5.9GB 内存空间和 1.73vCPU 是提供给 Pod 使用的。另外，如果还需要运行 CNI 组件（例如：Flannel）和日志收集组件（Flentd），又会进一步减少提供给 Pod 的资源。在统计完所有其他的资源占用情况后，集群的剩余空间就只够运行三个 Pod 了。所以第四个会一直保持 “pending” 状态，直到它被调度到其他的 Node 节点上。既然 Cluster Autoscaler 知道没有空间容纳第四个 Pod，为什么不提前配置一个新节点？ 为什么它要在 Pod 处于 “pending” 状态之后再触发创建新 Node 节点的操作？Kubernetes 的 Cluster Autoscaler 是如何工作的Cluster Autoscaler 不是通过观察内存或 CPU 的使用情况来触发自动伸缩的。相反地，是通过对事件的响应和每 10s 对不可调度的 Pod 进行检查。当 Scheduler 无法找到可以容纳它的 Node 节点时，Pod 就会变成不可调度状态。例如，当一个 Pod 需要 1vCPU 资源而集群只有 0.5vCPU 资源可用，Scheduler 就会把该 Pod 标记为不可调度状态。这时，Cluster Autoscaler 会开始创建新 Node 节点。创建完成后，它会扫描集群中的不可调度状态的 Pod，检查是否可以将这些 Pod 调度到新节点上。如果你的集群具有多种节点类型（通常也称为节点组或节点池），则 Cluster Autoscaler 将使用以下策略选择其中一种：Random - 随机选择一种节点类型（默认策略）。Most Pods - 选择将调度最多 Pod 的节点组。Least waste - 选择扩容后空闲 CPU 最少的节点组。Price - 选择成本最低的节点组（目前仅适用于 GCP）。Priority - 选择优先级最高的节点组（优先级可以手动设置）。一旦确定了节点类型，Cluster Autoscaler 将调用相关 API 来提供新的计算资源。如果你使用的是 AWS，Cluster Autoscaler 将预置一个新的 EC2 实例。在 Azure 上，它将创建一个新的虚拟机，并在 GCP 上创建一个新的计算引擎。创建的节点可能需要一些时间才能出现在 Kubernetes 中。计算资源准备就绪后，节点将被初始化并添加到可以部署未被调度 Pod 的集群中。不幸的是，配置一个新节点通常会很慢。它可能会花费好几分钟来做这件事。让我们来看看这几分钟到底干了什么。探索 Pod 自动伸缩前置期在新节点上创建新 Pod 所需的时间由四个主要因素决定：HPA 的反应时间。CA 的反应时间。Node 节点的反应时间。Pod 创建的时间。默认地，kubelet 每 10 秒抓取一次 Pod 的 CPU 和内存使用情况。每分钟，Metrics Server 都会聚合这些指标并将它们发送给 Kubernetes API 的其他组件。Horizontal Pod Autoscaler 控制器负责检查指标并决定扩大或缩小副本数量。默认地，Horizontal Pod Autoscaler 每 15 秒检查一次 Pod 指标。Cluster Autoscaler 每 10 秒检查一次集群中不可调度的 Pod。一旦 CA 检测到不可调度的 Pod，它就会运行一个算法来做决策：需要多少个节点来将所有的不可调度 Pod 部署完成。需要创建那种类型的节点组。整个过程的时间花费应该是：在少于 100 个节点且每个节点最多 30 个 Pod 的集群上不超过 30 秒。 平均延迟应该是大约 5 秒。在具有 100 到 1000 个节点的集群上不超过 60 秒。 平均延迟应约为 15 秒。然后是节点配置时间，这主要取决于云提供商。在 3-5 分钟内供应新的计算资源是非常标准的。最后，Pod 必须由容器运行时创建。启动一个容器应该不会超过几毫秒，但下载容器镜像可能需要几秒钟。 如果没有缓存容器映像，则从容器注册表下载映像可能需要几秒钟到一分钟的时间，具体取决于层的大小和数量。因此，当集群中没有空间而触发自动伸缩的时间消耗如下：Horizontal Pod Autoscaler 可能需要长达 1min30s 来增加副本数量。对于少于 100 个节点的集群，Cluster Autoscaler 应该花费不到 30s 的时间，对于超过 100 个节点的集群，应该不到 1min。云提供商可能需要 3-5min 来创建计算机资源。容器运行时可能需要长达 30s 才能下载容器映像。如果你的集群规模不是很大，在最坏的情况下，时间消耗：对于超过 100 个节点的集群，总延迟可能高达 7 分钟。在有更多 Pod 来处理突然激增的流量之前，您是否愿意等待这 7 分钟？这里提供了几种减少 scaling 时间的方法：调整 Horizontal Pod Autoscaler 的刷新时间（由 –horizontal-pod-autoscaler-sync-period 参数控制，默认 15s）。调整抓取 Pod 的 CPU 和内存使用情况的间隔频率（由 metric-resolution 变量控制，默认 60s）。调整 Cluster Autoscaler 扫描未被调度 Pod 的间隔频率（由 scan-interval 变量控制，默认10s）。调整 Node 节点上缓存容器镜像的方式（通过诸如 kube-fledged 等工具）。但即使将这些设置调整为很小的值，你仍然会收到云提供商创建计算资源的时间限制。有什么方式优化这个部分吗？这里可以做两件事：尽可能地避免创建新地 Node 节点。主动提前创建节点，以便在需要时能直接使用。为 Kubernetes 节点选择最佳实例大小选择正确的节点实例类型对集群的扩展策略有很大的影响。考虑一个这样的场景。 你有一个应用需要 1GB 的内存资源和 0.1 vCPU 资源。 你提供的 Node 节点有 4GB 的内存资源和 1 vCPU 资源。 在为操作系统、kubelet 和驱逐阀值保留内存和 CPU 后，将拥有约 2.5GB 的内存资源和 0.7 vCPU 可用于运行 Pod。 所以你的 Node 节点只能承载 2 个 Pod 的运行。每次扩展 Pod 副本时，都可能会产生最多 7 分钟的延迟（触发 HPA，CA 和云提供商配置计算资源的前置时间）。让我们来看看如果改成提供 64GB 的内存和 16 vCPU 的节点会发生什么。在为操作系统、kubelet 和驱逐阀值保留内存和 CPU 后，将拥有约 58.32GB 的内存资源和 15.8 vCPU 可用于运行 Pod。Node 节点可以承载 58 个 Pod 的运行，只有超过 58 个 Pod 副本时，才需要一个新的节点。此外，每次向集群中添加节点时，都可以部署多个 Pod。再次触发 Cluster Autoscaler 的机会更少。选择大型节点实例类型还有另一个好处。 为 kubelet 预留的资源、操作系统和驱逐阀值与运行 Pod 的可用资源之间的比率更大。 看看这张图，它描绘了 Pod 可用的内存。随着 Node 实例大小的增加，你可以注意到（按比例）可用于 Pod 的资源增加。换句话说，与拥有两个大小一半的实例相比，可以更高效地利用资源。那应该一直选择最大的实例吗？节点上可以拥有的 Pod 数量决定了效率的峰值。一些云提供商将 Pod 的数量限制为 110 个（比如 GKE）。其他一些限制是由底层网络基于每个实例（即AWS）规定的。你可以在这里查看大多数云提供商的限制所以选择更大的实例类型并不总是一个好的选择。我们还需要考虑：爆炸半径 - 如果你只有几个节点，那么一个失败节点的影响比你有很多节点的影响更大。自动伸缩的成本更高，因为下一个增量是（非常）大的节点。假设你为集群选择了正确的实例类型，你在配置新计算单元时可能仍然会遇到延迟。如果不是在需要扩展时创建新节点，而是提前创建相同的节点会怎么样？在 Kubernetes 集群中过度配置节点如果你可以负担得起随时可用的备用节点的话，你可以：提前创建一个空的 Node 节点。一旦空的 Node 节点上有 Pod 了，就会创建另一个空的 Node 节点。换句话说，让 Cluster Autoscaler 总是保持有一个备用的空 Node 节点。这是一种权衡：你会产生额外的成本，但扩展新节点的速度会提高。但有坏消息和好消息。坏消息是 Cluster Autoscaler 没有内置此功能。它不能被显式的配置，并且也没有提供相应的参数。好消息是你仍然可以通过一些 trick 的方式来达到这个目的。你可以运行具有足够请求的 Deployment 来保留一个完整的 Node 节点。你可以将这些 Pod 视为占位符 - 它旨在保留空间，而不是使用资源。一旦创建了真正的 Pod，就可以驱逐占位符并部署真正的 Pod。请注意，这一次你仍然需要等待 5 分钟才能将节点添加到集群中，但你可以继续使用当前节点。同时，在后台又提供了一个新的节点。如何做到这一点呢？可以使用运行永久休眠的 pod 的部署来配置过度配置。上图中，你需要特别关注内存和 CPU 配置。Scheduler 会使用这些值来决定部署 Pod 的位置。在这种特殊情况下，它们用于保留空间。你可以配置一个大型 Pod，该 Pod 的请求大致与可用节点资源相匹配。同时要确保你考虑了 kubelet、操作系统、kube-proxy 等消耗的资源。如果你的节点实例是 2 vCPU 和 8GB 内存，并且 pod 的可用空间是 1.73 vCPU 和 ~5.9GB 内存，则该节点就无法承载这个 Pod，因为实际的 Pod 可用资源是要小于所需资源的。为了确保在创建真正的 Pod 时能快速的驱逐占位Pod，可以使用优先级和抢占。Pod Priority 表示一个 Pod 相对于其他 Pod 的重要性。当一个 Pod 无法被调度时，Scheduler 会尝试抢占（驱逐）较低优先级的 Pod 以调度 “pending” 的 Pod。可以使用 PodPriorityClass 在集群中配置 Pod 优先级：由于 Pod 的默认优先级为 0，而过度配置的 PriorityClass 值为 -1，因此当集群空间不足时，这些 Pod 将首先被逐出。PriorityClass 还有两个可选字段：globalDefault 和 description。description 字段是提供给人阅读的关于 PriorityClass 的描述信息。globalDefault 字段表示这个 PriorityClass 的值应该用于没有 priorityClassName 的 Pod。系统中只能存在一个 global Default 设置为 true 的 PriorityClass。你可以使用下面的命令为你的 Pod 指定优先级：设置完成！当集群中没有足够的资源时，Pause Pod 会被抢占，并由新的 Pod 取而代之。由于 Pause pod 变得不可调度，它会强制 Cluster Autoscaler 向集群添加更多节点。现在，你已准备好过度配置集群，该是时候考虑优化应用程序以进行扩展了。为 Pod 选择正确的内存和CPU资源Cluster Autoscaler 会根据 “pending” Pod 的出现来做出 scaling 决策。Kubernetes Scheduler 根据 Node 节点的内存和 CPU 负载情况决定将 Pod 分配（或不分配）给节点。因此，必须为你的工作负载设置正确的资源使用请求，否则您可能会过晚（或过早）触发自动伸缩机制。让我们看一个例子。您决定要测试一个应用程序，并发现：在平均负载下，应用程序消耗 512MB 内存和 0.25 vCPU。在高峰期，应用程序应最多消耗 4GB 内存和 1 vCPU。你的容器的限制应该是 4GB 内存和 1 个 vCPU。但是，请求呢？Scheduler 在创建 Pod 之前使用 Pod 的内存和 CPU 请求来选择最佳节点。所以你可以：将请求设置为低于实际平均使用量。保守一点，分配更接近限制的请求。设置请求以匹配实际的限制。定义低于实际使用的请求是有问题的，因为你的节点经常会被过度使用。例如，你可以分配 256MB 的内存作为内存请求。Scheduler 可以为每个节点安装两倍的 Pod。然而，Pod 在实践中使用两倍的内存并开始竞争资源 (CPU) 并被驱逐（节点上没有足够的内存）。过度使用节点会导致过多的驱逐、更多的 kubelet 工作和大量的重新调度。如果将请求设置为与限制相同的值会发生什么？在 Kubernetes 中，这通常被称为 Guaranteed Quality of Service 类，指的是 pod 不太可能被终止和驱逐。Scheduler 将为分配的节点上的 Pod 保留整个 CPU 和内存。该类 Pod 运行稳定，但同时该节点的使用效率就会比较低。如果你的应用平均使用 512MB 的内存，但为它预留了 4GB，那么大部分时间有 3.5GB 未使用。这值得么？如果你想要更多的稳定性，是值得的。如果你想要效率，你可能希望降低请求并在这些请求与限制之间找到平衡。这通常被称为 Burstable Quality of Service 类，指的是 Pod 消耗稳定但偶尔会突然使用更多内存和 CPU。当你的请求与应用的实际使用相匹配时，Scheduler 将高效地将你的 Pod 打包到你的节点中。有时，应用程序可能需要更多内存或 CPU。如果 Node 中有资源，应用程序将会在达到最低消耗之前使用它们。如果 Node 中资源不足，Pod 将竞争资源（CPU），kubelet 可能会尝试驱逐 Pod（内存）。此时，你应该使用 Guaranteed Quality of Service 还是 Burstable Quality of Service？这取决于如下两点：当你希望最小化 Pod 的重新调度和驱逐时，请使用 Guaranteed Quality of Service（请求等于限制）。 一个很好的例子是用于数据库的 Pod。当你想要优化集群并明智地使用资源时，请使用 Burstable Quality of Service（请求匹配实际平均使用情况）。 如果您有 Web 应用程序或 REST API，您可能希望使用 Burstable Quality of Service。那如何选择正确的请求和限制值？你应该分析应用程序并测量空闲、负载和峰值时的内存和 CPU 消耗。更直接的策略包括部署 Vertical Pod Autoscaler 并等待它建议正确的值。Vertical Pod Autoscaler 从 Pod 收集数据并应用回归模型来推断请求和限制。您可以在本文中了解有关如何执行此操作的更多信息。关于集群的缩容每 10 秒，只有当请求利用率低于 50% 时，Cluster Autoscaler 才会决定删除节点。换句话说，对于同一节点上的所有 Pod，它会汇总 CPU 和内存请求。如果它们低于节点容量的一半，Cluster Autoscaler 将考虑当前节点进行缩减。值得注意的是，Cluster Autoscaler 不考虑实际的 CPU 和内存使用或限制，而只查看资源请求。在移除节点之前，Cluster Autoscaler 执行：Pod 检查以确保 Pod 可以移动到其他节点。Node 节点检查以防止节点过早被破坏。如果检查通过，Cluster Autoscaler 将从集群中删除节点。为什么不基于内存或CPU进行自动伸缩在扩缩容时，基于 CPU 或内存的 Cluster Autoscaler 不关心 pod。想象一下，有一个只有一个节点的集群，并设置 Autoscaler 来添加一个新节点当 CPU 使用率达到总容量的 80%。然后你决定创建一个具有 3 个副本的 Deployment。三个 Pod 的总资源使用率达到了 CPU 的 85%。一个新的 Node 节点被提供。如果你不需要更多 Pod 怎么办？你有一个完整节点处于空闲的状态——这不是很好。这种使用 Autoscaler 的方式是不鼓励的。总结在 Kubernetes 中定义和实施成功的扩缩容策略需要您掌握几个主题：熟悉 Kubernetes 节点中的可分配资源。微调 Metrics Server、Horizontal Pod Autoscaler 和 Cluster Autoscalers 的刷新间隔。规划集群和节点实例大小。做好容器镜像的缓存。做好应用程序基准测试和分析。但是上面这些还不够，你还需要使用适当的监控工具，反复测试您的扩缩容策略并调整集群的节点创建速度和成本。CP至 云原生社区
原文：https://learnk8s.io/troubleshooting-deployments 一份排障指南流程图。点击下载 PDF 版本。在 Kubernetes 中部署一个应用，需要定义 3 个组件：Deployment - 这是创建名为 Pod 的应用程序副本的菜谱Service - 内部负载均衡器，将流量路由到 PodsIngress - 有关流量如何从集群外部流向您的服务的描述下面是快速的回顾。在 Kubernetes 中，应用程序通过两层负载均衡暴露：内部和外部。 内部的负载平衡被称为 Service，而外部的被称为 Ingress。 Pod 并不是被直接部署的，而是通过 Deployment 创建 Pod 并监测。 假设您希望部署一个简单的 Hello World 应用程序，则该应用程序的 YAML 应该类似于以下内容： hello-world.yamlapiVersion: apps/v1 kind: Deployment metadata: name: my-deployment labels: track: canary spec: selector: matchLabels: any-name: my-app template: metadata: labels: any-name: my-app spec: containers: - name: cont1 image: learnk8s/app:1.0.0 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - port: 80 targetPort: 8080 selector: name: app --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: my-ingress spec: rules: - http: paths: - backend: serviceName: app servicePort: 80 path: /定义很长，很容易忽略组件之间的相互关系。例如：何时使用 80 端口？何时使用 8080 端口？是否应该为每个服务创建一个新端口，避免冲突？标签名称重要吗？到处都应该一样吗？在进行调试之前，让我们回顾一下这三个组件如何相互连接。我们从 Deployment 和 Service 开始。连接 Deployment 和 Service令人惊讶的消息是 Service 和 Deployment 根本没有连接。相反，Service 端直接连接了 Pod，完全跳过了 Deployment。所以你应该关心 Pod 和 Serivce 之前是如何相互关联的。你应该注意以下 3 点：Service 选择器（selector）应该匹配至少一个 Pod 的标签（label）Service targetPort 应该匹配 Pod 内部容器内的的 containerPortService port 可以是任何数字。因为 Serivce 有不同的 IP 地址，所以多个 Service 可以使用相同的端口。下图总结了如何连接端口：通过 Service 暴露下面的 Pod。 创建 Pod 时，应为 Pod 中的每个容器定义 containerPort。 创建服务时，您可以定义 port 和 targetPort，但是您应该选择连接哪一个容器？ tagetPort 和 containerPort 应该总是匹配。 如果您的容器暴露了端口 3000，那么 targetPort 应该总是匹配这个数字。 如果您查看 YAML 文件，可以发现标签和 ports/targetPort 应该匹配：hello-world.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-deployment labels: track: canary spec: selector: matchLabels: any-name: my-app template: metadata: labels: any-name: my-app spec: containers: - name: cont1 image: learnk8s/app:1.0.0 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - port: 80 targetPort: 8080 selector: any-name: my-appDeployment 部分中的 track: canary 是什么意思？那也应该匹配吗？该标签属于 Deployment，Service 的选择器（selector）不使用它来路由流量。换句话说，您可以安全地删除或为其分配其他值。那么 matchLabels 选择器呢？它始终必须匹配 Pod 标签，因为它是 Deployment 用来追踪 Pod 的。假设您做出了正确的更改，如何测试它？您可以使用一下命令检查 Pod 是否具有正确的标签：$ kubectl get pods --show-labels或者您有属于多个应用的 Pod：$ kubectl get pods --selector any-name=my-app --show-labels其中 any-time=my-app 表示标签 any-name: my-app。仍然存在问题？您可以直接连接 Pod！您可以在 kubectl 中使用 port-forward 命令连接 Service 并测试连接。$ kubectl port-forward service/<service_name> 3000:80其中：service/<service_name> 是 service 的名称 - 在当前 YAML 中是“my-service”。3000 是您想要在您的电脑中开放的端口80 是 Service 中 port 字段暴露的端口如果您能够连接，说明设置是正确的。如果您不能，那么您很可能配错了一个标签或者端口不匹配。连接 Service 和 Ingress下一步暴露您的应用的步骤是配置 Ingress。Ingress 必须知道如何获取到 Service 然后获取到 Pod 并路由流量到它们。Ingress 获取根据名称和暴露的端口获取到正确的 Service。Ingress 和 Service 中的两个属性应该匹配：Ingress 的 servicePort 应该匹配 Service 中的 portIngress 中的 serviceName 应该匹配 Service 中的 name下图总结了如何连接端口：您已经知道 Service 暴露了一个 port。 Ingress 中有一个字段叫做 servicePort。 Service 的 port 和 Ingress 的 servicePort 应该总是匹配。 如果您决定分配端口 80 给 service，您应该也修改 servicePort 为 80。 在实践中，您应该查看以下几行：hello-world.yaml apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - port: 80 targetPort: 8080 selector: any-name: my-app --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: my-ingress spec: rules: - http: paths: - backend: serviceName: my-service servicePort: 80 path: /您该如何测试 Ingress 的功能？您可以对 kubectl port-forward 使用与之前相同的策略，但是应该连接到 Ingress 控制器，而不是连接到 Service。首先，使用以下命令获取 Ingress 控制器的 Pod 名称：$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS kube-system coredns-5644d7b6d9-jn7cq 1/1 Running kube-system etcd-minikube 1/1 Running kube-system kube-apiserver-minikube 1/1 Running kube-system kube-controller-manager-minikube 1/1 Running kube-system kube-proxy-zvf2h 1/1 Running kube-system kube-scheduler-minikube 1/1 Running kube-system nginx-ingress-controller-6fc5bcc 1/1 Running找出 Ingress Pod（可能在其他 Namespace 中）并描述它以获取端口：$ kubectl describe pod nginx-ingress-controller-6fc5bcc \ --namespace kube-system \ | grep Ports Ports: 80/TCP, 443/TCP, 18080/TCP最后，连接 Pod：$ kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system此时，每次您访问计算机上的端口 3000 时，请求都会转发到 Ingress 控制器 Pod 上的端口 80。如果您访问 http://localhost:3000，则应该可以找到提供网页的应用程序。关于 port 的总结回顾快速回顾一下哪些端口和标签应该匹配：Service selector 应该匹配 Pod 的 labelService 的 targetPort 应该匹配 Pod 中容器的 containerPortService 的 port 可以是任意值。多个 Service 可以使用相同的 port，因为它们被分配了不同的 IP 地址Ingress 的 servicePort 应该匹配 Service 的 portService 的 name 应该匹配 Ingress 的 serviceName知道如何构造 YAML 定义只是故事的一部分。出了问题怎么办？Pod 可能无法启动，或者正在崩溃。排查 Kubernetes deployment 故障的 3 个步骤在深入 Debug 崩溃的 Deployment 之前，必须把 Kubernetes 的工作方式铭记于心。由于每个 deployment 包含 3 个组件，因此您必须从底层开始依次 debug 这些组件。您应该确保 Pod 处于 running 状态，然后专注于让服务将流量路由到 Pod，然后检查 Ingress 配置是否正确详细操作如下：您应该从底层开始对 Deployment 进行故障排查。首先检查 Pod 就绪并正在运行。 如果 Pod 已就绪，则应调查服务是否可以将流量分配到 Pod。 最后您应该检查 Service 与 Ingress 之间的连接 1. 排查 Pod 故障大部分情况下，问题出现在 Pod 自身。您应该确认您的 Pod 已经处于 Running 和 Ready 状态。您如何开始检查？$ kubectl get pods NAME READY STATUS RESTARTS AGE app1 0/1 ImagePullBackOff 0 47h app2 0/1 Error 0 47h app3-76f9fcd46b-xbv4k 1/1 Running 1 47h在上面的会话中，最后一个 Pod 已经处于 Running 和 Ready 状态 —— 然而，前两个 Pod 并未处于 Running 或 Ready 状态。您如何调查出了什么问题？有 4 个常用的命令可以用来排查 Pod 故障：kubectl logs <pod name> 有助于获取 Pod 容器的日志kubectl describe pod <pod name> 用于获取 Pod 相关的事件列表kubectl get po <pod name> 用于提取存储在 Kubernetes 中的 Pod 的 YAML 定义kubectl exec -it <pod name> bash 用于在 Pod 内的容器中运行一个交互式命令您应该选择哪一个？没有一个命令是万能的。相反，您应该结合使用它们。常见 Pod 错误Pod 可能会出现启动或运行时错误。启动错误如下：ImagePullBackOffImageInspectErrorErrImagePullErrImageNeverPullRegistryUnavailavleInvalidImageName运行时错误如下：CrashLoopBackOffRunContainerErrorKillContainerErrorVerifyNonRootErrorRunInitContainerErrorCreatePodSandboxErrorConfigPodSandboxErrorKillPodSandboxErrorSetupNetworkErrorTeardownNetworkError有些错误比其他错误更常见。以下是最常见的错误以及如何修复它们的列表。ImagePullBackOff这个错误出现的原因是 Kubernetes 不能拉取到 Pod 中的一个容器的镜像。有以下 3 中常见的原因：镜像名称无效 —— 例如，您错误输入了名称，或者镜像不存在您为镜像指定了一个不存在的 tag您试图拉取的镜像属于一个私有注册中心，并且 Kubernetes 没有权限访问它前两种情况可以通过修正镜像名称和 tag 来解决。对于最后一种情况，您应该将凭证添加到 Secret 中的私有注册中心，并在 Pod 中引用它。官方文档中有一个如何实现此目标的示例。CrashLoopBackOff如果容器无法启动，则 Kubernetes 将 CrashLoopBackOff 消息显示为状态。通常，在以下情况下容器无法启动：应用程序中存在错误，导致无法启动您未正确配置容器Liveness 探针失败次数过多您应该尝试从该容器中获取日志以调查其失败的原因。如果由于容器重启太快而看不到日志，则可以使用以下命令：$ kubectl logs <pod-name> --previous以上命令会打印前一个容器错误信息。RunContainerError当容器无法启动时出现错误。设置在容器内的应用程序启动之前。该问题通常是由于配置错误，例如：挂载不存在的卷，例如 ConfigMap 或 Secrets将只读卷挂载为可读写您应该使用 kubectl describe pod <pod-name> 来收集并分析错误信息。Pod 处于 Pending 状态当您创建一个 Pod 时，该 Pod 保持处于 Pending 状态。为什么？假设您的调度程序组件运行良好，原因如下：集群没有足够的资源（例如 CPU 和内存）来运行 Pod当前的 Namespace 具有 ResourceQuota 对象，创建 Pod 将使 Namespace 超过配额该 Pod 绑定到了一个处于 Pending 状态的 PersistenVolumeClain最好的选择是检查 kubectl describe 命令中的 Events 部分：$ kubectl describe pod <pod name>对于由于 ResourceQuotas 而造成的错误，可以使用以下方法检查集群日志：$ kubectl get events --sort-by=.metadata.creationTimestampPod 未处于 Ready 状态如果一个 Pod 处于 Running 状态但并未 Ready，这说明 Readiness 探针失败了。当 Readiness 探针失败时，Pod 未连接到 Service，并且没有流量转发到该实例。Readiness 探针失败是特定于应用程序的错误，因此您应该检查 kubectl describe 中的 Events 部分以找出错误。2. 排查 Service 故障如果 Pod 处于 Running 状态并 Ready，但仍无法收到应用程序的相应，则应检查 Service 的配置是否正确。Service 旨在根据 Pod 的标签将流量路由至 Pod。因此，您应该检查的第一件事是 Service 匹配了多少 Pod。您可以通过检查 Service 中的 Endpoint 来做到这一点：$ kubectl describe service <service-name> | grep Endpointsendpoint 是一对 <ip address: port>，并且在 Service（至少）有一个 Pod，当 Service 以 Pod 为目标时。如果“Endpoints”部分为空，则有两种解释：您并没有运行带有正确标签的 Pod（提示：您应检查自己是否在正确的 namespace）您在 Service 的 selector 的标签中有错别字如果您看到了 endpoints 列表，但仍然无法访问您的应用程序，则 service 中的 targetPort 可能是罪魁祸首。您如何测试服务？无论服务类型如何，都可以使用 kubectl port-forward 连接到它：$ kubectl port-forward service/<service-name> 3000:80其中：<service-name> 是 Service 的名称3000 是您希望在您的电脑上开放的端口80 是 Service 暴露的端口3. 排查 Ingress 故障如果您已到达本节，则：Pod 处于 Running 状态并 ReadyService 将流量分配到了 Pod但是您仍然看不到应用程序的响应。这意味着最有可能是 Ingress 配置错误。由于正在使用的 Ingress 控制器是集群中的第三方组件，因此针对 Ingress 控制器的类型有不同的调试技术。但是在深入研究 Ingress 专用工具之前，您可以使用一些简单的方法来检查。Ingress 使用 serviceName 和 servicePort 连接到 Service。您应该检查这些配置是否正确。你可以使用以下命令检查 Ingress 是否配置正确：$ kubectl describe ingress <ingress-name>如果 Backend 列表为空，则配置中必有一个错误。如果您可以在 Backend 列中看到 endpoint，但是仍然无法访问该应用程序，则可能是以下问题：您如何将 Ingress 暴露到互联网您如何将集群暴露到互联网您可以通过直连 Ingress Pod 将基础设施问题与 Ingress 隔离开来。首先，获取 Ingress 控制器的 Pod 名称（可以位于其他 namespace）：$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS kube-system coredns-5644d7b6d9-jn7cq 1/1 Running kube-system etcd-minikube 1/1 Running kube-system kube-apiserver-minikube 1/1 Running kube-system kube-controller-manager-minikube 1/1 Running kube-system kube-proxy-zvf2h 1/1 Running kube-system kube-scheduler-minikube 1/1 Running kube-system nginx-ingress-controller-6fc5bcc 1/1 Running $ 描述它以获取端口：$ kubectl describe pod nginx-ingress-controller-6fc5bcc --namespace kube-system \ | grep Ports $ 最后，连接到 Pod：$ kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
1.机器准备hostnameip地址系统版本cpu内存k8s-master192.168.88.35centos 7.94核8Gk8s-node192.168.88.37centos 7.94核8G2.初始化配置master和node设置主机名hostnamectl set-hostname k8s-master hostnamectl set-hostname k8s-node设置hosts解析cat >> /etc/hosts << EOF 192.168.88.35 k8s-master 192.168.88.37 k8s-node EOF临时关闭防火墙systemctl stop firewalld systemctl disable firwalld关闭selinuxsetenforce 0 sed -i 's/enforcing/disabled/' /etc/selinux/config关闭swap交换分区swapoff -a sed -i '/swap/s/^/#/' /etc/fstab设置内核参数modprobe br_netfilter cat >> /etc/sysctl.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward=1 EOF sysctl -p设置系统时区为上海timedatectl set-timezone Asia/Shanghai3. 安装Docker自 1.24.x版起,Docker不能再作为Kubernetes的容器运行时了,但因安装Docker会默认安装Containerd且个人觉得Docker用的顺手，所以这里还是安装Docker，也可以直接安装Containerd节省资源添加阿里yum源yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装并启动yum -y install docker-ce #也可以直接安装containerd #yum -y install containerd systemctl start docker systemctl enable docker添加阿里的镜像源cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://r3ucysd2.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"], "storage-driver": "overlay2" } EOF重启更新配置systemctl daemon-reload systemctl restart docker4. 配置Containerd生成默认配置文件并修改containerd config default > /etc/containerd/config.toml sed -i 's/k8s.gcr.io/registry.aliyuncs.com\/google_containers/' /etc/containerd/config.toml重启加载配置systemctl daemon-reload systemctl restart containerd5. 安装kubeadm、kubelet、kubectl添加K8S阿里源cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF查看可安装版本并安装yum list --showduplicates kubeadm yum install -y kubelet-1.25.0 kubeadm-1.25.0 kubectl-1.25.0设置kubelet开机自启systemctl enable kubelet6. 初始化主节点在主节点设置集群信息kubeadm init \ --apiserver-advertise-address=192.168.88.35 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.25.0 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16 \ --ignore-preflight-errors=all按照初始化日志提示创建目录mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config按照初始化日志提示将从节点加入集群kubeadm join 192.168.88.35:6443 --token geald1.4kx7yoiofgh0wtk8 \ --discovery-token-ca-cert-hash sha256:ec36596f455d84ee9119eaa2b1c4d84006c890edbddc92caba8f1e61d7fdad71 #第一行最后会有个"."要删除掉否则报错kubectl命令补全yum -y install bash-competion echo "source <(kubectl completion bash)" >> ~/.bashrc bash7. 安装Canal网络插件不安装网络插件节点无法正常通信 状态为NotReady下载Canal的 yaml 文件官网参考文档curl https://raw.githubusercontent.com/projectcalico/calico/v3.24.1/manifests/canal.yaml -O #不喜欢用Canal的也可以用Calico两个插件安装方法一样 curl https://projectcalico.docs.tigera.io/archive/v3.23/manifests/calico-etcd.yaml -O修改文件CALICO_IPV4POOL_CIDR字段#原文如下： # The default IPv4 pool to create on startup if none exists. Pod IPs will be # chosen from this range. Changing this value after installation will have # no effect. This should fall within `--cluster-cidr`. # - name: CALICO_IPV4POOL_CIDR # value: "192.168.0.0/16" #修改IP为master初始化 --pod-network-cidr选项的IP，并去掉注释格式与其他字段对齐，修改后如下： # The default IPv4 pool to create on startup if none exists. Pod IPs will be # chosen from this range. Changing this value after installation will have # no effect. This should fall within `--cluster-cidr`. - name: CALICO_IPV4POOL_CIDR value: "192.168.0.0/16"执行修改后的yaml，安装Canalkubectl apply -f canal.yaml查看节点状态是否Ready[root@k8s-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 17h v1.25.0 k8s-node Ready <none> 17h v1.25.0
背景docker-registry地址：docker-registry.kococ.cn新镜像仓库harbor地址：harbor.kococ.cn本例将docker-registry镜像迁移至 harbor 点我展开 666 安装可以从github项目直接下载使用二进制命令也可以在centos服务器上直接yum安装,需要epel源yum -y install skopeo获取test该namespace下所有服务镜像这里我的原镜像为docker-registrykubectl get deployment,statefulset,daemonset -o custom-columns='NAME:metadata.name,IMAGES:spec.template.spec.containers[*].image' -n test > all-image ## 获取到所有image之后将域名去掉只保留除域名外登录两个镜像仓库docker login harbor.kcooc.cn docker login docker-registry.kococ.cn迁移cat mv-image.sh #!/bin/bash for i in `cat all-image`;do skopeo copy docker://docker-registry.kococ.cn/${i} docker://harbor.kococ.cn/${i} --authfile /root/.docker/config.json done
日常使用docker时，经常会遇到一些很恼火的问题： ① 用dcoker run启动了新容器，但是忘了保存相关参数，过了一段时间后彻底忘记，每次都得docker inspect 然后仔细分析，很费精力 ② 现存的某个docker镜像，如何知道其dockerfile ​github上有大神们针对这俩问题开发了2个工具，还是非常好用的，这里简单介绍下： ​1 runlike 找回run命令该项目的github路径：https://github.com/lavie/runlike runlike有2种常见的安装方式：pip安装和docker容器直接使用# pip 安装 pip install runlike # runlike 后跟容器名或id查询 runlike # 再跟 -p 参数可以自动分行，便于查看 runlike -p pip安装不太方便。其实作者也将该工具打包成docker镜像了，镜像名为assaflavie/runlike 我们可以直接使用容器来跑命令# 给该一次性容器取个别名，以后就可以直接使用runlike了 alias runlike="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike" # 以后就可以直接以下命令查询了 runlike -p 使用举例： ​2 镜像导出Dockerfile该项目的github路径 https://github.com/P3GLEG/Whaler ​和上边的runlike一样，whaler也可以直接用ldocker容器来跑# 给该一次性容器取个别名，以后就可以直接使用whaler了 alias whaler="docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock:ro pegleg/whaler" # 以后就可以直接以下命令查询了 whaler # 还可以将docker客户端ID设置为特定版本 whaler -sV=1.36 使用举例：
消息队列的流派什么是 MQMessage Queue（MQ），消息队列中间件。很多人都说：MQ 通过将消息的发送和接收分离来实现应用程序的异步和解偶，这个给人的直觉是——MQ 是异步的，用来解耦的，但是这个只是 MQ 的效果而不是目的。MQ 真正的目的是为了通讯，屏蔽底层复杂的通讯协议，定义了一套应用层的、更加简单的通讯协议。一个分布式系统中两个模块之间通讯要么是HTTP，要么是自己开发的（rpc） TCP，但是这两种协议其实都是原始的协议。HTTP 协议很难实现两端通讯——模块 A 可以调用 B，B 也可以主动调用 A，如果要做到这个两端都要背上WebServer，而且还不支持⻓连接（HTTP 2.0 的库根本找不到）。TCP 就更加原始了，粘包、心跳、私有的协议，想一想头皮就发麻。MQ 所要做的就是在这些协议之上构建一个简单的“协议”——生产者/消费者模型。MQ 带给我的“协议”不是具体的通讯协议，而是更高层次通讯模型。它定义了两个对象——发送数据的叫生产者；接收数据的叫消费者， 提供一个SDK 让我们可以定义自己的生产者和消费者实现消息通讯而无视底层通讯协议有 Broker 的 MQ这个流派通常有一台服务器作为 Broker，所有的消息都通过它中转。生产者把消息发送给它就结束自己的任务了，Broker 则把消息主动推送给消费者（或者消费者主动轮询）重 Topickafka、JMS（ActiveMQ）就属于这个流派，生产者会发送 key 和数据到 Broker，由 Broker比较 key 之后决定给哪个消费者。这种模式是我们最常⻅的模式，是我们对 MQ 最多的印象。在这种模式下一个 topic 往往是一个比较大的概念，甚至一个系统中就可能只有一个topic，topic 某种意义上就是 queue，生产者发送 key 相当于说：“hi，把数据放到 key 的队列中”如上图所示，Broker 定义了三个队列，key1，key2，key3，生产者发送数据的时候会发送key1 和 data，Broker 在推送数据的时候则推送 data（也可能把 key 带上）。虽然架构一样但是 kafka 的性能要比 jms 的性能不知道高到多少倍，所以基本这种类型的MQ 只有 kafka 一种备选方案。如果你需要一条暴力的数据流（在乎性能而非灵活性）那么kafka 是最好的选择轻 Topic这种的代表是 RabbitMQ（或者说是 AMQP）。生产者发送 key 和数据，消费者定义订阅的队列，Broker 收到数据之后会通过一定的逻辑计算出 key 对应的队列，然后把数据交给队列这种模式下解耦了 key 和 queue，在这种架构中 queue 是非常轻量级的（在 RabbitMQ 中它的上限取决于你的内存），消费者关心的只是自己的 queue；生产者不必关心数据最终给谁只要指定 key 就行了，中间的那层映射在 AMQP 中叫 exchange（交换机）。AMQP 中有四种 exchangeDirect exchange：key 就等于 queueFanout exchange：无视 key，给所有的 queue 都来一份Topic exchange：key 可以用“宽字符”模糊匹配 queueHeaders exchange：无视 key，通过查看消息的头部元数据来决定发给那个queue（AMQP 头部元数据非常丰富而且可以自定义）这种结构的架构给通讯带来了很大的灵活性，我们能想到的通讯方式都可以用这四种exchange 表达出来。如果你需要一个企业数据总线（在乎灵活性）那么 RabbitMQ 绝对的值得一用无 Broker 的 MQ无 Broker 的 MQ 的代表是 ZeroMQ。该作者非常睿智，他非常敏锐的意识到——MQ 是更高级的 Socket，它是解决通讯问题的。所以 ZeroMQ 被设计成了一个“库”而不是一个中间件，这种实现也可以达到——没有 Broker 的目的节点之间通讯的消息都是发送到彼此的队列中，每个节点都既是生产者又是消费者。ZeroMQ做的事情就是封装出一套类似于 Socket 的 API 可以完成发送数据，读取数据ZeroMQ 其实就是一个跨语言的、重量级的 Actor 模型邮箱库。你可以把自己的程序想象成一个 Actor，ZeroMQ 就是提供邮箱功能的库；ZeroMQ 可以实现同一台机器的 RPC 通讯也可以实现不同机器的 TCP、UDP 通讯，如果你需要一个强大的、灵活、野蛮的通讯能力，别犹豫 ZeroMQ一、Kafka介绍Kafka是最初由Linkedin公司开发，是一个分布式、支持分区的（partition）、多副本的 （replica），基于zookeeper协调的分布式消息系统，它的最大的特性就是可以实时的处理 大量数据以满足各种需求场景：比如基于hadoop的批处理系统、低延迟的实时系统、 Storm/Spark流式处理引擎，web/nginx日志、访问日志，消息服务等等，用scala语言编 写，Linkedin于 2010 年贡献给了Apache基金会并成为顶级开源 项目。1.Kafka的使用场景日志收集：一个公司可以用Kafka收集各种服务的log，通过kafka以统一接口服务的方式 开放给各种consumer，例如hadoop、Hbase、Solr等。 消息系统：解耦和生产者和消费者、缓存消息等。 用户活动跟踪：Kafka经常被用来记录web用户或者app用户的各种活动，如浏览网⻚、 搜索、点击等活动，这些活动信息被各个服务器发布到kafka的topic中，然后订阅者通过 订阅这些topic来做实时的监控分析，或者装载到hadoop、数据仓库中做离线分析和挖 掘。 运营指标：Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据，生产 各种操作的集中反馈，比如报警和报告。2.Kafka基本概念kafka是一个分布式的，分区的消息(官方称之为commit log)服务。它提供一个消息系统应该 具备的功能，但是确有着独特的设计。可以这样来说，Kafka借鉴了JMS规范的思想，但是确 并 没有完全遵循JMS规范。首先，让我们来看一下基础的消息(Message)相关术语：名称解释Broker消息中间件处理节点，⼀个Kafka节点就是⼀个broker，⼀个或者多个Broker可以组成⼀个Kafka集群TopicKafka根据topic对消息进⾏归类，发布到Kafka集群的每条消息都需要指定⼀个topicProducer消息⽣产者，向Broker发送消息的客户端Consumer消息消费者，从Broker读取消息的客户端ConsumerGroup每个Consumer属于⼀个特定的Consumer Group，⼀条消息可以被多个不同的Consumer Group消费，但是⼀个Consumer Group中只能有⼀个Consumer能够消费该消息Partition物理上的概念，⼀个topic可以分为多个partition，每个partition内部消息是有序的因此，从一个较高的层面上来看，producer通过网络发送消息到Kafka集群，然后consumer 来进行消费，如下图： 服务端(brokers)和客户端(producer、consumer)之间通信通过 TCP协议 来完成。二、kafka基本使用1.安装前的环境准备安装jdk安装zk官网下载kafka的压缩包:http://kafka.apache.org/downloads解压缩至如下路径/usr/local/kafka/修改配置文件：/usr/local/kafka/kafka2.11-2.4/config/server.properties#broker.id属性在kafka集群中必须要是唯一broker.id= 0#kafka部署的机器ip和提供服务的端口号listeners=PLAINTEXT://192.168.65.60:9092#kafka的消息存储文件log.dir=/usr/local/data/kafka-logs#kafka连接zookeeper的地址zookeeper.connect= 192.168.65.60:2181## 2.启动kafka服务器进入到bin目录下。使用命令来启动./kafka-server-start.sh -daemon../config/server.properties验证是否启动成功：进入到zk中的节点看id是 0 的broker有没有存在（上线）ls /brokers/ids/server.properties核心配置详解：PropertyDefaultDescriptionbroker.id0每个broker都可以⽤⼀个唯⼀的⾮负整数id进⾏标识；这个id可以作为broker的“名字”，你可以选择任意你喜欢的数字作为id，只要id是唯⼀的即可。log.dirs/tmp/kafka-logskafka存放数据的路径。这个路径并不是唯⼀的，可以是多个，路径之间只需要使⽤逗号分隔即可；每当创建新partition时，都会选择在包含最少partitions的路径下进⾏。listenersPLAINTEXT://192.168.65.60:9092server接受客户端连接的端⼝，ip配置kafka本机ip即可zookeeper.connectlocalhost:2181zooKeeper连接字符串的格式为：hostname:port，此处hostname和port分别是ZooKeeper集群中某个节点的host和port；zookeeper如果是集群，连接⽅式为hostname1:port1, hostname2:port2,hostname3:port3log.retention.hours168每个⽇志⽂件删除之前保存的时间。默认数据保存时间对所有topic都⼀样。num.partitions1创建topic的默认分区数default.replication.factor1⾃动创建topic的默认副本数量，建议设置为⼤于等于2min.insync.replicas1当producer设置acks为-1时，min.insync.replicas指定replicas的最⼩数⽬（必须确认每⼀个repica的写数据都是成功的），如果这个数⽬没有达到，producer发送消息会产⽣异常delete.topic.enablefalse是否允许删除主题3.创建主题topictopic是什么概念？topic可以实现消息的分类，不同消费者订阅不同的topic。执行以下命令创建名为“test”的topic，这个topic只有一个partition，并且备份因子也设置为1./kafka-topics.sh --create --zookeeper 172.16.253.35:2181 --replication-factor 1 --partitions 1 --topic test查看当前kafka内有哪些topic./kafka-topics.sh --list --zookeeper 172.16.253.35:21814.发送消息kafka自带了一个producer命令客户端，可以从本地文件中读取内容，或者我们也可以以命令行中直接输入内容，并将这些内容以消息的形式发送到kafka集群中。在默认情况下，每一个行会被当做成一个独立的消息。使用kafka的发送消息的客户端，指定发送到的kafka服务器地址和topic./kafka-console-producer.sh --broker-list 172.16.253.38:9092 --topic test[5.消费消息对于consumer，kafka同样也携带了一个命令行客户端，会将获取到内容在命令中进行输 出， 默认是消费最新的消息 。使用kafka的消费者消息的客户端，从指定kafka服务器的指定 topic中消费消息方式一：从最后一条消息的偏移量+1开始消费./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092 --topic test方式二：从头开始消费./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092 --from-beginning --topic test几个注意点：消息会被存储消息是顺序存储消息是有偏移量的消费时可以指明偏移量进行消费三、Kafka中的关键细节1.消息的顺序存储消息的发送方会把消息发送到broker中，broker会存储消息，消息是按照发送的顺序进行存储。因此消费者在消费消息时可以指明主题中消息的偏移量。默认情况下，是从最后一个消息的下一个偏移量开始消费。[2. 单播消息的实现单播消息：一个消费组里 只会有一个消费者能消费到某一个topic中的消息。于是可以创建多个消费者，这些消费者在同一个消费组中。./kafka-console-consumer.sh --bootstrap-server 10.31.167.10:9092 --consumer-property group.id=testGroup --topic test3.多播消息的实现在一些业务场景中需要让一条消息被多个消费者消费，那么就可以使用多播模式。kafka实现多播，只需要让不同的消费者处于不同的消费组即可。./kafka-console-consumer.sh --bootstrap-server 10.31.167.10:9092 --consumer-property group.id=testGroup1 --topic test./kafka-console-consumer.sh --bootstrap-server 10.31.167.10:9092 --consumer-property group.id=testGroup2 --topic test4.查看消费组及信息# 查看当前主题下有哪些消费组./kafka-consumer-groups.sh --bootstrap-server 10.31.167.10:9092 --list# 查看消费组中的具体信息：比如当前偏移量、最后一条消息的偏移量、堆积的消息数量./kafka-consumer-groups.sh --bootstrap-server 172.16.253.38:9092 --describe --group testGroupCurrennt-offset: 当前消费组的已消费偏移量Log-end-offset: 主题对应分区消息的结束偏移量(HW)Lag: 当前消费组未消费的消息数四、主题、分区的概念1.主题Topic主题Topic可以理解成是一个类别的名称。2.partition分区一个主题中的消息量是非常大的，因此可以通过分区的设置，来分布式存储这些消息。比如一个topic创建了 3 个分区。那么topic中的消息就会分别存放在这三个分区中。为一个主题创建多个分区./kafka-topics.sh --create --zookeeper localhost:2181 --partitions 2 --topic test1可以通过这样的命令查看topic的分区信息./kafka-topics.sh --describe --zookeeper localhost:2181 --topic test1分区的作用：可以分布式存储可以并行写实际上是存在data/kafka-logs/test-0 和 test-1中的0000000.log文件中小细节：定期将自己消费分区的offset提交给kafka内部topic：__consumer_offsets，提交过去的 时候，key是consumerGroupId+topic+分区号，value就是当前offset的值，kafka会定 期清理topic里的消息，最后就保留最新的那条数据 因为__consumer_offsets可能会接收高并发的请求，kafka默认给其分配 50 个分区(可以 通过offsets.topic.num.partitions设置)，这样可以通过加机器的方式抗大并发。 通过如下公式可以选出consumer消费的offset要提交到__consumer_offsets的哪个分区 公式：hash(consumerGroupId) % __consumer_offsets主题的分区数五、Kafka集群及副本的概念1.搭建kafka集群， 3 个broker准备 3 个server.properties文件每个文件中的这些内容要调整server.propertiesbroker.id= 0listeners=PLAINTEXT://192.168.65.60:log.dir=/usr/local/data/kafka-logsserver1.propertiesbroker.id= 1listeners=PLAINTEXT://192.168.65.60:log.dir=/usr/local/data/kafka-logs-server2.propertiesbroker.id= 2listeners=PLAINTEXT://192.168.65.60:log.dir=/usr/local/data/kafka-logs-### 使用如下命令来启动 3 台服务器./kafka-server-start.sh -daemon../config/server0.properties./kafka-server-start.sh -daemon../config/server1.properties./kafka-server-start.sh -daemon../config/server2.properties搭建完后通过查看zk中的/brokers/ids 看是否启动成功2.副本的概念副本是对分区的备份。在集群中，不同的副本会被部署在不同的broker上。下面例子：创建 1个主题， 2 个分区、 3 个副本。./kafka-topics.sh --create --zookeeper 172.16.253.35:2181 --replication-factor 3 --partitions 2 --topic my-replicated-topic通过查看主题信息，其中的关键数据：replicas：当前副本存在的broker节点leader：副本里的概念每个partition都有一个broker作为leader。消息发送方要把消息发给哪个broker？就看副本的leader是在哪个broker上面。副本里的leader专⻔用来接收消息。接收到消息，其他follower通过poll的方式来同步数据。follower：leader处理所有针对这个partition的读写请求，而follower被动复制leader，不提供读写（主要是为了保证多副本数据与消费的一致性），如果leader所在的broker挂掉，那么就会进行新leader的选举，至于怎么选，在之后的controller的概念中介绍。通过kill掉leader后再查看主题情况# kill掉leaderps -aux | grep server.propertieskill 17631# 查看topic情况./kafka-topics.sh --describe --zookeeper 172.16.253.35:2181 --topic my-replicated-topicisr： 可以同步的broker节点和已同步的broker节点，存放在isr集合中。3.broker、主题、分区、副本kafka集群中由多个broker组成一个broker中存放一个topic的不同partition——副本4.kafka集群消息的发送./kafka-console-producer.sh --broker-list 172.16.253.38:9092,172.16.253.38:9093,172.16.253.38:9094 --topic my-replicated-topic5.kafka集群消息的消费./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092,172.16.253.38:9093,172.16.253.38:9094 --from-beginning --topic my-replicated-topic6.关于分区消费组消费者的细节图中Kafka集群有两个broker，每个broker中有多个partition。一个partition只能被一个消费组里的某一个消费者消费，从而保证消费顺序。Kafka只在partition的范围内保证消息消费的局部顺序性，不能在同一个topic中的多个partition中保证总的消费顺序性。一个消费者可以消费多个partition。消费组中消费者的数量不能比一个topic中的partition数量多，否则多出来的消费者消费不到消息。六、Kafka集群Controller、Rebalance和HW1.ControllerKafka集群中的broker在zk中创建临时序号节点，序号最小的节点（最先创建的节点）将作为集群的controller，负责管理整个集群中的所有分区和副本的状态：当某个分区的leader副本出现故障时，由控制器负责为该分区选举新的leader副本。当检测到某个分区的ISR集合发生变化时，由控制器负责通知所有broker更新其元数据信息。当使用kafka-topics.sh脚本为某个topic增加分区数量时，同样还是由控制器负责让新分区被其他节点感知到。2.Rebalance机制前提是：消费者没有指明分区消费。当消费组里消费者和分区的关系发生变化，那么就会触发rebalance机制。这个机制会重新调整消费者消费哪个分区。在触发rebalance机制之前，消费者消费哪个分区有三种策略：range：通过公示来计算某个消费者消费哪个分区轮询：大家轮着消费sticky：在触发了rebalance后，在消费者消费的原分区不变的基础上进行调整。3.HW和LEOHW俗称高水位，HighWatermark的缩写，取一个partition对应的ISR中最小的LEO(log-end-offset)作为HW，consumer最多只能消费到HW所在的位置。另外每个replica都有HW,leader和follower各自负责更新自己的HW的状态。对于leader新写入的消息，consumer不能立刻消费，leader会等待该消息被所有ISR中的replicas同步后更新HW，此时消息才能被consumer消费。这样就保证了如果leader所在的broker失效，该消息仍然可以从新选举的leader中获取。七、Kafka线上问题优化1.如何防止消息丢失发送方： ack是 1 或者-1/all 可以防止消息丢失，如果要做到99.9999%，ack设成all，把min.insync.replicas配置成分区备份数消费方：把自动提交改为手动提交。2.如何防止消息的重复消费一条消息被消费者消费多次。如果为了消息的不重复消费，而把生产端的重试机制关闭、消费端的手动提交改成自动提交，这样反而会出现消息丢失，那么可以直接在防治消息丢失的手段上再加上消费消息时的幂等性保证，就能解决消息的重复消费问题。幂等性如何保证：mysql 插入业务id作为主键，主键是唯一的，所以一次只能插入一条使用redis或zk的分布式锁（主流的方案）3.如何做到顺序消费RocketMQ发送方：在发送时将ack不能设置 0 ，关闭重试，使用同步发送，等到发送成功再发送下一条。确保消息是顺序发送的。接收方：消息是发送到一个分区中，只能有一个消费组的消费者来接收消息。因此，kafka的顺序消费会牺牲掉性能。4.解决消息积压问题消息积压会导致很多问题，比如磁盘被打满、生产端发消息导致kafka性能过慢，就容易出现服务雪崩，就需要有相应的手段：方案一：在一个消费者中启动多个线程，让多个线程同时消费。——提升一个消费者的消费能力（增加分区增加消费者）。方案二：如果方案一还不够的话，这个时候可以启动多个消费者，多个消费者部署在不同的服务器上。其实多个消费者部署在同一服务器上也可以提高消费能力——充分利用服务器的cpu资源。方案三：让一个消费者去把收到的消息往另外一个topic上发，另一个topic设置多个分区和多个消费者 ，进行具体的业务消费。5.延迟队列延迟队列的应用场景：在订单创建成功后如果超过 30 分钟没有付款，则需要取消订单，此时可用延时队列来实现创建多个topic，每个topic表示延时的间隔topic_5s: 延时5s执行的队列topic_1m: 延时 1 分钟执行的队列topic_30m: 延时 30 分钟执行的队列消息发送者发送消息到相应的topic，并带上消息的发送时间消费者订阅相应的topic，消费时轮询消费整个topic中的消息如果消息的发送时间，和消费的当前时间超过预设的值，比如 30 分钟如果消息的发送时间，和消费的当前时间没有超过预设的值，则不消费当前的offset及之后的offset的所有消息都消费下次继续消费该offset处的消息，判断时间是否已满足预设值八、Kafka-eagle监控平台安装Kafka-eaglehttp://www.kafka-eagle.org/安装jdk解压缩后修改配置文件 system-config.properties# 配置zk 去掉cluster2efak.zk.cluster.alias=cluster1cluster1.zk.list=172.16.253.35:2181# cluster2.zk.list=xdn10:2181,xdn11:2181,xdn12:2181# 配置mysqlkafka.eagle.driver=com.mysql.cj.jdbc.Driverkafka.eagle.url=jdbc:mysql://172.16.253.22:3306/ke?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNullkafka.eagle.username=rootkafka.eagle.password= 123456修改/etc/profileexport JAVA_HOME=/usr/local/jdk/jdk1.8.0_191CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jarexport KE_HOME=/home/aisys/efak-web-2.0.9export PATH=$PATH:$KE_HOME/bin:$JAVA_HOME/bin刷新配置source /etc/profile进入到bin目录，为ke.sh增加可执行的权限chmod +x ke.sh启动kafka-eagle./ke.sh start
在k8s中部署的难点在k8s中部署nacos集群和在裸机器上直接部署nacos机器其实差别不大。最主要的区别是k8s中部署的服务没有固定的IP，而nacos集群部署需要配置所有实例的IP地址因Nacos本身是直接拿MySQL的数据,故在此服务中不做持久化操作解决在k8s中通过StatefulSet和Headless Service为每个nacos实例生成一个唯一的dns地址，创建一个普通Service给可客户端使用部署创建数据库配置CMapiVersion: v1 kind: ConfigMap metadata: name: nacos-cm data: mysql.host: "192.168.1.3" mysql.db.name: "nacos_devtest" mysql.port: "3306" mysql.user: "nacos" mysql.password: "nacos"部署Headless Service无头服务SVCHeadless Service为每个Pod（nacos实例）生成一个地址，用作NACOS_SERVERS配置apiVersion: v1 kind: Service metadata: name: nacos-headless labels: app: nacos annotations: service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" spec: ports: - port: 8848 name: server targetPort: 8848 clusterIP: None selector: app: nacos使用StatefulSet控制器部署NacosStatefulSet部署方式为每个POD生成固定的名称，如nacos-0、nacos-1、nacos-2等。apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos spec: serviceName: nacos-headless replicas: 3 template: metadata: labels: app: nacos annotations: pod.alpha.kubernetes.io/initialized: "true" spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - nacos-headless topologyKey: "kubernetes.io/hostname" containers: - name: k8snacos imagePullPolicy: Always image: nacos/nacos-server:latest resources: requests: memory: "2Gi" cpu: "500m" ports: - containerPort: 8848 name: client env: - name: NACOS_REPLICAS value: "3" - name: MYSQL_SERVICE_HOST valueFrom: configMapKeyRef: name: nacos-cm key: mysql.host - name: MYSQL_SERVICE_DB_NAME valueFrom: configMapKeyRef: name: nacos-cm key: mysql.db.name - name: MYSQL_SERVICE_PORT valueFrom: configMapKeyRef: name: nacos-cm key: mysql.port - name: MYSQL_SERVICE_USER valueFrom: configMapKeyRef: name: nacos-cm key: mysql.user - name: MYSQL_SERVICE_PASSWORD valueFrom: configMapKeyRef: name: nacos-cm key: mysql.password - name: MODE value: "cluster" - name: NACOS_SERVER_PORT value: "8848" - name: PREFER_HOST_MODE value: "hostname" - name: NACOS_SERVERS value: "nacos-0.nacos-headless.default.svc.cluster.local:8848 nacos-1.nacos-headless.default.svc.cluster.local:8848 nacos-2.nacos-headless.default.svc.cluster.local:8848" # 这里注意更换你的K8s内部域名 selector: matchLabels: app: nacos 部署普通SVC(NodePort)当不需要NodePort形式暴露时,删除type字段与nodePort相关字段即可apiVersion: v1 kind: Service metadata: annotations: service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" labels: app: nacos name: nacos-nodeport namespace: prdtsp spec: ports: - name: server nodePort: 30235 port: 8848 - name: client-rpc nodePort: 31294 port: 9848 - name: raft-rpc nodePort: 30311 port: 9849 - name: old-raft-rpc nodePort: 30904 port: 7848 selector: app: nacos type: NodePort配置域名访问(Ingress)apiVersion: extensions/v1beta1 kind: Ingress metadata: name: nacos-web namespace: default spec: rules: - host: nacos-web.nacos-demo.com http: paths: - path: / backend: serviceName: nacos-service #这里填写你的SVC名称 servicePort: server #这里填写SVC中spec.ports.name字段,选择你想暴露出来的端口名称即可,也可直接填写spec.ports.port字段.
kubernetes coredns 添加自定义DNS解析记录coredns 自带 hosts 插件， 允许像配置 hosts 一样配置自定义 DNS 解析，修改 kube-system 中 configMap 的 coredns 添加如下设置即可 hosts { 172.21.91.28 cache.redis 172.21.91.28 persistent.redis fallthrough }修改后文件如下.:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } hosts { 172.21.91.28 cache.redis 172.21.91.28 persistent.redis fallthrough } prometheus :9153 proxy . /etc/resolv.conf cache 30 loop reload loadbalance }
最近在监控k8s当中的Jenkins 的时候,新增ServiceMonitor总是不显示,这里的不显示指的是不在Prometheus-Operator中显示,我认为这其中一定有问题,于是查阅了大量的资料,终于从中找到了答案, 原因竟是Prometheus-Operator在安装的时候默认只会自动发现default,kube-system,monitoring这三个名称空间下新建的ServiceMonitor,而我的ServiceMonitor又是新建在别的名称空间下所以他无法进行自动发现更奇怪的是,网上对这个问题的记录文章几乎为0,我根本找不到,所以才写下这个笔记,供看到的人借鉴!1. 监控其他 namespace 中的 endpoint 资源需要做的操作创建新增命名空间的 role， 用于获取监控的信息。将创建的role绑定到 monitoring 命名空间中的 prometheus-k8s SA。kubectl create ns test namespace=test cat <<EOF | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: prometheus-k8s namespace: ${namespace} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: prometheus-k8s subjects: - kind: ServiceAccount name: prometheus-k8s namespace: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: prometheus-k8s namespace: ${namespace} rules: - apiGroups: - "" resources: - services - endpoints - pods verbs: - get - list - watch - apiGroups: - extensions resources: - ingresses verbs: - get - list - watch EOF COPY2. 监控其他 namespace 中的 serviceMonitor 资源serviceMonitorNamespaceSelector 命名空间的标签匹配，不指定时，只匹配自身命名空间的资源serviceMonitorSelector serviceMonitor的标签匹配， 不指定时，只匹配自身命名空间的资源修改 Prometheus 资源配置apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: labels: prometheus: k8s name: k8s namespace: monitoring spec: ... # 增加 ns 匹配的标签 serviceMonitorNamespaceSelector: matchLabels: serviceMonitor: prometheus # 或者 增加下面的匹配，用来匹配 serviceMonitor serviceMonitorSelector: matchLabels: serviceMonitor: prometheus COPY添加命名空间的标签for ns in default kube-system monitoring test; do kubectl patch ns $ns --patch '{"metadata":{"labels":{"serviceMonitor": "prometheus" } } }' done COPY添加 serviceMonitor 的标签kubectl patch -n test servicemonitor demo-app --patch '{"metadata":{"labels":{"serviceMonitor":"prometheus"}}}' --type=merge COPY测试cat <<EOF | kubectl-test apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: ingress-demo-app namespace: test labels: app: ingress-demo-app spec: replicas: 2 selector: matchLabels: app: ingress-demo-app template: metadata: labels: app: ingress-demo-app namespace: test spec: containers: - name: whoami image: traefik/whoami:v1.6.1 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: ingress-demo-app namespace: test labels: app: ingress-demo-app spec: type: ClusterIP selector: app: ingress-demo-app ports: - name: http port: 80 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-demo-app namespace: test labels: app: ingress-demo-app annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: app.demo.com http: paths: - path: / pathType: Prefix backend: service: name: ingress-demo-app port: number: 80 --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: name: ingress-demo-app name: ingress-demo-app namespace: test spec: endpoints: - port: http path: /health interval: 5s selector: matchLabels: app: ingress-demo-app EOF希望