Health Check

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/ready dan parameter fall 3 rise 2 untuk routing yang lebih presisi.
  • startupProbe di Kubernetes untuk aplikasi dengan startup time lama — mencegah liveness probe membunuh aplikasi yang masih dalam proses startup.

← Sebelumnya: Tracing   Berikutnya: Incident Response →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact