포스트

알럿은 Slack으로 — webhook이 404(no_service) 뱉던 삽질과 SSM 하드닝

알럿은 Slack으로 — webhook이 404(no_service) 뱉던 삽질과 SSM 하드닝

지표를 모아 봐도 “이상하면 어떻게 알지?”가 남는다. 사람이 대시보드를 24시간 보고 있을 순 없으니, 규칙에 걸리면 Alertmanager가 Slack으로 알리게 했다. 그 과정에서 webhook이 404 no_service를 뱉는 삽질과, webhook 비밀을 SSM으로 뺀 기록.

이 글은 QA 관측 스택 구축기의 ④ Alert→Slack 조각을 자세히 푼 것이다. 계정 번호 자리는 <계정ID>로 적는다.

flowchart LR
    P["Prometheus<br/>alert.rules.yml 평가"] -->|"조건 충족 (예: up==0 1분)"| AM["Alertmanager<br/>group · dedup · route"]
    AM -->|"webhook POST"| SL["Slack 채널"]

알럿 규칙 + 발화 검증

먼저 규칙 둘을 만들었다 — InstanceDown(타겟이 죽으면)과 HighMemory(메모리 85% 초과).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
groups:
  - name: basic
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 1m
        labels: { severity: critical }
        annotations:
          summary: "Target down:  "
      - alert: HighMemory
        expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 85
        for: 2m
        labels: { severity: warning }
        annotations:
          summary: "High memory on "

for: 1m은 “조건이 1분 동안 계속 참이어야 발화”라는 뜻이다(순간 깜빡임으로 안 울리게). 일부러 타겟을 내려 up == 0을 만들자 1분 뒤 FIRING으로 바뀌었다.

Prometheus Alerts — InstanceDown FIRING

InstanceDown FIRING — up == 0이 1분 지속되자 빨간 FIRING. 이게 Alertmanager로 넘어가 Slack으로 가야 한다.

막힌 두 함정

함정 1 — webhook이 404 no_service (채널 오버라이드 거부)

Slack Incoming Webhook을 만들고 alertmanager.yml에 channel: '#qa-alerts'로 보낼 채널을 적었다. 그런데 Alertmanager 로그에 —

Alertmanager 로그의 channel 404 no_service 에러

1
2
3
level=ERROR msg="Notify for alerts failed"
  err="slack-default/slack[0]: notify retry canceled due to unrecoverable error after 1 attempts:
       channel \"#qa-alerts\": unexpected status code 404: no_service"

원인: 앱 기반 Slack webhook은 생성 시점에 채널이 고정된다. payload로 channel을 오버라이드하려 하면 거부(404 no_service)한다. → 해결은 간단하다. alertmanager.yml에서 channel: 줄을 빼면 webhook이 자기 고정 채널로 보낸다.

함정 2 — “안 온다”는 착각 (dedup)

channel을 고쳤는데도 테스트가 “안 왔다”. 알고 보니 같은 alertname은 repeat_interval(4h) 전에는 재통지를 안 한다(중복 억제, dedup). 이미 한 번 알린 그룹이라 조용했던 것. → 테스트는 매번 유니크한 alertname(SlackTest-$(date +%s) 같은)으로 해야 한다.

최종 alertmanager.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
route:
  receiver: slack-default
  group_by: [alertname]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
  - name: slack-default
    slack_configs:
      - api_url_file: /etc/alertmanager/slack_webhook_url   # URL을 파일에서 읽음 (아래 참고)
        send_resolved: true
        # channel: 줄 없음 ← 앱 webhook은 채널 고정이라 오버라이드 금지
        title: ''
        text: '\n'

webhook 비밀을 SSM으로 빼기

webhook URL은 그 자체가 비밀(아는 사람은 누구나 그 채널에 글 쓸 수 있음)이다. config/git에 박으면 커밋에 유출되니, SSM Parameter Store(SecureString)에 넣고 박스는 실행 때만 파일로 받아 쓰게 했다.

SSM Parameter Store의 slack-webhook SecureString 파라미터

SSM 파라미터 — /myton/qa/alertmanager/slack-webhook을 SecureString으로. 목록엔 이름·타입만 보이고 값은 KMS로 암호화돼 숨는다. (우상단 계정 정보는 가림)

박스에서 받아 파일로 저장 → compose로 마운트:

1
2
3
aws ssm get-parameter --name /myton/qa/alertmanager/slack-webhook \
  --with-decryption --query Parameter.Value --output text \
  | tr -d '\n' | sudo tee /opt/monitoring/slack_webhook_url
1
2
3
4
  alertmanager:
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
      - ./slack_webhook_url:/etc/alertmanager/slack_webhook_url:ro   # 파일로 주입

mon 박스 역할엔 그 파라미터만 읽는 최소 권한(+ 복호화)을 줬다.

1
2
3
4
5
6
7
8
9
{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": "ssm:GetParameter",
      "Resource": "arn:aws:ssm:ap-northeast-2:<계정ID>:parameter/myton/qa/alertmanager/slack-webhook" },
    { "Effect": "Allow", "Action": "kms:Decrypt", "Resource": "*",
      "Condition": { "StringEquals": { "kms:ViaService": "ssm.ap-northeast-2.amazonaws.com" } } }
  ]
}

함정 3 — api_url vs api_url_file: webhook URL을 파일로 뺐으면 alertmanager.yml에선 api_url_file(파일 경로에서 읽기)을 써야 한다. 파일 경로를 api_url(URL 직접)에 그대로 넣으면 Alertmanager가 그걸 URL로 해석해 unsupported scheme ""로 크래시한다.

남는 것 / 솔직한 평가

박스 1대짜리 QA에선 SSM 하드닝의 이득이 “gitignore한 파일”과 비교해 크지 않다. 진짜 가치는 다수 박스·재빌드·로테이션·감사에서 나온다 — 비밀을 한 곳(SSM)에 두고 IAM으로 접근을 통제하고 CloudTrail로 감사하며, 새 박스가 알아서 받아가고, 로테이션은 한 곳만 바꾸면 전파된다. 지금은 “좋은 습관 + 포폴 패턴” 성격이 크지만, 그 패턴을 작게라도 해두면 박스가 늘 때 그대로 확장된다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.