我使用kube-prometheus在EKS上建立了一个监控环境

這篇文章是 Amazon EKS Advent Calendar 2019 的第23天的文章。

我在OPENREC.tv担任SRE团队的一员。在项目中使用kube-prometheus在EKS上构建了监控环境,因此我总结了搭建方法。

kube-promethus是什么?

kube-prometheus是由CoreOS开发的项目,它可以帮助您在Kubernetes集群中使用Prometheus进行监控,并提供了一套用于必要组件的清单文件,以便您能够更轻松地准备。
使用kube-prometheus,您可以创建以下组件的清单文件。

    • Prometheus Operator

 

    • 冗長構成のPrometheus

 

    • 冗長構成のAleertmanager

 

    • node-exporter

 

    • Prometheus Adaptor

 

    • kube-state-metrics

 

    Grafana

除了上述的内容之外,我们还提供了Kubernetes集群监控所需的Grafana仪表板和Prometheus警报设置。这些Grafana仪表板和Prometheus警报的很多配置都是使用kubernetes-mixin项目中的内容进行构建的。

スクリーンショット 2019-12-20 15.56.13.png
スクリーンショット 2019-12-20 15.57.02.png

使用kube-prometheus可以省去创建Kubernetes集群所需的Grafana仪表板和Prometheus警报规则的麻烦,这也是kube-prometheus的一大魅力所在。

kube-prometheus的自定义项目配置,用于EKS。

通过创建自定义项目并扩展kube-prometheus的jsonnet来进行操作,以便向EKS添加所需的Kubernetes资源等,使其能够运行。

这个定制项目创建的监控环境大致结构如下。

monitoring.png

Alertmanager和Prometheus以3个副本的冗余配置进行部署。
Prometheus收集的指标数据存储在Amazon EBS中。由于EBS无法跨越可用区访问,因此在使用多区域可用区配置时,必须至少为Prometheus提供与可用区数量相等的副本数,或者如果使用少于可用区数量的副本数来运行Prometheus,则需要注意通过Affinity等方式指定Prometheus所在的可用区。

在这个自定义项目中,ALB Ingress Controller负责创建用于Grafana、Prometheus和Alertmanager的ALB,而ExternalDNS负责创建每个ALB的DNS A记录。前提是ALB Ingress Controller和ExternalDNS在同一个Kubernetes集群中运行。

文件组织结构

定制项目的文件结构如下所示。

.
├── base.libsonnet
├── build.sh
├── dev.jsonnet
├── stg.jsonnet
├── prd.jsonnet
├── jsonnetfile.json
├── jsonnetfile.lock.json
├── custom-dashboards
│   └── cluster-autoscaler-stats.json
├── vendor
│   ├── 省略(kube-prometheusと依存するプロジェクトのjsonnetファイル群)
└── manifests
    ├── dev
    │   ├── 省略(dev環境のマニフェストファイル)
    ├── stg
    │   ├── 省略(stg環境のマニフェストファイル)
    └── prd
        ├── 省略(prd環境のマニフェストファイル)

jsonnetfile.json和jsonnetfile.lock.json是在下面所示的自定义项目初始化过程中生成的文件。它们由jsonnet-bundler软件包管理器管理。

$ jb init
$ jb install github.com/coreos/kube-prometheus/jsonnet/kube-prometheus@release-0.3

要更新依赖于jsonnet项目的版本,请执行以下命令。

$ jb update

接下来,我会依次介绍自定义项目中的文件。

首先,kube-prometheus的版本被指定为0.3。kube-prometheus的版本在jsonnetfile.json文件中指定(release-0.3指定了版本)。
顺便说一下,我们之前一直使用kube-prometheus的0.1版本来进行运营,但是升级到了0.3版本后,可以生成用于监视CoreDNS的Service的清单文件,并且Grafana的仪表盘也增加了许多,让人感到非常激动。

{
    "dependencies": [
        {
            "name": "kube-prometheus",
            "source": {
                "git": {
                    "remote": "https://github.com/coreos/kube-prometheus",
                    "subdir": "jsonnet/kube-prometheus"
                }
            },
            "version": "release-0.3"
        }
    ]
}

