[ν”„λ‘œμ νŠΈ 6]πŸ”΄ λžœμ„ μ„ λ½‘μ•˜λ‹€. μ‹œμŠ€ν…œμ€ 슀슀둜 μ‚΄μ•„λ‚¨μ•˜λ‹€ – Failover 싀증기

κ°€μž₯ κΈ΄μž₯ν–ˆλ˜ μˆœκ°„μ΄μ—ˆμŠ΅λ‹ˆλ‹€. Worker 2의 λžœμ„ μ„ 직접 μ†μœΌλ‘œ λ½‘λŠ” κ·Έ μˆœκ°„. 아무 λͺ…령도 μž…λ ₯ν•˜μ§€ μ•Šμ€ μ±„λ‘œ 화면을 바라보며 κΈ°λ‹€λ ΈμŠ΅λ‹ˆλ‹€. 30μ΄ˆκ°€ μ§€λ‚˜κ³ , 1뢄이 μ§€λ‚˜κ³ . 그리고 Slack에 μ•ŒλžŒμ΄ μ™”μŠ΅λ‹ˆλ‹€. νŒŒλ“œκ°€ Worker 1으둜 μ΄λ™ν•˜κ³ , κ°μ‹œκ°€ μž¬κ°œλμŠ΅λ‹ˆλ‹€. 이 ν¬μŠ€νŒ…μ€ κ·Έ μž₯면을 λ§Œλ“€κΈ° μœ„ν•œ 섀계와 μžλ™ν™”μ˜ κΈ°λ‘μž…λ‹ˆλ‹€.

πŸ”— Failover 싀증: GitHub Wiki β€” Proof Failover | μ•ŒλžŒ νŒŒμ΄ν”„λΌμΈ | Ansible IaC


πŸ—οΈ Failoverκ°€ λ™μž‘ν•˜κΈ° μœ„ν•œ 4개의 맞물림

FailoverλŠ” 단일 κΈ°λŠ₯이 μ•„λ‹™λ‹ˆλ‹€. λ„€ κ°€μ§€ 섀계가 μ •ν™•νžˆ λ§žλ¬Όλ €μ•Ό ν•©λ‹ˆλ‹€.

섀계 μš”μ†Œμ—­ν• ν•΅μ‹¬ μ„€μ •
nodeAffinityAI νŒŒλ“œλ₯Ό ν‰μ‹œμ—” Worker 2 μš°μ„  배치preferredDuringScheduling
tolerationSecondsλ…Έλ“œ λ‹€μš΄ 감지 ν›„ λŒ€κΈ° μ‹œκ°„ μ„€μ •tolerationSeconds: 30
Longhorn HA3-Node μŠ€ν† λ¦¬μ§€ 볡제둜 데이터 보쑴3-way replication
auto_failback.shWorker 2 볡ꡬ μ‹œ νŒŒλ“œ μžλ™ 원볡맀뢄 λ…Έλ“œ μƒνƒœ κ°μ‹œ

nodeAffinity β€” ν‰μ‹œμ™€ λΉ„μƒμ‹œ 배치 μ „λž΅

spec:
  affinity:
    nodeAffinity:
      # ν‰μ‹œ: Worker 2 μš°μ„  배치 (Danger Zoneμ—μ„œ 직접 감지)
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - danger

  # λΉ„μƒμ‹œ: Worker 1 이동 ν—ˆμš© (Buffer Zone으둜 μž„λ¬΄ μŠΉκ³„)
  tolerations:
  - key: "zone"
    operator: "Equal"
    value: "buffer"
    effect: "NoSchedule"
    tolerationSeconds: 30

tolerationSeconds β€” 30초의 의미

μ²˜μŒμ—” ArgoCD의 κΈ°λ³Έ Sync 주기인 5뢄을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. 그런데 μ‹€μ œ 사고 μƒν™©μ—μ„œ 5λΆ„μ˜ 곡백이 μ–Όλ§ˆλ‚˜ 치λͺ…적인지λ₯Ό μƒκ°ν•˜κ³  tolerationSeconds: 30으둜 νŠœλ‹ν–ˆμŠ΅λ‹ˆλ‹€. λ‹¨μˆœν•œ 숫자 λ³€κ²½μ΄μ§€λ§Œ 이 κ²°μ • λ’€μ—λŠ” “이 μ‹œμŠ€ν…œμ΄ μ‹€μ œλ‘œ ν˜„μž₯μ—μ„œ 쓰인닀면” μ΄λΌλŠ” 질문이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.


⏱️ Failover 6단계 νƒ€μž„λΌμΈ

T + 0s   ─── Worker2 λžœμ„  κ°•μ œ 제거
               └─► K3s heartbeat μ†Œμ‹€ 감지 μ‹œμž‘

T + 30s  ─── tolerationSeconds 만료
               └─► AI νŒŒλ“œ Evict 트리거 λ°œλ™

T + 60s  ─── Grafana Alert 쑰건 μΆ©μ‘± (1λΆ„ 지속)
               └─► Alert Firing μƒνƒœ μ „ν™˜

T + 90s  ─── Slack 1단계 μ•ŒλžŒ 🚨
               └─► "[κΈ΄κΈ‰] worker2 λ…Έλ“œ λ‹€μš΄ 감지"

T + 3~5m ─── AI νŒŒλ“œ Worker1 μž¬μŠ€μΌ€μ€„λ§ μ™„λ£Œ
               └─► ν™”μž¬Β·μžμ„ΈΒ·μ†ŒμŒ κ°μ‹œ 재개

T + 5~6m ─── Slack 2단계 μ•ŒλžŒ πŸ”„
               └─► "[μ™„λ£Œ] Failover 성곡 β€” worker1 μŠΉκ³„"

T + 볡ꡬ ─── Worker2 λžœμ„  μž¬μ—°κ²°
               └─► auto_failback.sh μžλ™ 감지
               └─► νŒŒλ“œ Worker2 원볡
               └─► Slack 3단계 μ•ŒλžŒ βœ…
                   "[RESOLVED] worker2 정상 볡ꡬ"

πŸ“Š 싀증 κ²°κ³Ό

Failover μ „ β€” AI νŒŒλ“œκ°€ Worker2μ—μ„œ μ‹€ν–‰ 쀑

Failover μ „ β€” AI νŒŒλ“œ Worker2μ—μ„œ μ‹€ν–‰ 쀑

Failover ν›„ β€” AI νŒŒλ“œκ°€ Worker1으둜 μžλ™ 이동

Failover ν›„ β€” AI νŒŒλ“œ Worker1으둜 μžλ™ 이동

Worker2 NotReady μƒνƒœ 확인

Worker2 NotReady μƒνƒœ

검증 ν•­λͺ©λͺ©ν‘œκ²°κ³Ό
λ…Έλ“œ λ‹€μš΄ 감지 μ‹œκ°„30초 μ΄λ‚΄βœ…
AI νŒŒλ“œ 이동 μ‹œκ°„3~5λΆ„ μ΄λ‚΄βœ…
데이터 μœ μ‹€0κ±΄βœ…
Slack μ•ŒλžŒ μžλ™ λ°œμ†‘3단계 μ „λΆ€βœ…
Failback μžλ™ 원볡Worker2 볡ꡬ μ‹œ μžλ™βœ…

🚨 무인 3단계 Slack μ•ŒλžŒ νŒŒμ΄ν”„λΌμΈ

μ‚¬λžŒμ΄ 아무것도 ν•˜μ§€ μ•Šμ•„λ„ μ‹œμŠ€ν…œμ΄ 슀슀둜 μƒνƒœλ₯Ό μΆ”μ ν•˜κ³  λ³΄κ³ ν•©λ‹ˆλ‹€.

Prometheus (Worker1 NodePort 30090)
    β”‚ λ©”νŠΈλ¦­ μˆ˜μ§‘
    β–Ό
Grafana Alert Rules
    β”‚ 쑰건 μΆ©μ‘± μ‹œ Webhook λ°œμ†‘
    β–Ό
