อยากเก็บ metrics ใน k8s หรอ? มาใช้ Prometheus กัน!!!

Supakarin Niansupornpun
Thinc.
Published in
5 min readMar 13, 2023

ใครอยากรู้ว่า Prometheus คืออะไรทำอะไรได้บ้างมาลองอ่านกันเลย!!!

สำหรับบทความนี้เกิดจากไอคนที่เขียนบทความนี้อยากจะเก็บ metrics มา visualize มากๆแต่ไม่รู้อะไรเลย เลยตั้งใจเขียนบทความนี้ขึ้นมาเพื่อจะได้มาเรียนรู้พร้อมๆกับเพื่อนๆที่อ่านบทความนี้ เพราะฉะนั้นบทความนี้จะพยายามย่อยทุกอย่างให้เข้าใจง่ายที่สุดฉบับคนไม่รู้อะไรเกี่ยวกับ Prometheus มาเลย ถ้าผิดพลาดอะไรก็ขอโทษไว้ก่อนเลยนะครับผม 😅

บทความนี้จะครอบคุมการติดตั้งจนถึงการ target application เพื่อ scrape metric มาเก็บที่ Prometheus

📕 ต้องรู้อะไรมาก่อนบ้าง

🤔 อะไรคือ Prometheus?

Prometheus คือ open-source tool สำหรับการเก็บ metrics เพื่อเอามา monitor หรือ ทำระบบแจ้งเตือน

รูปภาพ diagram ของ prometheus
credits: prometheus.io

โดยวิธีการทำงาน จะไปดึงข้อมูล (scraping) ผ่านการส่ง HTTP request ไปขอ metrics ใน endpoint ของแต่ละ application ที่เรา config ไว้ จากนั้นนำข้อมูลมาเก็บใน time-series database เราสามารถใช้ภาษา PromQL ในการ query ข้อมูลต่างๆได้

สามารถอ่านเพิ่มที่ document ของ Prometheus ได้เลย

ก่อนเริ่ม บทความนี้จะอ้างอิงโค้ดจาก repo ด้านล่าง ซึ่งเราจะค่อยๆเรียนรู้ไปพร้อมกันทีละส่วน

🏁 มาเริ่มติดตั้ง Prometheus กัน!!!

ก่อนอื่นหลังจาก clone repo ด้านบนมาแล้วเราจะทำการสร้าง CRD (Kubernetes CustomResourceDefinitions) และ namespace โดยใช้คำสั่ง

kubectl create -f manifests/setup

หลังจากนั้นเราจะใช้คำสั่ง

kubectl create -f manifests/

เพื่อ apply components ต่างๆเข้าไป

kubectl -n monitoring get svc

จะเห็นได้ว่ามี service ต่างๆ เราจะมาดูกันว่าแต่ละ service / component ทำหน้าที่อะไรบ้าง

💡 อะไรคือ Prometheus-Operator?

Prometheus Operator จะเป็นคนที่คอยจัดการ deploy และจัดการ components อื่นๆที่ต้องใช้ในการทำ monitor ภายใน Kubernetes

ฟีเจอร์หลักๆของ Prometheus operator

  • จัดการ Custom Resources: เป็นคนควบคุมการ deploy และการจัดการ Prometheus, Alertmanager และ components อื่นๆที่เกี่ยวข้องผ่าน Custom Resources ที่เราสร้างไว้
  • Simplified Deployment Configuration: จัดการพวก configuration พื้นฐานเช่น version, persistence, retention policies, และ replicase จาก native Kubernetes resource
  • Prometheus Target Configuration: generate configuration ให้ไป target พวก service ที่เราอยาก monitor อัตโนมัติ โดยอิงจาก Kubernetes label queries

อ่านเพิ่มเติมได้ที่ docs ของ Prometheus Operator

💡 อะไรคือ kube-state-metrics (KSM)?

kube-state-metrics เป็น service ที่คุยกับ Kubernetes API เพื่อเก็บ metrics ต่างๆเกี่ยวกับ state ของ object

อ่านเพิ่มเติมได้ที่นี่ Github

💡 อะไรคือ Node exporter?

Node exporter เป็น service เอาไว้ดึงข้อมูลของ hardware ในแต่ละ node เช่น cpu used, memory used, หรือ พวกข้อมูลพวก filesystem ต่างๆ

อ่านเพิ่มเติมได้ที่นี่ Github

💡 อะไรคือ Blackbox exporter?

Blackbox exporter เป็น service เอาไว้ดึง metric ของพวก endpoint สามารถ export ข้อมูลจาก HTTP, HTTPS, DNS, TCP, และ ICMP

อ่านเพิ่มเติมได้ที่นี่ Github

💡 อะไรคือ Grafana?

Grafana เป็น UI สำหรับการเอา metrics ที่ได้ไป visualize หรือทำ monitoring ซึ่งในบทความจะไม่ได้กล่าวถึง