附加说明:关于kube-prometheus的依赖项目的包管理

kube-prometheus项目使用jsonnet进行配置,同时依赖多个github项目中的jsonnet文件,通过组合这些jsonnet文件来构建清单文件。jsonnet的包管理器使用jsonnet-bundler,并通过执行上述定制项目的初始化步骤中的jb install命令,将kube-prometheus及其依赖项目的jsonnet文件集下载到vendor目录下。

自定义项目的jsonnet文件结构

为了扩展kube-prometheus的清单文件,我们创建了一个名为base.libsonnet的文件,并添加了所需的元素。

base.libsonnet
local k = import ‘ksonnet/ksonnet.beta.3/k.libsonnet’;
local secret = k核心.v1.私密;
local ingress = k扩展.v1beta1.进入;
local ingressRule = ingress.mixin.spec.rulesType;
local httpIngressPath = ingressRule.mixin.http.pathsType;
local limitRange = k核心.v1限制范围;
local limitRangeItem = limitRange.mixin.spec.limitsType;
local pvc = k核心.v1持久卷索赔;
local sc = k存储.v1.存储类;(import ‘kube-prometheus/kube-prometheus.libsonnet’) +
(import ‘kube-prometheus/kube-prometheus-eks.libsonnet’) +
(import ‘kube-prometheus/kube-prometheus-node-ports.libsonnet’) +
(import ‘kube-prometheus/kube-prometheus-managed-cluster.libsonnet’) +
{
配置:: {
env: ”,
namespace: ‘monitoring’,
urlProtocol: ”,
domain: ”,
subnets: ”,
grafana: {
安全组: ”,
认证: {
管理员密码: ”,
客户端ID: ”,
客户端密钥: ”
}
},
alertmanager: {
安全组: ”,
启发: {
严重: {
频道: ”,
api网址: ”
},
警告: {
频道: ”,
api网址: ”
},
看门狗: {
频道: ”,
api网址: ”
}
},
},
prometheus: {
安全组: ”,
存储: {
保留时间: ”,
大小: ”,
回收策略: ‘Delete’,
允许卷扩展: true
},
},
域前缀: if $.config.env == ‘prd’ then ” else $.config.env + ‘-‘,
cognito: ”,
},
} + {
_config +:: {
jobs: {
Kubelet: $._config.kubelet选择器,
KubeAPI: $._config.kubeApi服务器

以下是dev环境用的dev.jsonnet文件。由于安全组和子网等信息在每个环境中可能不同,因此我们将这些配置值的变量准备在base.libsonnet文件的config元素中。我们通过将带有环境标识符的jsonnet文件(如dev.jsonnet)导入base.libsonnet,并通过覆盖config元素的变量来反映每个环境的配置值。

((import "base.libsonnet") +
{
  config+:: {
    env: 'dev',
    urlProtocol: 'https',
    domain: 'hoge.com',
    subnets: 'subnet-xxxx,subnet-yyyy,subnet-zzzz',
    acmArn: 'arn:aws:acm:ap-northeast-1:xxxx:certificate/xxxx',
    cognito: '{"UserPoolArn":"arn:aws:cognito-idp:ap-northeast-1:xxxx:userpool/ap-northeast-1_xxxx","UserPoolClientId":"xxxx","UserPoolDomain":"developer"}',
    grafana: {
      securityGroup: 'sg-xxxx',
      auth: {
        adminPassword: 'hoge',
        clientId: 'xxxx',
        clientSecret: 'yyyy'
      }
    },
    alertmanager+: {
      securityGroup: 'sg-yyyy',
      slack: {
        critical: {
          channel: '#prom-alert-dev',
          api_url: 'https://hooks.slack.com/services/xxxx/yyyy/zzzz'
        },
        warning: {
          channel: '#prom-alert-dev',
          api_url: 'https://hooks.slack.com/services/xxxx/yyyy/zzzz'
        },
        watchdog: {
          channel: '#prom-alert-watchdog',
          api_url: 'https://hooks.slack.com/services/xxxx/yyyy/zzzz'
        }
      },
    },
    prometheus+: {
      securityGroup: 'sg-zzzz',
      storage+: {
        retention: '30d',
        size: '30Gi',
      },
    },
  },
}).build()

如何输出清单文件

我们准备了一个脚本build.sh来输出清单文件。

#!/bin/sh

set -exo pipefail

environment=$1
target_dir=manifests/${environment}
temp_dir=temp

rm -rf ${temp_dir}
mkdir -p ${temp_dir}

jsonnet -J vendor -m ${temp_dir} "${environment}.jsonnet" | xargs -I{} sh -c 'cat {} | gojsontoyaml > {}.yaml; rm -f {}' -- {}

rm -rf ${target_dir}
mkdir -p ${target_dir}
mv -f ${temp_dir}/*.yaml ${target_dir}/

通过在build.sh的参数中指定环境标识符(dev|stg|prd)来执行build.sh,从而将名为环境标识符的目录(例如manifests/dev)下的manifests目录中的文件输出为一个清单文件。

为了在EKS上运行,所添加的元素

我将解释如何通过扩展kube-prometheus的清单文件来在EKS环境中运行它所需的添加组件。

导入 kube-prometheus-eks.libsonnet

自 kube-prometheus 的 0.3 版本开始,新增了 kube-prometheus-eks.libsonnet。通过导入该文件,将输出访问 CNI 插件的指标终端点所需的 Service 清单文件。

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: aws-node
  name: aws-node
  namespace: kube-system
spec:
  clusterIP: None
  ports:
  - name: cni-metrics-port
    port: 61678
    targetPort: 61678
  selector:
    k8s-app: aws-node

另外,还新增了检测IP地址枯竭的警报规则。

alertrule-cni.png

参考:CNI监控EKS特定配置的更新

ALB Ingress Controller为NodePort和Ingress的定义

就像这篇文章的开头所提到的那样,我们可以通过Application Load Balancer(ALB)访问Grafana、Prometheus和Alertmanager。而且这些ALB是通过ALB Ingress Controller创建的。因此,我们需要配置清单文件,以便ALB Ingress Controller可以创建ALB。

首先,为了将Grafana、Prometheus和Alertmanager的Service类型更改为NodePort,在base.libsonnet中导入’kube-prometheus/kube-prometheus-node-ports.libsonnet’。

(import 'kube-prometheus/kube-prometheus-node-ports.libsonnet') +

只需要一个候选项:
通过这一操作,Service的spec.type将被更改为NodePort,并且PortnodePort也将被定义在manifest文件中,作为NodePort Service的入口。

apiVersion: v1
kind: Service
metadata:
  labels:
    prometheus: k8s
  name: prometheus-k8s
  namespace: monitoring
spec:
  ports:
  - name: web
    nodePort: 30900
    port: 9090
    targetPort: web
  selector:
    app: prometheus
    prometheus: k8s
  sessionAffinity: ClientIP
  type: NodePort

接下来,将为Grafana、Prometheus和Alertmanager添加Ingress资源的定义到base.libsonnet文件中。

为了定义Ingress资源,可以使用jsonnet来定义Kubernetes资源的ksonnet。在base.libsonnet的开头定义如下,并在ksonnet中定义必需的变量以定义Ingress资源。

local k = import 'ksonnet/ksonnet.beta.3/k.libsonnet';

local ingress = k.extensions.v1beta1.ingress;
local ingressRule = ingress.mixin.spec.rulesType;
local httpIngressPath = ingressRule.mixin.http.pathsType;

下一步是定义 Ingress 资源。下面是从 base.libsonnet 文件中提取出来的用于 Alertmanager 的 Ingress 定义。

  ingress+:: {
    'alertmanager-main':
      ingress.new() +
      ingress.mixin.metadata.withName('alertmanager-main') +
      ingress.mixin.metadata.withNamespace($.config.namespace) +
      ingress.mixin.metadata.withAnnotations({
        'alb.ingress.kubernetes.io/auth-idp-cognito': $.config.cognito,
        'alb.ingress.kubernetes.io/auth-on-unauthenticated-request': 'authenticate',
        'alb.ingress.kubernetes.io/auth-scope': 'openid',
        'alb.ingress.kubernetes.io/auth-session-cookie': 'AWSELBAuthSessionCookie',
        'alb.ingress.kubernetes.io/auth-session-timeout': '604800',
        'alb.ingress.kubernetes.io/auth-type': 'cognito',
        'alb.ingress.kubernetes.io/healthcheck-path': '/-/healthy',
        'alb.ingress.kubernetes.io/scheme': 'internet-facing',
        'alb.ingress.kubernetes.io/security-groups': $.config.alertmanager.securityGroup,
        'alb.ingress.kubernetes.io/subnets': $.config.subnets,
        'alb.ingress.kubernetes.io/tags': 'env=%s,app=alertmanager-main' % $.config.env,
        'alb.ingress.kubernetes.io/target-group-attributes': 'stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=60',
        'alb.ingress.kubernetes.io/certificate-arn': $.config.acmArn,
        'kubernetes.io/ingress.class': 'alb',
      }) +
      ingress.mixin.spec.withRules(
        ingressRule.new() +
        ingressRule.withHost('%salertmanager.%s' % [$.config.domainPrefix, $.config.domain]) +
        ingressRule.mixin.http.withPaths(
          httpIngressPath.new() +
          httpIngressPath.mixin.backend.withServiceName('alertmanager-main') +
          httpIngressPath.mixin.backend.withServicePort('web')
        ),
      ),

由于我们想要使用Cognito进行身份验证,因此标注的定义变得冗长。
我们将各个环境中存在差异的信息吸收到先前提到的config字段中的变量中,并进行引用。

最终,在base.libsonnet的build()函数内增加一个输出Ingress使用的清单文件的定义。

{
  build()::
    { ['00namespace-' + name]: $.kubePrometheus[name] for name in std.objectFields($.kubePrometheus) } +
    { ['0prometheus-operator-' + name]: $.prometheusOperator[name] for name in std.objectFields($.prometheusOperator) } +
    { ['node-exporter-' + name]: $.nodeExporter[name] for name in std.objectFields($.nodeExporter) } +
    { ['kube-state-metrics-' + name]: $.kubeStateMetrics[name] for name in std.objectFields($.kubeStateMetrics) } +
    { ['alertmanager-' + name]: $.alertmanager[name] for name in std.objectFields($.alertmanager) } +
    { ['prometheus-' + name]: $.prometheus[name] for name in std.objectFields($.prometheus) } +
    { ['prometheus-adapter-' + name]: $.prometheusAdapter[name] for name in std.objectFields($.prometheusAdapter) } +
    { ['grafana-' + name]: $.grafana[name] for name in std.objectFields($.grafana) } +
+    { ['ingress-' + name]: $.ingress[name] for name in std.objectFields($.ingress) } +
    { ['storage-class-' + name]: $.storageClass[name] for name in std.objectFields($.storageClass) } +
    { ['common-' + name]: $.common[name] for name in std.objectFields($.common) }
}

将会输出名为 ingress-alertmanager-main.yaml、ingress-grafana.yaml 和 ingress-prometheus-k8s.yaml 的清单文件。

定义用于EBS的StorageClass和volumeClaimTemplate。

正如前面所述,Prometheus收集的指标数据被保存在EBS上。Prometheus的Pod是使用StatefulSet来部署的,我们会添加定义,以确保Prometheus的Pod可以访问EBS。

首先,我们需要添加 StorageClass 的定义。

  storageClass+:: {
    prometheus:
      sc.new() +
      sc.withProvisioner('kubernetes.io/aws-ebs') +
      sc.mixin.metadata.withName('prometheus') +
      sc.withParameters({
        'type': 'gp2'
      }) +
      sc.withReclaimPolicy($.config.prometheus.storage.reclaimPolicy) +
      sc.withAllowVolumeExpansion($.config.prometheus.storage.allowVolumeExpansion),
  },

添加以下定义,以便能够输出上述定义的StorageClass的清单文件。

{
  build()::
    { ['00namespace-' + name]: $.kubePrometheus[name] for name in std.objectFields($.kubePrometheus) } +
    { ['0prometheus-operator-' + name]: $.prometheusOperator[name] for name in std.objectFields($.prometheusOperator) } +
    { ['node-exporter-' + name]: $.nodeExporter[name] for name in std.objectFields($.nodeExporter) } +
    { ['kube-state-metrics-' + name]: $.kubeStateMetrics[name] for name in std.objectFields($.kubeStateMetrics) } +
    { ['alertmanager-' + name]: $.alertmanager[name] for name in std.objectFields($.alertmanager) } +
    { ['prometheus-' + name]: $.prometheus[name] for name in std.objectFields($.prometheus) } +
    { ['prometheus-adapter-' + name]: $.prometheusAdapter[name] for name in std.objectFields($.prometheusAdapter) } +
    { ['grafana-' + name]: $.grafana[name] for name in std.objectFields($.grafana) } +
    { ['ingress-' + name]: $.ingress[name] for name in std.objectFields($.ingress) } +
+    { ['storage-class-' + name]: $.storageClass[name] for name in std.objectFields($.storageClass) } +
    { ['common-' + name]: $.common[name] for name in std.objectFields($.common) }
}

根据以上的定义,将会生成下面的StorageClass资源清单文件。

allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: prometheus
parameters:
  type: gp2
provisioner: kubernetes.io/aws-ebs
reclaimPolicy: Delete

最后添加 Prometheus CRD 中的 spec.storage.volumeClaimTemplate。

  prometheus+:: {
    prometheus+: {
      spec+: {
        externalUrl: '%s://%sprometheus.%s' % [$.config.urlProtocol, $.config.domainPrefix, $.config.domain],
        retention: $.config.prometheus.storage.retention,
        storage: {
          volumeClaimTemplate:
            pvc.new() +
            pvc.mixin.spec.withAccessModes('ReadWriteOnce') +
            pvc.mixin.spec.resources.withRequests({ storage: $.config.prometheus.storage.size }) +
            pvc.mixin.spec.withStorageClassName('prometheus'),
        },
      },
    },

使用上述定义,将在输出的Prometheus资源清单文件中添加spec.storage.volumeClaimTemplate。

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  labels:
    prometheus: k8s
  name: k8s
  namespace: monitoring
spec:
  alerting:
    alertmanagers:
    - name: alertmanager-main
      namespace: monitoring
      port: web
  baseImage: quay.io/prometheus/prometheus
  externalUrl: https://dev-hoge.com
  nodeSelector:
    beta.kubernetes.io/os: linux
  replicas: 3
  resources:
    requests:
      memory: 400Mi
  retention: 30d
  ruleSelector:
    matchLabels:
      prometheus: k8s
      role: alert-rules
  securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  serviceAccountName: prometheus-k8s
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  storage:
    volumeClaimTemplate:
      apiVersion: v1
      kind: PersistentVolumeClaim
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 30Gi
        storageClassName: prometheus
  version: v2.7.2

现在,Prometheus可以将数据保存到EBS上了。

其他关于base.libsonnet的定义

虽然与在EKS上运行相关性不大,但在这里我们将给出关于base.libsonnet文件中其他定义的解释作为补充。
首先,我们要讲的是位于base.libsonnet顶层的_config字段。
_config字段也被定义在通过前面提到的jb命令下载的kube-prometheus及其依赖的jsonnet文件中,并且在各个地方引用_config字段的值来生成清单文件。因此,通过覆盖_config字段下的值,我们可以修改生成的清单文件的内容。

  _config+:: {
    jobs: {
      Kubelet: $._config.kubeletSelector,
      KubeAPI: $._config.kubeApiserverSelector,
      KubeStateMetrics: $._config.kubeStateMetricsSelector,
      NodeExporter: $._config.nodeExporterSelector,
      Alertmanager: $._config.alertmanagerSelector,
      Prometheus: $._config.prometheusSelector,
      PrometheusOperator: $._config.prometheusOperatorSelector,
      CoreDNS: $._config.coreDNSSelector,
    },
    namespace: $.config.namespace,
    grafana+:: {
      plugins+: ['grafana-piechart-panel'],
      config+: {
        sections+: {
          server+: {
            root_url: '%s://%sgrafana.%s' % [$.config.urlProtocol, $.config.domainPrefix, $.config.domain],
          },
          security: {
            admin_password: $.config.grafana.auth.adminPassword
          },
          'auth.github': {
            enabled: true,
            allow_sign_up: true,
            client_id: $.config.grafana.auth.clientId,
            client_secret: $.config.grafana.auth.clientSecret,
            scopes: 'user:email,read:org',
            auth_url: 'https://github.com/login/oauth/authorize',
            token_url: 'https://github.com/login/oauth/access_token',
            api_url: 'https://api.github.com/user',
            team_ids: '',
            allowed_organizations: 'hoge'
          }
        },
      },
    },
    alertmanager+:: {
      config: {
        global: {
          resolve_timeout: '5m',
          slack_api_url: $.config.alertmanager.slack.critical.api_url
        },
        route: {
          group_wait: '10s',
          group_interval: '5m',
          repeat_interval: '30m',
          receiver: 'slack-critical',
          routes: [
            {
              receiver: 'silent-receiver',
              match: {
                alertname: 'KubeCPUOvercommit'
              }
            },
            {
              receiver: 'silent-receiver',
              match: {
                alertname: 'CPUThrottlingHigh'
              }
            },
            {
              receiver: 'silent-receiver',
              match: {
                alertname: 'KubeVersionMismatch'
              }
            },
            {
              receiver: 'slack-critical',
              group_wait: '10s',
              match: {
                severity: 'error',
              },
              continue: true
            },
            {
              receiver: 'slack-warning',
              group_wait: '10s',
              match: {
                severity: 'warning',
              },
              continue: true
            },
            {
              receiver: 'slack-watchdog',
              group_wait: '10s',
              match: {
                alertname: 'Watchdog',
              },
              continue: true
            },
          ],
        },
        receivers: [
          {
            name: 'slack-critical',
            slack_configs: [
              {
                api_url: $.config.alertmanager.slack.critical.api_url,
                channel: $.config.alertmanager.slack.critical.channel,
                title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification',
                text: '{{ range .Alerts }}\n  *Description:* {{ .Annotations.message }}\n  *Graph:* <{{ .GeneratorURL }}|:chart_with_upwards_trend:> *Runbook:* <{{ .Annotations.runbook_url }}|:spiral_note_pad:>\n  *Details:*\n  {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`\n  {{ end }}\n{{ end }}',
                send_resolved: true
              }
            ]
          },
          {
            name: 'slack-warning',
            slack_configs: [
              {
                api_url: $.config.alertmanager.slack.warning.api_url,
                channel: $.config.alertmanager.slack.warning.channel,
                title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification',
                text: '{{ range .Alerts }}\n  *Description:* {{ .Annotations.message }}\n  *Graph:* <{{ .GeneratorURL }}|:chart_with_upwards_trend:> *Runbook:* <{{ .Annotations.runbook_url }}|:spiral_note_pad:>\n  *Details:*\n  {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`\n  {{ end }}\n{{ end }}',
                send_resolved: true
              }
            ]
          },
          {
            name: 'slack-watchdog',
            slack_configs: [
              {
                api_url: $.config.alertmanager.slack.watchdog.api_url,
                channel: $.config.alertmanager.slack.watchdog.channel,
                title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification',
                text: '{{ range .Alerts }}\n  *Description:* {{ .Annotations.message }}\n  *Graph:* <{{ .GeneratorURL }}|:chart_with_upwards_trend:> *Runbook:* <{{ .Annotations.runbook_url }}|:spiral_note_pad:>\n  *Details:*\n  {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`\n  {{ end }}\n{{ end }}',
                send_resolved: true
              }
            ]
          },
          {
            name: 'silent-receiver'
          }
        ],
      },
    },
    prometheus+:: {
      replicas: 3,
    }
  },

我会逐一解释上述_config文件中的每个设置选项。

下述字段是向Prometheus规则清单文件prometheus-rules.yaml添加用于检测服务是否已停止的警报规则。

    jobs: {
      Kubelet: $._config.kubeletSelector,
      KubeAPI: $._config.kubeApiserverSelector,
      KubeStateMetrics: $._config.kubeStateMetricsSelector,
      NodeExporter: $._config.nodeExporterSelector,
      Alertmanager: $._config.alertmanagerSelector,
      Prometheus: $._config.prometheusSelector,
      PrometheusOperator: $._config.prometheusOperatorSelector,
      CoreDNS: $._config.coreDNSSelector,
    },

如果将Kubelet的字段定义如上所示,将在prometheus-rules.yaml中添加以下定义。


    - alert: KubeletDown
      annotations:
        message: Kubelet has disappeared from Prometheus target discovery.
        runbook_url: https://github.com/kubernetes-monitoring/kubernetes-mixin/tree/master/runbook.md#alert-name-kubeletdown
      expr: |
        absent(up{job="kubelet"} == 1)
      for: 15m
      labels:
        severity: critical

只需提供一个选项:
此外,您还可以在作业中添加以下定义,以添加kube-scheduler和kube-controller-manager的警报规则。但是,在EKS集群中无法将这些组件设置为Prometheus监视的目标(如果有可行的方法,请见谅),因此未添加此设置。

      KubeScheduler: $._config.kubeSchedulerSelector,
      KubeControllerManager: $._config.kubeControllerManagerSelector,

我们将查看_config后面的字段。
在namespace字段中,定义目标命名空间。将创建指定命名空间名称的命名空间资源清单文件。

    namespace: $.config.namespace,

接下来,我们将看一下 _config 中的 Grafana 字段。
Grafana.config 字段的值将定义为 grafana.ini 这个 Grafana 配置文件的内容。

    grafana+:: {
      plugins+: ['grafana-piechart-panel'],
      config+: {
        sections+: {
          server+: {
            root_url: '%s://%sgrafana.%s' % [$.config.urlProtocol, $.config.domainPrefix, $.config.domain],
          },
          security: {
            admin_password: $.config.grafana.auth.adminPassword
          },
          'auth.github': {
            enabled: true,
            allow_sign_up: true,
            client_id: $.config.grafana.auth.clientId,
            client_secret: $.config.grafana.auth.clientSecret,
            scopes: 'user:email,read:org',
            auth_url: 'https://github.com/login/oauth/authorize',
            token_url: 'https://github.com/login/oauth/access_token',
            api_url: 'https://api.github.com/user',
            team_ids: '',
            allowed_organizations: 'hoge'
          }
        },
      },
    },

如果按照上述定义,将生成下述内容的grafana.ini。实际上,它会以Base64编码的形式在Secret资源中定义,并以grafana-config.yaml作为文件名。

[auth.github]
allow_sign_up = true
allowed_organizations = hoge
api_url = https://api.github.com/user
auth_url = https://github.com/login/oauth/authorize
client_id = hoge
client_secret = fuga
enabled = true
scopes = user:email,read:org
team_ids =
token_url = https://github.com/login/oauth/access_token
[security]
admin_password = xxxxx
[server]
root_url = https://dev-grafana.hoge.com

不详细说明grafana.ini的设置内容,但通过[auth.github]部分进行了Grafana的Github登录设置,并通过[security]部分的admin_password设置了Grafana的管理员密码。

然后我们来看一下_config字段的alertmanager。
alertmanager.config字段的值定义了Alertmanager配置文件的内容。
配置文件的内容经过Base64编码,并在名为alertmanager-secret.yaml的Secret资源中进行定义。

    alertmanager+:: {
      config: {
        global: {
          resolve_timeout: '5m',
          slack_api_url: $.config.alertmanager.slack.critical.api_url
        },
        route: {
          group_wait: '10s',
          group_interval: '5m',
          repeat_interval: '30m',
          receiver: 'slack-critical',
          routes: [
            {
              receiver: 'slack-critical',
              group_wait: '10s',
              match: {
                severity: 'error',
              },
              continue: true
            },
            {
              receiver: 'slack-warning',
              group_wait: '10s',
              match: {
                severity: 'warning',
              },
              continue: true
            },
            {
              receiver: 'slack-watchdog',
              group_wait: '10s',
              match: {
                alertname: 'Watchdog',
              },
              continue: true
            },
          ],
        },
        receivers: [
          {
            name: 'slack-critical',
            slack_configs: [
              {
                api_url: $.config.alertmanager.slack.critical.api_url,
                channel: $.config.alertmanager.slack.critical.channel,
                title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification',
                text: '{{ range .Alerts }}\n  *Description:* {{ .Annotations.message }}\n  *Graph:* <{{ .GeneratorURL }}|:chart_with_upwards_trend:> *Runbook:* <{{ .Annotations.runbook_url }}|:spiral_note_pad:>\n  *Details:*\n  {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`\n  {{ end }}\n{{ end }}',
                send_resolved: true
              }
            ]
          },
          {
            name: 'slack-warning',
            slack_configs: [
              {
                api_url: $.config.alertmanager.slack.warning.api_url,
                channel: $.config.alertmanager.slack.warning.channel,
                title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification',
                text: '{{ range .Alerts }}\n  *Description:* {{ .Annotations.message }}\n  *Graph:* <{{ .GeneratorURL }}|:chart_with_upwards_trend:> *Runbook:* <{{ .Annotations.runbook_url }}|:spiral_note_pad:>\n  *Details:*\n  {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`\n  {{ end }}\n{{ end }}',
                send_resolved: true
              }
            ]
          },
          {
            name: 'slack-watchdog',
            slack_configs: [
              {
                api_url: $.config.alertmanager.slack.watchdog.api_url,
                channel: $.config.alertmanager.slack.watchdog.channel,
                title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] Monitoring Event Notification',
                text: '{{ range .Alerts }}\n  *Description:* {{ .Annotations.message }}\n  *Graph:* <{{ .GeneratorURL }}|:chart_with_upwards_trend:> *Runbook:* <{{ .Annotations.runbook_url }}|:spiral_note_pad:>\n  *Details:*\n  {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`\n  {{ end }}\n{{ end }}',
                send_resolved: true
              }
            ]
          },
          {
            name: 'silent-receiver'
          }
        ],
      },
    },

如果按照上面的slack_configs配置,将以以下方式通知到slack。单击Graph:右侧的图像,会跳转到Prometheus并显示图表。

slack-alert.png

接下来是关于_config字段中的prometheus字段。
replicas字段用于定义Prometheus Pod的副本数量。该值将反映在Prometheus资源的定义内,用于Prometheus Operator的自定义资源(CD),并以prometheus-prometheus.yaml文件名进行输出。

    prometheus+:: {
      replicas: 3,
    }

尽管我想解释其他要素,但由于体力不支,这一次就到此为止吧。

最后【】

通过以上介绍的方法已经搭建了监控环境,从12月中旬开始正式运营,所以可能会在未来发现各种改进点,但目前先将现有配置总结成文章。

由于base.libsonnet的内容变得相当庞大且冗长,可以考虑将文件进行分割或共享冗长描述。我们将努力进行重构。

Prometheus的数据目前存储在EBS上,但正在考虑使用Thanos进行迁移并永久存储在S3上的方法。

Kube-prometheus在开始时感觉有些困难,但现在已经可以相对容易地进行扩展。虽然我没有尝试过,但或许使用prometheus-operator的Helm图表可以更轻松地创建相似的配置(笑)。

广告
将在 10 秒后关闭
bannerAds