Slack 기반 AIOps로 NAS Agent 운영 자동화하기
Synology NAS self-hosted agent, Azure DevOps, Static Web Apps 배포 흐름을 Slack 중심의 AIOps 루프로 묶어 감시, 분류, 자동 복구, 향후 확장까지 설계한 운영 기록입니다.
- Azure Pipelines Agent
- 4.273.0
- Slack
- Webhook and slash command
- Runtime
- Synology NAS
이 글의 목차
이번 Slack 기반 AIOps 작업은 “알림 하나 더 붙이기”가 아니었다. 목표는 HWMOON publishing system의 운영 루프를 사람이 아침마다 확인하는 방식에서, 시스템이 먼저 감지하고 안전한 조치를 수행한 뒤 Slack에 기록을 남기는 방식으로 바꾸는 것이다.
Synology NAS self-hosted agent는 이미 Azure DevOps의 Microsoft-hosted minutes 한도를 피하기 위한 실행 계층으로 들어왔다. 하지만 runner를 직접 운영하기 시작하면 새로운 질문이 생긴다.
- agent가 online인지 어떻게 빠르게 알 수 있는가
- schedule이 꺼졌을 때 누가 먼저 발견하는가
- 글은 생성됐는데 배포가 안 됐을 때 어디서 멈췄는가
- 매번 Azure Portal, Azure DevOps, NAS Container Manager를 돌아다녀야 하는가
- 복구 가능한 작업까지 사람이 직접 눌러야 하는가
이 질문에 답하려면 Slack을 단순 메시지 채널이 아니라 운영 제어면으로 써야 한다. 여기서 말하는 AIOps는 과장된 “AI가 모든 것을 알아서 운영한다”가 아니다. 운영 이벤트를 수집하고, 상태를 분류하고, 반복 조치를 자동화하고, 필요한 경우 사람에게 판단 가능한 문맥을 주는 체계에 가깝다.
배경
news.hwmoon.com과 tech.hwmoon.com은 정적 사이트로 배포되지만, 운영은 정적이지 않다. 정해진 시간에 글을 만들고, 품질 검사를 통과시키고, 정적 빌드를 수행하고, Azure Static Web Apps에 배포해야 한다.
처음에는 Azure DevOps의 Microsoft-hosted agent에서 모든 작업을 실행했다.
Azure DevOps schedule
-> Microsoft-hosted agent
-> article generation
-> validation
-> static build
-> Azure Static Web Apps deploy
이 구조는 시작하기 쉽다. 하지만 자동 발행이 반복되면 hosted minutes가 병목이 된다. 실제로 job이 시작되기도 전에 아래와 같은 상태로 멈출 수 있다.
Your organization has no free minutes remaining.
그래서 이미 켜져 있는 Synology NAS를 Azure Pipelines self-hosted agent로 등록했다.
Azure DevOps schedule
-> self-hosted agent pool
-> Synology NAS agent container
-> article generation
-> validation
-> build and deploy
이 결정은 비용 관점에서는 맞았다. 하지만 이후에도 아침에 글이 제때 올라오지 않는 현상이 반복됐다. 원인은 한 가지가 아니었다.
| Symptom | Possible cause | Layer |
|---|---|---|
| scheduled run이 없다 | YAML schedule 또는 pipeline queue disabled | Azure DevOps |
| job이 queue에 머문다 | NAS agent offline 또는 busy | Runner |
| job은 성공했는데 글이 없다 | generation flag 또는 content window 문제 | Application |
| 글은 있는데 URL이 404다 | Static Web Apps deploy 누락 또는 route 지연 | Deploy |
| Container Manager가 지저분하다 | exited helper container 누적 | NAS |
이때 필요한 것은 더 많은 알림이 아니라 더 나은 운영 판단이다. “문제가 있다”에서 끝나면 사람이 다시 원인을 찾아야 한다. Slack AIOps의 목표는 “어디서 막혔고, 무엇을 자동 조치했고, 사람이 해야 할 일이 있는지”까지 한 메시지에 남기는 것이다.
왜 Slack이 운영 중심이 되어야 했나
개인 프로젝트라도 운영 표면은 금방 늘어난다. Azure DevOps, Azure Static Web Apps, NAS Docker, DNS, Git, 콘텐츠 생성 스크립트, 검색 색인, 광고 심사까지 서로 다른 시스템이 연결된다.
문제가 생겼을 때 매번 여러 화면을 열어 확인하는 방식은 오래 버티기 어렵다.
Slack을 운영 중심에 두려는 이유는 네 가지다.
| Reason | Explanation |
|---|---|
| 빠른 인지 | 장애가 발생한 뒤 사이트를 직접 열어보기 전에 먼저 알 수 있다. |
| 문맥 보존 | 원인, 조치, 결과가 한 메시지 thread에 남는다. |
| 안전한 수동 명령 | Portal 접속 없이 허용된 runbook만 실행할 수 있다. |
| 향후 AI 확장 | 로그 요약, 원인 분류, 권장 조치를 같은 채널에서 제공할 수 있다. |
Slack은 운영자가 이미 보는 화면이다. 그래서 운영 상태를 Slack에 모으면 “감시 시스템을 감시하는 문제”가 줄어든다.
목표 운영 모델
최종 목표는 아래 루프다.
Detect
-> Classify
-> Remediate when safe
-> Notify result
-> Escalate only when human input is required
핵심은 자동 복구와 사람 승인을 분리하는 것이다. 모든 것을 자동으로 고치려 하면 위험하다. 반대로 모든 것을 사람에게 물으면 자동화가 피로해진다.
이 프로젝트에서 자동 조치가 가능한 작업은 다음과 같다.
- pipeline queue status 복구
- 누락된 article generation 재실행
- Static Web Apps CD pipeline 재실행
- public URL health recheck
- NAS agent restart maintenance job 실행
- exited helper container cleanup
- Slack status report 발송
반대로 사람 승인이 필요한 작업은 아래처럼 별도 경계에 둔다.
- PAT 재발급
- Azure credential 갱신
- DNS record 변경
- Terraform apply
- 유료 Azure 리소스 생성
- Slack app 권한 확대
- NAS 패키지 설치 또는 삭제
이 경계를 먼저 정해두면 AIOps가 무서운 자동화가 아니라 관리 가능한 운영 도구가 된다.
전체 아키텍처
Slack이 NAS에 직접 접속하는 구조는 피한다. NAS에 inbound endpoint를 열면 보안 경계가 커지고, 운영 자동화가 원격 shell 실행처럼 변질될 수 있다.
권장 구조는 Azure DevOps를 제어 경로로 두는 방식이다.
Scheduled watchdog
-> Azure DevOps REST API
-> NAS self-hosted agent
-> checks and safe remediation
-> Slack incoming webhook
Slack slash command
-> public HTTPS endpoint
-> verify Slack signing secret
-> Azure DevOps REST API
-> maintenance pipeline
-> NAS self-hosted agent
-> Slack response URL or webhook
NAS는 계속 outbound 방식으로 Azure DevOps에 연결한다. Slack command는 maintenance pipeline을 queue할 뿐이고, 실제 실행은 기존 agent pool에서 수행된다.
이 방식의 장점은 로그와 감사 이력이 Azure DevOps에 남는다는 점이다. 누가 어떤 명령을 실행했고, 어떤 commit 또는 pipeline run과 연결됐는지 추적할 수 있다.
감시 대상
첫 번째 구현 단위는 watchdog이다. 사람이 /hwmoon status를 입력하기 전에 시스템이 먼저 상태를 봐야 한다.
watchdog은 아래 항목을 본다.
| Category | Check | Healthy state | Safe action |
|---|---|---|---|
| Schedule | 발행 window가 살아 있는가 | KST 06:00~23:00 slot이 유효 | drift report |
| Pipeline | article-generation queue가 enabled인가 | queueStatus enabled | enabled로 복구 |
| Agent | NAS agent가 online인가 | self-hosted agent online | restart pipeline queue |
| Article | 최근 slot 글이 생성됐는가 | expected timestamp within range | generation rerun |
| Build | static build가 성공했는가 | latest build succeeded | 실패 로그 요약 |
| Deploy | public route가 200인가 | homepage and latest post 200 | CD rerun |
| Cleanup | exited helper가 누적됐는가 | threshold 이하 | safe cleanup |
| Cost | hosted pool을 잘못 쓰는가 | self-hosted pool 사용 | drift warning |
여기서 중요한 것은 “재실행”이 마지막 수단이라는 점이다. 먼저 어떤 계층의 문제인지 분류해야 한다.
Slack 메시지 설계
Slack 메시지는 짧아야 하지만 운영 판단에는 충분해야 한다. 그래서 상태 리포트와 이슈 리포트를 나눈다.
정상 리포트는 이렇게 간결하게 보낸다.
[HWMOON Ops] 2026-06-01 09:05 KST
Schedule: OK
Pipeline: OK
NAS Agent: Online
Article generation: Last success 08:00 KST
Deploy: Public URL 200
Cleanup: 2 exited helper containers removed
Action: No human action required
이슈 리포트는 원인과 조치 결과를 같이 남긴다.
[HWMOON Ops Issue] Article generation missing
Category: Article
Detected: 2026-06-01 08:10 KST
Cause: Scheduled run did not produce a new published article
Action: Re-ran article generation pipeline on NAS self-hosted agent
Result: Succeeded
Next check: Public route verification
Human action: Not required
이 형식은 나중에 incident review에도 쓸 수 있다. Slack 메시지가 단순 알림이 아니라 작은 운영 기록이 된다.
알림 주기 조정
처음에는 watchdog을 15분마다 실행하는 안을 검토했다. 하지만 이 방식은 개인 운영 환경에서는 알림 피로도가 너무 높다. 특히 news.hwmoon.com의 핵심 위험은 “사이트가 15분 동안 죽어 있음”보다 “정시 기사 발행이 조용히 멈춤”에 가깝다. 그래서 상태 리포트는 더 넓은 간격으로 보내고, 실제 pipeline 실패는 별도 실패 알림으로 즉시 보내는 구조가 더 맞다.
최종 운영 스케줄은 KST 기준 08:15부터 20:15까지 2시간 단위로 잡았다. 정시 기사 생성과 같은 분에 실행하지 않고, 10분 grace가 지난 뒤 상태를 확인하기 위한 15분 offset이다.
| KST | Purpose |
|---|---|
| 08:15 | 오전 첫 운영 상태 확인 |
| 10:15 | 오전 발행 흐름 확인 |
| 12:15 | 정오 상태 확인 |
| 14:15 | 오후 초반 발행 확인 |
| 16:15 | 오후 운영 상태 확인 |
| 18:15 | 저녁 전 발행 확인 |
| 20:15 | 야간 전 최종 상태 확인 |
Azure Pipelines cron은 UTC 기준이므로 YAML은 두 개의 schedule로 나눈다.
schedules:
# 08:15 KST
- cron: "15 23 * * *"
displayName: Ops watchdog 08:15 KST
# 10:15-20:15 KST every 2 hours
- cron: "15 1-11/2 * * *"
displayName: Ops watchdog 10:15-20:15 KST every 2 hours
이 결정의 핵심은 알림을 줄이되 감지를 포기하지 않는 것이다. article-generation, ci-app, cd-dev-static-web-app 같은 핵심 파이프라인은 실패 시 즉시 Slack alert를 보낸다. 반면 정기 watchdog은 2시간마다 전체 상태와 최신 기사 freshness를 확인한다. 즉, “장애 이벤트”는 즉시 알리고, “운영 상태 리포트”는 적당한 간격으로 보낸다.
Slash command
Slash command는 초기부터 모든 기능을 넣을 필요가 없다. 가장 많이 쓰는 runbook만 허용한다.
| Command | Purpose |
|---|---|
/hwmoon status | 전체 운영 상태 조회 |
/hwmoon rerun article | 현재 시간대 article generation 재실행 |
/hwmoon repair article-pipeline | queue, schedule, generation flag 점검 및 복구 |
/hwmoon redeploy news | news Static Web Apps CD 재실행 |
/hwmoon redeploy tech | tech Static Web Apps CD 재실행 |
/hwmoon restart agent | NAS agent restart maintenance job 실행 |
/hwmoon cleanup containers | 종료된 helper container 정리 |
/hwmoon cost today | 당일 비용과 이상 징후 요약 |
명령은 allowlist 기반이어야 한다. Slack에서 받은 문자열을 그대로 shell에 넘기면 안 된다.
allowed command
-> mapped pipeline definition
-> fixed parameters
-> Azure DevOps queue request
예를 들어 /hwmoon restart agent는 NAS에 SSH로 들어가 docker restart를 직접 실행하지 않는다. 대신 Azure DevOps maintenance pipeline을 queue한다. 그 pipeline이 self-hosted agent에서 정해진 스크립트만 실행한다.
AIOps에서 AI가 들어갈 위치
이 구조에서 AI는 처음부터 복구 권한을 갖지 않는다. 먼저 읽고 요약하는 역할이 더 적합하다.
AI가 맡을 수 있는 영역은 아래와 같다.
- 최근 pipeline 로그 요약
- 실패 원인 후보 분류
- 이전 incident와 유사도 비교
- Slack 메시지의 사람이 읽기 좋은 요약 생성
- runbook 추천
- 다음 확인 명령 제안
- 반복 장애 패턴 감지
예를 들어 배포가 실패했을 때 AI는 아래처럼 요약할 수 있다.
Likely cause:
Static Web Apps deploy finished, but route verification failed.
Evidence:
- Build completed successfully
- Deploy task returned success
- Latest post URL returned 404 for 90 seconds
Recommended action:
Run /hwmoon redeploy tech or wait for next route verification.
복구 실행은 여전히 allowlist와 정책에 따라 제한한다. AI는 판단을 돕고, 실행은 검증된 runbook이 맡는 구성이 안전하다.
보안 경계
Slack 기반 운영 자동화에서 가장 중요한 것은 편의성보다 경계다.
필수 원칙은 다음과 같다.
- Slack signing secret을 검증한다.
- replay attack 방지를 위해 timestamp를 확인한다.
- Slack user 또는 channel allowlist를 둔다.
- command는 allowlist만 허용한다.
- NAS inbound port를 열지 않는다.
- PAT와 webhook URL은 repository에 남기지 않는다.
- Azure DevOps variable group 또는 Key Vault를 사용한다.
- destructive action은 기본적으로 차단한다.
- 유료 리소스 생성과 Terraform apply는 사람 승인을 요구한다.
Slack command endpoint는 공개 HTTPS여야 하지만, 공개 API처럼 열려 있으면 안 된다. Slack signature 검증, rate limit, idempotency key, audit log가 필요하다.
실제 운영에서 기대하는 효과
이번 구조의 효과는 단순히 “알림을 빨리 받는다”가 아니다.
첫 번째 효과는 MTTA 감소다. 문제가 발생한 뒤 사람이 사이트를 보고 알아차리는 시간이 줄어든다.
두 번째 효과는 MTTR 감소다. pipeline disabled, missed generation, deploy rerun 같은 반복 조치는 Slack command 또는 watchdog으로 빠르게 처리할 수 있다.
세 번째 효과는 운영 피로도 감소다. 정상 상태 리포트가 오면 굳이 Azure DevOps와 NAS를 열어보지 않아도 된다.
네 번째 효과는 운영 지식 축적이다. Slack thread가 incident record가 되고, 나중에는 이 기록을 기반으로 runbook을 개선할 수 있다.
다국가 확장성
Slack AIOps는 한국어 브리핑 하나만 운영할 때보다 여러 국가로 확장할 때 가치가 커진다.
kr article generation
us article generation
jp article generation
au article generation
de article generation
국가가 늘어나면 실패 지점도 늘어난다.
- 특정 locale만 글 생성 실패
- 특정 locale만 source 부족
- 특정 locale만 배포 route 404
- 이미지 수집 실패
- translation 또는 editorial policy mismatch
- 비용 급증
이때 Slack report는 locale별 상태를 나눠 보여줘야 한다.
[HWMOON Publishing Status]
KR: OK, latest 09:00
US: OK, latest 08:30
JP: Warning, source count low
AU: OK
DE: Failed, generation retry queued
Human action: Not required
향후에는 locale별 SLO를 둘 수 있다.
| Metric | Example target |
|---|---|
| Scheduled generation success | 99% per day |
| Public route verification | 200 within 5 minutes |
| Auto-remediation success | 90% for safe actions |
| Human escalation noise | fewer than 3 per week |
| Cost anomaly detection | same day alert |
이렇게 되면 Slack은 단순 알림 채널이 아니라 publisher infrastructure의 control room이 된다.
향후 확장 계획
확장은 네 단계로 나눈다.
1. Webhook report
가장 먼저 incoming webhook으로 정기 상태 리포트를 보낸다. 이 단계는 위험이 낮다. 읽기 중심이고, 운영자는 Slack에서 현재 상태를 볼 수 있다.
2. Watchdog pipeline
다음은 watchdog pipeline이다. schedule, pipeline queue, agent, article freshness, deploy health를 주기적으로 확인한다. 안전한 조치만 자동으로 실행한다.
3. Slash command
세 번째는 slash command다. 운영자가 Slack에서 허용된 runbook을 실행한다. 이때도 실제 작업은 Azure DevOps pipeline으로 위임한다.
4. AI-assisted incident review
마지막은 AI-assisted review다. 실패 로그, 최근 변경, pipeline run, public check 결과를 묶어서 원인 후보와 다음 조치를 요약한다.
이 순서를 지키면 자동화의 위험을 단계적으로 늘릴 수 있다.
결론
NAS self-hosted agent는 hosted minutes 한도 회피라는 원래 목적에 맞는 선택이었다. 하지만 runner를 붙이는 것만으로 운영이 안정화되지는 않는다.
운영 안정성은 runner 자체보다 운영 루프에서 나온다. 감시하고, 분류하고, 안전한 것은 자동 복구하고, 결과를 Slack에 남기고, 정말 필요한 경우에만 사람에게 올리는 흐름이 필요하다.
Slack 기반 AIOps의 핵심은 “AI가 운영을 대신한다”가 아니라 “운영자가 반복 확인과 반복 조치에서 벗어나도록 시스템이 문맥과 실행 경로를 제공한다”는 것이다.
이 구조가 자리 잡으면 HWMOON의 NAS는 단순한 Docker 실행기가 아니라, Azure DevOps와 Slack을 잇는 publishing operations layer가 된다.
시리즈
Publisher Infrastructure
전체 8편 중 5편입니다. 관련 구축 기록을 순서대로 모아 전체 맥락을 쉽게 확인할 수 있습니다.
- 1. Azure DNS와 Zoho Mail로 도메인 이메일 만들기
- 2. Synology NAS로 Azure DevOps Self-hosted Agent 운영하기
- 3. Pipeline은 성공했는데 Static Web Apps가 404를 냈던 이유
- 4. Azure Repos를 운영 원본으로 두고 GitHub를 백업으로 쓰는 전략
- 5. Slack 기반 AIOps로 NAS Agent 운영 자동화하기
- 6. news.hwmoon.com 기사 자동 발행 중단 재발 방지 기록
- 7. news.hwmoon.com AdSense 승인 전 준비와 운영 전환 기록
- 8. news.hwmoon.com 배포 중단 원인과 Agent 자가복구 체계 구축 기록