Health Check #
Monitoring memberitahu kamu saat sesuatu sudah rusak. Health check mencegah traffic dikirim ke komponen yang belum siap atau sudah rusak. Keduanya perlu — tapi health check bekerja di lapisan yang lebih real-time dan lebih operasional: load balancer mengandalkannya untuk routing, Kubernetes mengandalkannya untuk restart otomatis, dan pipeline deployment mengandalkannya untuk menentukan apakah deployment berhasil atau perlu di-rollback. Artikel ini membahas cara mengimplementasikan dan mengotomasi health check yang tepat menggunakan Ansible.
Dua Jenis Health Check yang Berbeda #
Sebelum mengimplementasikan, pahami perbedaan antara dua jenis health check:
Liveness Check — "Apakah aplikasi masih hidup?"
Tujuan: Deteksi deadlock atau kondisi yang tidak bisa pulih sendiri
Respons saat gagal: Container di-restart
Contoh endpoint: GET /health/live → 200 jika proses berjalan
Yang TIDAK boleh dicek: koneksi database, dependency eksternal
Alasan: Jika database down, kamu tidak mau semua container ikut restart
Readiness Check — "Apakah aplikasi siap menerima traffic?"
Tujuan: Cegah traffic ke instance yang belum siap atau sedang overwhelmed
Respons saat gagal: Instance dikeluarkan dari load balancer rotation
Contoh endpoint: GET /health/ready → 200 jika semua dependency siap
Yang BOLEH dicek: koneksi database, cache, dependency kritis
Alasan: Jika database tidak terjangkau, instance memang belum siap melayani
Mengimplementasikan Health Endpoint di Aplikasi #
Untuk aplikasi Python/Flask, tambahkan endpoint health check:
# roles/app-health/tasks/main.yml
---
- name: Deploy health check module ke aplikasi
template:
src: health.py.j2
dest: "{{ app_dir }}/health.py"
owner: "{{ app_user }}"
mode: '0644'
notify: Restart aplikasi
# templates/health.py.j2
# Health check endpoints untuk {{ app_name }}
from flask import Blueprint, jsonify
import psycopg2
import redis
import time
health_bp = Blueprint('health', __name__)
@health_bp.route('/health/live')
def liveness():
"""Liveness: cek proses masih berjalan — tidak cek dependency"""
return jsonify({
"status": "ok",
"service": "{{ app_name }}",
"version": "{{ app_version }}",
"timestamp": time.time()
}), 200
@health_bp.route('/health/ready')
def readiness():
"""Readiness: cek semua dependency siap — return 503 jika ada yang gagal"""
checks = {}
overall_status = "ok"
# Cek database
try:
conn = psycopg2.connect("{{ db_url }}")
conn.close()
checks["database"] = "ok"
except Exception as e:
checks["database"] = f"failed: {str(e)}"
overall_status = "degraded"
# Cek Redis
try:
r = redis.from_url("{{ redis_url }}")
r.ping()
checks["redis"] = "ok"
except Exception as e:
checks["redis"] = f"failed: {str(e)}"
overall_status = "degraded"
status_code = 200 if overall_status == "ok" else 503
return jsonify({
"status": overall_status,
"checks": checks,
"timestamp": time.time()
}), status_code
Health Check Berbasis Ansible untuk Post-Deployment #
Setelah deployment, Ansible perlu memverifikasi bahwa semua komponen sehat sebelum deployment dianggap berhasil:
# playbooks/verify-deployment.yml
---
- name: Verifikasi health setelah deployment
hosts: appservers
gather_facts: false
tasks:
- name: Tunggu port aplikasi terbuka
wait_for:
port: "{{ app_port }}"
host: "{{ inventory_hostname }}"
timeout: 60
state: started
- name: Verifikasi liveness endpoint
uri:
url: "http://{{ inventory_hostname }}:{{ app_port }}/health/live"
method: GET
status_code: 200
timeout: 10
register: liveness_result
until: liveness_result.status == 200
retries: 6
delay: 10
- name: Verifikasi readiness endpoint
uri:
url: "http://{{ inventory_hostname }}:{{ app_port }}/health/ready"
method: GET
status_code: 200
timeout: 10
register: readiness_result
until: readiness_result.status == 200
retries: 12
delay: 10
failed_when: readiness_result.status not in [200]
- name: Verifikasi versi yang berjalan sesuai target
assert:
that:
- readiness_result.json.version is defined
- readiness_result.json.version == app_version
fail_msg: >
Versi tidak sesuai!
Diharapkan: {{ app_version }}
Aktual: {{ readiness_result.json.version | default('tidak terdeteksi') }}
- name: Tampilkan ringkasan health check
debug:
msg:
- "✓ Liveness: {{ liveness_result.json.status }}"
- "✓ Readiness: {{ readiness_result.json.status }}"
- "✓ Version: {{ readiness_result.json.version }}"
- "✓ Database: {{ readiness_result.json.checks.database }}"
- "✓ Redis: {{ readiness_result.json.checks.redis }}"
Konfigurasi Health Check di Load Balancer #
Deploy konfigurasi health check ke HAProxy menggunakan Ansible:
# roles/haproxy/tasks/health-check.yml
---
- name: Deploy konfigurasi HAProxy dengan health check
template:
src: haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
validate: haproxy -c -f %s
notify: Reload HAProxy
{# templates/haproxy.cfg.j2 #}
global
log /dev/log local0
maxconn 50000
defaults
log global
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend http_front
bind *:80
default_backend app_servers
backend app_servers
balance roundrobin
option httpchk GET /health/ready HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
{% for host in groups['appservers'] %}
server {{ host }} {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ app_port }} \
check interval 10s fall 3 rise 2
{% endfor %}
Konfigurasi fall 3 rise 2 berarti: keluarkan server dari rotation setelah 3 health check gagal berturut-turut, kembalikan setelah 2 health check berhasil.
Health Check untuk Kubernetes Probe #
Konfigurasi liveness dan readiness probe di Kubernetes Deployment menggunakan Ansible:
- name: Deploy Deployment dengan probe yang dikonfigurasi dengan benar
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
spec:
template:
spec:
containers:
- name: "{{ app_name }}"
image: "{{ app_image }}:{{ app_version }}"
livenessProbe:
httpGet:
path: /health/live
port: "{{ app_port }}"
initialDelaySeconds: 15 # Tunggu 15 detik sebelum mulai cek
periodSeconds: 10 # Cek setiap 10 detik
failureThreshold: 3 # Restart setelah 3 kali gagal
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /health/ready
port: "{{ app_port }}"
initialDelaySeconds: 5 # Lebih cepat dari liveness
periodSeconds: 5
failureThreshold: 3 # Keluarkan dari traffic setelah 3 kali gagal
successThreshold: 1 # Kembalikan setelah 1 kali berhasil
timeoutSeconds: 3
startupProbe: # Untuk aplikasi yang butuh waktu lama untuk start
httpGet:
path: /health/live
port: "{{ app_port }}"
failureThreshold: 30 # Beri waktu 300 detik (30 x 10s) untuk startup
periodSeconds: 10
Health Check Dashboard #
Buat dashboard Grafana yang menampilkan status health semua service:
- name: Deploy health check dashboard
copy:
src: files/dashboards/health-overview.json
dest: /var/lib/grafana/dashboards/health-overview.json
owner: grafana
mode: '0640'
notify: Reload Grafana dashboards
Alert rule untuk health check yang gagal:
# roles/prometheus/templates/rules/health-checks.yml.j2
groups:
- name: health_checks
rules:
- alert: ServiceHealthCheckFailing
expr: >
probe_success{job="blackbox"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Health check gagal: {{ '{{' }} $labels.instance {{ '}}' }}"
description: "Endpoint {{ '{{' }} $labels.instance {{ '}}' }} tidak merespons selama 2 menit"
runbook_url: "https://wiki.company.com/runbooks/service-down"
Ringkasan #
- Liveness dan readiness adalah dua endpoint yang berbeda dengan tujuan berbeda — jangan gabungkan keduanya dalam satu endpoint.
- Liveness probe tidak boleh cek dependency (database, Redis) — jika dependency down, kamu tidak mau semua container ikut restart dan memperparah situasi.
- Readiness probe boleh dan harus cek dependency — instance yang dependency-nya tidak terjangkau memang belum siap melayani traffic.
- Dalam deployment pipeline Ansible, selalu verify deployment dengan health check setelah rolling update sebelum marking deployment sebagai sukses.
- Konfigurasi HAProxy dengan
httpchk GET /health/readydan parameterfall 3 rise 2untuk routing yang lebih presisi.startupProbedi Kubernetes untuk aplikasi dengan startup time lama — mencegah liveness probe membunuh aplikasi yang masih dalam proses startup.