Better Uptime คืออะไรและทำไมต้องใช้ Feature Flag ร่วมกัน
Better Uptime เป็นแพลตฟอร์ม Uptime Monitoring ที่ออกแบบมาสำหรับทีม DevOps และ SRE โดยเฉพาะ รองรับการตรวจสอบสถานะเว็บไซต์ API และบริการต่างๆผ่าน HTTP, TCP, UDP, DNS, ICMP และ Cron Job Monitoring สามารถแจ้งเตือนผ่านหลายช่องทางทั้ง Slack, PagerDuty, SMS, Email และ Webhook
Feature Flag (หรือ Feature Toggle) คือเทคนิคการเปิดปิดฟีเจอร์ในระบบโดยไม่ต้อง deploy ใหม่ ทำให้ทีมพัฒนาสามารถปล่อยฟีเจอร์ใหม่ให้ผู้ใช้บางกลุ่มทดสอบก่อน หรือปิดฟีเจอร์ที่มีปัญหาได้ทันทีโดยไม่ต้อง rollback deployment
การนำ Better Uptime มาใช้ร่วมกับ Feature Flag Management ช่วยให้ระบบตรวจจับปัญหาที่เกิดจากฟีเจอร์ใหม่ได้อัตโนมัติ เมื่อ Better Uptime ตรวจพบว่า Uptime หรือ Response Time ผิดปกติ ระบบจะส่ง Webhook ไปปิด Feature Flag ที่เพิ่งเปิดใช้งานโดยอัตโนมัติ ช่วยลดเวลา Mean Time To Recovery (MTTR) ได้อย่างมาก
สถาปัตยกรรมของระบบประกอบด้วย Better Uptime ทำหน้าที่ monitor สถานะบริการ, Unleash หรือ LaunchDarkly เป็น Feature Flag Server และ Webhook Middleware ที่เชื่อมทั้งสองระบบเข้าด้วยกัน
ติดตั้งและตั้งค่า Better Uptime Monitoring
เริ่มจากการตั้งค่า Better Uptime ผ่าน API เพื่อสร้าง Monitor สำหรับบริการที่ต้องการติดตาม
# สร้าง Monitor ผ่าน Better Uptime API
curl -X POST "https://betteruptime.com/api/v2/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "status",
"url": "https://api.example.com/health",
"pronounceable_name": "Production API Health",
"check_frequency": 30,
"request_timeout": 15,
"confirmation_period": 0,
"http_method": "get",
"expected_status_codes": [200],
"regions": ["us", "eu", "ap"],
"maintenance_from": null,
"paused": false,
"follow_redirects": true,
"remember_cookies": false
}'
# สร้าง Monitor สำหรับ API Response Time
curl -X POST "https://betteruptime.com/api/v2/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "expected_status_code",
"url": "https://api.example.com/v2/orders",
"pronounceable_name": "Orders API Latency",
"check_frequency": 60,
"request_timeout": 5,
"http_method": "get",
"expected_status_codes": [200, 201]
}'
# ดูรายการ Monitors ทั้งหมด
curl -s "https://betteruptime.com/api/v2/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" | python3 -m json.tool
ตั้งค่า Webhook สำหรับส่งการแจ้งเตือนไปยังระบบ Feature Flag
# สร้าง Webhook Integration
curl -X POST "https://betteruptime.com/api/v2/webhooks" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://ff-middleware.example.com/webhook/betteruptime",
"webhook_type": "custom",
"name": "Feature Flag Auto-Rollback",
"recovery": true,
"acknowledged": false
}'
# ตั้งค่า Escalation Policy
curl -X POST "https://betteruptime.com/api/v2/policies" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Feature Flag Escalation",
"repeat_count": 3,
"repeat_delay": 60,
"steps": [
{
"step_type": "webhook_integration",
"wait_before": 0,
"webhook_id": "WEBHOOK_ID_HERE"
},
{
"step_type": "slack_integration",
"wait_before": 120,
"slack_id": "SLACK_ID_HERE"
}
]
}'
สร้างระบบ Feature Flag ด้วย Unleash
Unleash เป็น Feature Flag Server แบบ Open Source ที่สามารถ self-host ได้ รองรับหลาย SDK ทั้ง Node.js, Python, Go, Java และอื่นๆ ติดตั้งผ่าน Docker Compose ได้ง่าย
# docker-compose.yml สำหรับ Unleash
version: "3.9"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: unleash
POSTGRES_USER: unleash
POSTGRES_PASSWORD: some-secret-password
volumes:
- unleash_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U unleash"]
interval: 5s
timeout: 3s
retries: 5
unleash:
image: unleashorg/unleash-server:latest
ports:
- "4242:4242"
environment:
DATABASE_URL: "postgres://unleash:some-secret-password@postgres:5432/unleash"
DATABASE_SSL: "false"
LOG_LEVEL: "info"
INIT_ADMIN_API_TOKENS: "*:*.unleash-admin-api-token"
INIT_CLIENT_API_TOKENS: "default:development.unleash-client-token"
depends_on:
postgres:
condition: service_healthy
volumes:
unleash_data:
# เริ่มต้นระบบ
# docker compose up -d
# เข้าถึง Unleash UI ที่ http://localhost:4242
สร้าง Feature Flag ผ่าน Unleash API
# สร้าง Feature Flag ใหม่
curl -X POST "http://localhost:4242/api/admin/projects/default/features" \
-H "Authorization: *:*.unleash-admin-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "new-checkout-flow",
"description": "ระบบชำระเงินแบบใหม่ที่ปรับปรุง UX",
"type": "release",
"impressionData": true
}'
# เปิด Feature Flag ใน Development Environment
curl -X POST "http://localhost:4242/api/admin/projects/default/features/new-checkout-flow/environments/development/on" \
-H "Authorization: *:*.unleash-admin-api-token"
# เพิ่ม Strategy แบบ Gradual Rollout (เปิดให้ 10% ก่อน)
curl -X POST "http://localhost:4242/api/admin/projects/default/features/new-checkout-flow/environments/production/strategies" \
-H "Authorization: *:*.unleash-admin-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "flexibleRollout",
"parameters": {
"rollout": "10",
"stickiness": "userId",
"groupId": "new-checkout-flow"
}
}'
# ดูสถานะ Feature Flag
curl -s "http://localhost:4242/api/admin/projects/default/features/new-checkout-flow" \
-H "Authorization: *:*.unleash-admin-api-token" | python3 -m json.tool
เชื่อมต่อ Feature Flag กับ Uptime Monitoring
สร้าง Webhook Middleware Server ที่รับ alert จาก Better Uptime แล้วควบคุม Feature Flag ใน Unleash
# ff_middleware.py — Webhook Middleware Server
from flask import Flask, request, jsonify
import requests
import json
import os
import logging
from datetime import datetime
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
UNLEASH_URL = os.getenv("UNLEASH_URL", "http://localhost:4242")
UNLEASH_TOKEN = os.getenv("UNLEASH_TOKEN", "*:*.unleash-admin-api-token")
SLACK_WEBHOOK = os.getenv("SLACK_WEBHOOK", "")
# Mapping ระหว่าง Monitor กับ Feature Flag
MONITOR_FLAG_MAP = {
"Production API Health": ["new-checkout-flow", "new-search-engine"],
"Orders API Latency": ["new-checkout-flow"],
"Payment Gateway": ["new-payment-provider"],
}
def disable_feature_flag(flag_name, environment="production"):
url = f"{UNLEASH_URL}/api/admin/projects/default/features/{flag_name}/environments/{environment}/off"
r = requests.post(url, headers={
"Authorization": UNLEASH_TOKEN,
"Content-Type": "application/json"
})
logger.info(f"Disabled flag {flag_name}: {r.status_code}")
return r.status_code == 200
def enable_feature_flag(flag_name, environment="production"):
url = f"{UNLEASH_URL}/api/admin/projects/default/features/{flag_name}/environments/{environment}/on"
r = requests.post(url, headers={
"Authorization": UNLEASH_TOKEN,
"Content-Type": "application/json"
})
logger.info(f"Enabled flag {flag_name}: {r.status_code}")
return r.status_code == 200
def notify_slack(message):
if SLACK_WEBHOOK:
requests.post(SLACK_WEBHOOK, json={"text": message})
@app.route("/webhook/betteruptime", methods=["POST"])
def handle_betteruptime():
data = request.json
monitor_name = data.get("data", {}).get("attributes", {}).get("pronounceable_name", "")
status = data.get("data", {}).get("attributes", {}).get("status", "")
flags = MONITOR_FLAG_MAP.get(monitor_name, [])
if not flags:
return jsonify({"status": "no_flags_mapped"}), 200
timestamp = datetime.now().isoformat()
if status == "down":
for flag in flags:
disable_feature_flag(flag)
notify_slack(f"[{timestamp}] ALERT: {monitor_name} is DOWN. Disabled flags: {', '.join(flags)}")
return jsonify({"action": "disabled", "flags": flags})
elif status == "up":
notify_slack(f"[{timestamp}] RECOVERY: {monitor_name} is UP. Flags remain disabled for manual review.")
return jsonify({"action": "recovery_noted", "flags": flags})
return jsonify({"status": "ignored"}), 200
@app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "ok"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9090)
Deploy middleware นี้ด้วย Docker
# Dockerfile สำหรับ middleware
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY ff_middleware.py .
EXPOSE 9090
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:9090", "ff_middleware:app"]
# requirements.txt
flask==3.0.0
gunicorn==21.2.0
requests==2.31.0
# สร้างและรัน
docker build -t ff-middleware .
docker run -d --name ff-middleware \
-p 9090:9090 \
-e UNLEASH_URL=http://unleash:4242 \
-e UNLEASH_TOKEN="*:*.unleash-admin-api-token" \
-e SLACK_WEBHOOK="https://hooks.slack.com/services/xxx" \
ff-middleware
ตั้งค่า Auto-Rollback เมื่อ Uptime ต่ำกว่า Threshold
นอกจากการปิด Feature Flag อัตโนมัติเมื่อ monitor down แล้ว ยังสามารถตั้งค่าให้ตรวจสอบ metrics อื่นๆเช่น Response Time, Error Rate หรือ Apdex Score เพื่อตัดสินใจ rollback ได้ละเอียดขึ้น
#!/usr/bin/env python3
# auto_rollback_checker.py — ตรวจสอบ metrics และ rollback อัตโนมัติ
import requests
import time
import json
import os
import sys
BETTERUPTIME_TOKEN = os.getenv("BETTERUPTIME_TOKEN")
UNLEASH_URL = os.getenv("UNLEASH_URL", "http://localhost:4242")
UNLEASH_TOKEN = os.getenv("UNLEASH_TOKEN")
THRESHOLDS = {
"uptime_percent_min": 99.5,
"response_time_max_ms": 2000,
"incidents_max": 2,
"check_interval_seconds": 300,
}
RECENT_FLAGS = [] # เก็บ flags ที่เพิ่งเปิดในช่วง 24 ชั่วโมง
def get_monitor_stats(monitor_id):
url = f"https://betteruptime.com/api/v2/monitors/{monitor_id}/sla"
r = requests.get(url, headers={"Authorization": f"Bearer {BETTERUPTIME_TOKEN}"})
if r.status_code != 200:
return None
data = r.json().get("data", {}).get("attributes", {})
return {
"availability": float(data.get("availability", 100)),
"response_time": float(data.get("average_response_time", 0)),
"incidents": int(data.get("number_of_incidents", 0)),
}
def get_recent_flags():
url = f"{UNLEASH_URL}/api/admin/events?type=feature-environment-enabled"
r = requests.get(url, headers={"Authorization": UNLEASH_TOKEN})
events = r.json().get("events", [])
recent = []
for e in events[:20]:
if e.get("environment") == "production":
recent.append(e.get("featureName"))
return recent
def disable_flag(flag_name):
url = f"{UNLEASH_URL}/api/admin/projects/default/features/{flag_name}/environments/production/off"
requests.post(url, headers={
"Authorization": UNLEASH_TOKEN,
"Content-Type": "application/json"
})
print(f"[ROLLBACK] Disabled: {flag_name}")
def check_and_rollback(monitor_id):
stats = get_monitor_stats(monitor_id)
if not stats:
print("Failed to get monitor stats")
return
print(f"Uptime: {stats['availability']}% | "
f"Response: {stats['response_time']}ms | "
f"Incidents: {stats['incidents']}")
needs_rollback = False
if stats["availability"] < THRESHOLDS["uptime_percent_min"]:
print(f"ALERT: Uptime {stats['availability']}% below threshold")
needs_rollback = True
if stats["response_time"] > THRESHOLDS["response_time_max_ms"]:
print(f"ALERT: Response time {stats['response_time']}ms above threshold")
needs_rollback = True
if stats["incidents"] > THRESHOLDS["incidents_max"]:
print(f"ALERT: {stats['incidents']} incidents above threshold")
needs_rollback = True
if needs_rollback:
recent_flags = get_recent_flags()
for flag in recent_flags:
disable_flag(flag)
print(f"Rolled back {len(recent_flags)} flags")
else:
print("All metrics OK")
if __name__ == "__main__":
monitor_id = sys.argv[1] if len(sys.argv) > 1 else "MONITOR_ID"
while True:
check_and_rollback(monitor_id)
time.sleep(THRESHOLDS["check_interval_seconds"])
ตั้ง cron job หรือ Kubernetes CronJob ให้รันตรวจสอบทุก 5 นาที
# Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: uptime-flag-checker
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: checker
image: python:3.12-slim
command: ["python3", "/app/auto_rollback_checker.py", "MONITOR_ID"]
env:
- name: BETTERUPTIME_TOKEN
valueFrom:
secretKeyRef:
name: monitoring-secrets
key: betteruptime-token
- name: UNLEASH_URL
value: "http://unleash.default:4242"
- name: UNLEASH_TOKEN
valueFrom:
secretKeyRef:
name: monitoring-secrets
key: unleash-token
restartPolicy: OnFailure
Dashboard และ Alerting สำหรับ Feature Flag
สร้าง Status Page ที่แสดงสถานะของ Feature Flag ร่วมกับ Uptime ได้ด้วย Better Uptime Status Page API
# สร้าง Status Page
curl -X POST "https://betteruptime.com/api/v2/status-pages" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subdomain": "status-myapp",
"company_name": "MyApp",
"company_url": "https://example.com",
"timezone": "Asia/Bangkok",
"subscribable": true
}'
# เพิ่ม Resource เข้า Status Page
curl -X POST "https://betteruptime.com/api/v2/status-pages/STATUS_PAGE_ID/resources" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resource_id": "MONITOR_ID",
"resource_type": "Monitor",
"public_name": "API Server",
"status_page_section_id": "SECTION_ID"
}'
สำหรับ Grafana Dashboard ที่แสดงข้อมูล Feature Flag และ Uptime ร่วมกัน ใช้ Unleash Metrics API และ Better Uptime API เป็น data source
# Prometheus metrics exporter สำหรับ Feature Flag status
# ff_exporter.py
from prometheus_client import start_http_server, Gauge
import requests
import time
import os
UNLEASH_URL = os.getenv("UNLEASH_URL", "http://localhost:4242")
UNLEASH_TOKEN = os.getenv("UNLEASH_TOKEN")
flag_status = Gauge("feature_flag_enabled", "Feature flag status", ["flag_name", "environment"])
flag_rollout = Gauge("feature_flag_rollout_percent", "Rollout percentage", ["flag_name"])
def collect_metrics():
r = requests.get(f"{UNLEASH_URL}/api/admin/projects/default/features",
headers={"Authorization": UNLEASH_TOKEN})
features = r.json().get("features", [])
for f in features:
name = f["name"]
for env in f.get("environments", []):
enabled = 1 if env.get("enabled") else 0
flag_status.labels(flag_name=name, environment=env["name"]).set(enabled)
for strategy in env.get("strategies", []):
rollout = strategy.get("parameters", {}).get("rollout", "100")
flag_rollout.labels(flag_name=name).set(float(rollout))
if __name__ == "__main__":
start_http_server(9091)
while True:
collect_metrics()
time.sleep(30)
FAQ คำถามที่พบบ่อย
Q: Better Uptime กับ UptimeRobot ต่างกันอย่างไร?
A: Better Uptime มี Incident Management ในตัว รองรับ On-call Scheduling และ Status Page ที่สวยกว่า ส่วน UptimeRobot เน้นความเรียบง่ายและมีแผนฟรีที่ใช้งานได้ดี สำหรับระบบที่ต้องการ Webhook Integration กับ Feature Flag แนะนำ Better Uptime เพราะ API ครบถ้วนกว่า
Q: Feature Flag ทำให้ระบบช้าลงไหม?
A: ผลกระทบน้อยมากถ้าใช้ SDK ที่ cache flag evaluation ไว้ในหน่วยความจำ Unleash SDK จะ poll ค่า flag จาก server ทุก 15 วินาทีแล้วเก็บไว้ใน memory ดังนั้นการ evaluate flag จะใช้เวลาเพียงไม่กี่ microsecond ไม่กระทบ response time ของแอปพลิเคชัน
Q: ควร self-host Unleash หรือใช้ Managed Service?
A: ถ้าทีมมี DevOps ที่ดูแล infrastructure ได้ self-host จะประหยัดค่าใช้จ่ายและควบคุมข้อมูลได้เต็มที่ แต่ถ้าต้องการความสะดวกและ SLA ที่รับประกัน Unleash Cloud หรือ LaunchDarkly เป็นตัวเลือกที่ดี สำหรับองค์กรที่ต้องการ compliance เช่น SOC2 หรือ GDPR แนะนำ self-host เพราะข้อมูลอยู่ใน infrastructure ของตัวเอง
Q: ถ้า Feature Flag Server ล่มจะเกิดอะไรขึ้น?
A: Unleash SDK มี fallback mechanism ถ้าเชื่อมต่อ server ไม่ได้จะใช้ค่าล่าสุดที่ cache ไว้ และสามารถกำหนด default value ได้เมื่อไม่สามารถ evaluate flag ได้ ดังนั้นแม้ Feature Flag Server ล่มแอปพลิเคชันจะยังทำงานได้ตามปกติด้วย flag value ล่าสุด
