CLOVER🍀

That was when it all began.

OKD/Minishiftで、特定のPodに絞ってPrometheusからDiscoveryする

これは、なにをしたくて書いたもの?

以前、PrometheusをOKD(Kubernetes)にデプロイしてみました。

OKD/Minishift上に、Prometheusをデプロイしてみる - CLOVER🍀

こちらは、とにかくPrometheusをOKD(Kubernetes)上で動かすことだけを目標にしていましたが、今回は特定のPodといった
対象を絞り込んでのメトリクス収集にフォーカスしたいと思います。

NodeやKubernetes自体の情報ではなく、その上で自分でデプロイして動かしているリソースにフォーカスしてみようかなと。

環境

今回の環境は、こちらです。

$ minishift version
minishift v1.33.0+ba29431


$ oc version
oc v3.11.0+0cbc58b
kubernetes v1.11.0+d4cacc0
features: Basic-Auth GSSAPI Kerberos SPNEGO

Server https://192.168.42.27:8443
kubernetes v1.11.0+d4cacc0

こちらに、簡単なサンプルアプリケーションを作成してデプロイし、そのPodに絞ってメトリクスを収集することを考えたいと
思います。もちろん、Prometheusもデプロイします。

サンプルアプリケーション

では、まずはサンプルアプリケーションを作成します。Node.jsで作成することにしましょう。

Node.js用のPrometheusクライアントライブラリ、Expressを利用。

$ npm i prom-client express

バージョン。

  "dependencies": {
    "express": "^4.16.4",
    "prom-client": "^11.3.0"
  }

エンドポイントは、メトリクスを返すものと、ホスト名を返すものの2つを用意します。簡単に。
server.js

const os = require('os');

const prometheusClient = require("prom-client");

const collectDefaultMetrics = prometheusClient.collectDefaultMetrics;
collectDefaultMetrics({ timeout: 5000 });

const exporess = require("express");
const app = exporess();

app.get("/metrics", (req, res) => {
    res.set("Content-Type", "text/plain");
    res.send(prometheusClient.register.metrics());
});

app.get("/node", (req, res) => {
    res.send(`this server name = ${os.hostname()}`);
});

app.listen(8080, () => console.log(`[${new Date()}] server startup.`));

package.jsonでの指定。

  "scripts": {
    "start": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

こちらを適当なGitリポジトリに登録して、OKDにデプロイ。

$ oc new-app --name pod-discovery [GitリポジトリのURL]
$ oc expose svc pod-discovery


$ oc get all
NAME                        READY     STATUS      RESTARTS   AGE
pod/pod-discovery-1-build   0/1       Completed   0          1m
pod/pod-discovery-1-hshmv   1/1       Running     0          20s

NAME                                    DESIRED   CURRENT   READY     AGE
replicationcontroller/pod-discovery-1   1         1         1         22s

NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/pod-discovery   ClusterIP   172.30.8.236   <none>        8080/TCP   1m

NAME                                               REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/pod-discovery   1          1         1         config,image(pod-discovery:latest)

NAME                                           TYPE      FROM      LATEST
buildconfig.build.openshift.io/pod-discovery   Source    Git       1

NAME                                       TYPE      FROM          STATUS     STARTED              DURATION
build.build.openshift.io/pod-discovery-1   Source    Git@baa3367   Complete   About a minute ago   39s

NAME                                           DOCKER REPO                               TAGS      UPDATED
imagestream.image.openshift.io/pod-discovery   172.30.1.1:5000/myproject/pod-discovery   latest    23 seconds ago

NAME                                     HOST/PORT                                      PATH      SERVICES        PORT       TERMINATION   WILDCARD
route.route.openshift.io/pod-discovery   pod-discovery-myproject.192.168.42.27.nip.io             pod-discovery   8080-tcp                 None

確認。

$ curl pod-discovery-myproject.192.168.42.27.nip.io/node
this server name = pod-discovery-1-hshmv


$ curl pod-discovery-myproject.192.168.42.27.nip.io/metrics
# HELP process_cpu_user_seconds_total Total user CPU time spent in seconds.
# TYPE process_cpu_user_seconds_total counter
process_cpu_user_seconds_total 0.093472 1554361868786

# HELP process_cpu_system_seconds_total Total system CPU time spent in seconds.
# TYPE process_cpu_system_seconds_total counter
process_cpu_system_seconds_total 0.021420999999999996 1554361868786

# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0.11489300000000001 1554361868786

# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1554361794

# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 27590656 1554361868787

〜省略〜

これで、アプリケーションの準備はおしまいです。

Prometheusをデプロイする

続いて、PrometheusをOKDにデプロイします。

Service Account、ClusterRole、ClusterRoleBindingを作成。今回は、先ほど作成したPodのみにアクセスするため、ClusterRoleで
扱うリソースは絞っています。 rbac.yml

---
## ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus-sa
  namespace: myproject


---
## ClusterRole
apiVersion: v1
kind: ClusterRole
metadata:
  name: prometheus-cr
rules:
- apiGroups: [""]
  resources:
  - pods
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]


---
## ClusterRoleBinding
apiVersion: v1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  kind: ClusterRole
  name: prometheus-cr
subjects:
- kind: ServiceAccount
  name: prometheus-sa
  namespace: myproject

Prometheusに関するリソース定義。
resources.yml

---
## ImageStream
apiVersion: v1
kind: ImageStream
metadata:
  labels:
    app: prometheus
  name: prometheus
spec:
  lookupPolicy:
    local: false
  tags:
  - from:
      kind: DockerImage
      name: prom/prometheus:v2.8.1
    importPolicy: {}
    name: v2.8.1
    referencePolicy:
      type: Source


---
## DeploymentConfig
apiVersion: v1
kind: DeploymentConfig
metadata:
  labels:
    app: prometheus
  name: prometheus
spec:
  replicas: 1
  selector:
    app: prometheus
    deploymentconfig: prometheus
  strategy:
    type: Rolling
  template:
    metadata:
      labels:
        app: prometheus
        deploymentconfig: prometheus
    spec:
      serviceAccountName: prometheus-sa
      containers:
      - image: prom/prometheus:v2.8.1
        imagePullPolicy: IfNotPresent
        name: prometheus
        ports:
        - containerPort: 9090
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - name: config-volume
          mountPath: /etc/prometheus
        - name: prometheus-volume-1
          mountPath: /prometheus
      restartPolicy: Always
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: config-volume
        configMap:
         name: prometheus-config
      - emptyDir: {}
        name: prometheus-volume-1
  triggers:
  - type: ConfigChange
  - imageChangeParams:
      automatic: true
      containerNames:
      - prometheus
      from:
        kind: ImageStreamTag
        name: prometheus:v2.8.1
    type: ImageChange

---
## ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: prometheus
  name: prometheus-config
data:
  prometheus.yml: |
    global:
      scrape_interval: 10s
      evaluation_interval: 10s
    scrape_configs:
    # pod
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      # 「__meta_kubernetes_pod_label_」以降をlabelとして追加する
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      # 「__meta_kubernetes_namespace」の値をlabel「kubernetes_namespace」として追加する
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      # 「__meta_kubernetes_pod_name」の値をlabel「kubernetes_pod_name」として追加する
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: kubernetes_pod_name

---
## Service
apiVersion: v1
kind: Service
metadata:
  labels:
    app: prometheus
  name: prometheus
spec:
  ports:
  - name: 9090-tcp
    port: 9090
    protocol: TCP
    targetPort: 9090
  selector:
    app: prometheus
    deploymentconfig: prometheus
  sessionAffinity: None
  type: ClusterIP


---
## Route
apiVersion: v1
kind: Route
metadata:
  labels:
    app: prometheus
  name: prometheus
spec:
  port:
    targetPort: 9090-tcp
  to:
    kind: Service
    name: prometheus
    weight: 100
  wildcardPolicy: None

設定は、ConfigMapで行います。Scrapeするのは、Podだけにしています。

    scrape_configs:
    # pod
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      # 「__meta_kubernetes_pod_label_」以降をlabelとして追加する
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      # 「__meta_kubernetes_namespace」の値をlabel「kubernetes_namespace」として追加する
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      # 「__meta_kubernetes_pod_name」の値をlabel「kubernetes_pod_name」として追加する
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: kubernetes_pod_name

これらを適用していきます。RBAC関連のみ、adminで作成します。

$ oc apply -f rbac.yml --as system:admin
$ oc apply -f resources.yml

Routeも作っているので、PrometheusのWeb UIを見ることができます。

試しに、upメトリクスを見てみましょう。

f:id:Kazuhira:20190404161648p:plain

Podが大量に並びます。

ではここで、先ほど作成したアプリケーションのみを対象とするようにrelabel_configsを設定してみます。

    scrape_configs:
    # pod
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      # 「__meta_kubernetes_pod_label_」以降をlabelとして追加する
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      # 「__meta_kubernetes_namespace」の値をlabel「kubernetes_namespace」として追加する
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      # 「__meta_kubernetes_pod_name」の値をlabel「kubernetes_pod_name」として追加する
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: kubernetes_pod_name
      # 「__meta_kubernetes_pod_label_app」labelがregexで指定されたもののみ残す
      # 前述のlabelmapでlabelを変換しているので、「app」でも可
      - source_labels: [__meta_kubernetes_pod_label_app]
      # - source_labels: [app]
        action: keep
        regex: pod-discovery

この部分ですね。「app」Label、もしくは「__meta_kubernetes_pod_label_app」Labelが「pod-discovery」のもののみ残します。

      # 「__meta_kubernetes_pod_label_app」labelがregexで指定されたもののみ残す
      # 前述のlabelmapでlabelを変換しているので、「app」でも可
      - source_labels: [__meta_kubernetes_pod_label_app]
      # - source_labels: [app]
        action: keep
        regex: pod-discovery

適用して、Prometheusをrollout。

$ oc get all
$ oc rollout latest dc/prometheus

すると、対象のPodが絞られます。

f:id:Kazuhira:20190404162047p:plain

relabel_configsで絞り込むことに成功したようです。

では、Podをスケールアウトさせてみましょう。

$ oc scale dc pod-discovery --replicas=3

しばらくすると、Prometheus側でも増えたPodのメトリクスを確認することができます。

f:id:Kazuhira:20190404162233p:plain

Labelで絞り込んだPodが増えても、追従してくれることが確認できました、と。

今回はOKDで「new-app」で作成する時にデフォルトで設定されるLabel「app」を使って絞り込みましたが、relabel_configsを
使って対象を柔軟に絞り込んでモニタリングしていく感じになるんでしょうね。

見る人と、利用する環境でどこまで見る?という話かな…。

とりあえず、思ったとおりの絞り込みは確認できたので、良しとしょうましょう。