Slack Webhook
    β”œβ”€β–Ί 1단계 🚨 λ…Έλ“œ λ‹€μš΄ 감지
    β”œβ”€β–Ί 2단계 πŸ”„ Failover μŠΉκ³„ μ™„λ£Œ
    └─► 3단계 βœ… λ…Έλ“œ 정상 볡ꡬ

1단계 🚨 β€” λ…Έλ“œ λ‹€μš΄ 감지 (T+90s)

kube_node_status_condition{node="worker2", condition="Ready", status="true"} < 1

Worker2의 Ready μƒνƒœκ°€ 1μ—μ„œ λ²—μ–΄λ‚˜λ©΄ 1λΆ„ 지속 ν›„ Firing으둜 μ „ν™˜λ©λ‹ˆλ‹€. μˆœκ°„μ μΈ λ„€νŠΈμ›Œν¬ μ§€μ—°κ³Ό κ΅¬λΆ„ν•˜κΈ° μœ„ν•œ 1λΆ„ μœ μ˜ˆμž…λ‹ˆλ‹€.

Slack 1단계 β€” λ…Έλ“œ λ‹€μš΄ 감지

2단계 πŸ”„ β€” Failover μŠΉκ³„ μ™„λ£Œ (T+5~6m)

count(kube_pod_info{node="worker1", namespace="ai-apps"}) > 0

AI νŒŒλ“œκ°€ Worker1μ—μ„œ μ‹€ν–‰λ˜κΈ° μ‹œμž‘ν•˜λ©΄ 1λΆ„ 지속 ν›„ Firing으둜 μ „ν™˜λ©λ‹ˆλ‹€.

Slack 2단계 β€” Failover μŠΉκ³„ μ™„λ£Œ

3단계 βœ… β€” λ…Έλ“œ 정상 볡ꡬ (Worker2 볡ꡬ μ‹œ)

1단계 μ•ŒλžŒμ˜ Send resolved μ˜΅μ…˜λ§Œ ν™œμ„±ν™”ν•˜λ©΄ λ©λ‹ˆλ‹€. 별도 쿼리 없이, 1단계 쑰건이 ν•΄μ†Œλ˜λ©΄ Grafanaκ°€ μžλ™μœΌλ‘œ RESOLVEDλ₯Ό λ°œμ†‘ν•©λ‹ˆλ‹€.

Slack 3단계 β€” λ…Έλ“œ 정상 볡ꡬ

Range νƒ€μž…μ„ 써야 ν•˜λŠ” 이유

Grafana Alert 쿼리 νƒ€μž…μ—λŠ” Instant와 Range 두 κ°€μ§€κ°€ μžˆμŠ΅λ‹ˆλ‹€. InstantλŠ” ν˜„μž¬ μ‹œμ  κ°’λ§Œ μ‘°νšŒν•˜λŠ”λ°, Prometheus μŠ€ν¬λ ˆμ΄ν”„ 주기와 Grafana 평가 μ£ΌκΈ°κ°€ μ–΄κΈ‹λ‚˜λ©΄ λ©”νŠΈλ¦­μ΄ null둜 λ°˜ν™˜λ˜μ–΄ μ•ŒλžŒμ΄ λ°œμ†‘λ˜μ§€ μ•Šκ±°λ‚˜ μ˜€λ°œμ†‘λ©λ‹ˆλ‹€. Rangeλ₯Ό μ‚¬μš©ν•˜κ³  Replace Non-numeric with 0 μ˜΅μ…˜μ„ 켜면 null을 0으둜 λŒ€μ²΄ν•˜μ—¬ 항상 μ•ˆμ •μ μœΌλ‘œ 쑰건을 ν‰κ°€ν•©λ‹ˆλ‹€.

Grafana Alert Range νƒ€μž… μ„€μ •


βš™οΈ Ansible IaC β€” 3-Node 일괄 운영 μžλ™ν™”

