10 KiB
관측성 운영 접근·알림 전환 Runbook
목적
공용 관측성 플랫폼의 검증된 dashboard·rule·Alertmanager 설정을 적용한 뒤, 가장 마지막에 Grafana private HTTPS 경로를 여는 절차와 rollback 경계를 고정한다.
이 문서는 Secret 값, bearer token, Cookie, 사용자 ID, authorization code/state, webhook 원문을 기록하지 않는다. 실행하지 않은 검사는 성공으로 기록하지 않는다.
운영 alert의 runbook_url로 사용할 공개 위치는 다음 경로다. 이 파일이 해당
저장소에 실제 게시되어 HTTPS 200으로 도달하기 전에는 rules-alerts 적용을 시작하지
않는다.
https://git.learn.hyeonworks.com/donghyeon.kang/project-infra/src/branch/main/docs/runbooks/2026-07-31-observability-access-cutover.md
현재 상태
기준 시각: 2026-08-12T07:58:23Z
상태: 부분 구현 — substrate와 metric target은 live, rules-alerts와 full Nginx cutover는 미실행
- Prometheus active target
30, healthy30, scrape pool25, unhealthy0 - Grafana와 Blackbox Exporter Deployment 각각
1/1Ready - target-initial inventory SHA-256
79688d017d38eec9a6f100f8d0f784a5474e79802046ef1c2c11b30d170b0b0c - post-substrate inventory SHA-256
b1c3049206a1a88165ee672ae9aceac7945673a3bb9c3cf3670b7f0d56c3f291 - Host Nginx active config는 Grafana deny-only guard SHA-256
dbef6d443bcba58b26a5351ea76f6d09f6da8c2ef07a806e22745cf26c88f518 - full Host Nginx candidate SHA-256
7d2de2a92c3597a0859775da1d2ccbf5a3d72c0b2af5cac2439c82311361f801 - Grafana OIDC Deployment는 exact
grafana-keycloak-oidc의client-id,client-secret만 참조하며1/1Ready - 새 platform dashboard ConfigMap, 새 platform rule, AlertmanagerConfig와 Slack webhook Secret은 아직 live acceptance 전이다.
영향
rules-alerts 단계는 다음 object만 생성 또는 수정한다.
- dashboard ConfigMap 5개
- platform PrometheusRule 4개
- AlertmanagerConfig 1개
- kube-prometheus-stack Alertmanager/Prometheus owner object
- 필요한 observability NetworkPolicy
Grafana·Blackbox workload, Probe, Ingress, PVC, Secret과 Host Nginx는 이 단계에서 변경하지 않는다. full Nginx 단계는 rules-alerts acceptance가 끝난 뒤 Grafana upstream을 deny endpoint에서 loopback Traefik으로 전환한다.
사전 조건
다음 조건이 모두 통과해야 한다.
- current context와 API server가 각각
default,https://127.0.0.1:6443이다. - K3s Secret encryption은
reencrypt_finished이고 current restore evidence가 유효하다. - 같은 rollback ID 아래 root-only ledger root가
root:root 0700으로 존재한다. - Grafana local credential, Slack webhook, Keycloak의 recovery evidence가 각각 유효하다.
- Slack Secret은
observability/alertmanager-slack-webhook, typeOpaque, key는 정확히url하나다. 값은 검사 출력에 포함하지 않는다. - 두 inventory directory는
0700, 파일은0600, non-symlink, link count 1이며 위 SHA-256과 일치한다. - Blackbox private-edge source proof는 같은 rollback ID와 active deny SHA에
결속되고 24시간 이내이며 세 hostname 모두 status
403이다. - 이 runbook의 공개 URL이 HTTPS
200으로 도달하고 모든 새 alert annotation이 해당 URL을 사용한다. - public Grafana A/AAAA는 없고 LAN·Tailscale·Pod resolver만 승인된 private IP를 반환한다.
- Grafana exact-SAN 인증서, Keycloak discovery, Gitea/Admin endpoint, 내부 Grafana health가 정상이다.
절차 — 권위 실행 순서
아래 순서만 허용한다. 각 도구의 no-argument 실행과 focused test가 먼저 통과해야
하며, 현재 독립 검토에서 Ready: No인 도구는 실행하지 않는다.
cd /home/donghyeon/workspace/platform
OBS_ROLLBACK_ID="$(date -u +%Y%m%dT%H%M%SZ)"
[[ "$OBS_ROLLBACK_ID" =~ ^[0-9]{8}T[0-9]{6}Z$ ]]
: "${CERTBOT_EMAIL:?set the operator-managed Certbot contact email}"
sudo install -d -o root -g root -m 0700 \
"/var/lib/hyeonworks/platform-rollbacks/observability-$OBS_ROLLBACK_ID"
export PLATFORM_OBSERVABILITY_ROLLBACK_ID="$OBS_ROLLBACK_ID"
bash scripts/validate/observability-core-smoke.sh --execute
bash scripts/bootstrap/apply-private-dns.sh --execute
bash scripts/bootstrap/apply-host-nginx-observability.sh \
--execute --metrics-guard-only
bash scripts/bootstrap/apply-host-nginx-observability.sh \
--execute --certificate-only --certbot-email "$CERTBOT_EMAIL"
bash scripts/bootstrap/apply-host-nginx-observability.sh \
--execute --grafana-deny-guard-only
bash scripts/validate/validate-blackbox-edge-source.sh --execute --context default
bash scripts/validate/k3s-secret-encryption.sh --expect-reencrypted
bash scripts/validate/k3s-secret-encryption-restore-evidence.sh --check
bash scripts/bootstrap/create-observability-secrets.sh \
--execute --grafana-admin \
--grafana-admin-user-file /home/donghyeon/.secrets/grafana/admin-user \
--grafana-admin-password-file /home/donghyeon/.secrets/grafana/admin-password
bash scripts/bootstrap/create-observability-secrets.sh \
--execute --slack-webhook \
--slack-webhook-file /home/donghyeon/.secrets/alertmanager/slack-webhook
bash scripts/bootstrap/configure-keycloak-grafana-oidc.sh --execute
bash scripts/bootstrap/create-observability-secrets.sh \
--check-grafana-recovery-evidence
bash scripts/bootstrap/create-observability-secrets.sh \
--check-slack-recovery-evidence
bash scripts/bootstrap/configure-keycloak-grafana-oidc.sh \
--check-recovery-evidence
SOURCE_METRIC_ROOT=/tmp/platform-observability-metrics.VUpsZn
METRIC_ROOT="$(mktemp -d /tmp/platform-observability-metrics.XXXXXX)"
chmod 0700 "$METRIC_ROOT"
# Blackbox substrate가 이미 live이므로 target-initial을 현재 상태에서 재수집하면 안 된다.
# 실행 당시 보존한 두 authoritative inventory만 새 rules-alerts root로 복제한 뒤 renderer가
# schema, phase, file metadata와 위 두 hash를 다시 검증하게 한다.
[[ -d "$SOURCE_METRIC_ROOT/target-initial" ]]
[[ -d "$SOURCE_METRIC_ROOT/post-substrate" ]]
cp -a -- "$SOURCE_METRIC_ROOT/target-initial" "$METRIC_ROOT/"
cp -a -- "$SOURCE_METRIC_ROOT/post-substrate" "$METRIC_ROOT/"
chmod 0700 "$METRIC_ROOT/target-initial" "$METRIC_ROOT/post-substrate"
chmod 0600 "$METRIC_ROOT/target-initial/"* "$METRIC_ROOT/post-substrate/"*
PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \
bash scripts/validate/render-observability-access.sh \
--component rules-alerts --verified-output-dir "$METRIC_ROOT"
PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \
bash scripts/bootstrap/apply-observability-access.sh \
--execute --rules-alerts --verified-output-dir "$METRIC_ROOT"
bash scripts/bootstrap/apply-host-nginx-observability.sh \
--execute --verified-output-dir "$METRIC_ROOT"
사용자 group membership은 위 transaction과 분리한다. 실제 기존 realm username은 stdin으로 받고 명령 인자·이 문서에 기록하지 않는다.
검증 — 자동 수락 조건
rules-alerts 성공 조건:
- Prometheus와 Alertmanager controller가 Ready로 수렴한다.
- 모든 새 rule이 Prometheus API에서 구문·평가 가능하다.
- Alertmanager generated config에 exact
platform-slackreceiver가 있다. - Grafana sidecar가 exact dashboard ConfigMap 5개를 실제로 읽었다.
- 기존 active target set은 유지되고 down/error target이 없다.
- Grafana·Blackbox·Probe·Ingress·Host Nginx·PVC·Secret의 UID와 계약이 보존된다.
- root-only acceptance marker는 위 조건이 모두 끝난 뒤에만 기록된다.
full Nginx 성공 조건:
- reload 뒤 연속 세 번 안정된 probe가 통과한다.
- 승인된 LAN/Tailscale source의 Grafana는
200또는 로그인 redirect다. - 비허용 local/public source는
403이다. - Gitea와 Grafana 외부
/metrics는404다. - Keycloak·Gitea·storage-admin·db-admin 회귀가 없다.
- unknown SNI는 거부되고 LAN의 NodePort
30080/30443은 계속 refused다.
사람 확인
다음은 자동 검사로 대체하지 않는다.
- observability admin group 사용자의 Grafana organization
Admin, server adminfalse - viewer group 사용자의
Viewer와 edit/admin 동작 거부 - 두 group이 모두 없는 별도 사용자의 로그인 거부
- local break-glass 계정의 private path 로그인과 server admin 확인
- Slack temporary test alert의 firing/resolved 메시지, cluster/namespace/alertname grouping, severity와 이 runbook URL 확인
- LAN/Tailscale 밖 별도 client에서 public boundary 확인
확인 결과에는 역할·시각·성공/실패만 기록한다.
롤백
자동 rollback은 같은 PLATFORM_OBSERVABILITY_ROLLBACK_ID의 root-only ledger만
권위 입력으로 사용한다.
- full Nginx를 exact Grafana deny-only guard payload/hash로 복원하고
nginx -t, reload, 연속 probe를 확인한다. - rules-alerts ledger를 역순 처리한다. 이번 transaction의
false/apply만 UID precondition으로 삭제하고true/apply만 exact sanitized payload와 현재 UID/RV로 복원한다.controller는 owner 처리 뒤 수렴만 기다리고preserve는 변경하지 않는다. - Alertmanager/Prometheus/Grafana sidecar 수렴과 기존 target set을 다시 확인한다.
- target metric을 끄고 기존 endpoint 회귀를 확인하기 전에는 metrics guard보다 이전 Nginx 설정으로 돌아가지 않는다.
- PVC, Secret, CRD, PV, Loki/Tempo object와 bucket은 자동 삭제하지 않는다.
UID 변경, API timeout 뒤 결과 불명, payload drift, controller 비수렴 또는 ledger
불일치는 자동 성공으로 처리하지 않는다. 보호 정책과 rollback root를 보존하고
MANUAL_RECOVERY_REQUIRED로 에스컬레이션한다.
에스컬레이션
- Slack webhook이나 recovery evidence가 없으면 rules-alerts mutation
0으로 중단한다. - runbook URL이 실제 HTTPS로 도달하지 않으면 rule source를 적용하지 않는다.
- Blackbox source가
200/302로 보이면 source allowlist를 완화하지 않고 네트워크 경계를 재설계한다. - 외부 client 검증, 세 OIDC 역할 또는 Slack firing/resolved가 하나라도 없으면 전체
Phase 4를
부분 구현으로 유지한다. - Secret payload, token, Cookie 또는 원본 Nginx log line이 출력되면 즉시 중단하고 관련 credential을 별도 절차로 회전한다.