อ่านเพิ่มเติมได้ที่นี่ เว็บไซต์

Port-forward มาลองใช้กัน!!!

kubectl -n monitoring port-forward svc/prometheus-k8s 9090

ลองเข้าหน้า UI แล้วกดไปที่ target จะเห็น target ของ metrics ต่างๆที่ได้มา

รูปภาพ targets

🏗️ คราวนี้มาลองสร้าง metrics ของตัวเองกันบ้างดีกว่า

ในส่วนนี้เราจะลองเขียน application เองขึ้นมาสักตัวแล้วให้ Prometheus มา scrape ข้อมูลจาก application ของเราออกไป

โดยโค้ดที่ใช้อันนี้จะขอยืมมากจาก ช่อง Anton Putra นะครับผม

อันนี้จะเป็น application ที่เราจะ deploy กัน

package main

import (
"log"
"math/rand"
"net/http"
"time"

"github.com/gofiber/fiber/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

type metrics struct {
duration *prometheus.SummaryVec
}

func NewMetrics(reg prometheus.Registerer) *metrics {
m := &metrics{
duration: prometheus.NewSummaryVec(prometheus.SummaryOpts{
Namespace: "tester",
Name: "duration_seconds",
Help: "Duration of the request.",
Objectives: map[float64]float64{0.9: 0.01, 0.99: 0.001},
}, []string{"path", "status"}),
}
reg.MustRegister(m.duration)
return m
}

type Device struct {
ID int `json:"id"`
Mac string `json:"mac"`
Firmware string `json:"firmware"`
}

func main() {
reg := prometheus.NewRegistry()
m := NewMetrics(reg)

pMux := http.NewServeMux()
promHandler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
pMux.Handle("/metrics", promHandler)

go func() {
log.Fatal(http.ListenAndServe(":8081", pMux))
}()

go simulateTraffic(m)

app := fiber.New()

app.Get("/api/devices", getDevices)

log.Fatal(app.Listen(":8080"))
}

func getDevices(c *fiber.Ctx) error {
sleep(1000)
dvs := []Device{
{1, "5F-33-CC-1F-43-82", "2.1.6"},
{2, "EF-2B-C4-F5-D6-34", "2.1.6"},
}

return c.JSON(dvs)
}

func simulateTraffic(m *metrics) {
for {
now := time.Now()
sleep(1000)
m.duration.WithLabelValues("/fake", "200").Observe(time.Since(now).Seconds())
}
}

func sleep(ms int) {
rand.Seed(time.Now().UnixNano())
now := time.Now()
n := rand.Intn(ms + now.Second())
time.Sleep(time.Duration(n) * time.Millisecond)
}

เป็น fiber application ตัวนึงที่มี api path /api/devices ความพิเศษคือจะมีการ สร้าง metrics ปลอมอันนึงว่ามีคน request เข้ามาทาง path /fake แล้วตอบกลับเป็น statusCode 200 เราจะ expose metrics นี้ที่ port 8081 path /metrics

ซึ่งเค้าก็มี Docker Container พร้อมกับ deployment มาให้เราเรียบร้อยแล้วนะครับเราก็จะใช้ของเค้าเลย ใครหาไม่เจอกด ตรงนี้

โดยทั่วไปแล้วการที่เราจะให้ Prometheus ไป scraping metrics ให้เรานั้นเราจะต้องเขียน config file ให้ตัว Prometheus แต่ด้วยความที่เรามี Prometheus Operator ที่ทำให้เราสามารถเขียน object เข้าไปมันก็จะทำการ auto generate config และจะการแก้ไข Prometheus ให้เราเลย

PodMonitor

โดย object ตัวแรกที่เราจะพูดถึงคือ PodMonitor เป็น CRD ที่ทำให้เราสามารถ target pod ที่เราต้องการ monitor ได้โดย config จากการใช้ label selection

จากตัวอย่างที่ให้ไปสามารถเข้าไปดูใน folder deploy/3-pod-monitring.yaml

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: myapp
namespace: staging
labels:
prometheus: main
spec:
namespaceSelector:
matchNames:
- staging
selector:
matchLabels:
app: myapp
podMetricsEndpoints:
- port: http-metrics
path: /metrics

หลักๆคือการตัวตัว selector ให้ชี้ไปที่ pod ที่ต้องการ และ บอก endpoint ที่ metrics เราอยู่ซึ่ง path /metrics เป็น default path อยู่แล้วสามารถละได้

ServiceMonitor

ตัวถัดมาคือ ServiceMonitor เป็น CRD ที่ทำให้เราสามารถ target service ที่เราต้องการ monitor ได้ได้โดย config จากการใช้ label selection ซึ่งใช่ครับมันคล้ายกับ PodMonitor เลยแค่เพียงจากที่เราจะ monitor ที่ละ pod เราก็ทำการ monitor ทั้ง service เลย ซึ่ง ServiceMonitoring ต้องการ Endpoints object ซึ่งโดยปกติแล้ว Endpoints จากถูกสร้างจาก Service object อยู่แล้ว เพราะฉะนั้นก่อนสร้าง ServiceMonitor เราต้องสร้าง Service มาก่อน

ซึ่งการสร้าง service จะมีสองอันคือ endpoint ปกติ และ endpoint ของ metrics ซึ่งขอละไว้นะครับสามารถเข้าไปดูใน folder deploy/2-service.yaml และ deploy/4-prom-service.yaml

เมื่อเราสร้าง service เสร็จเราก็มาสร้าง ServiceMonitoring ต่อ สามารถเข้าไปดูใน folder deploy/5-service-monitor.yaml

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp
namespace: staging
labels:
prometheus: main
spec:
namespaceSelector:
matchNames:
- staging
selector:
matchLabels:
app: myapp-monitoring
endpoints:
- port: http-metrics
path: /metrics

ซึ่งทุกอย่างก็จะเหมือนกับ PodMonitor config selector และ endpoints

จริง

หลังจากเรา create object ต่างๆ เข้าไปและลองไปเปิด UI > Configuration

หน้า Configuration

เราจะเห็นว่า serviceMonitor และ PodMonitor ขึ้นมาใน Config แล้วแต่ยังไม่ขึ้นในหน้า Target นั้นเป็นเพราะ role ของ Prometheus ถูก scope อยู่ใน namespace สามอย่างคือ default, kube-system, และ monitoring สามารถเข้าไปดูใน ไฟล์ prometheus-roleSpecificNamespace.yaml เนื่องจากเราต้องการให้มันเข้าถึง namespace ด้วย เราจึงเพิ่ม Role ด้านล่างเข้าไปในไฟล์ด้วย

- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app.kubernetes.io/component: prometheus
app.kubernetes.io/instance: k8s
app.kubernetes.io/name: prometheus
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 2.42.0
name: prometheus-k8s
namespace: staging
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- pods
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- list
- watch

และต้องเพิ่ม RoleBinding ในไฟล์ prometheus-roleBindingSpecificNamespace.yaml

- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app.kubernetes.io/component: prometheus
app.kubernetes.io/instance: k8s
app.kubernetes.io/name: prometheus
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 2.42.0
name: prometheus-k8s
namespace: staging
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: prometheus-k8s
subjects:
- kind: ServiceAccount
name: prometheus-k8s
namespace: monitoring

apply ทุกอย่างเข้าไป!!!

รอสักพักให้ Prometheus Operator จัดการทุกอย่างให้เรา

หลังจากนั้นเราก็จะได้ Target ของเราขึ้นมาแล้ววว เรามาลอง Query กันดีกว่า

🔎 Query

เข้าไปที่หน้า Graph ลอง query tester_duration_seconds

ลอง Query ดูก็จะได้ metrics จาก app ของเราแล้วววว!!!

🎉 สรุป

สำหรับบทความนี้นะครับเราก็ได้มาทัวร์กันว่า Prometheus คือ opensource ตัวนึงที่ทำหน้าที่รวบรวม metrics ต่างๆโดยการ ยิง HTTP request มาเก็บไว้ที่ตัวเอง โดยเราได้ใช้ kube-prometheus stack ในการ setup Prometheus ลงใน k8s ซึ่ง provide CRD และ tools เบื้องต้นมาให้ โดยหัวใจสำคัญคือ prometheus-operator ที่ทำหน้าที่ จัดการ Prometheus ให้เป็นไปตาม config ที่เรา define ไว้ใน object และ เราสามารถสร้าง target เองได้โดยใช้ PodMonitor หรือ ServiceMonitor

ต่อไปคือเราสามารถนำ metrics ที่อยู่ใน Prometheus ไป visualize ใน Grafana ได้ ถ้ามีโอกาสอาจจะมีบทความเกี่ยวกับเรื่องนี้มานะครับ แล้วก็ note ไว้นิดนึงนะครับ ข้อมูล ที่เก็บตอนนี้ไม่ได้เก็บใน persistance storage แปลว่าถ้า pod Prometheus ล่มข้อมูลก็จะหายไปด้วยนะครับ

ก็ขอบคุณทุกคนมากเลยนะครับที่อ่านมาถึงตรงนี้ สำหรับบทความนี้ผมเองก็เรียนรู้ไปพร้อมๆกับตอนที่เขียนบทความถ้าผิดพลาดยังไงก็ขอโทษด้วยนะครับไว้เจอกันบทความหน้าครับ

--

--

Supakarin Niansupornpun
Thinc.
Writer for

Just the ordinary guys who want to be extraordinary.