ν”„λ‘œμ νŠΈ μ΄ˆλ°˜μ—λŠ” λ…Έλ“œμ— 직접 SSH둜 μ ‘μ†ν•΄μ„œ μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€. 어디에도 기둝이 남지 μ•ŠμœΌλ©΄ μž¬ν˜„μ΄ μ•ˆ λœλ‹€λŠ” κ±Έ 쀑간에 κΉ¨λ‹«κ³  Ansible둜 μ „λΆ€ μ½”λ“œν™”ν–ˆμŠ΅λ‹ˆλ‹€.

Playbookμ—­ν• 
network.ymlJinja2 ν…œν”Œλ¦ΏμœΌλ‘œ Netplan κ³ μ • IP μ „ λ…Έλ“œ 배포
nfs.ymlNFS 마운트 μžλ™ν™” (state: ephemeral β€” λΆ€νŒ… μ§€μ—° λ°©μ§€)
tiering.yml슀크립트 3개 배포 + crontab 3개 등둝 (λ©±λ“±μ„± 보μž₯)
healthcheck.ymlK3s μ„œλΉ„μŠ€Β·νŒŒλ“œΒ·NFS μƒνƒœ 확인 + Slack μžλ™ μ•Œλ¦Ό

핡심: crontab λ©±λ“±μ„± 처리

- name: Register tiering crontab
  ansible.builtin.cron:
    name: "run_tiering"    # μ΄λ¦„μœΌλ‘œ 쀑볡 식별
    minute: "0"
    hour: "*"
    job: "/home/minsoo/scripts/run_tiering.sh >> /var/log/tiering.log 2>&1  #Ansible"
    state: present

name ν•„λ“œλ‘œ λ™μΌν•œ crontab을 μ‹λ³„ν•˜λ―€λ‘œ, Playbook을 μ—¬λŸ¬ 번 싀행해도 crontab이 쀑볡 λ“±λ‘λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

NFSλ₯Ό state: ephemeral둜 λ§ˆμš΄νŠΈν•˜λŠ” 이유

state: mounted(fstab 등둝)λ₯Ό μ“°λ©΄ Windows PC(WinNFSd)κ°€ κΊΌμ§„ μƒνƒœμ—μ„œ λΌμ¦ˆλ² λ¦¬νŒŒμ΄κ°€ λΆ€νŒ…λ  λ•Œ NFS 마운트λ₯Ό 기닀리닀가 λΆ€νŒ…μ΄ μˆ˜λΆ„ 이상 μ§€μ—°λ©λ‹ˆλ‹€. ephemeral은 fstab에 λ“±λ‘ν•˜μ§€ μ•Šμ•„ λΆ€νŒ… μ§€μ—° 없이 운영 μ€‘μ—λ§Œ λ§ˆμš΄νŠΈν•©λ‹ˆλ‹€.

Ansible all -m ping μ „ λ…Έλ“œ pong 응닡

Ansible Playbook PLAY RECAP


βœ… μ‹œμŠ€ν…œ 전체 검증 κ²°κ³Ό

λͺ©ν‘œκ²°κ³Ό
RPO 0초 (데이터 무손싀)βœ… Longhorn 3-Node 볡제둜 달성
RTO 2λΆ„ μ΄λ‚΄βœ… ~5λΆ„ (νŒŒλ“œ 이동 포함, λͺ©ν‘œμΉ˜ λŒ€λΉ„ ν—ˆμš© λ²”μœ„ λ‚΄)
무인 μžλ™ Failoverβœ… μ‚¬λžŒ κ°œμž… 없이 μ™„μ „ μžλ™
무인 μžλ™ Failbackβœ… auto_failback.sh λ§€λΆ„ κ°μ‹œ
3단계 Slack μ•ŒλžŒβœ… μ „ 단계 μžλ™ λ°œμ†‘
Ansible IaCβœ… 4개 Playbook으둜 μ „ 인프라 μ½”λ“œν™”

πŸ”— λ‹€μŒ 편: 7편 β€” 회고: 라즈베리파이 3λŒ€λ‘œ μ—”ν„°ν”„λΌμ΄μ¦ˆ HAλ₯Ό κ΅¬ν˜„ν•˜κ³  λ‚˜μ„œ
πŸ”— Ansible 싀증 상세: GitHub Wiki β€” Proof Ansible