Prometheus 서버에서 메트릭 로드하기

TensorFlow.org에서 보기 Google Colab에서 실행하기 GitHub에서소스 보기 노트북 다운로드하기

주의: Python 패키지 외에 이 노트북에서는 sudo apt-get install을 사용하여 타자 패키지를 설치합니다.

개요

이 튜토리얼은 Prometheus 서버에서 tf.data.Dataset로 CoreDNS 메트릭을 로드한 다음 훈련과 추론에 tf.keras를 사용합니다.

CoreDNS는 서비스 검색에 중점을 둔 DNS 서버이며 Kubernetes 클러스터의 일부로 널리 배포됩니다. 이 때문에 종종 연산을 통해 면밀하게 모니터링됩니다.

이 튜토리얼은 머신러닝을 통해 연산을 자동화하려는 DevOps에서 사용할 수 있는 예입니다.

설정 및 사용법

필요한 tensorflow-io 패키지를 설치하고 런타임 다시 시작하기

import os
try:
  %tensorflow_version 2.x
except Exception:
  pass
TensorFlow 2.x selected.
pip install tensorflow-io
from datetime import datetime

import tensorflow as tf
import tensorflow_io as tfio

CoreDNS 및 Prometheus 설치 및 설정하기

데모 목적으로, DNS 쿼리를 수신하기 위해 포트 9053이 열려 있고 스크래핑에 대한 메트릭을 노출하기 위해 포트 9153(기본값)이 열려 있는 CoreDNS 서버가 로컬에 있습니다. 다음은 CoreDNS에 대한 기본 Corefile 구성이며 다운로드할 수 있습니다.

.:9053 {   prometheus   whoami }

설치에 대한 자세한 내용은 CoreDNS 설명서에서 찾을 수 있습니다.

curl -s -OL https://github.com/coredns/coredns/releases/download/v1.6.7/coredns_1.6.7_linux_amd64.tgz
tar -xzf coredns_1.6.7_linux_amd64.tgz

curl -s -OL https://raw.githubusercontent.com/tensorflow/io/master/docs/tutorials/prometheus/Corefile

cat Corefile
.:9053 {
  prometheus
  whoami
}
# Run `./coredns` as a background process.
# IPython doesn't recognize `&` in inline bash cells.
get_ipython().system_raw('./coredns &')

다음 단계로 Prometheus 서버를 설정하고 Prometheus를 사용하여 위의 포트 9153에서 노출된 CoreDNS 메트릭을 스크래핑합니다. 구성을 위한 prometheus.yml 파일도 다운로드할 수 있습니다.

curl -s -OL https://github.com/prometheus/prometheus/releases/download/v2.15.2/prometheus-2.15.2.linux-amd64.tar.gz
tar -xzf prometheus-2.15.2.linux-amd64.tar.gz --strip-components=1

curl -s -OL https://raw.githubusercontent.com/tensorflow/io/master/docs/tutorials/prometheus/prometheus.yml

cat prometheus.yml
global:
  scrape_interval:     1s
  evaluation_interval: 1s
alerting:
  alertmanagers:

  - static_configs:
    - targets:
rule_files:
scrape_configs:
- job_name: 'prometheus'
  static_configs:
  - targets: ['localhost:9090']
- job_name: "coredns"
  static_configs:
  - targets: ['localhost:9153']
# Run `./prometheus` as a background process.
# IPython doesn't recognize `&` in inline bash cells.
get_ipython().system_raw('./prometheus &')

일부 활동을 표시하기 위해 dig 명령을 사용하여 설정된 CoreDNS 서버에 대해 몇 가지 DNS 쿼리를 생성할 수 있습니다.

sudo apt-get install -y -qq dnsutils
dig @127.0.0.1 -p 9053 demo1.example.org
; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> @127.0.0.1 -p 9053 demo1.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53868
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 855234f1adcb7a28 (echoed)
;; QUESTION SECTION:
;demo1.example.org.     IN  A

;; ADDITIONAL SECTION:
demo1.example.org.  0   IN  A   127.0.0.1
_udp.demo1.example.org. 0   IN  SRV 0 0 45361 .

