Kubernetes taint & toleration

一、概述前一篇文章讲解了 Kubernetes 亲和性调度, 所涉及的内容都是描述 pod 的属性,来声明此 pod 希望调度到哪类 nodes。而本文介绍的 Taint(污点) 刚好相反,它是node 的一个属性,允许 node 主动排斥 pod 的调度。对应的 k8s 又给 pod 新增了配套属性 toleration(容忍) ,用于表示这些 pod 可以(但不强制要求)被调度到具有相应 taints 的 nodes 上。这两者经常一起搭配,来确保不将 pod 调度到不合适的 nodes。看下 taint & toleration 结构体,下面足点介绍时会涉及相关字段。type Taint struct { Key string Value string Effect TaintEffect // add taint 的时间点 // 只有 Effect = NoExecute, 该值才会被 nodeController 写入 TimeAdded *metav1.Time}type Toleration struct { Key string Operator TolerationOperator Value string Effect TaintEffect // 容忍时间 TolerationSeconds *int64}type TaintEffect stringconst ( TaintEffectNoSchedule TaintEffect = “NoSchedule” TaintEffectPreferNoSchedule TaintEffect = “PreferNoSchedule” TaintEffectNoExecute TaintEffect = “NoExecute”)type TolerationOperator stringconst ( TolerationOpExists TolerationOperator = “Exists” TolerationOpEqual TolerationOperator = “Equal”)二、Taint我们可以对 node 设置多个 taints,当然也可以在 pod 配置相同个数的 tolerations。影响调度和运行的具体行为,我们可以分为以下几类:如果至少有一个 effect == NoSchedule 的 taint 没有被 pod toleration,那么 pod 不会被调度到该节点上。如果所有 effect == NoSchedule 的 taints 都被 pod toleration,但是至少有一个 effect == PreferNoSchedule 没有被 pod toleration,那么 k8s 将努力尝试不把 pod 调度到该节点上。如果至少有一个 effect == NoExecute 的 taint 没有被 pod toleration,那么不仅这个 pod 不会被调度到该节点,甚至这个节点上已经运行但是也没有设置容忍该污点的 pods,都将被驱逐。三、Toleration再看下 PodSpec 配置 Tolerations,其中的 key、value、effect 与 Node Taint 设置需要保持一致,operator 支持两类:Exists: 这个配置下,不需要指定 value。Equal: 需要配置 value 值。(operator 的默认值)有几个特殊情况:key 为空并且 operator 等于 Exists,表示匹配了所有的 keys,values 和 effects。换句话说就是容忍了所有的 taints。tolerations:- operator: “Exists"effect 为空,则表示匹配所有的 effects(NoSchedule、PreferNoSchedule、NoExecute)tolerations:- key: “key” operator: “Exists"还有一个 TolerationSeconds,该值与 effect 为 NoExecute 配套使用。用来指定在 node 添加了 effect = NoExecute 的 taint 后,能容忍该 taint 的 pods 可停留在 node 上的时间。例如:tolerations: - key: “key1” operator: “Equal” value: “value1” effect: “NoExecute” tolerationSeconds: 3600表示如果这个 pod 已经运行在 node 上并且该 node 添加了一个对应的 taint,那么这个 pod 将会在 node 上停留 3600 秒后才会被驱逐。但是如果 taint 在这个时间前被移除,那么这个 pod 也就不会被驱逐了。来个比较形象的图,描述下:该图参考: Taints and tolerations, pod and node affinities demystified · Banzai Cloud四、内置行为kubernetes 1.6 版本,node controller 会跟进系统情况自动设置 node taint 属性。内置 taint key 如下:node.kubernetes.io/not-ready: 节点尚未就绪node.kubernetes.io/unreachable: 节点无法被访问node.kubernetes.io/unschedulable: 节点不可调度node.kubernetes.io/out-of-disk: 节点磁盘不足node.kubernetes.io/memory-pressure: 节点有内存压力node.kubernetes.io/disk-pressure: 节点有磁盘压力node.kubernetes.io/network-unavailable: 节点网络不可用node.kubernetes.io/pid-pressure: 节点有 pid 压力node.cloudprovider.kubernetes.io/uninitialized: 云节点未初始化node.cloudprovider.kubernetes.io/shutdown: 云节点已下线kubernetes 会通过 DefaultTolerationSeconds admission controller 为创建的 pod 添加两个默认的 toleration: node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable,并且设置 tolerationSeconds = 300。当然这个默认配置,用户可以自行覆盖。这些自动添加的默认配置,确保 node 出现问题后,pod 可以继续在 node 上停留 5 分钟。DefaultTolerationSeconds admission controller 设置的 tolerationSeconds 值,也可以由用户指定。具体参考: https://github.com/kubernetes…还有一个默认行为,就是 DaemonSet。所有 DaemonSet 创建的 pod 都会添加两个 toleration: node.alpha.kubernetes.io/unreachable 和 node.alpha.kubernetes.io/notReady。设置 effect = NoExecute,并且不指定 tolerationSeconds。目的是确保在 node 出现 unreachable 或 notReady 的问题时,DaemonSet Pods 永远不会被驱逐。示例专用节点如果你希望将一组节点专用于特定的用户,那可以将这些节点设置 taints: kubectl taint nodes nodename dedicated=groupName:NoSchedule, 然后为 pods 设置对应的 tolerations。如果你希望节点被专用并且确保服务仅使用这批节点,那么你还应该向这批节点设置 labels (dedicated=groupName),并且为对应的 pods 设置 NodeAffinity,以控制 pods 只能跑到这批节点上。具有特殊硬件的节点有部分带有特殊硬件的节点,比如 GPU、FPGA 等,要确保不将不需要专用硬件的 pods 调度到这些节点。也可以和 专有节点 一样的方式设置 taints:kubectl taint nodes nodename special=true:NoSchedule 或 kubectl taint nodes nodename special=true:PreferNoSchedule。建议还可以通过 Extended Resources 和 ExtendedResourceToleration admission controller 来更方便的调度依赖特殊硬件的 pods,而不需要手动添加容器的 toleration基于 taint 的驱逐前面也提到了,可以通过 NoExecute taint 来驱逐节点上已经运行的 pods。五、参考资料community/taint-toleration-dedicated.md at master · kubernetes/community · GitHubcommunity/taint-node-by-condition.md at master · kubernetes/community · GitHubTaints and TolerationsTaints and tolerations, pod and node affinities demystified · Banzai Cloud ...

March 9, 2019 · 2 min · jiezi

Kubernetes 调度器浅析

一、概述Kubernetes 是 Google 开源的容器集群管理系统(谷歌内部:Borg),而今天要介绍的 kube-scheduler 是 k8s 系统的核心组件之一,其主要职责就是通过自身的调度算法,为新创建的 Pod 寻找一个最合适的 Node。主要包含如下几个步骤:通过一组叫做谓词 predicates 的过滤算法,先挑出满足条件的 Node;通过一组叫做优先级 priorities 的打分算法,来给上一步符合条件的每个 Node 进行打分排名;最终选择得分最高的节点,当然如果得分一样就随机一个节点,填回 Pod 的 spec.nodeName 字段。官方流程图如下:For given pod: +———————————————+ | Schedulable nodes: | | | | +——–+ +——–+ +——–+ | | | node 1 | | node 2 | | node 3 | | | +——–+ +——–+ +——–+ | | | +——————-+————————-+ | | v +——————-+————————-+ Pred. filters: node 3 doesn’t have enough resource +——————-+————————-+ | | v +——————-+————————-+ | remaining nodes: | | +——–+ +——–+ | | | node 1 | | node 2 | | | +——–+ +——–+ | | | +——————-+————————-+ | | v +——————-+————————-+ Priority function: node 1: p=2 node 2: p=5 +——————-+————————-+ | | v select max{node priority} = node 2scheduler 的工作看似很简单,但其实不然。考虑的问题非常多,比如要保证每个节点被公平调度,提高资源利用率,提高 pod 调度效率,提升调度器扩展能力等等。可涉及的内容非常多,接下来会围绕两个核心步骤对 k8s 的 默认调度策略 深入了解。参考 Kubernetes 版本: v1.12二、PredicatesPredicates 在调度过程中的作用就是先进行过滤,过滤掉所有不符合条件的节点后,剩下的所有节点就都是可以运行带调度 Pod。Predicates 的可以分为如下四类:GeneralPredicates:负责最基础的调度策略,比如 PodFitsResources 计算宿主机资源是否够用。与 Volume 相关的过滤规则:负责与容器持久化 Volume 相关的调度策略。与宿主机相关的过滤规则:负责考察待调度 Pod 是否满足 Node 本身的一些条件。与已运行 Pod 相关的过滤规则:负责检查待调度 Pod 与 Node 上已有 Pod 之间的亲和性关系。具体的 Predicates 默认策略,可以参考: 默认调度策略当开始调度一个 Pod 的时候,调度器会同时开启多个协程并发的进行 Node Predicates 过滤,最后返回一个可以运行 Pod 的节点列表。每个协程都是按照固定的顺序进行计算过滤的。接下来,我们看下四大类具体运行的调度策略内容。1. GeneralPredicates看字面意思就知道 GeneralPredicates 负责的是最基础的调度策略,其包含的具体策略如下:PodFitsResources: 计算宿主机的 CPU、内存、扩展资源(如 GPU)等是否够用。PodFitsHost: 检查宿主机的名字是否跟 Pod 的 spec.nodeName 匹配。PodFitsHostPorts: 检查 Pod 申请的宿主机端口有没有冲突。PodMatchNodeSelector: 检查节点是否能匹配 Pod 的 nodeSelector 和 nodeAffinity。因为 GeneralPredicates 是最基础的调度策略,所以该接口也会被别的组件直接调用,比如 kubelet、daemonSet controller。kubelet 在启动 pod 之前,还会再执行一遍 GeneralPredicates,用于二次确认。2. 与 Volume 相关的过滤规则不废话就直接列举具体的策略了:NoDiskConflict:检查该节点上所有的 Pods 是否与待调度的 Pod 的 Volume 有冲突,比如 AWS、GCE 的 Volume 是不允许被两个 Pod 同时使用的。VolumeZonePredicate:检查 Pod Volume 的 zone 标签是否与节点的 zone 标签匹配。如果 Node 没有 zone 标签则认定为匹配。MaxPDVolumeCountPredicate:检查节点上某种类型的 Volume 是否已经超过指定数目。CSIMaxVolumeLimitPredicate:检查 csi volume 相关的限制VolumeBindingPredicate:检查 Pod 对应的 Local PV 的 nodeAffinity 字段,是否跟某个节点的标签相匹配。如果该 Pod PVC 还没有绑定 PV 的话,则调度器还要负责检查所有待绑定的 PV,且该 PV 的 nodeAffinity 是否与节点标签匹配。3. 与宿主机相关的过滤规则这些规则主要考察待调度的 Pod 是否满足 Node 本身的一些条件。具体的策略如下:NodeConditionPredicate:检查 Node 是否还未准备好或者处于NodeOutOfDisk、NodeNetworkUnavailable 状态,又或者 Node spec.Unschedulable 设置为 true,那该节点都将无法被调度。PodToleratesNodeTaints:检查 Node 的 taint(污点)机制。只有当 Pod 的 Toleration 与 Node 的 Taint 匹配时,Pod 才能调度到该节点上。NodeMemoryPressurePredicate:检查当前节点的内存是否已经不够使用。NodeDiskPressurePredicate:检查当前节点的磁盘是否已经不够使用。NodePIDPressurePredicate:检查当前节点的 PID 是否已经不够使用。4. 与已运行 Pod 相关的过滤规则该规则主要就是 PodAffinityPredicate,用于检查待调度 Pod 与 Node 上已有的 Pod 之间的亲和性和反亲和性关系。具体的亲和性相关的调度,后面会单独拿一篇文章进行介绍。三、Priorities完成了前一个阶段的节点 “过滤” 之后,便需要通过 Priorities 为这些节点打分,选择得分最高的节点,作为调度对象。打分函数很多,总得分可以参考:总分 = (权重1 * 打分函数1) + (权重2 * 打分函数2) + … + (权重n * 打分函数n)。每一次打分的范围是 0 — 10 分。10 表示非常合适,0 表示非常不合适。并且每个打分函数都可以配置对应的权重值,下面介绍 调度器策略配置 时,也会涉及权重值的配置。默认权重值是 1,如果觉得某个打分函数特别重要,便可以加大该权重值。具体的 Priorities 默认策略可以参考: defaultPriorities 。Priorities 最常用到的一个打分规则是 LeastRequestedPriority, 该算法用于选出空闲资源(cpu & memory)最多的宿主机。还有一个常见的是 BalancedResourceAllocation,该规则主要目的是资源平衡。在所有节点里选择各种资源分配最均衡的节点,避免出现某些节点 CPU 被大量分配,但是 Memory 大量剩余的情况。此外,还有 InterPodAffinityPriority、NodeAffinityPriority、TaintTolerationPriority,与亲和性与污点调度有关,后面会有单独的文章进行介绍。这里表示节点满足的规则越多,那得分就越高。在 K8S v1.12 版本还引入了一个调度策略,即 ImageLocalityPriority。该策略主要目的是优先选择那些已经存有 Pod 所需 image 的节点,可以避免实际运行 Pod 时,再去下载 image。注意: pod 运行时是否会下载 image,还跟 Pod ImagePullPolicy 配置有关。可以看到 k8s scheduler 完成一次调度所需的信息非常之多。所以在实际的调度过程中,大量的信息都事先已经缓存,提高了 Pod 的调度效率。四、调度策略配置Kubernetes 调度器有默认的调度策略,具体可以参考 default 。当然用户也可以修改调度策略,可以通过命令行参数 policy-config-file 指定一个 JSON 文件来描述哪些 predicates 和 priorities 在启动 k8s 时被使用, 通过这个参数调度就能使用管理者定义的策略了。示例如下:{“kind” : “Policy”,“apiVersion” : “v1”,“predicates” : [ {“name” : “PodFitsHostPorts”}, {“name” : “PodFitsResources”}, {“name” : “NoDiskConflict”}, {“name” : “NoVolumeZoneConflict”}, {“name” : “MatchNodeSelector”}, {“name” : “HostName”} ],“priorities” : [ {“name” : “LeastRequestedPriority”, “weight” : 1}, {“name” : “BalancedResourceAllocation”, “weight” : 1}, {“name” : “ServiceSpreadingPriority”, “weight” : 1}, {“name” : “EqualPriority”, “weight” : 1} ],“hardPodAffinitySymmetricWeight” : 10,“alwaysCheckAllPredicates” : false}五、自定义调度器前面提到了调度器的扩展能力,除了使用 k8s 自带的调度器,你也可以编写自己的调度器。通过修改 Pod 的 spec.schedulername 参数来指定调度器的名字。参考资料The Kubernetes SchedulerScheduler Algorithm in Kubernetes ...

March 9, 2019 · 2 min · jiezi

Kubernetes 亲和性调度

一、概述前一篇文章 Kubernetes 调度器浅析,大致讲述了调度器的工作原理及相关调度策略。这一章会继续深入调度器,介绍下“亲和性调度”。Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定更倾向于在某些特定 Node 上运行。有几种方式可以实现这个功能:NodeName: 最简单的节点选择方式,直接指定节点,跳过调度器。NodeSelector: 早期的简单控制方式,直接通过键—值对将 Pod 调度到具有特定 label 的 Node 上。NodeAffinity: NodeSelector 的升级版,支持更丰富的配置规则,使用更灵活。(NodeSelector 将被淘汰.)PodAffinity: 根据已在节点上运行的 Pod 标签来约束 Pod 可以调度到哪些节点,而不是根据 node label。二、NodeNamenodeName 是 PodSpec 的一个字段,用于直接指定调度节点,并运行该 pod。调度器在工作时,实际选择的是 nodeName 为空的 pod 并进行调度然后再回填该 nodeName,所以直接指定 nodeName 实际是直接跳过了调度器。换句话说,指定 nodeName 的方式是优于其他节点选择方法。方法很简单,直接来个官方示例:apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx nodeName: kube-01当然如果选择的节点不存在,或者资源不足,那该 pod 必然就会运行失败。三、NodeSelectornodeSelector 也是 PodSpec 中的一个字段,指定键—值对的映射。如果想要将 pod 运行到对应的 node 上,需要先给这些 node 打上 label,然后在 podSpec.NodeSelector 指定对应 node labels 即可。步骤如下:设置标签到 node 上:kubectl label nodes kubernetes-node type=gpupod 配置添加 nodeSelector 字段:apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx nodeSelector: type: gpu内置 Node 标签Kubernetes 内置了一些节点标签:kubernetes.io/hostnamebeta.kubernetes.io/instance-typebeta.kubernetes.io/osbeta.kubernetes.io/archfailure-domain.beta.kubernetes.io/zonefailure-domain.beta.kubernetes.io/region有些标签是对云提供商使用。还有些表示 node role 的 labels(可以指定 master、lb 等):kubernetes.io/rolenode-role.kubernetes.io四、NodeAffinitynodeSelector 通过 k-v 的方式非常简单的支持了 pod 调度限制到具有特定标签的节点上。而 nodeAffinity 根据亲和力 & 反亲和力极大地扩展了能够表达的约束信息。nodeAffinity 特性的设计初衷就是为了替代 nodeSelector。nodeAffinity 当前支持的匹配符号包括:In、NotIn、Exists、DoesNotExists、Gt、Lt 。nodeAffinity 当前支持两种调度模式:requiredDuringSchedulingIgnoredDuringExecution: 一定要满足的条件,如果没有找到满足条件的节点,则 Pod 创建失败。所有也称为hard 模式。preferredDuringSchedulingIgnoredDuringExecution: 优先选择满足条件的节点,如果没有找到满足条件的节点,则在其他节点中择优创建 Pod。所有也称为 soft 模式。两种模式的名字特长,这是 k8s 的命名风格。其中IgnoredDuringExecution的意义就跟 nodeSelector 的实现一样,即使 node label 发生变更,也不会影响之前已经部署且又不满足 affinity rules 的 pods,这些 pods 还会继续在该 node 上运行。换句话说,亲和性选择节点仅在调度 Pod 时起作用。k8s 社区正在计划提供 requiredDuringSchedulingRequiredDuringExecution 模式,便于驱逐 node 上不满足 affinity rules 的 pods。来个官方示例,看下怎么玩:apiVersion: v1kind: Podmetadata: name: with-node-affinityspec: affinity: nodeAffinity: # 必须选择 node label key 为 kubernetes.io/e2e-az-name, # value 为 e2e-az1 或 e2e-az2. requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/e2e-az-name operator: In values: - e2e-az1 - e2e-az2 # 过滤掉上面的必选项后,再优先选择 node label key 为 another-node-label-key # value 为 another-node-label-value. preferredDuringSchedulingIgnoredDuringExecution: # 如果满足节点亲和,积分加权重(优选算法,会对 nodes 打分) # weight: 0 - 100 - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0简单看下 NodeAffinity 的结构体,下面介绍注意事项时会涉及:type NodeAffinity struct { RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm}type NodeSelector struct { NodeSelectorTerms []NodeSelectorTerm}type NodeSelectorTerm struct { MatchExpressions []NodeSelectorRequirement MatchFields []NodeSelectorRequirement}配置相关的注意点:如果 nodeSelector 和 nodeAffinity 两者都指定,那 node 需要两个条件都满足,pod 才能调度。如果指定了多个 NodeSelectorTerms,那 node 只要满足其中一个条件,pod 就可以进行调度。如果指定了多个 MatchExpressions,那必须要满足所有条件,才能将 pod 调度到该 node。五、PodAffinitynodeSelector & nodeAffinity 都是基于 node label 进行调度。而有时候我们希望调度的时候能考虑 pod 之间的关系,而不只是 pod 和 node 的关系。举个例子,会有需求希望服务 A 和 B 部署在同一个机房、机架或机器上,因为这些服务可能会对网路延迟比较敏感,需要低延时;再比如,希望服务 C 和 D 又希望尽量分开部署,即使一台主机甚至一个机房出了问题,也不会导致两个服务一起挂而影响服务可用性,提升故障容灾的能力。podAffinity 会基于节点上已经运行的 pod label 来约束新 pod 的调度。其规则就是“如果 X 已经运行了一个或者多个符合规则 Y 的 Pod,那么这个 Pod 应该(如果是反亲和性,则是不应该)调度到 X 上”。这里的 Y 是关联 namespace 的 labelSelector,当然 namespace 也可以是 all。和 node 不同,pod 是隶属于 namespace 下的资源,所以基于 pod labelSelector 必须指定具体的 namespace;而 X 则可以理解为一个拓扑域,类似于 node、rack、zone、cloud region 等等,就是前面提到的 内置 Node 标签 ,当然也可以自定义。看下 pod affinity 涉及的结构体,便于进行功能介绍:type Affinity struct { // NodeAffinity 前面介绍了 NodeAffinity *NodeAffinity // pod 亲和性 PodAffinity *PodAffinity // pod 反亲和性 PodAntiAffinity *PodAntiAffinity}type PodAffinity struct { // hard 模式, 必选项 RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm // soft 模式, 进行 node 优先 PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm}type PodAffinityTerm struct { LabelSelector *metav1.LabelSelector Namespaces []string TopologyKey string}type WeightedPodAffinityTerm struct { Weight int32 PodAffinityTerm PodAffinityTerm}podAffinity 和 nodeAffinity 有相似的地方,使用了 labelSelector 进行匹配,支持的匹配符号包括:In、NotIn、Exists、DoesNotExists;也支持两种调度模式 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution, 功能和 nodeAffinity 一样,这里就不在累述。podAffinity 和 nodeAffinity 也有较大的差异,前面讲了 pod 是 namespace 资源,所以必然会需要配置 namespaces,支持配置多个 namespace。如果省略的话,默认为待调度 pod 所属的 namespace;如果定义了但是值为空,则表示使用 “all” namespaces。还有一个较大的差别 TopologyKey, 便于理解进行单独介绍。TopologyKeyTopologyKey 用于定义 in the same place,前面也介绍了是拓扑域的概念。看下面的图,这两个 pod 到底该如何算在一个拓扑域?如果我们使用k8s.io/hostname,in the same place 则意味着在同一个 node,那下图的 pods 就不在一个 place:如果我们使用failure-domain.k8s.io/zone 来表示一个 place,那下图的 pods 就表示在一个 zone:当然我们也可以自定义 node labels 作为 TopologyKey。比如我们可以给一组 node 打上 rack 标签,那下图的 pods 表示在同一个 place:原则上,topologyKey 可以是任何合法的 label key。但是出于性能和安全考虑,topologyKey 存在一些限制:对于亲和性和反亲和性的 requiredDuringSchedulingIgnoredDuringExecution 模式,topologyKey 不能为空pod 反亲和性 requiredDuringSchedulingIgnoredDuringExecution 模式下,LimitPodHardAntiAffinityTopology 权限控制器会限制 topologyKey 只能设置为 kubernetes.io/hostname。当然如果你想要使用自定义 topology,那可以简单禁用即可。pod 反亲和性 preferredDuringSchedulingIgnoredDuringExecution 模式下,topologyKey 为空则表示所有的拓扑域。截止 v1.12 版本,所有的拓扑域还只能是 kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region 的组合。除此之外,topologyKey 可以是任何合法的 label key。示例来个官方示例,有三节点集群,需要分别部署 3 份 web 和 redis 服务。希望 web 与 redis 服务共存,但需要保证各个服务的副本分散部署。先创建 redis 集群:apiVersion: apps/v1kind: Deploymentmetadata: name: redis-cachespec: selector: matchLabels: app: store replicas: 3 template: metadata: labels: app: store spec: affinity: // pod 反亲和性, 打散 redis 各个副本 podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: “kubernetes.io/hostname” containers: - name: redis-server image: redis:3.2-alpine再部署 web 服务,需要打散并且与 redis 服务共存,配置如下:apiVersion: apps/v1kind: Deploymentmetadata: name: web-serverspec: selector: matchLabels: app: web-store replicas: 3 template: metadata: labels: app: web-store spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web-store topologyKey: “kubernetes.io/hostname” podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: “kubernetes.io/hostname” containers: - name: web-app image: nginx:1.12-alpine注意1: pod affinity 需要进行大量处理,所以会明显减慢大型集群的调度时间,不建议在大于几百个节点的集群中使用该功能。注意2: pod antiAffinity 要求对节点进行一致标志,即集群中的所有节点都必须具有适当的标签用于配置给 topologyKey,如果节点缺少指定的 topologyKey 指定的标签,则可能会导致意外行为。六、参考资料官方Assigning Pods to Nodes - Kubernetesnode affinitypod affinity性能优化: Improve performance of affinity/anti-affinity predicate by 20x in large clusters by bsalamatblogK8S 的调度 (一) 抽象优雅的 AffinityScheduling in Kubernetes, Part 2: Pod Affinity – Koki – Medium ...

March 9, 2019 · 3 min · jiezi

Rancher搭建、web应用部署

续:Dockerfile构建PHP开发镜像:Alpine+Nginx+PHP7+Supervisor+Crontab+Laravel一、Rancher搭建1.1 Rancher安装 首先rancher需要安装了docker的linux环境,我的CentOS系统版本为:CentOS Linux release 7.4.1708 (Core) 在docker的基础上启动rancher服务器(传送门:Docker安装),Rancher 服务器是一个 Docker image,所以其软件本身不需要安装,只需要执行 Docker 命令下载并且成功运行 Docker 服务器镜像即可。sudo docker run -d –restart=always -p 8080:8080 rancher/server启动容器并指定端口,如果没有rancher/server镜像会自动下载。1.2 添加主机然后进行添加主机操作,根据网站指引操作,基础架构->保存->生成一条命令,在docker中运行。成功添加主机:二、添加服务2.1 添加基础镜像然后就可以看见在服务中看见刚才添加的服务,并且成功访问:2.2 添加后台管理系统基于基础镜像发布后台管理系统:待续!GitHub地址:https://github.com/tcyfree/anpscDocker Hub镜像地址:https://cloud.docker.com/repo…

March 8, 2019 · 1 min · jiezi

Kubernetes概念与术语

综述学习Kubernetes时,发现它的概念和术语还是比较多的,光靠啃官方文档比较晦涩。所以边学习边整理,对主要的概念和术语做一下分类及简要说明。感觉把重要概念都理解了,对Kubernetes的设计思想,整体框架也就有了初步的认识。从功能上来说,我把Kubernetes的概念或术语大概分为以下三类:组件:指实际在集群中运行的Kubernetes程序集,分为Master组件、Node组件及附加组件。对象:对象是一个抽象的概念实体,Kubernetes把应用、运行场景、功能、集群状态等都抽象成一个个对象,并通过API对这些对象进行管理,从而管理整个集群的状态。标识:对组件和对象的各种表示方式,例如命名、标签、注释等。标识是KubernetesAPI与操作对象间的纽带。组件Master组件Master组件提供了k8s管理集群的核心功能,k8s通过Master组件实现整个集群的调度管理,它就像集群的大脑,会根据集群当前的状态,不断进行调整,使集群一直保持在期望的状态。Master组件包括以下几个进程:kube-apiserverapiserver进程对外暴露REST API接口给外部客户端及内部组件调用,它封装了对各个对象的增、删、改、查操作,可以说是整个集群管理的总入口。kube-schedulerkube-scheduler做的事情说来简单–监控新pod的创建,并把pod分配到合适的node来运行。但实际上,选择“合适的Node”是一个很复杂的过程,需要考虑的因素包括单node及整个集群的资源需求、软硬件资源的限制策略、数据存储、工作负载间的干扰等等。kube-controllers-managercontrollers-manager进程从字面上理解,是用来做控制器管理的。controllers-manager其实是一组守护进程,通过监控集群各组件的状态,并不断调整集群实际状态,使的集群向期望状态调整,它由一组子进程构成:replication controllerendpoints controllernamespace controllerserviceaccounts controllerEtcdEtcd是Kubernetes集群的基础组件,用于保存集群所有的元数据信息、配置信息、对象状态等。Node组件运行Node组件的节点称为Node节点,是k8s集群实际的工作负载节点。Node节点可以是物理机、虚拟机或任何可以运行Node组件的设备。所有的业务应用都运行在Node节点中。Node组件包括以下几个部分:Kubeletkubelet负责实际的容器管理,Kubernetes通过apiserver给kubelet发送Pod管理请求,同时把Pod及Node的运行状态汇报给apiserver。kube-proxykube-proxy负责集群内部的请求转发及负载均衡工作。它通过Service对象的配置信息,为Pod创建代理服务,实现请求从Service到Pod的路由转发。容器运行时实际的容器运行引擎。Kubernetes其实支持其他的容器技术比如rkt,但是Docker相比与它们处于绝对的优势地位。所以,Kubernetes生态中的容器也基本特指指Docker容器。对象Kubernetes中,所谓对象指的就是API对象。Kubernetes集群为每个对象维护三类信息:对象元数据(ObjectMeta)、期望状态(Desired State)与实际状态(Autual State),元数据指对象的基本信息,比如命名、标签、注释等等,用于识别对象;期望状态一般由用户配置spec来描述的;实际状态是由集群各个组件上报的集群实际的运行情况。Kubernetes努力使每个对象的实际状态与期望状态相匹配,从而使整个集群的状态与用户配置的期望状态一致。Kubernetes对象一般用yaml文件来描述,有几个个字段是必须的:apiVersion: 创建该对象所使用的 Kubernetes API 的版本kind: 想要创建的对象的类型metadata: 帮助识别对象唯一性的数据,包括一个 name 字符串、UID 和可选的 namespacespec: 对象期望状态的描述用户通过命令行工具kubectl来操作这些对象,Kubectl吧yaml转换成JSON格式来执行API请求。下面是一个POD对象的描述文件示例及注释:apiVersion: v1kind: Pod # 对象名称metadata: # 对象的基本信息 name: pod-example # 对象唯一标示spec: # 期望状态 containers: # 指定pod中运行的容器镜像及运行参数(类似Dockerfile) - name: ubuntu image: ubuntu:trusty command: [“echo”] args: [“Hello World”]其中,kind字段描述对象类型"Pod",spec之前是对象的ObjectMeta,spec开始,就是对象期望状态的描述。根据官方API对象参考文档的分类,下面我们把对象分为几大类来分别简要说明。计划以后的文章,会结合实际操作,对各个重要对象做更深入的说明。Workloads 资源对象- workloads类的对象用于管理运行容器实例Pod: Pod是Kubernetes里最重要的对象,也是Kubernetes集群中运行部署应用或服务的最小单位。Pod对象描述一个Pod由哪些容器镜像构成,以及这些容器应该怎么运行。类比传统的业务架构,Pod更接近于虚拟机的角色而不是应用进程的角色。ReplicaSet: ReplicaSet对象是对Pod的更高一层抽象,它用来管理一组相同POD副本,并确保这组相同Pod的数量始终与用户期望一致,并实现Pod的高可用。即如果有容器异常退出,ReplicaSet会自动创建新的Pod来替代,保证Pod数量永远与用户配置一致。Deployment: Deployment对象是Kubernetes中最常用的对象之一,用于部署无状态服务。它在ReplicaSet对象的基础上,又做了一层抽象。通过管理多组ReplicaSet对象,来实现POD的滚动升级、回滚、扩容缩容等。StatefulSet: 不同于Deployment,StatefulSet对象顾名思义是对有状态服务的一种抽象。所以,StatefulSet对象在定义时,相比与Deployment多了一些与状态相关的内容,比如持久化存储、服务对外的不变的唯一标示、部署、扩容、滚动升级时确保有序等等。DaemonSet: DaemonSet对象是Kubernetes对守护进程的抽象。当我们需要集群中的每个Node都跑一些如日志收集、监控等守护进程时,就可以把这类型进程包装成Pod,并用调用DaemonSet来部署了。DaemonSet做的事情其实和Deployment差不多,只不过Deployment对象部署的Pod,会根据集群情况,在不同Node间自动调度。而DaemonSet会指定的NODE上(默认是集群所有Node),都部署定义Pod,确保这些NODE都有跑着守护进程Pod。Job、CronJob: 除了上面几个对象所抽象描述的应用工作场景外,实际业务场景中,还有一类应用或服务并不需要一直运行,比如一些一次性任务,或需要定时执行的任务。这种不需要长时间运行的任务,完成了就可以退出的服务,就可以用Job对象来定义。CronJob和Job对象的关系,和Deployment与ReplicaSet的关系很像,可以类比来理解。首先,Job也是直接用于管理Pod的,同时可以定义这个一次性任务Pod的执行时间、超时时间、错误处理(重启还是生成新Pod)等任务属性。而CronJob管理是用来管理Job,类似crobtab,它可以通过yaml文件中的schedule字段配置具体时间,来控制指定Job定时执行,从而实现定时执行特定的一次性任务。Discovery & LB 资源对象- 该类对象用于服务发现和负载均衡的对象Service:上面提到的对象都是用于管理POD及容器实例的。但是,业务系统光有POD实例还不够,还必须对外提供服务。这里就会衍生出两个问题:Pod的IP地址并不是固定,而是随着编排不断变化的,外面的请求怎么找到对应的POD,这个也就是大多数分布式业务都会遇到的服务发现问题多个相同Pod一起提供服务的时候,它们的负载均衡怎么实现而Service对象就是用来解决这两个问题的。它用固定的VIP或DNS名称和指定标识的一组Pod对应起来,不管Pod IP怎么变,Service对外的VIP都不会变化,并且,自动的将请求在这组Pod中负载均衡。Config & Storage 资源对象- 该类对象用于初始化容器配置及持久化容器存储的对象Volume:前面说到,Pod里的不同容器可以共享存储,这个共享存储就靠Volume来配置的。要注意的是,这里Volume与Docker中的定义不用,Kubernetes中Volume跟Pod生命周期一致,Pod终止了,该Pod挂载的Volume就失效了(根据挂载volume的类型不同,数据可能丢失也可能不丢失)。PV,PVC:Volume可以支持多种类型的存储,除了直接在Volume字段中去声明所有存储细节,K8S还抽象出了PV和PVC对象。简单来说PV是对具体存储资源的描述,比如存储类型、地址、访问账户等;而PVC,是对怎么使用资源的方法的描述,比如只读只写,需要的空间等;而Pod通过Volume字段挂载具体要使用的PVC。PV和PVC是独立于Pod单独定义的,这样,就把共享存储的配置、分割、挂载等操作都解耦了。比如,一个NFS迁移后ip地址变了,存储管理员只需要修改PV中的配置,而不用关心具体哪个业务POD在使用这个NFS。ConfigMap,SecuretK8S中除了常见的存储,还有一类特殊的Volume,不是为了Pod存放数据,而是为了给Pod提供数据的。ConfigMap和Secret对象就是用来定义这类型的Volume。简单来说,可以将它们理解为一份Key:Value格式的数据,Pod可以通过Volume挂载它们,将这份数据保存成文件随时调用。唯一的区别是,ConfigMap对象保存的数据是明文,一般作为应用配置文件;而Secret对象保存的对象要求是经过Base64转码的,用于提供数据库密码等对安全要求比较高的配置。不过这些配置,直接在做容器镜像时就配置不就好了,为啥要多此一举呢?原因和上面PV和PVC一样,都是为了尽可能解耦业务核心与经常可能变化的依赖配置。比如数据库更换了账号,只需要修改Secret对象,不用重新去构建容器镜像。Cluster resources objects- 该类对象定义整个K8S集群运行方式的对象NamespaceKubernetes作为一个集群管理平台,为不同用户划分不同权限管理,例如多租户等,是必备的功能。而不同用户作用域的隔离,就是靠Namespace对象实现的。Namespace是Kubernetes项目中的一个逻辑管理单位,不同Namespace的API对象,在调用API进行操作是,是相互隔离开的。通过不同用户角色与Namespace关联,从而实现权限管理。Metadata resources- 除了上述分类外的其他对象都归为这一类,一般用来管理集群的特殊功能比如弹性伸缩等Horizontal Pod Autoscaling容器作为一个轻量级的承载应用的技术,快速启动和快速部署是它的优点之一。自然的,根据业务负载自动扩缩容等需求,在容器集群的可行性和效率就可以变得很高。而Kubernetes中 Horizontal Pod Autoscaling对象,就是用于进行POD自动水平缩放的,这也是Kubernetes最能体现运维优势的特性之一。Horizontal Pod Autoscaling具体的操作对象是Deployment和ReplicaSet,通过不同循环获取每个Pod的资源情况,比如CPU利用率,并根据设定的目标利率来计算目前需要的Pod副本缩放的比例,然后通过访问相应的Deployment或ReplicaSet对象,来对Pod数量进行自动缩放,从而提高了集群资源整体利用率。标识标识是Kubernetes中最重要的概念,因为它是所有API的操作与操作对象间的纽带。Kubernetes中的标识主要有以下几种:LabelLabel可以说是Kubernetes中最最重要的核心概念,一个Lable是一个KEY=Value的键值对。任何对象资源都可以有一个或多个Label,而同个Label,也可以配置在多个对象上。然后重点来了,大多数API对象的yaml字段中,都有Label Selector字段,可以实现对Label中的key或value的多维度选择,例如in、not in、and、or等等。这样,Kubernetes对API作用对象就能进行多维度,细粒度的选择,从而实现比较精细化的容器编排调度工作,比如,对所有"ENV:release"执行扩容操作,或者把部分请求灰度到有"ver:v1.14.0"的pod上等等。Label把资源对象和应用、基础设施都解耦了,你不用关心这个Pod具体在那个Node上运行,也不用关心具体是什么应用用了哪个容器镜像,只要Label符合Label Selector,就可以通过API调用统一进行操作。Names除了Label,所有资源对象肯定还必须有一个全局唯一的标识,即Names字段。AnnotationAnnotation很好理解,与通常意义的注释一样,用于描述作者、版本、配置说明等等任何需要的信息,需要说明的是,Annotation也必须是Key:Value格式的。总结通过上面对Kubernetes重要概念的说明,我们可以大致梳理出Kubernetes的一些设计理念:Kubernetes对容器应用进行了抽象,例如把一个容器或强关联的一组容器抽象为Pod,把各类存储都抽象成Volume,把一组Pod抽象成Service等等基础对象。在基础对象的层面上,Kubernetes又对应用使用场景,做了一层抽象。极客时间的Kubernetes课程有一张图画的很好:可以看到,Kubernetes中各个对象实际就是对生产业务场景的各类需求的抽象。抽象出各类型对象以后,用户可以通过yaml文件(或直接命令行调用API)来描述这些对象的期望状态,确认了各对象些期望状态的集合,也就确认了整个集群的期望状态。这些所有的操作,都是声明式的,而不是命令集群要怎么做Kubernetes通过控制器循环不断将各组件收集来的集群实际状态与各对象的期望状态对比,并自动化的将集群实际状态向期望状态转移,这个过程,也就是Kubernetes最核心的概念:编排。本文对Kubernetes中的重要概念做了分类和简要,后面的文章会结合集群的实际操作,对每个概念做更详细的说明。 ...

March 6, 2019 · 1 min · jiezi

使用OperatorHub.io自动化群集上的操作

作者:Diane Mueller,红帽云平台社区发展总监开发者和Kubernetes管理员面临的重要挑战之一,是缺乏快速查找在Kubernetes提供运营就绪的公共服务的能力。通常情况下,存在特定服务的Operator - 这种模式在2016年推出并获得了动力 - 对于Kubernetes服务的运营就绪是一个很好的信号。但是,迄今为止还没有Operator注册表来简化发现此类服务。为了帮助应对这一挑战,今天Red Hat与AWS、Google Cloud和Microsoft合作推出OperatorHub.io。OperatorHub.io使开发者和Kubernetes管理员能够查找和安装策划好的、Operator支持的服务,其中包括基础文档、社区或供应商的主动维护、基本测试以及Kubernetes优化生命周期管理的打包。目前在OperatorHub.io中的Operator只是开始。我们邀请Kubernetes社区加入我们,通过在OperatorHub.io上开发、打包和发布Operator,为Operator建立一个充满活力的社区。OperatorHub.io提供什么?OperatorHub.io旨在满足Kubernetes开发者和用户的需求。对于前者,它提供了通用的注册表,他们可以在其中发布他们的Operator以及描述、相关的详细信息,如版本、镜像、代码仓库,并打包准备方便安装。他们也可以对已发布的Operator发布更新版本。用户可以在一个中心位置发现和下载Operator,该Operator的内容已根据前面提到的标准进行筛选并扫描已知漏洞。此外,开发者可以使用他们引入的CustomResources的说明性示例,指导其Operator的用户,与应用程序进行交互。Operator是什么?Operator最初由CoreOS于2016年推出,并已被Red Hat和Kubernetes社区用作打包、部署和管理Kubernetes原生应用程序的方法。Kubernetes原生应用程序是一个部署在Kubernetes上的应用程序,使用Kubernetes API和众所周知的工具进行管理,如kubectl。Operator实现为自定义控制器,用于监视某些Kubernetes资源的显示、修改或删除。这些通常是Operator“拥有”的CustomResourceDefinition。在这些对象的spec属性中,用户声明应用程序或操作的所需状态。Operator的协调循环将选择这些,并执行所需的操作以实现所需的状态。例如,可以通过创建EtcdCluster类型的新资源,来表达创建高可用性etcd集群的意图:apiVersion: “etcd.database.coreos.com/v1beta2"kind: “EtcdCluster"metadata: name: “my-etcd-cluster"spec: size: 3 version: “3.3.12"这样,EtcdOperator将负责创建运行版本v3.3.12的3节点etcd集群。类似地,可以定义类型为EtcdBackup的对象,以表示创建etcd数据库一致备份到S3存储桶的意图。如何创建和运行Operator?一种入门方法是使用Operator框架,这是一个开源工具包,提供SDK、生命周期管理、计量和监视功能。它使开发者能够构建、测试和打包Operator。Operator可以用几种编程和自动化语言实现,包括Go、Helm和Ansible,这三种语言都直接由SDK支持。如果你有兴趣创建自己的Operator,我们建议你查看Operator框架以开始使用。Operator的功能范围各不相同,从基本功能到应用程序的特定操作逻辑,以及备份、恢复或调整等高级方案的自动化。除了基本安装之外,高级Operator可以更加无缝地处理升级并自动应对故障。目前,OperatorHub.io上的Operator来自不同成熟度范围,但我们预计它们会随着时间而持续成熟。虽然不需要使用SDK实现OperatorHub.io上的Operator,但它们是打包给通过Operator Lifecycle Manager(OLM)进行部署。格式主要由称为ClusterServiceVersion的YAML清单组成。它提供有关Operator拥有或要求的CustomResourceDefinitions的信息、所需的RBAC定义、存储图像的位置等。此文件通常附带定义Operator自己的CRD的其他YAML文件。OLM在用户请求安装Operator以提供依赖性解析和自动化时处理此信息。OperatorHub.io上的Operator列表是什么意思?要列出,Operator必须成功显示群集生命周期功能,打包为CSV并通过OLM维护,以及为其预期用户提供可接受的文档。目前在OperatorHub.io上列出的Operator的一些示例包括:Amazon Web Services Operator、Couchbase Autonomous Operator、CrunchyData’s PostgreSQL、etcd Operator、Jaeger Operator for Kubernetes、Kubernetes Federation Operator、MongoDB Enterprise Operator、Percona MySQL Operator、PlanetScale’s Vitess Operator、Prometheus Operator和Redis Operator。想要将你的Operator添加到OperatorHub.io?跟着这些步骤如果你有现有的Operator,请遵循贡献指南使用社区Operator仓库的分支。每个贡献包含CSV、所有CustomResourceDefinitions、访问控制规则以及安装和运行Operator所需的容器映像的资料,其功能描述和支持的Kubernetes版本等其他信息。EtcdOperator可以作为完整的示例,包括Operator的多个版本。在你自己的集群上测试Operator之后,将PR提交到社区存储库,其中包含此目录结构的所有YAML文件。可以以相同的方式发布Operator的后续版本。刚开始这将是手动审查,但往后会自动化。由维护者合并之后,它将显示在OperatorHub.io上,以及其文档和方便的安装方法。想了解更多?参加即将举行的Kubernetes Operator框架实践研讨会:3月7日在Pasadena的ScaleX举行,以及3月11日在Santa Clara的OpenShift Commons Gathering on Operating举行听听Daniel Messer和Diane Mueller关于“Operator现况”的OpenShift Commons简报加入社区Kubernetes-Operator Slack Channel和Operator框架Google Group的在线对话最后,阅读如何将你的Operator添加到OperatorHub.io:https://operatorhub.io/contri…KubeCon + CloudNativeCon和Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon和Open Source Summit赞助方案KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon和Open Source Summit购票窗口,立即购票! ...

March 4, 2019 · 1 min · jiezi

容器监控实践—Prometheus的配置与服务发现

本文将分析Prometheus的常见配置与服务发现,分为概述、配置详解、服务发现、常见场景四个部分进行讲解。一. 概述Prometheus的配置可以用命令行参数、或者配置文件,如果是在k8s集群内,一般配置在configmap中(以下均为prometheus2.7版本)查看可用的命令行参数,可以执行 ./prometheus -h也可以指定对应的配置文件,参数:–config.file 一般为prometheus.yml如果配置有修改,如增添采集job,Prometheus可以重新加载它的配置。只需要向其进程发送SIGHUP或向/-/reload端点发送HTTP POST请求。如:curl -X POST http://localhost:9090/-/reload二. 配置详解2.1 命令行参数执行./prometheus -h 可以看到各个参数的含义,例如:–web.listen-address=“0.0.0.0:9090” 监听端口默认为9090,可以修改只允许本机访问,或者为了安全起见,可以改变其端口号(默认的web服务没有鉴权)–web.max-connections=512 默认最大连接数:512–storage.tsdb.path=“data/” 默认的存储路径:data目录下–storage.tsdb.retention.time=15d 默认的数据保留时间:15天。原有的storage.tsdb.retention配置已经被废弃–alertmanager.timeout=10s 把报警发送给alertmanager的超时限制 10s–query.timeout=2m 查询超时时间限制默认为2min,超过自动被kill掉。可以结合grafana的限时配置如60s–query.max-concurrency=20 并发查询数 prometheus的默认采集指标中有一项prometheus_engine_queries_concurrent_max可以拿到最大查询并发数及查询情况–log.level=info 日志打印等级一共四种:[debug, info, warn, error],如果调试属性可以先改为debug等级…..在prometheus的页面上,status的Command-Line Flags中,可以看到当前配置,如promethues-operator的配置是:2.2 prometheus.yml从官方的download页下载的promethues二进制文件,会自带一份默认配置prometheus.yml-rw-r–r–@ LICENSE-rw-r–r–@ NOTICEdrwxr-xr-x@ console_librariesdrwxr-xr-x@ consoles-rwxr-xr-x@ prometheus-rw-r–r–@ prometheus.yml-rwxr-xr-x@ promtoolprometheus.yml配置了很多属性,包括远程存储、报警配置等很多内容,下面将对主要属性进行解释:# 默认的全局配置global: scrape_interval: 15s # 采集间隔15s,默认为1min一次 evaluation_interval: 15s # 计算规则的间隔15s默认为1min一次 scrape_timeout: 10s # 采集超时时间,默认为10s external_labels: # 当和其他外部系统交互时的标签,如远程存储、联邦集群时 prometheus: monitoring/k8s # 如:prometheus-operator的配置 prometheus_replica: prometheus-k8s-1# Alertmanager的配置alerting: alertmanagers: - static_configs: - targets: - 127.0.0.1:9093 # alertmanager的服务地址,如127.0.0.1:9093 alert_relabel_configs: # 在抓取之前对任何目标及其标签进行修改。 - separator: ; regex: prometheus_replica replacement: $1 action: labeldrop # 一旦加载了报警规则文件,将按照evaluation_interval即15s一次进行计算,rule文件可以有多个rule_files: # - “first_rules.yml” # - “second_rules.yml”# scrape_configs为采集配置,包含至少一个jobscrape_configs: # Prometheus的自身监控 将在采集到的时间序列数据上打上标签job=xx - job_name: ‘prometheus’ # 采集指标的默认路径为:/metrics,如 localhost:9090/metric # 协议默认为http static_configs: - targets: [’localhost:9090’]# 远程读,可选配置,如将监控数据远程读写到influxdb的地址,默认为本地读写remote_write: 127.0.0.1:8090# 远程写remote_read: 127.0.0.1:8090 2.3 scrape_configs配置prometheus的配置中,最常用的就是scrape_configs配置,比如添加新的监控项,修改原有监控项的地址频率等。最简单配置为:scrape_configs:- job_name: prometheus metrics_path: /metrics scheme: http static_configs: - targets: - localhost:9090完整配置为(附prometheus-operator的推荐配置):# job 将以标签形式出现在指标数据中,如node-exporter采集的数据,job=node-exporterjob_name: node-exporter# 采集频率:30sscrape_interval: 30s# 采集超时:10sscrape_timeout: 10s# 采集对象的path路径metrics_path: /metrics# 采集协议:http或者httpsscheme: https# 可选的采集url的参数params: name: demo# 当自定义label和采集到的自带label冲突时的处理方式,默认冲突时会重名为exported_xxhonor_labels: false# 当采集对象需要鉴权才能获取时,配置账号密码等信息basic_auth: username: admin password: admin password_file: /etc/pwd# bearer_token或者文件位置(OAuth 2.0鉴权)bearer_token: kferkhjktdgjwkgkrwgbearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token# https的配置,如跳过认证,或配置证书文件tls_config: # insecure_skip_verify: true ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt server_name: kubernetes insecure_skip_verify: false# 代理地址proxy_url: 127.9.9.0:9999# Azure的服务发现配置azure_sd_configs:# Consul的服务发现配置consul_sd_configs: # DNS的服务发现配置dns_sd_configs:# EC2的服务发现配置ec2_sd_configs:# OpenStack的服务发现配置openstack_sd_configs:# file的服务发现配置file_sd_configs:# GCE的服务发现配置gce_sd_configs:# Marathon的服务发现配置marathon_sd_configs:# AirBnB的服务发现配置nerve_sd_configs:# Zookeeper的服务发现配置serverset_sd_configs:# Triton的服务发现配置triton_sd_configs:# Kubernetes的服务发现配置kubernetes_sd_configs: - role: endpoints namespaces: names: - monitoring# 对采集对象进行一些静态配置,如打特定的标签static_configs: - targets: [’localhost:9090’, ’localhost:9191’] labels: my: label your: label # 在Prometheus采集数据之前,通过Target实例的Metadata信息,动态重新写入Label的值。如将原始的__meta_kubernetes_namespace直接写成namespace,简洁明了relabel_configs: - source_labels: [__meta_kubernetes_namespace] separator: ; regex: (.) target_label: namespace replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_name] separator: ; regex: (.) target_label: service replacement: $1 action: replace - source_labels: [_meta_kubernetes_pod_name] separator: ; regex: (.) target_label: pod replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_name] separator: ; regex: (.) target_label: job replacement: ${1} action: replace - separator: ; regex: (.*) target_label: endpoint replacement: web action: replace# 指标relabel的配置,如丢掉某些无用的指标metric_relabel_configs: - source_labels: [name] separator: ; regex: etcd(debugging|disk|request|server).* replacement: $1 action: drop # 限制最大采集样本数,超过了采集将会失败,默认为0不限制sample_limit: 0三. 服务发现上边的配置文件中,有很多*sd_configs的配置,如kubernetes_sd_configs,就是用于服务发现的采集配置。支持的服务发现类型:// prometheus/discovery/config/config.gotype ServiceDiscoveryConfig struct { StaticConfigs []*targetgroup.Group yaml:"static_configs,omitempty" DNSSDConfigs []*dns.SDConfig yaml:"dns_sd_configs,omitempty" FileSDConfigs []*file.SDConfig yaml:"file_sd_configs,omitempty" ConsulSDConfigs []*consul.SDConfig yaml:"consul_sd_configs,omitempty" ServersetSDConfigs []*zookeeper.ServersetSDConfig yaml:"serverset_sd_configs,omitempty" NerveSDConfigs []*zookeeper.NerveSDConfig yaml:"nerve_sd_configs,omitempty" MarathonSDConfigs []*marathon.SDConfig yaml:"marathon_sd_configs,omitempty" KubernetesSDConfigs []kubernetes.SDConfig yaml:"kubernetes_sd_configs,omitempty" GCESDConfigs []gce.SDConfig yaml:"gce_sd_configs,omitempty" EC2SDConfigs []ec2.SDConfig yaml:"ec2_sd_configs,omitempty" OpenstackSDConfigs []openstack.SDConfig yaml:"openstack_sd_configs,omitempty" AzureSDConfigs []azure.SDConfig yaml:"azure_sd_configs,omitempty" TritonSDConfigs []triton.SDConfig yaml:"triton_sd_configs,omitempty"}因为prometheus采用的是pull方式来拉取监控数据,这种方式需要由server侧决定采集的目标有哪些,即配置在scrape_configs中的各种job,pull方式的主要缺点就是无法动态感知新服务的加入,因此大多数监控都默认支持服务发现机制,自动发现集群中的新端点,并加入到配置中。Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮询这些Target获取监控数据。对于kubernetes而言,Promethues通过与Kubernetes API交互,然后轮询资源端点。目前主要支持5种服务发现模式,分别是:Node、Service、Pod、Endpoints、Ingress。对应配置文件中的role: node/role:service如:动态获取所有节点node的信息,可以添加如下配置:- job_name: kubernetes-nodes scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: node namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true relabel_configs: - separator: ; regex: _meta_kubernetes_node_label(.+) replacement: $1 action: labelmap - separator: ; regex: (.) target_label: address replacement: kubernetes.default.svc:443 action: replace - source_labels: [__meta_kubernetes_node_name] separator: ; regex: (.+) target_label: metrics_path replacement: /api/v1/nodes/${1}/proxy/metrics action: replace就可以在target中看到具体内容对应的service、pod也是同样的方式。需要注意的是,为了能够让Prometheus能够访问收到Kubernetes API,我们要对Prometheus进行访问授权,即serviceaccount。否则就算配置了,也没有权限获取。prometheus的权限配置是一组ClusterRole+ClusterRoleBinding+ServiceAccount,然后在deployment或statefulset中指定serviceaccount。ClusterRole.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: namespace: kube-system name: prometheusrules:- apiGroups: [""] resources: - configmaps - secrets - nodes - pods - nodes/proxy - services - resourcequotas - replicationcontrollers - limitranges - persistentvolumeclaims - persistentvolumes - namespaces - endpoints verbs: [“get”, “list”, “watch”]- apiGroups: [“extensions”] resources: - daemonsets - deployments - replicasets - ingresses verbs: [“get”, “list”, “watch”]- apiGroups: [“apps”] resources: - daemonsets - deployments - replicasets - statefulsets verbs: [“get”, “list”, “watch”]- apiGroups: [“batch”] resources: - cronjobs - jobs verbs: [“get”, “list”, “watch”]- apiGroups: [“autoscaling”] resources: - horizontalpodautoscalers verbs: [“get”, “list”, “watch”]- apiGroups: [“policy”] resources: - poddisruptionbudgets verbs: [“get”, list", “watch”]- nonResourceURLs: ["/metrics"] verbs: [“get”]ClusterRoleBinding.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: namespace: kube-system name: prometheusroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheussubjects:- kind: ServiceAccount name: prometheus namespace: kube-systemServiceAccount.yamlapiVersion: v1kind: ServiceAccountmetadata: namespace: kube-system name: prometheusprometheus.yaml….spec: serviceAccountName: prometheus….完整的kubernete的配置如下:- job_name: kubernetes-apiservers scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: endpoints namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true relabel_configs: - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] separator: ; regex: default;kubernetes;https replacement: $1 action: keep- job_name: kubernetes-nodes scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: node namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true relabel_configs: - separator: ; regex: _meta_kubernetes_node_label(.+) replacement: $1 action: labelmap - separator: ; regex: (.) target_label: address replacement: kubernetes.default.svc:443 action: replace - source_labels: [__meta_kubernetes_node_name] separator: ; regex: (.+) target_label: metrics_path replacement: /api/v1/nodes/${1}/proxy/metrics action: replace- job_name: kubernetes-cadvisor scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: node namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: false relabel_configs: - separator: ; regex: _meta_kubernetes_node_label(.+) replacement: $1 action: labelmap - separator: ; regex: (.) target_label: address replacement: kubernetes.default.svc:443 action: replace - source_labels: [__meta_kubernetes_node_name] separator: ; regex: (.+) target_label: metrics_path replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor action: replace- job_name: kubernetes-service-endpoints scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: http kubernetes_sd_configs: - api_server: null role: endpoints namespaces: names: [] relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] separator: ; regex: “true” replacement: $1 action: keep - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] separator: ; regex: (https?) target_label: scheme replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] separator: ; regex: (.+) target_label: metrics_path replacement: $1 action: replace - source_labels: [address, __meta_kubernetes_service_annotation_prometheus_io_port] separator: ; regex: ([^:]+)(?::\d+)?;(\d+) target_label: address replacement: $1:$2 action: replace - separator: ; regex: _meta_kubernetes_service_label(.+) replacement: $1 action: labelmap - source_labels: [__meta_kubernetes_namespace] separator: ; regex: (.) target_label: kubernetes_namespace replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_name] separator: ; regex: (.) target_label: kubernetes_name replacement: $1 action: replace配置成功后,对应的target是:四. 常见场景1.获取集群中各节点信息,并按可用区或地域分类如使用k8s的role:node采集集群中node的数据,可以通过"meta_domain_beta_kubernetes_io_zone"标签来获取到该节点的地域,该label为集群创建时为node打上的标记,kubectl decribe node可以看到。然后可以通过relabel_configs定义新的值relabel_configs:- source_labels: [“meta_domain_beta_kubernetes_io_zone”] regex: “(.)” replacement: $1 action: replace target_label: “zone"后面可以直接通过node{zone=“XX”}来进行地域筛选2.过滤信息,或者按照职能(RD、运维)进行监控管理对于不同职能(开发、测试、运维)的人员可能只关心其中一部分的监控数据,他们可能各自部署的自己的Prometheus Server用于监控自己关心的指标数据,不必要的数据需要过滤掉,以免浪费资源,可以最类似配置;metric_relabel_configs: - source_labels: [name] separator: ; regex: etcd(debugging|disk|request|server).* replacement: $1 action: dropaction: drop代表丢弃掉符合条件的指标,不进行采集。3.搭建prometheus联邦集群,管理各IDC(地域)监控实例如果存在多个地域,每个地域又有很多节点或者集群,可以采用默认的联邦集群部署,每个地域部署自己的prometheus server实例,采集自己地域的数据。然后由统一的server采集所有地域数据,进行统一展示,并按照地域归类配置:scrape_configs: - job_name: ‘federate’ scrape_interval: 15s honor_labels: true metrics_path: ‘/federate’ params: ‘match[]’: - ‘{job=“prometheus”}’ - ‘{name=“job:.*”}’ - ‘{name=“node.*”}’ static_configs: - targets: - ‘192.168.77.11:9090’ - ‘192.168.77.12:9090’本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 4 min · jiezi

容器监控实践—PromQL查询解析

一. 概述Prometheus除了存储数据外,还提供了一种强大的功能表达式语言 PromQL,允许用户实时选择和汇聚时间序列数据。表达式的结果可以在浏览器中显示为图形,也可以显示为表格数据,或者由外部系统通过 HTTP API 调用。通过PromQL用户可以非常方便地查询监控数据,或者利用表达式进行告警配置如:k8s中的node在线率:sum(kube_node_status_condition{condition=“Ready”, status=“true”}) / sum(kube_node_info) 100 Metric类型关于时间序列存储,可以参考:https://www.infoq.cn/article/…Prometheus会将所有采集到的样本数据以时间序列(time-series)的方式保存在内存数据库TSDB中,并且定时保存到硬盘上。time-series是按照时间戳和值的序列顺序存放的,我们称之为向量(vector)。每条time-series通过指标名称(metrics name)和一组标签集(labelset)命名。在time-series中的每一个点称为一个样本(sample),样本由以下三部分组成:指标(metric):metric name和描述当前样本特征的labelsets;时间戳(timestamp):一个精确到毫秒的时间戳;样本值(value): 一个folat64的浮点型数据表示当前样本的值。如某一时刻的node_cpu指标为459.71node_cpu{app=“node-exporter”,cpu=“cpu0”,instance=“192.168.0.4:9100”,job=“kubernetes-service-endpoints”,kubernetes_name=“node-exporter”,kubernetes_namespace=“kube-system”,mode=“guest”} 459.71Prometheus定义了4中不同的指标类型(metric type):Counter 计数器计数器,只增不减,如http_requests_total请求总数例如,通过rate()函数获取HTTP请求量的增长率:rate(http_requests_total[5m])Gauge 仪表盘当前状态,可增可减。如kube_pod_status_ready当前pod可用数可以获取样本在一段时间返回内的变化情况,如:delta(kube_pod_status_ready[2h])Histogram 直方图Histogram 由 <basename>_bucket{le="<upper inclusive bound>"},<basename>_bucket{le="+Inf"}, <basename>_sum,<basename>_count 组成,主要用于表示一段时间范围内对数据进行采样(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted, 表示 Prometheus 中每个时序需要存储的 chunks 数量,我们可以用它计算待持久化的数据的分位数。Summary 摘要Summary 和 Histogram 类似,由 <basename>{quantile="<>"},<basename>_sum,<basename>_count 组成,主要用于表示一段时间内数据采样结果(通常是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。例如 Prometheus server 中 prometheus_target_interval_length_seconds。Histogram 需要通过 <basename>_bucket 计算 quantile, 而 Summary 直接存储了 quantile 的值。基础查询PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。如http_requests_total指标你可以通过附加一组标签,并用{}括起来,来进一步筛选这些时间序列。下面这个例子只选择有http_requests_total名称的、有prometheus工作标签的、有canary组标签的时间序列:http_requests_total{job=“prometheus”,group=“canary”}如果条件为空,可以写为:http_requests_total{}另外,也可以也可以将标签值反向匹配,或者对正则表达式匹配标签值。如操作符: =:选择正好相等的字符串标签 !=:选择不相等的字符串标签 =~:选择匹配正则表达式的标签(或子标签) !=:选择不匹配正则表达式的标签(或子标签) 范围查询类似http_requests_total{job=“prometheus”,group=“canary”}的方式,得到的是瞬时值,如果想得到一定范围内的值,可以使用范围查询时间范围通过时间范围选择器[]进行定义。例如,通过以下表达式可以选择最近5分钟内的所有样本数据,如:http_request_total{}[5m]除了分钟,支持的单位有:s - 秒m - 分钟h - 小时d - 天w - 周y - 年偏移查询如:查询http_requests_total在当前时刻的一周的速率:rate(http_requests_total{} offset 1w)偏移修饰符允许更改查询中单个即时向量和范围向量的时间偏移量,例如,以下表达式返回相对于当前查询时间5分钟前的http_requests_total值:http_requests_total offset 5m等价于http_requests_total{job=“prometheus”}[5m]请注意,偏移量修饰符始终需要跟随选择器,即以下是正确的:sum(http_requests_total{method=“GET”} offset 5m) // GOOD.下面是错误的:sum(http_requests_total{method=“GET”}) offset 5m // INVALID.操作符Prometheus 的查询语言支持基本的逻辑运算和算术运算二元算术运算:加法减法乘法/ 除法% 模^ 幂等运算中用到的基础数据类型:瞬时向量(Instant vector) - 一组时间序列,每个时间序列包含单个样本,它们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式。区间向量(Range vector) - 一组时间序列,每个时间序列包含一段时间范围内的样本数据。标量(Scalar) - 一个浮点型的数据值。字符串(String) - 一个简单的字符串值。二元运算操作符支持 scalar/scalar(标量/标量)、vector/scalar(向量/标量)、和 vector/vector(向量/向量) 之间的操作。在两个标量之间进行数学运算,得到的结果也是标量。例如,如果我们想根据 node_disk_bytes_written 和 node_disk_bytes_read 获取主机磁盘IO的总量,可以使用如下表达式:node_disk_bytes_written + node_disk_bytes_read或者node的内存数GBnode_memory_free_bytes_total / (1024 * 1024)布尔运算== (相等)!= (不相等)(大于)< (小于)= (大于等于)<= (小于等于)如:获取http_requests_total请求总数是否超过10000,返回0和1,1则报警http_requests_total > 10000 # 结果为 true 或 falsehttp_requests_total > bool 10000 # 结果为 1 或 0集合运算and (并且)or (或者)unless (排除)优先级四则运算有优先级,promql的复杂运算也有优先级例如,查询主机的CPU使用率,可以使用表达式:100 * (1 - avg (irate(node_cpu{mode=‘idle’}[5m])) by(job) )其中irate是PromQL中的内置函数,用于计算区间向量中时间序列每秒的即时增长率在PromQL操作符中优先级由高到低依次为:^, /, %+, -==, !=, <=, <, >=, >and, unlessor匹配模式(联合查询)与数据库中的join类似,promsql有两种典型的匹配查询:一对一(one-to-one)多对一(many-to-one)或一对多(one-to-many)例如当存在样本:method_code:http_errors:rate5m{method=“get”, code=“500”} 24method_code:http_errors:rate5m{method=“get”, code=“404”} 30method_code:http_errors:rate5m{method=“put”, code=“501”} 3method_code:http_errors:rate5m{method=“post”, code=“500”} 6method_code:http_errors:rate5m{method=“post”, code=“404”} 21method:http_requests:rate5m{method=“get”} 600method:http_requests:rate5m{method=“del”} 34method:http_requests:rate5m{method=“post”} 120使用 PromQL 表达式:method_code:http_errors:rate5m{code=“500”} / ignoring(code) method:http_requests:rate5m该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在所有请求中的比例。如果没有使用 ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。因此结果如下:{method=“get”} 0.04 // 24 / 600{method=“post”} 0.05 // 6 / 120同时由于 method 为 put 和 del 的样本找不到匹配项,因此不会出现在结果当中。多对一模式例如,使用表达式:method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m该表达式中,左向量 method_code:http_errors:rate5m 包含两个标签 method 和 code。而右向量 method:http_requests:rate5m 中只包含一个标签 method,因此匹配时需要使用 ignoring 限定匹配的标签为 code。 在限定匹配标签后,右向量中的元素可能匹配到多个左向量中的元素 因此该表达式的匹配模式为多对一,需要使用 group 修饰符 group_left 指定左向量具有更好的基数。最终的运算结果如下:{method=“get”, code=“500”} 0.04 // 24 / 600{method=“get”, code=“404”} 0.05 // 30 / 600{method=“post”, code=“500”} 0.05 // 6 / 120{method=“post”, code=“404”} 0.175 // 21 / 120提醒:group 修饰符只能在比较和数学运算符中使用。在逻辑运算 and,unless 和 or 操作中默认与右向量中的所有元素进行匹配。聚合操作Prometheus 还提供了下列内置的聚合操作符,这些操作符作用域瞬时向量。可以将瞬时表达式返回的样本数据进行聚合,形成一个具有较少样本值的新的时间序列。sum (求和)min (最小值)max (最大值)avg (平均值)stddev (标准差)stdvar (标准差异)count (计数)count_values (对 value 进行计数)bottomk (样本值最小的 k 个元素)topk (样本值最大的k个元素)quantile (分布统计)这些操作符被用于聚合所有标签维度,或者通过 without 或者 by 子语句来保留不同的维度。without 用于从计算结果中移除列举的标签,而保留其它标签。by 则正好相反,结果向量中只保留列出的标签,其余标签则移除。通过 without 和 by 可以按照样本的问题对数据进行聚合。例如:如果指标 http_requests_total 的时间序列的标签集为 application, instance, 和 group,我们可以通过以下方式计算所有 instance 中每个 application 和 group 的请求总量:sum(http_requests_total) without (instance)等价于 sum(http_requests_total) by (application, group)如果只需要计算整个应用的 HTTP 请求总量,可以直接使用表达式:sum(http_requests_total)count_values 用于时间序列中每一个样本值出现的次数。count_values 会为每一个唯一的样本值输出一个时间序列,并且每一个时间序列包含一个额外的标签。这个标签的名字由聚合参数指定,同时这个标签值是唯一的样本值。例如要计算运行每个构建版本的二进制文件的数量:count_values(“version”, build_version)返回结果如下:{count=“641”} 1{count=“3226”} 2{count=“644”} 4topk 和 bottomk 则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。获取 HTTP 请求数前 5 位的时序样本数据,可以使用表达式:topk(5, http_requests_total)quantile 用于计算当前样本数据值的分布情况 quantile(, express) ,其中 0 ≤ ≤ 1例如,当 为 0.5 时,即表示找到当前样本数据中的中位数:quantile(0.5, http_requests_total)返回结果如下:{} 656内置函数Prometheus 提供了其它大量的内置函数,可以对时序数据进行丰富的处理。如上文提到的irate100 * (1 - avg (irate(node_cpu{mode=‘idle’}[5m])) by(job) )常用的有:两分钟内的平均CPU使用率: rate(node_cpu[2m])和irate(node_cpu[2m])需要注意的是使用rate或者increase函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。为了解决该问题,PromQL提供了另外一个灵敏度更高的函数irate(v range-vector)。irate同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个两本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。irate函数相比于rate函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate的这种灵敏度反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用rate函数。完整的函数列表为:abs()absent()ceil()changes()clamp_max()clamp_min()day_of_month()day_of_week()days_in_month()delta()deriv()exp()floor()histogram_quantile()holt_winters()hour()idelta()increase()irate()label_join()label_replace()ln()log2()log10()minute()month()predict_linear()rate()resets()round()scalar()sort()sort_desc()sqrt()time()timestamp()vector()year()<aggregation>_over_time()API访问Prometheus当前稳定的HTTP API可以通过/api/v1访问错误状态码:404 Bad Request:当参数错误或者缺失时。422 Unprocessable Entity 当表达式无法执行时。503 Service Unavailiable 当请求超时或者被中断时。所有的API请求均使用以下的JSON格式:{ “status”: “success” | “error”, “data”: <data>, // 为error时,有如下报错信息 “errorType”: “<string>”, “error”: “<string>"}通过HTTP API我们可以分别通过/api/v1/query和/api/v1/query_range查询PromQL表达式当前或者一定时间范围内的计算结果。瞬时数据查询URL请求参数:query=:PromQL表达式。time=:用于指定用于计算PromQL的时间戳。可选参数,默认情况下使用当前系统时间。timeout=:超时设置。可选参数,默认情况下使用-query,timeout的全局设置。$ curl ‘http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z’返回:{ “status” : “success”, “data” : { “resultType” : “vector”, “result” : [ { “metric” : { “name” : “up”, “job” : “prometheus”, “instance” : “localhost:9090” }, “value”: [ 1435781451.781, “1” ] }, { “metric” : { “name” : “up”, “job” : “node”, “instance” : “localhost:9100” }, “value” : [ 1435781451.781, “0” ] } ] }}区间查询URL请求参数:query=: PromQL表达式。start=: 起始时间。end=: 结束时间。step=: 查询步长。timeout=: 超时设置。可选参数,默认情况下使用-query,timeout的全局设置。$ curl ‘http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s’返回:{ “status” : “success”, “data” : { “resultType” : “matrix”, “result” : [ { “metric” : { “name” : “up”, “job” : “prometheus”, “instance” : “localhost:9090” }, “values” : [ [ 1435781430.781, “1” ], [ 1435781445.781, “1” ], [ 1435781460.781, “1” ] ] }, { “metric” : { “name” : “up”, “job” : “node”, “instance” : “localhost:9091” }, “values” : [ [ 1435781430.781, “0” ], [ 1435781445.781, “0” ], [ 1435781460.781, “1” ] ] } ] }}本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 3 min · jiezi

容器监控实践—Prometheus基本架构

系统架构图1.x版本的Prometheus的架构图为:目前Prometheus版本为2.7,架构图为:Prometheus从exporter拉取数据,或者间接地通过网关gateway拉取数据(如果在k8s内部署,可以使用服务发现的方式),它默认本地存储抓取的所有数据,并通过一定规则进行清理和整理数据,并把得到的结果存储到新的时间序列中,采集到的数据有两个去向,一个是报警,另一个是可视化。PromQL和其他API可视化地展示收集的数据,并通过Alertmanager提供报警能力。组件内容Prometheus Server负责从 Exporter 拉取和存储监控数据,并提供一套灵活的查询语言(PromQL)Retrieval: 采样模块TSDB: 存储模块默认本地存储为tsdbHTTP Server: 提供http接口查询和面板,默认端口为9090Exporters/Jobs负责收集目标对象(host, container…)的性能数据,并通过 HTTP 接口供 Prometheus Server 获取。支持数据库、硬件、消息中间件、存储系统、http服务器、jmx等。只要符合接口格式,就可以被采集。Short-lived jobs瞬时任务的场景,无法通过pull方式拉取,需要使用push方式,与PushGateway搭配使用PushGateway可选组件,主要用于短期的 jobs。由于这类 jobs 存在时间较短,可能在 Prometheus 来 pull 之前就消失了。为此,这次 jobs 可以直接向 Prometheus server 端推送它们的 metrics。这种方式主要用于服务层面的 metrics,对于机器层面的 metrices,需要使用 node exporter。客户端sdk官方提供的客户端类库有go、java、scala、python、ruby,其他还有很多第三方开发的类库,支持nodejs、php、erlang等PromDash使用rails开发的dashboard,用于可视化指标数据,已废弃Alertmanager从 Prometheus server 端接收到 alerts 后,会进行去除重复数据,分组,并路由到对收的接受方式,发出报警。常见的接收方式有:电子邮件,pagerduty,OpsGenie, webhook 等。Service Discovery服务发现,Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮训这些Target获取监控数据。其大概的工作流程是:Prometheus server 定期从配置好的 jobs 或者 exporters 中拉 metrics,或者接收来自 Pushgateway 发过来的 metrics,或者从其他的 Prometheus server 中拉 metrics。Prometheus server 在本地存储收集到的 metrics,并运行已定义好的 alert.rules,记录新的时间序列或者向 Alertmanager 推送警报。Alertmanager 根据配置文件,对接收到的警报进行处理,发出告警。在图形界面中,可视化采集数据。关于Push与PullPrometheus采集数据是用的pull也就是拉模型,通过HTTP协议去采集指标,只要应用系统能够提供HTTP接口就可以接入监控系统,相比于私有协议或二进制协议来说开发、简单。优点主要是:开发任何新功能,你甚至可以在电脑上查看你的监控如果目标实例挂掉,你可以很快知道你可以手动指定目标实例,并且在浏览器中查看他的健康状态总体来说,Pull模式比Push模式更好一些,在监控系统中这也不是一个很重要的点。如果要使用push的方式,可以使用Pushgateway的方式,如定时任务的采集。对于定时任务这种短周期的指标采集,如果采用pull模式,可能造成任务结束了,Prometheus还没有来得及采集,这个时候可以使用加一个中转层,客户端推数据到Push Gateway缓存一下,由Prometheus从push gateway pull指标过来。(需要额外搭建Push Gateway,同时需要新增job去从gateway采数据)推的代表有 ElasticSearch,InfluxDB,OpenTSDB 等,需要你从程序中将指标使用 TCP,UDP 等方式推送至相关监控应用,只是使用 TCP 的话,一旦监控应用挂掉或存在瓶颈,容易对应用本身产生影响,而使用 UDP 的话,虽然不用担心监控应用,但是容易丢数据。拉的代表,主要代表就是 Prometheus,让我们不用担心监控应用本身的状态。而且,可以利用 DNS-SRV 或者 Consul 等服务发现功能就可以自动添加监控。当然,InfluxDB 加上 collector,或者 ES 加上 metricbeat 也可以变为 『拉』,而 Prometheus 加上 Push Gateway 也可以变为 『推』。更多区别可以参考下图:存储机制Prometheus有着非常高效的时间序列数据存储方法,每个采样数据仅仅占用3.5byte左右空间,上百万条时间序列,30秒间隔,保留60天,大概花了200多G(引用官方PPT)。Prometheus内部主要分为三大块,Retrieval是负责定时去暴露的目标页面上去抓取采样指标数据,Storage是负责将采样数据写磁盘,PromQL是Prometheus提供的查询语言模块。Prometheus内置了一个基于本地存储的时间序列数据库。在Prometheus设计上,使用本地存储可以降低Prometheus部署和管理的复杂度同时减少高可用(HA)带来的复杂性。 在默认情况下,用户只需要部署多套Prometheus,采集相同的Targets即可实现基本的HA。同时由于Promethus高效的数据处理能力,单个Prometheus Server基本上能够应对大部分用户监控规模的需求。同时为了适应数据持久化的问题,Prometheus提供了remote_write和remote_read的特性,支持将数据存储到远端和从远端读取数据。通过将监控与数据分离,Prometheus能够更好地进行弹性扩展。关于存储用量规划:https://www.jianshu.com/p/934…更多:Prometheus存储机制详解https://yunlzheng.gitbook.io/…https://www.cnblogs.com/vovli...https://www.linuxidc.com/Linu...https://segmentfault.com/a/11...https://www.infoq.cn/article/…关于日志处理不建议将日志监控放在Prometheus中,这不是他的专长,还是使用ELK或EFK的方式处理日志信息竞品对比参考: https://toutiao.io/posts/fsjq…未来规划服务端度量指标元数据支持在度量指标类型和其他元数据仅仅在客户库和展示格式中使用,并不会在Prometheus服务中持久保留或者利用。将来我们计划充分利用这些元数据。第一步是在Prometheus服务的内存中聚合这些数据,并开放一些实验性的API来提供服务支持OpenMetricsOpenMetrics组开放了一个新的监控指标暴露标准,我们将支持这种标准:https://openmetrics.io/回溯时间序列允许将过去一段时间的数据发送到其他的监控系统HTTP服务支持TLS安全认证当前的Prometheus, Alertmanager和一些官方exporter,暴露服务时,都不支持tls认证,有很大的安全风险,现在的实现都是基于反向代理,之后将内置到组件中支持子查询当前的Promq不支持子查询,如max_over_time() of a rate()),后续将会支持支持生态建设Prometheus有很多的client库和exporter,我们将会对其进行规范和生态建设。在K8S中使用在之前的版本中,k8s默认以及推荐的监控体系是它自己的一套东西:Heapster + cAdvisor + Influxdb + Grafana,1.8后Heaspter由Metric-server替代。如果你部署了Dashboard,就能看到监控数据(来自heapster)k8s 自身的 HPA (Horizontal Pod Autoscaler),默认从 Heapster 中获取数据进行自动伸缩1.8版本以后,K8S希望将核心监控指标收拢到metric api的形式,而自定义监控指标,由prometheus来实现,prometheus正式成为k8s推荐的监控实现方案。参考文档:https://www.ibm.com/developer…https://prometheus.io/docs/in...https://www.kancloud.cn/cdh08...https://yunlzheng.gitbook.io/…本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 1 min · jiezi

容器监控实践—Prometheus部署方案

一.单独部署二进制安装各版本下载地址:https://prometheus.io/download/Docker运行运行命令:docker run –name prometheus -d -p 127.0.0.1:9090:9090 prom/prometheus暴露服务: http://localhost:9090/二.在K8S中部署如果在Kubernetes中部署Prometheus,可以使用prometheus in kubernetes,含exporter、grafana等组件。安装方式:kubectl apply \ –filename https://raw.githubusercontent.com/giantswarm/kubernetes-prometheus/master/manifests-all.yaml卸载方式:kubectl delete namespace monitoring该方式为大多数用户和云厂商使用的方式,可以基于Prometheus的服务发现:在annotation中设置prometheus.io/scrape为true,就可以把K8S的所有服务都加入到监控中,但在使用的过程中会有一些问题:1.如果增加了新的exporter,如nginx-exporter,需要修改prometheus配置并重启2.服务本身和监控配置没有分离3.监控集群多实例的状态不好管理4.报警配置也包含在prometheus的配置中,监控与报警没有分离,添加规则麻烦以上问题一般的处理方式为:在prometheus上加一个控制台,来动态配置target、报警规则,并向后端server发起修改、重启操作。同时有权限控制、日志审计、整体配置过期时间等功能。但如果使用了Prometheus Operator,就可以将以上大多数操作抽象为k8s中的资源提交、修改,减少上层封装的工作量。三.Prometheus Operator部署Prometheus-Operator是一套为了方便整合prometheus和kubernetes的开源方案,使用Prometheus-Operator可以非常简单的在kubernetes集群中部署Prometheus服务,用户能够使用简单的声明性配置来配置和管理Prometheus实例,这些配置将响应、创建、配置和管理Prometheus监控实例。官方地址:https://github.com/coreos/pro…目前状态:beta状态,还不够完整,但向后兼容。将成为趋势前置条件:要求k8s的版本>=1.8.0(应该是因为metric api和CRD支持的限制)Operator的核心思想是将Prometheus的部署与它监控的对象的配置分离,做到部署与监控对象的配置分离之后,就可以轻松实现动态配置。使用Operator部署了Prometheus之后就可以不用再管Prometheus Server了,以后如果要添加监控对象或者添加告警规则,只需要编写对应的ServiceMonitor和Prometheus资源就可以,不用再重启Prometheus服务,Operator会动态的观察配置的改动,并将其生成为对应的prometheus配置文件其中Operator可以部署、管理Prometheus Service四种CRD作用如下:Prometheus: 由 Operator 依据一个自定义资源kind: Prometheus类型中,所描述的内容而部署的 Prometheus Server 集群,可以将这个自定义资源看作是一种特别用来管理Prometheus Server的StatefulSets资源。ServiceMonitor: 一个Kubernetes自定义资源(和kind: Prometheus一样是CRD),该资源描述了Prometheus Server的Target列表,Operator 会监听这个资源的变化来动态的更新Prometheus Server的Scrape targets并让prometheus server去reload配置(prometheus有对应reload的http接口/-/reload)。而该资源主要通过Selector来依据 Labels 选取对应的Service的endpoints,并让 Prometheus Server 通过 Service 进行拉取(拉)指标资料(也就是metrics信息),metrics信息要在http的url输出符合metrics格式的信息,ServiceMonitor也可以定义目标的metrics的url。Alertmanager:Prometheus Operator 不只是提供 Prometheus Server 管理与部署,也包含了 AlertManager,并且一样通过一个 kind: Alertmanager 自定义资源来描述信息,再由 Operator 依据描述内容部署 Alertmanager 集群。PrometheusRule:对于Prometheus而言,在原生的管理方式上,我们需要手动创建Prometheus的告警文件,并且通过在Prometheus配置中声明式的加载。而在Prometheus Operator模式中,告警规则也编程一个通过Kubernetes API 声明式创建的一个资源.告警规则创建成功后,通过在Prometheus中使用想servicemonitor那样用ruleSelector通过label匹配选择需要关联的PrometheusRule即可。安装方式:创建命名空间:monitoring执行yaml文件:https://github.com/coreos/pro…prometheus的target列表: grafana的自带监控图列表:常见问题:因为要operator中要支持聚合api,在某些版本的集群上可能需要一些配置,如下:安装cfssl证书生成工具:http://www.cnblogs.com/xuling…生成证书cfssl gencert -ca=/etc/kubernetes/pki/ca.pem -ca-key=/etc/kubernetes/pki/ca-key.pem -config=/etc/kubernetes/pki/ca-config.json -profile=jpaas metrics-server-csr.json | cfssljson -bare metrics-server{ “CN”: “aggregator”, “host”: [], “key”: { “algo”: “rsa”, “size”: 2048 }, “names”: [ { “C”: “CN”, “ST”: “BeiJing”, “L”: “BeiJing”, “O”: “k8s”, “OU”: “cloudnative” } ]}配置master组件参数,以支持metric-servervim /etc/systemd/system/kube-apiserver.service–requestheader-client-ca-file=/etc/kubernetes/pki/ca.pem --requestheader-allowed-names=“aggregator” --requestheader-extra-headers-prefix=“X-Remote-Extra-” --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --proxy-client-cert-file=/etc/kubernetes/pki/metrics-server.pem --proxy-client-key-file=/etc/kubernetes/pki/metrics-server-key.pem --runtime-config=api/all=true --enable-aggregator-routing=true \systemctl daemon-reloadsystemctl restart kube-apiserver.servicesystemctl status kube-apiserver.servicevim /etc/systemd/system/kube-controller.service–horizontal-pod-autoscaler-use-rest-clients=truesystemctl daemon-reloadsystemctl restart kube-controller.servicesystemctl status kube-controller.service启动成功后,prometheus的target中,kubelet没有值,401报错vim /etc/systemd/system/kubelet.service–authentication-token-webhook=true–authorization-mode=Webhooksystemctl daemon-reloadsystemctl restart kubelet.service参考文档:https://github.com/coreos/pro…https://www.yinjk.cn/2018/09/...http://www.servicemesher.com/…本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 1 min · jiezi

容器监控实践—Metrics Server

概述从 v1.8 开始,资源使用情况的监控可以通过 Metrics API的形式获取,具体的组件为Metrics Server,用来替换之前的heapster,heapster从1.11开始逐渐被废弃。Metrics-Server是集群核心监控数据的聚合器,从 Kubernetes1.8 开始,它作为一个 Deployment对象默认部署在由kube-up.sh脚本创建的集群中,如果是其他部署方式需要单独安装,或者咨询对应的云厂商。Metrics API介绍Metrics-Server之前,必须要提一下Metrics API的概念Metrics API相比于之前的监控采集方式(hepaster)是一种新的思路,官方希望核心指标的监控应该是稳定的,版本可控的,且可以直接被用户访问(例如通过使用 kubectl top 命令),或由集群中的控制器使用(如HPA),和其他的Kubernetes APIs一样。官方废弃heapster项目,就是为了将核心资源监控作为一等公民对待,即像pod、service那样直接通过api-server或者client直接访问,不再是安装一个hepater来汇聚且由heapster单独管理。假设每个pod和node我们收集10个指标,从k8s的1.6开始,支持5000节点,每个节点30个pod,假设采集粒度为1分钟一次,则:10 x 5000 x 30 / 60 = 25000 平均每分钟2万多个采集指标因为k8s的api-server将所有的数据持久化到了etcd中,显然k8s本身不能处理这种频率的采集,而且这种监控数据变化快且都是临时数据,因此需要有一个组件单独处理他们,k8s版本只存放部分在内存中,于是metric-server的概念诞生了。其实hepaster已经有暴露了api,但是用户和Kubernetes的其他组件必须通过master proxy的方式才能访问到,且heapster的接口不像api-server一样,有完整的鉴权以及client集成。这个api现在还在alpha阶段(18年8月),希望能到GA阶段。类api-server风格的写法:generic apiserver有了Metrics Server组件,也采集到了该有的数据,也暴露了api,但因为api要统一,如何将请求到api-server的/apis/metrics请求转发给Metrics Server呢,解决方案就是:kube-aggregator,在k8s的1.7中已经完成,之前Metrics Server一直没有面世,就是耽误在了kube-aggregator这一步。kube-aggregator(聚合api)主要提供:Provide an API for registering API servers.Summarize discovery information from all the servers.Proxy client requests to individual servers.详细设计文档:参考链接metric api的使用:Metrics API 只可以查询当前的度量数据,并不保存历史数据Metrics API URI 为 /apis/metrics.k8s.io/,在 k8s.io/metrics 维护必须部署 metrics-server 才能使用该 API,metrics-server 通过调用 Kubelet Summary API 获取数据如:http://127.0.0.1:8001/apis/metrics.k8s.io/v1beta1/nodeshttp://127.0.0.1:8001/apis/metrics.k8s.io/v1beta1/nodes/<node-name>http://127.0.0.1:8001/apis/metrics.k8s.io/v1beta1/namespace/<namespace-name>/pods/<pod-name>Metrics-ServerMetrics server定时从Kubelet的Summary API(类似/ap1/v1/nodes/nodename/stats/summary)采集指标信息,这些聚合过的数据将存储在内存中,且以metric-api的形式暴露出去。Metrics server复用了api-server的库来实现自己的功能,比如鉴权、版本等,为了实现将数据存放在内存中吗,去掉了默认的etcd存储,引入了内存存储(即实现Storage interface)。因为存放在内存中,因此监控数据是没有持久化的,可以通过第三方存储来拓展,这个和heapster是一致的。Metrics server出现后,新的Kubernetes 监控架构将变成上图的样子核心流程(黑色部分):这是 Kubernetes正常工作所需要的核心度量,从 Kubelet、cAdvisor 等获取度量数据,再由metrics-server提供给 Dashboard、HPA 控制器等使用。监控流程(蓝色部分):基于核心度量构建的监控流程,比如 Prometheus 可以从 metrics-server 获取核心度量,从其他数据源(如 Node Exporter 等)获取非核心度量,再基于它们构建监控告警系统。官方地址:https://github.com/kubernetes…使用如上文提到的,metric-server是扩展的apiserver,依赖于kube-aggregator,因此需要在apiserver中开启相关参数。–requestheader-client-ca-file=/etc/kubernetes/certs/proxy-ca.crt–proxy-client-cert-file=/etc/kubernetes/certs/proxy.crt–proxy-client-key-file=/etc/kubernetes/certs/proxy.key–requestheader-allowed-names=aggregator–requestheader-extra-headers-prefix=X-Remote-Extra—requestheader-group-headers=X-Remote-Group–requestheader-username-headers=X-Remote-User安装文件下载地址:1.8+,注意更换镜像地址为国内镜像kubectl create -f metric-server/安装成功后,访问地址api地址为:Metrics Server的资源占用量会随着集群中的Pod数量的不断增长而不断上升,因此需要addon-resizer垂直扩缩这个容器。addon-resizer依据集群中节点的数量线性地扩展Metrics Server,以保证其能够有能力提供完整的metrics API服务。具体参考:链接其他基于Metrics Server的HPA:参考链接kubernetes的新监控体系中,metrics-server属于Core metrics(核心指标),提供API metrics.k8s.io,仅提供Node和Pod的CPU和内存使用情况。而其他Custom Metrics(自定义指标)由Prometheus等组件来完成,后续文章将对自定义指标进行解析。本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

January 13, 2019 · 1 min · jiezi