GitOps สำหรับ Monitoring
GitOps เป็นแนวทางที่ใช้ Git เป็น Single Source of Truth สำหรับทุกอย่างในระบบ รวมถึง Monitoring Configuration เมื่อรวม BetterUptime กับ GitOps ได้ระบบ Monitoring ที่จัดการผ่าน Code ทุกการเปลี่ยนแปลงผ่าน PR มี Review มี Audit Trail
ข้อดีคือ Version Control ย้อนกลับได้ Consistency ทุก Environment เหมือนกัน Automation ไม่ต้อง Manual Setup และ Review Process ป้องกันผิดพลาด
BetterUptime Configuration as Code
# === BetterUptime GitOps Configuration ===
# monitoring/config.yaml — Monitoring Configuration in Git
monitors:
- name: "Production API"
url: "https://api.example.com/health"
monitor_type: "status"
check_frequency: 30
request_timeout: 15
confirmation_period: 3
regions: ["us", "eu", "as"]
expected_status_codes: [200]
alert_channels: ["slack-ops", "pagerduty-oncall"]
- name: "Production Frontend"
url: "https://www.example.com"
monitor_type: "status"
check_frequency: 60
request_timeout: 30
regions: ["us", "eu", "as", "au"]
alert_channels: ["slack-ops"]
- name: "Database Health"
url: "https://api.example.com/db-health"
monitor_type: "status"
check_frequency: 60
request_timeout: 10
alert_channels: ["slack-ops", "pagerduty-oncall"]
- name: "Staging API"
url: "https://staging-api.example.com/health"
monitor_type: "status"
check_frequency: 120
alert_channels: ["slack-dev"]
- name: "SSL Certificate"
url: "https://www.example.com"
monitor_type: "ssl"
check_frequency: 86400
alert_channels: ["slack-ops"]
heartbeats:
- name: "Backup Job"
period: 86400
grace: 3600
alert_channels: ["slack-ops"]
- name: "Data Sync"
period: 3600
grace: 600
alert_channels: ["slack-ops"]
status_pages:
- name: "Example Status"
subdomain: "status-example"
custom_domain: "status.example.com"
sections:
- name: "Core Services"
monitors: ["Production API", "Production Frontend"]
- name: "Infrastructure"
monitors: ["Database Health"]
alert_channels:
slack-ops:
type: "slack"
webhook: ""
slack-dev:
type: "slack"
webhook: ""
pagerduty-oncall:
type: "pagerduty"
key: ""
Python — GitOps Sync Script
# gitops_sync.py — Sync BetterUptime Config จาก Git
# pip install requests pyyaml
import yaml
import requests
import os
import json
import sys
from pathlib import Path
class BetterUptimeGitOps:
"""Sync BetterUptime Configuration จาก YAML Config"""
BASE_URL = "https://betteruptime.com/api/v2"
def __init__(self, api_token):
self.session = requests.Session()
self.session.headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json",
}
def load_config(self, config_path):
"""โหลด Config จาก YAML"""
with open(config_path) as f:
raw = f.read()
# Replace environment variables
for key, value in os.environ.items():
raw = raw.replace(f"}}", value)
return yaml.safe_load(raw)
def get_existing_monitors(self):
"""ดึง Monitors ที่มีอยู่"""
resp = self.session.get(f"{self.BASE_URL}/monitors")
monitors = resp.json().get("data", [])
return {m["attributes"]["pronounceable_name"]: m for m in monitors}
def sync_monitors(self, config):
"""Sync Monitors ตาม Config"""
existing = self.get_existing_monitors()
desired = {m["name"]: m for m in config.get("monitors", [])}
created, updated, deleted = 0, 0, 0
# Create or Update
for name, spec in desired.items():
payload = {
"pronounceable_name": name,
"url": spec["url"],
"monitor_type": spec.get("monitor_type", "status"),
"check_frequency": spec.get("check_frequency", 60),
"request_timeout": spec.get("request_timeout", 15),
"confirmation_period": spec.get("confirmation_period", 3),
"regions": ",".join(spec.get("regions", ["us", "eu"])),
}
if name in existing:
# Update
monitor_id = existing[name]["id"]
resp = self.session.patch(
f"{self.BASE_URL}/monitors/{monitor_id}",
json=payload,
)
if resp.status_code == 200:
updated += 1
print(f" Updated: {name}")
else:
# Create
resp = self.session.post(
f"{self.BASE_URL}/monitors",
json=payload,
)
if resp.status_code in [200, 201]:
created += 1
print(f" Created: {name}")
# Delete monitors not in config
for name, monitor in existing.items():
if name not in desired:
resp = self.session.delete(
f"{self.BASE_URL}/monitors/{monitor['id']}"
)
if resp.status_code in [200, 204]:
deleted += 1
print(f" Deleted: {name}")
return {"created": created, "updated": updated, "deleted": deleted}
def sync_heartbeats(self, config):
"""Sync Heartbeats"""
for hb in config.get("heartbeats", []):
payload = {
"name": hb["name"],
"period": hb.get("period", 3600),
"grace": hb.get("grace", 300),
}
resp = self.session.post(f"{self.BASE_URL}/heartbeats", json=payload)
status = "created" if resp.status_code in [200, 201] else "exists"
print(f" Heartbeat {hb['name']}: {status}")
def plan(self, config_path):
"""Plan — แสดงการเปลี่ยนแปลงที่จะเกิดขึ้น (Dry Run)"""
config = self.load_config(config_path)
existing = self.get_existing_monitors()
desired = {m["name"]: m for m in config.get("monitors", [])}
print("\n=== GitOps Plan ===")
for name in desired:
if name in existing:
print(f" ~ Update: {name}")
else:
print(f" + Create: {name}")
for name in existing:
if name not in desired:
print(f" - Delete: {name}")
def apply(self, config_path):
"""Apply — Sync Configuration"""
config = self.load_config(config_path)
print("\n=== GitOps Apply ===")
print("\nMonitors:")
result = self.sync_monitors(config)
print(f"\n Summary: {result['created']} created, "
f"{result['updated']} updated, {result['deleted']} deleted")
print("\nHeartbeats:")
self.sync_heartbeats(config)
print("\n=== Apply Complete ===")
# Usage:
# gitops = BetterUptimeGitOps(os.environ["BETTERUPTIME_TOKEN"])
# gitops.plan("monitoring/config.yaml")
# gitops.apply("monitoring/config.yaml")
CI/CD Pipeline สำหรับ GitOps Monitoring
# === GitHub Actions — GitOps Monitoring Pipeline ===
# .github/workflows/monitoring-gitops.yml
name: Monitoring GitOps
on:
push:
branches: [main]
paths: ["monitoring/**"]
pull_request:
branches: [main]
paths: ["monitoring/**"]
env:
BETTERUPTIME_TOKEN: }
SLACK_OPS_WEBHOOK: }
SLACK_DEV_WEBHOOK: }
PAGERDUTY_KEY: }
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Dependencies
run: pip install pyyaml jsonschema
- name: Validate Config
run: |
python -c "
import yaml, sys
with open('monitoring/config.yaml') as f:
config = yaml.safe_load(f.read())
monitors = config.get('monitors', [])
print(f'Monitors: {len(monitors)}')
for m in monitors:
assert 'name' in m, f'Missing name in monitor'
assert 'url' in m, f'Missing url in {m[\"name\"]}'
print(f' OK: {m[\"name\"]}')
print('Validation passed!')
"
plan:
runs-on: ubuntu-latest
needs: validate
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install requests pyyaml
- name: Plan Changes
run: python scripts/gitops_sync.py plan monitoring/config.yaml
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
const output = `### Monitoring GitOps Plan
Changes detected in monitoring configuration.
Review the plan output above before approving.`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
apply:
runs-on: ubuntu-latest
needs: validate
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install requests pyyaml
- name: Apply Changes
run: python scripts/gitops_sync.py apply monitoring/config.yaml
- name: Notify Slack
if: success()
run: |
curl -X POST $SLACK_OPS_WEBHOOK \
-H 'Content-Type: application/json' \
-d '{"text":"Monitoring config updated via GitOps"}'
# === ArgoCD Application สำหรับ Monitoring Stack ===
# argocd/monitoring-app.yaml
# apiVersion: argoproj.io/v1alpha1
# kind: Application
# metadata:
# name: monitoring-stack
# namespace: argocd
# spec:
# project: default
# source:
# repoURL: https://github.com/myorg/monitoring-config
# path: k8s/monitoring
# targetRevision: main
# destination:
# server: https://kubernetes.default.svc
# namespace: monitoring
# syncPolicy:
# automated:
# prune: true
# selfHeal: true
# syncOptions:
# - CreateNamespace=true
Best Practices
- Config in Git: เก็บ Monitoring Config ใน Git Repository เหมือน Infrastructure Code
- PR Review: ทุกการเปลี่ยน Monitor ต้องผ่าน Pull Request มี Review ก่อน Apply
- Plan Before Apply: แสดง Plan ก่อน Apply เหมือน Terraform Plan ป้องกันผิดพลาด
- Environment Variables: เก็บ Secrets (API Keys, Webhooks) ใน CI/CD Secrets ไม่ Hardcode
- Drift Detection: ตรวจสอบว่า Config จริงตรงกับ Git อย่างสม่ำเสมอ
- Rollback: ถ้า Apply ผิดพลาด Revert Git Commit แล้ว Apply ใหม่
GitOps คืออะไร
แนวทางจัดการ Infrastructure โดยใช้ Git เป็น Single Source of Truth ทุกการเปลี่ยนแปลงผ่าน PR มี Review ใช้ ArgoCD Flux CD Reconcile สถานะจริงให้ตรงกับ Git อัตโนมัติ
BetterUptime ใช้กับ GitOps อย่างไร
ใช้ BetterUptime API จัดการ Monitors Alerts Status Pages ผ่าน Code เก็บ Config ใน Git ทุกการเปลี่ยนแปลงผ่าน PR CI/CD Pipeline Apply Config อัตโนมัติ Monitoring เป็น Infrastructure as Code
ข้อดีของ GitOps สำหรับ Monitoring คืออะไร
Version Control ย้อนกลับได้ Audit Trail ดูว่าใครเปลี่ยนอะไร Consistency ทุก Environment เหมือนกัน Automation ไม่ต้อง Manual Review Process ป้องกันผิดพลาด
ArgoCD คืออะไร
GitOps Controller สำหรับ Kubernetes ดึง Config จาก Git Sync กับ Cluster อัตโนมัติ มี Web UI รองรับ Helm Kustomize Plain YAML ตรวจจับ Drift ระหว่าง Git กับ Cluster
สรุป
BetterUptime ร่วมกับ GitOps ทำให้ Monitoring Configuration เป็น Code เก็บใน Git ทุกการเปลี่ยนแปลงผ่าน PR มี Review CI/CD Pipeline Plan และ Apply อัตโนมัติ มี Version Control ย้อนกลับได้ Audit Trail Consistency ทุก Environment ใช้ ArgoCD สำหรับ Kubernetes Monitoring Stack Drift Detection ตรวจสอบว่า Config จริงตรงกับ Git