;; Query time: 0 msec
;; SERVER: 127.0.0.1#9053(127.0.0.1)
;; WHEN: Tue Mar 03 22:35:20 UTC 2020
;; MSG SIZE  rcvd: 132
dig @127.0.0.1 -p 9053 demo2.example.org
; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> @127.0.0.1 -p 9053 demo2.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53163
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: f18b2ba23e13446d (echoed)
;; QUESTION SECTION:
;demo2.example.org.     IN  A

;; ADDITIONAL SECTION:
demo2.example.org.  0   IN  A   127.0.0.1
_udp.demo2.example.org. 0   IN  SRV 0 0 42194 .

;; Query time: 0 msec
;; SERVER: 127.0.0.1#9053(127.0.0.1)
;; WHEN: Tue Mar 03 22:35:21 UTC 2020
;; MSG SIZE  rcvd: 132

이제 CoreDNS 서버의 메트릭을 Prometheus 서버에서 스크래핑하고 TensorFlow에서 사용할 준비가 됩니다.

CoreDNS 메트릭에 대한 Dataset를 만들고 TensorFlow에서 사용하기

PostgreSQL 서버에서 사용할 수 있고 tfio.experimental.IODataset.from_prometheus를 통해 수행할 수 있는 CoreDNS 메트릭의 Dataset를 만듭니다. 최소한 두 가지 인수가 필요합니다. query는 메트릭을 선택하기 위해 Prometheus 서버로 전달되고 length는 Dataset에 로드하려는 기간입니다.

"coredns_dns_request_count_total""5"(초)로 시작하여 아래 Dataset를 만들 수 있습니다. 튜토리얼 앞부분에서 두 개의 DNS 쿼리가 보내졌기 때문에 "coredns_dns_request_count_total"에 대한 메트릭은 시계열 마지막에서 "2.0"이 될 것으로 예상됩니다.

dataset = tfio.experimental.IODataset.from_prometheus(
      "coredns_dns_request_count_total", 5, endpoint="http://localhost:9090")


print("Dataset Spec:\n{}\n".format(dataset.element_spec))

print("CoreDNS Time Series:")
for (time, value) in dataset:
  # time is milli second, convert to data time:
  time = datetime.fromtimestamp(time // 1000)
  print("{}: {}".format(time, value['coredns']['localhost:9153']['coredns_dns_request_count_total']))
Dataset Spec:
(TensorSpec(shape=(), dtype=tf.int64, name=None), {'coredns': {'localhost:9153': {'coredns_dns_request_count_total': TensorSpec(shape=(), dtype=tf.float64, name=None)} } })

CoreDNS Time Series:
2020-03-03 22:35:17: 2.0
2020-03-03 22:35:18: 2.0
2020-03-03 22:35:19: 2.0
2020-03-03 22:35:20: 2.0
2020-03-03 22:35:21: 2.0

Dataset의 사양을 자세히 살펴보겠습니다.

(   TensorSpec(shape=(), dtype=tf.int64, name=None),   {     'coredns': {       'localhost:9153': {         'coredns_dns_request_count_total': TensorSpec(shape=(), dtype=tf.float64, name=None)       }     }   } )

데이터세트는 (time, values) 튜플로 구성되는 것을 분명히 알 수 있으며, values 필드는 다음으로 확장된 Python dict입니다.

"job_name": {   "instance_name": {     "metric_name": value,   }, }

위의 예에서 'coredns'는 작업 이름이고, 'localhost:9153'은 인스턴스 이름이며, 'coredns_dns_request_count_total'은 메트릭 이름입니다. 사용된 Prometheus 쿼리에 따라 여러 작업/인스턴스/메트릭이 반환될 수 있습니다. 이것은 또한 Python dict이 Dataset의 구조에 사용된 이유이기도 합니다.

다른 쿼리 "go_memstats_gc_sys_bytes"를 예로 들어 보겠습니다. CoreDNS와 Prometheus가 모두 Golang으로 작성되었으므로 "go_memstats_gc_sys_bytes" 메트릭은 "coredns" 작업과 "prometheus" 작업 모두에 사용할 수 있습니다.

참고: 이 셀은 처음 실행할 때 오류가 발생할 수 있습니다. 다시 실행하면 통과됩니다.

dataset = tfio.experimental.IODataset.from_prometheus(
    "go_memstats_gc_sys_bytes", 5, endpoint="http://localhost:9090")

print("Time Series CoreDNS/Prometheus Comparision:")
for (time, value) in dataset:
  # time is milli second, convert to data time:
  time = datetime.fromtimestamp(time // 1000)
  print("{}: {}/{}".format(
      time,
      value['coredns']['localhost:9153']['go_memstats_gc_sys_bytes'],
      value['prometheus']['localhost:9090']['go_memstats_gc_sys_bytes']))
Time Series CoreDNS/Prometheus Comparision:
2020-03-03 22:35:17: 2385920.0/2775040.0
2020-03-03 22:35:18: 2385920.0/2775040.0
2020-03-03 22:35:19: 2385920.0/2775040.0
2020-03-03 22:35:20: 2385920.0/2775040.0
2020-03-03 22:35:21: 2385920.0/2775040.0

생성된 Dataset는 이제 훈련 또는 추론 목적으로 tf.keras로 직접 전달할 수 있습니다.

모델 훈련에 Dataset 사용하기

메트릭 Dataset가 생성되면 모델 훈련 또는 추론을 위해 Dataset를 tf.keras로 바로 전달할 수 있습니다.

데모 목적으로 이 튜토리얼에서는 1개의 특성과 2개의 스텝을 입력으로 포함하는 매우 간단한 LSTM 모델을 사용합니다.

n_steps, n_features = 2, 1
simple_lstm_model = tf.keras.models.Sequential([
    tf.keras.layers.LSTM(8, input_shape=(n_steps, n_features)),
    tf.keras.layers.Dense(1)
])

simple_lstm_model.compile(optimizer='adam', loss='mae')

사용할 데이터세트는 10개의 샘플이 있는 CoreDNS의 'go_memstats_sys_bytes' 값입니다. 그러나 window=n_stepsshift=1의 슬라이딩 윈도우가 형성되기 때문에 추가 샘플이 필요합니다(연속된 두 요소에 대해 첫 번째 요소는 x로, 두 번째 요소는 훈련을 위해 y로 입력됨). 합계는 10 + n_steps - 1 + 1 = 12초입니다.

데이터 값의 스케일도 [0, 1]로 조정됩니다.

n_samples = 10

dataset = tfio.experimental.IODataset.from_prometheus(
    "go_memstats_sys_bytes", n_samples + n_steps - 1 + 1, endpoint="http://localhost:9090")

# take go_memstats_gc_sys_bytes from coredns job 
dataset = dataset.map(lambda _, v: v['coredns']['localhost:9153']['go_memstats_sys_bytes'])

# find the max value and scale the value to [0, 1]
v_max = dataset.reduce(tf.constant(0.0, tf.float64), tf.math.maximum)
dataset = dataset.map(lambda v: (v / v_max))

# expand the dimension by 1 to fit n_features=1
dataset = dataset.map(lambda v: tf.expand_dims(v, -1))

# take a sliding window
dataset = dataset.window(n_steps, shift=1, drop_remainder=True)
dataset = dataset.flat_map(lambda d: d.batch(n_steps))


# the first value is x and the next value is y, only take 10 samples
x = dataset.take(n_samples)
y = dataset.skip(1).take(n_samples)

dataset = tf.data.Dataset.zip((x, y))

# pass the final dataset to model.fit for training
simple_lstm_model.fit(dataset.batch(1).repeat(10),  epochs=5, steps_per_epoch=10)
Train for 10 steps
Epoch 1/5
10/10 [==============================] - 2s 150ms/step - loss: 0.8484
Epoch 2/5
10/10 [==============================] - 0s 10ms/step - loss: 0.7808
Epoch 3/5
10/10 [==============================] - 0s 10ms/step - loss: 0.7102
Epoch 4/5
10/10 [==============================] - 0s 11ms/step - loss: 0.6359
Epoch 5/5
10/10 [==============================] - 0s 11ms/step - loss: 0.5572
<tensorflow.python.keras.callbacks.History at 0x7f1758f3da90>

이 튜토리얼에서 설정한 CoreDNS 서버에는 어떤 워크로드도 없기 때문에 위의 훈련된 모델은 실제로 그다지 유용하지 않습니다. 그러나 이 모델은 실제 운영 서버에서 메트릭을 로드하는 데 사용할 수 있는 파이프라인입니다. 따라서 모델을 개선하여 DevOps 자동화의 실제 문제를 해결할 수 있습니다.