775 lines
31 KiB
Markdown
775 lines
31 KiB
Markdown
# Prometheus & Grafana 모니터링 시스템 구축 가이드
|
|
|
|
**Version:** 1.0.0
|
|
**Last Modified:** 2025-08-08
|
|
|
|
## 개요
|
|
|
|
본 문서는 Prometheus, Grafana, Alertmanager를 포함하는 모니터링 스택을 구축하는 엔지니어링 절차를 상세히 기술한다. 시스템은 서버 인프라의 핵심 메트릭을 수집, 시각화하며, 정의된 임계값에 따라 MS Teams로 자동화된 알림을 전송하는 것을 목표로 한다.
|
|
|
|
구축 과정에서 발생한 기술적 문제와 해결 과정을 '트러블슈팅' 섹션에 상세히 기록하여, 향후 유사 시스템 구축 시 재현성과 안정성을 보장하고 기술적 부채를 최소화하는 데 중점을 둔다.
|
|
|
|
## 목차
|
|
|
|
1. [시스템 아키텍처](#1-시스템-아키텍처)
|
|
2. [설치 및 구성 절차](#2-설치-및-구성-절차)
|
|
1. [사전 준비 (`ds-commandcenter` 서버)](#21-사전-준비-ds-commandcenter-서버)
|
|
2. [Node Exporter 설치 (모든 대상 서버)](#22-node-exporter-설치-모든-대상-서버)
|
|
3. [Prometheus 설치 및 구성](#23-prometheus-설치-및-구성)
|
|
4. [Alertmanager 설치 및 구성](#24-alertmanager-설치-및-구성)
|
|
5. [Prometheus-MSTeams (Docker) 설치](#25-prometheus-msteams-docker-설치)
|
|
6. [Grafana 설치 및 구성](#26-grafana-설치-및-구성)
|
|
7. [Load Balancer 및 TLS 설정](#27-load-balancer-및-tls-설정)
|
|
3. [최종 확인 및 테스트](#3-최종-확인-및-테스트)
|
|
4. [트러블슈팅 (Troubleshooting)](#4-트러블슈팅-troubleshooting)
|
|
5. [부록 (Appendix)](#5-부록)
|
|
1. [주요 컴포넌트 버전](#51-주요-컴포넌트-버전)
|
|
2. [보안 권장 사항](#52-보안-권장-사항)
|
|
|
|
---
|
|
|
|
## 1. 시스템 아키텍처
|
|
|
|
 <!-- 다이어그램 이미지가 있다면 여기에 링크를 추가하세요 -->
|
|
|
|
1. **데이터 수집 (Data Collection):** 모든 대상 서버에 설치된 `Node Exporter`가 시스템 메트릭을 `:9500` 포트로 노출한다.
|
|
2. **수집 및 평가 (Scraping & Evaluation):** `ds-commandcenter` 서버의 `Prometheus`가 모든 Node Exporter로부터 메트릭을 수집하고, `resource_alert.yml` 규칙에 따라 알림 조건을 평가한다.
|
|
3. **알림 라우팅 (Alert Routing):** 알림 조건이 충족되면, `Prometheus`는 `Alertmanager`에게 알림을 전송한다.
|
|
4. **알림 처리 및 프록시 (Alert Processing & Proxying):** `Alertmanager`는 알림을 그룹화하고, `msteams.tmpl` 템플릿을 적용하여 `prometheus-msteams` Docker 컨테이너로 웹훅을 전송한다.
|
|
5. **최종 발송 (Final Delivery):** `prometheus-msteams` 컨테이너는 Alertmanager로부터 받은 데이터를 MS Teams 카드 형식으로 변환하여 최종적으로 MS Teams 채널에 알림을 보낸다.
|
|
6. **시각화 (Visualization):** `Grafana`는 Prometheus를 데이터 소스로 사용하여 모든 메트릭을 대시보드로 시각화한다.
|
|
7. **외부 접속 (External Access):** 사용자는 `Load Balancer`를 통해 HTTPS로 안전하게 Grafana와 Prometheus UI에 접근한다.
|
|
|
|
---
|
|
|
|
## 2. 설치 및 구성 절차
|
|
|
|
### 2.1. 사전 준비 (`ds-commandcenter` 서버)
|
|
* **목적:** 서비스 실행에 필요한 시스템 계정, 디렉토리 생성 및 패키지 다운로드.
|
|
* **실행 위치:** `ds-commandcenter` 서버.
|
|
* **명령어:**
|
|
```bash
|
|
# 시스템 계정 생성
|
|
useradd --no-create-home --shell /bin/false prometheus
|
|
useradd --no-create-home --shell /bin/false alertmanager
|
|
|
|
# 디렉토리 생성 및 소유권 변경
|
|
mkdir -p /etc/prometheus/rules /etc/alertmanager/templates /data/prometheus /data/alertmanager /data/promteams
|
|
chown -R prometheus:prometheus /etc/prometheus /data/prometheus
|
|
chown -R alertmanager:alertmanager /etc/alertmanager /data/alertmanager
|
|
|
|
# 패키지 다운로드
|
|
cd /data
|
|
wget https://github.com/prometheus/prometheus/releases/download/v3.5.0/prometheus-3.5.0.linux-amd64.tar.gz
|
|
wget https://github.com/prometheus/alertmanager/releases/download/v0.28.1/alertmanager-0.28.1.linux-amd64.tar.gz
|
|
wget https://github.com/prometheus/node_exporter/releases/download/v1.9.1/node_exporter-1.9.1.linux-amd64.tar.gz
|
|
|
|
# 압축 해제
|
|
tar -xvf prometheus-3.5.0.linux-amd64.tar.gz
|
|
tar -xvf alertmanager-0.28.1.linux-amd64.tar.gz
|
|
tar -xvf node_exporter-1.9.1.linux-amd64.tar.gz
|
|
```
|
|
|
|
### 2.2. Node Exporter 설치 (모든 대상 서버)
|
|
* **목적:** 메트릭 수집 에이전트 설치 및 실행.
|
|
* **실행 위치:** 모니터링할 모든 서버 (공용, 게임 서버 포함).
|
|
* **실행 스크립트:**
|
|
```bash
|
|
#!/bin/bash
|
|
# Node Exporter 설치 및 실행 스크립트
|
|
|
|
# 바이너리 파일 이동
|
|
mv /data/node_exporter-1.9.1.linux-amd64/node_exporter /usr/local/bin/
|
|
|
|
# 시스템 계정 생성
|
|
useradd --no-create-home --shell /bin/false node_exporter
|
|
|
|
# systemd 서비스 파일 생성
|
|
cat <<EOF > /etc/systemd/system/node_exporter.service
|
|
[Unit]
|
|
Description=Node Exporter
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
User=node_exporter
|
|
Group=node_exporter
|
|
Type=simple
|
|
ExecStart=/usr/local/bin/node_exporter --web.listen-address=":9500"
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# 서비스 등록 및 시작
|
|
systemctl daemon-reload
|
|
systemctl enable node_exporter
|
|
systemctl start node_exporter
|
|
systemctl status node_exporter
|
|
```
|
|
|
|
### 2.3. Prometheus 설치 및 구성
|
|
* **목적:** 메트릭 수집 서버 설정.
|
|
* **실행 위치:** `ds-commandcenter` 서버.
|
|
* **설치 명령어:**
|
|
```bash
|
|
# 바이너리 및 설정 파일 이동
|
|
mv /data/prometheus-3.5.0.linux-amd64/{prometheus,promtool} /usr/local/bin/
|
|
mv /data/prometheus-3.5.0.linux-amd64/{consoles,console_libraries} /etc/prometheus
|
|
chown -R prometheus:prometheus /etc/prometheus/*
|
|
chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
|
|
```
|
|
* **`/etc/prometheus/prometheus.yml`**
|
|
```yaml
|
|
global:
|
|
scrape_interval: 1m
|
|
evaluation_interval: 1m
|
|
|
|
alerting:
|
|
alertmanagers:
|
|
- static_configs:
|
|
- targets:
|
|
- localhost:9094
|
|
|
|
rule_files:
|
|
- "/etc/prometheus/rules/resource_alert.yml"
|
|
|
|
scrape_configs:
|
|
- job_name: "prometheus"
|
|
static_configs:
|
|
- targets: ["localhost:9091"]
|
|
|
|
- job_name: "common_servers"
|
|
static_configs:
|
|
- targets: ["10.0.10.21:9500"]
|
|
labels:
|
|
hostname: "ds-battlefield"
|
|
ip: "10.0.10.21"
|
|
- targets: ["10.0.10.6:9500"]
|
|
labels:
|
|
hostname: "ds-commandcenter"
|
|
ip: "10.0.10.6"
|
|
- targets: ["10.0.10.7:9500"]
|
|
labels:
|
|
hostname: "ds-crashreport"
|
|
ip: "10.0.10.7"
|
|
- targets: ["10.0.10.17:9500"]
|
|
labels:
|
|
hostname: "ds-maingate001"
|
|
ip: "10.0.10.17"
|
|
- targets: ["10.0.10.18:9500"]
|
|
labels:
|
|
hostname: "ds-maingate002"
|
|
ip: "10.0.10.18"
|
|
- targets: ["10.0.10.14:9500"]
|
|
labels:
|
|
hostname: "ds-mongodb001"
|
|
ip: "10.0.10.14"
|
|
- targets: ["10.0.10.15:9500"]
|
|
labels:
|
|
hostname: "ds-mongodb002"
|
|
ip: "10.0.10.15"
|
|
- targets: ["10.0.10.16:9500"]
|
|
labels:
|
|
hostname: "ds-mongodb003"
|
|
ip: "10.0.10.16"
|
|
- targets: ["10.0.10.8:9500"]
|
|
labels:
|
|
hostname: "ds-opensearch001"
|
|
ip: "10.0.10.8"
|
|
- targets: ["10.0.10.9:9500"]
|
|
labels:
|
|
hostname: "ds-opensearch002"
|
|
ip: "10.0.10.9"
|
|
- targets: ["10.0.10.10:9500"]
|
|
labels:
|
|
hostname: "ds-opensearch003"
|
|
ip: "10.0.10.10"
|
|
- targets: ["10.0.10.22:9500"]
|
|
labels:
|
|
hostname: "ds-promotor"
|
|
ip: "10.0.10.22"
|
|
- targets: ["10.0.10.24:9500"]
|
|
labels:
|
|
hostname: "ds-racetrack"
|
|
ip: "10.0.10.24"
|
|
- targets: ["10.0.10.11:9500"]
|
|
labels:
|
|
hostname: "ds-redis001"
|
|
ip: "10.0.10.11"
|
|
- targets: ["10.0.10.12:9500"]
|
|
labels:
|
|
hostname: "ds-redis002"
|
|
ip: "10.0.10.12"
|
|
- targets: ["10.0.10.13:9500"]
|
|
labels:
|
|
hostname: "ds-redis003"
|
|
ip: "10.0.10.13"
|
|
- targets: ["10.0.10.23:9500"]
|
|
labels:
|
|
hostname: "ds-social"
|
|
ip: "10.0.10.23"
|
|
- targets: ["10.0.10.25:9500"]
|
|
labels:
|
|
hostname: "ds-tavern"
|
|
ip: "10.0.10.25"
|
|
- targets: ["10.0.10.19:9500"]
|
|
labels:
|
|
hostname: "ds-warehouse001"
|
|
ip: "10.0.10.19"
|
|
- targets: ["10.0.10.20:9500"]
|
|
labels:
|
|
hostname: "ds-warehouse002"
|
|
ip: "10.0.10.20"
|
|
|
|
- job_name: "game_servers"
|
|
static_configs:
|
|
- targets: ["110.234.163.37:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game001"
|
|
ip: "110.234.163.37"
|
|
- targets: ["110.234.163.30:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game002"
|
|
ip: "110.234.163.30"
|
|
- targets: ["110.234.161.170:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game003"
|
|
ip: "110.234.161.170"
|
|
- targets: ["110.234.160.149:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game004"
|
|
ip: "110.234.160.149"
|
|
- targets: ["110.234.162.181:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game005"
|
|
ip: "110.234.162.181"
|
|
- targets: ["110.234.160.50:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game006"
|
|
ip: "110.234.160.50"
|
|
- targets: ["110.234.165.61:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game007"
|
|
ip: "110.234.165.61"
|
|
- targets: ["110.234.163.151:9500"]
|
|
labels:
|
|
hostname: "ds-jpn-game008"
|
|
ip: "110.234.163.151"
|
|
- targets: ["110.234.195.8:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game001"
|
|
ip: "110.234.195.8"
|
|
- targets: ["110.234.193.164:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game002"
|
|
ip: "110.234.193.164"
|
|
- targets: ["110.234.193.189:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game003"
|
|
ip: "110.234.193.189"
|
|
- targets: ["110.234.192.213:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game004"
|
|
ip: "110.234.192.213"
|
|
- targets: ["110.234.194.108:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game005"
|
|
ip: "110.234.194.108"
|
|
- targets: ["110.234.194.199:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game006"
|
|
ip: "110.234.194.199"
|
|
- targets: ["110.234.194.179:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game007"
|
|
ip: "110.234.194.179"
|
|
- targets: ["110.234.193.159:9500"]
|
|
labels:
|
|
hostname: "ds-sgn-game008"
|
|
ip: "110.234.193.159"
|
|
- targets: ["44.198.4.245:9500"]
|
|
labels:
|
|
hostname: "ds-us-game001"
|
|
ip: "44.198.4.245"
|
|
- targets: ["52.5.176.32:9500"]
|
|
labels:
|
|
hostname: "ds-us-game002"
|
|
ip: "52.5.176.32"
|
|
- targets: ["98.86.208.130:9500"]
|
|
labels:
|
|
hostname: "ds-us-game003"
|
|
ip: "98.86.208.130"
|
|
- targets: ["98.87.57.10:9500"]
|
|
labels:
|
|
hostname: "ds-us-game004"
|
|
ip: "98.87.57.10"
|
|
- targets: ["18.153.131.248:9500"]
|
|
labels:
|
|
hostname: "ds-de-game001"
|
|
ip: "18.153.131.248"
|
|
- targets: ["18.185.201.217:9500"]
|
|
labels:
|
|
hostname: "ds-de-game002"
|
|
ip: "18.185.201.217"
|
|
- targets: ["3.124.28.212:9500"]
|
|
labels:
|
|
hostname: "ds-de-game003"
|
|
ip: "3.124.28.212"
|
|
- targets: ["3.69.139.75:9500"]
|
|
labels:
|
|
hostname: "ds-de-game004"
|
|
ip: "3.69.139.75"
|
|
|
|
- job_name: "game_info"
|
|
static_configs:
|
|
- targets: ["10.0.10.22:9200"]
|
|
labels:
|
|
hostname: "ds-promotor"
|
|
ip: "10.0.10.22"
|
|
- targets: ["110.234.163.37:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game001"
|
|
ip: "110.234.163.37"
|
|
- targets: ["110.234.163.30:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game002"
|
|
ip: "110.234.163.30"
|
|
- targets: ["110.234.161.170:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game003"
|
|
ip: "110.234.161.170"
|
|
- targets: ["110.234.160.149:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game004"
|
|
ip: "110.234.160.149"
|
|
- targets: ["110.234.162.181:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game005"
|
|
ip: "110.234.162.181"
|
|
- targets: ["110.234.160.50:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game006"
|
|
ip: "110.234.160.50"
|
|
- targets: ["110.234.165.61:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game007"
|
|
ip: "110.234.165.61"
|
|
- targets: ["110.234.163.151:9200"]
|
|
labels:
|
|
hostname: "ds-jpn-game008"
|
|
ip: "110.234.163.151"
|
|
- targets: ["110.234.195.8:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game001"
|
|
ip: "110.234.195.8"
|
|
- targets: ["110.234.193.164:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game002"
|
|
ip: "110.234.193.164"
|
|
- targets: ["110.234.193.189:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game003"
|
|
ip: "110.234.193.189"
|
|
- targets: ["110.234.192.213:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game004"
|
|
ip: "110.234.192.213"
|
|
- targets: ["110.234.194.108:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game005"
|
|
ip: "110.234.194.108"
|
|
- targets: ["110.234.194.199:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game006"
|
|
ip: "110.234.194.199"
|
|
- targets: ["110.234.194.179:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game007"
|
|
ip: "110.234.194.179"
|
|
- targets: ["110.234.193.159:9200"]
|
|
labels:
|
|
hostname: "ds-sgn-game008"
|
|
ip: "110.234.193.159"
|
|
- targets: ["44.198.4.245:9200"]
|
|
labels:
|
|
hostname: "ds-us-game001"
|
|
ip: "44.198.4.245"
|
|
- targets: ["52.5.176.32:9200"]
|
|
labels:
|
|
hostname: "ds-us-game002"
|
|
ip: "52.5.176.32"
|
|
- targets: ["98.86.208.130:9200"]
|
|
labels:
|
|
hostname: "ds-us-game003"
|
|
ip: "98.86.208.130"
|
|
- targets: ["98.87.57.10:9200"]
|
|
labels:
|
|
hostname: "ds-us-game004"
|
|
ip: "98.87.57.10"
|
|
- targets: ["18.153.131.248:9200"]
|
|
labels:
|
|
hostname: "ds-de-game001"
|
|
ip: "18.153.131.248"
|
|
- targets: ["18.185.201.217:9200"]
|
|
labels:
|
|
hostname: "ds-de-game002"
|
|
ip: "18.185.201.217"
|
|
- targets: ["3.124.28.212:9200"]
|
|
labels:
|
|
hostname: "ds-de-game003"
|
|
ip: "3.124.28.212"
|
|
- targets: ["3.69.139.75:9200"]
|
|
labels:
|
|
hostname: "ds-de-game004"
|
|
ip: "3.69.139.75"
|
|
|
|
```
|
|
* **`/etc/prometheus/rules/resource_alert.yml`**
|
|
```yaml
|
|
groups:
|
|
- name: 리소스 사용량 경고
|
|
rules:
|
|
- alert: CPU사용량경고
|
|
expr: 100 - (avg by(instance, hostname, ip) (rate(node_cpu_seconds_total{mode="idle"}[10m])) * 100) > 70
|
|
for: 10m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "높은 CPU 사용량 감지"
|
|
description: "{{ $labels.hostname }}에서 지난 10분 동안 CPU 사용량이 70%를 초과했습니다."
|
|
value: "{{ $value | printf \"%.2f\" }}"
|
|
runbook_url: "https://grafana.dungeonstalkers.com:8443"
|
|
|
|
- alert: CPU사용량심각
|
|
expr: 100 - (avg by(instance, hostname, ip) (rate(node_cpu_seconds_total{mode="idle"}[10m])) * 100) > 80
|
|
for: 10m
|
|
labels:
|
|
severity: critical
|
|
annotations:
|
|
summary: "심각한 CPU 사용량 감지"
|
|
description: "{{ $labels.hostname }}에서 지난 10분 동안 CPU 사용량이 80%를 초과했습니다."
|
|
value: "{{ $value | printf \"%.2f\" }}"
|
|
runbook_url: "https://grafana.dungeonstalkers.com:8443"
|
|
|
|
- alert: 메모리사용량경고
|
|
expr: (1 - (node_memory_MemAvailable_bytes{hostname!=""} / node_memory_MemTotal_bytes{hostname!=""})) * 100 > 70
|
|
for: 10m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "높은 메모리 사용량 감지"
|
|
description: "{{ $labels.hostname }}에서 지난 10분 동안 메모리 사용량이 70%를 초과했습니다."
|
|
value: "{{ $value | printf \"%.2f\" }}"
|
|
runbook_url: "https://grafana.dungeonstalkers.com:8443"
|
|
|
|
- alert: 메모리사용량심각
|
|
expr: (1 - (node_memory_MemAvailable_bytes{hostname!=""} / node_memory_MemTotal_bytes{hostname!=""})) * 100 > 80
|
|
for: 10m
|
|
labels:
|
|
severity: critical
|
|
annotations:
|
|
summary: "심각한 메모리 사용량 감지"
|
|
description: "{{ $labels.hostname }}에서 지난 10분 동안 메모리 사용량이 80%를 초과했습니다."
|
|
value: "{{ $value | printf \"%.2f\" }}"
|
|
runbook_url: "https://grafana.dungeonstalkers.com:8443"
|
|
```
|
|
* **`/etc/systemd/system/prometheus.service`**
|
|
```ini
|
|
[Unit]
|
|
Description=Prometheus
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
User=prometheus
|
|
Group=prometheus
|
|
Type=simple
|
|
ExecStart=/usr/local/bin/prometheus \
|
|
--config.file /etc/prometheus/prometheus.yml \
|
|
--storage.tsdb.path /data/prometheus/ \
|
|
--web.listen-address=":9091" \
|
|
--web.enable-lifecycle \
|
|
--web.external-url=https://prometheus.dungeonstalkers.com:8444/
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
* **서비스 시작:**
|
|
```bash
|
|
systemctl daemon-reload
|
|
systemctl enable prometheus
|
|
systemctl start prometheus
|
|
```
|
|
|
|
### 2.4. Alertmanager 설치 및 구성
|
|
* **목적:** 알림 처리 및 라우팅 서버 설정.
|
|
* **실행 위치:** `ds-commandcenter` 서버.
|
|
* **설치 명령어:**
|
|
```bash
|
|
mv /data/alertmanager-0.28.1.linux-amd64/{alertmanager,amtool} /usr/local/bin/
|
|
chown alertmanager:alertmanager /usr/local/bin/alertmanager /usr/local/bin/amtool
|
|
```
|
|
* **`/etc/alertmanager/alertmanager.yml`**
|
|
```yaml
|
|
route:
|
|
group_by: ['alertname', 'hostname']
|
|
group_wait: 15s
|
|
group_interval: 1m
|
|
repeat_interval: 10m
|
|
receiver: "resource_alert"
|
|
|
|
receivers:
|
|
- name: "resource_alert"
|
|
webhook_configs:
|
|
- url: "http://127.0.0.1:2000/resource_alert"
|
|
|
|
templates:
|
|
- '/etc/alertmanager/templates/msteams.tmpl'
|
|
```
|
|
* **`/etc/alertmanager/templates/msteams.tmpl`**
|
|
```go-template
|
|
{{ define "teams.card" }}
|
|
{
|
|
"@type": "MessageCard",
|
|
"@context": "http://schema.org/extensions",
|
|
"summary": "{{ .CommonAnnotations.summary }}",
|
|
"themeColor": "0078D7",
|
|
"title": "🚨 {{ .CommonAnnotations.summary }}",
|
|
"sections": [
|
|
{{ $root := . }}
|
|
{{ range $index, $alert := .Alerts }}
|
|
{
|
|
"activityTitle": "{{ $alert.Annotations.description }}",
|
|
"facts": [
|
|
{
|
|
"name": "상태",
|
|
"value": "**{{ printf "%.0f" $alert.Annotations.value }}%**"
|
|
},
|
|
{
|
|
"name": "심각도",
|
|
"value": "{{ $alert.Labels.severity }}"
|
|
},
|
|
{
|
|
"name": "호스트명",
|
|
"value": "{{ $alert.Labels.hostname }}"
|
|
},
|
|
{
|
|
"name": "IP 주소",
|
|
"value": "{{ $alert.Labels.ip }}"
|
|
},
|
|
{
|
|
"name": "발생 일시",
|
|
"value": "{{ $alert.StartsAt }}"
|
|
}
|
|
],
|
|
"markdown": true
|
|
}{{ if ne (add $index 1) (len $root.Alerts) }},{{ end }}
|
|
{{ end }}
|
|
],
|
|
"potentialAction": [
|
|
{
|
|
"@type": "OpenUri",
|
|
"name": "Grafana에서 보기",
|
|
"targets": [
|
|
{
|
|
"os": "default",
|
|
"uri": "{{ .CommonAnnotations.runbook_url }}"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
{{ end }}
|
|
```
|
|
* **`/etc/systemd/system/alertmanager.service`**
|
|
```ini
|
|
[Unit]
|
|
Description=Alertmanager
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
User=alertmanager
|
|
Group=alertmanager
|
|
Type=simple
|
|
ExecStart=/usr/local/bin/alertmanager \
|
|
--config.file=/etc/alertmanager/alertmanager.yml \
|
|
--storage.path=/data/alertmanager/ \
|
|
--web.listen-address=":9094" \
|
|
--cluster.listen-address=":9095"
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
* **서비스 시작:**
|
|
```bash
|
|
systemctl daemon-reload
|
|
systemctl enable alertmanager
|
|
systemctl start alertmanager
|
|
```
|
|
|
|
### 2.5. Prometheus-MSTeams (Docker) 설치
|
|
* **목적:** Alertmanager와 MS Teams를 연결하는 프록시 설치.
|
|
* **실행 위치:** `ds-commandcenter` 서버.
|
|
* **`/data/promteams/config.env`**
|
|
```env
|
|
# MS Teams Webhook URL
|
|
WEBHOOK_URL="https://oneunivrs.webhook.office.com/webhookb2/7248d32a-3473-43bd-961b-c2a2516f28f5@1e8605cc-8007-46b0-993f-b388917f9499/IncomingWebhook/ac17804386cc4efdad5c78b3a8c182f7/f5368752-03f7-4e64-93e6-b40991c04c0c/V2jpWgnliaoihAzy3iMA2p_2KWou2hMIj4T32F8MCMVH01"
|
|
|
|
# Alertmanager의 webhook_configs.url 경로와 일치해야 하는 요청 URI
|
|
REQUEST_URI="resource_alert"
|
|
|
|
# 사용할 템플릿 파일의 호스트 경로 (마운트할 원본 파일)
|
|
TEMPLATE_HOST_PATH="/etc/alertmanager/templates/msteams.tmpl"
|
|
# 컨테이너 내부에서 템플릿 파일이 위치할 경로
|
|
TEMPLATE_CONTAINER_PATH="/app/default-message-card.tmpl"
|
|
```
|
|
* **`/data/promteams/start_promteams.sh`**
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
# 설정 파일 로드
|
|
source /data/promteams/config.env
|
|
|
|
# 필수 변수 확인
|
|
if [ -z "$WEBHOOK_URL" ] || [ -z "$REQUEST_URI" ]; then
|
|
echo "필수 설정 값이 누락되었습니다. config.env 파일을 확인하세요."
|
|
exit 1
|
|
fi
|
|
|
|
echo "기존 promteams 컨테이너를 중지하고 삭제합니다."
|
|
docker stop promteams >/dev/null 2>&1
|
|
docker rm promteams >/dev/null 2>&1
|
|
|
|
echo "환경변수 방식을 사용하는 구버전 이미지(v1.5.2)로 Prometheus-MSTeams 컨테이너를 시작합니다."
|
|
docker run -d -p 2000:2000 \
|
|
--name="promteams" \
|
|
--restart=always \
|
|
-e TEAMS_INCOMING_WEBHOOK_URL="$WEBHOOK_URL" \
|
|
-e TEAMS_REQUEST_URI="$REQUEST_URI" \
|
|
-v "$TEMPLATE_HOST_PATH:$TEMPLATE_CONTAINER_PATH" \
|
|
quay.io/prometheusmsteams/prometheus-msteams:v1.5.2
|
|
|
|
echo "컨테이너가 시작되었습니다. 아래 명령어로 상태를 확인하세요:"
|
|
echo "docker ps | grep promteams"
|
|
```
|
|
* **`/data/promteams/stop_promteams.sh`**
|
|
```bash
|
|
#!/bin/bash
|
|
CONTAINER_NAME="promteams"
|
|
|
|
if [ $(docker ps -q -f name=$CONTAINER_NAME) ]; then
|
|
echo "Prometheus-MSTeams 컨테이너($CONTAINER_NAME)를 중지하고 삭제합니다."
|
|
docker stop $CONTAINER_NAME
|
|
docker rm $CONTAINER_NAME
|
|
echo "완료되었습니다."
|
|
else
|
|
echo "실행 중인 Prometheus-MSTeams 컨테이너가 없습니다."
|
|
fi
|
|
```
|
|
* **컨테이너 실행:**
|
|
```bash
|
|
chmod +x /data/promteams/*.sh
|
|
/data/promteams/start_promteams.sh
|
|
```
|
|
|
|
### 2.6. Grafana 설치 및 구성
|
|
* **목적:** 시각화 대시보드 설치 및 설정.
|
|
* **실행 위치:** `ds-commandcenter` 서버.
|
|
* **설치 명령어:**
|
|
```bash
|
|
apt-get update && apt-get install -y adduser libfontconfig1 musl
|
|
wget https://dl.grafana.com/enterprise/release/grafana-enterprise_12.1.0_amd64.deb
|
|
dpkg -i grafana-enterprise_12.1.0_amd64.deb
|
|
```
|
|
* **`/etc/grafana/grafana.ini` 수정:** 아래 `sed` 명령어는 주요 설정을 변경한다.
|
|
```bash
|
|
# 외부 접속 주소 및 포트 설정
|
|
sed -i 's/;http_port = 3000/http_port = 3001/' /etc/grafana/grafana.ini
|
|
sed -i 's/;domain = localhost/domain = grafana.dungeonstalkers.com/' /etc/grafana/grafana.ini
|
|
sed -i "s|;root_url = .*|root_url = https://grafana.dungeonstalkers.com:8443/|" /etc/grafana/grafana.ini
|
|
|
|
# 임베딩 및 익명 접속 설정 추가
|
|
cat <<'EOF' | tee -a /etc/grafana/grafana.ini
|
|
|
|
[security]
|
|
allow_embedding = true
|
|
|
|
[auth.anonymous]
|
|
enabled = true
|
|
org_name = Main Org.
|
|
org_role = Viewer
|
|
EOF
|
|
```
|
|
* **서비스 시작:**
|
|
```bash
|
|
systemctl enable grafana-server
|
|
systemctl start grafana-server
|
|
```
|
|
|
|
### 2.7. Load Balancer 및 TLS 설정
|
|
* **목적:** 외부 접속을 위한 HTTPS 통신 및 포트 포워딩 설정.
|
|
* **설정 위치:** AWS, GCP 등 클라우드 콘솔 또는 L4 장비.
|
|
* **구성 요약:**
|
|
* `https://grafana.dungeonstalkers.com:8443` -> `http://10.0.10.6:3001`
|
|
* `https://prometheus.dungeonstalkers.com:8444` -> `http://10.0.10.6:9091`
|
|
* `dungeonstalkers.com`에 대한 유효한 TLS 인증서 필요.
|
|
|
|
---
|
|
|
|
## 3. 최종 확인 및 테스트
|
|
|
|
1. **웹 UI 접속:**
|
|
* `https://grafana.dungeonstalkers.com:8443`
|
|
* `https://prometheus.dungeonstalkers.com:8444`
|
|
2. **Prometheus Targets 확인:** Prometheus UI의 'Status' -> 'Targets' 페이지에서 모든 대상이 `UP` 상태인지 확인.
|
|
3. **전체 알림 파이프라인 테스트:**
|
|
```bash
|
|
amtool alert add \
|
|
--alertmanager.url="http://localhost:9094" \
|
|
alertname="Final-System-Test" \
|
|
severity="critical" \
|
|
hostname="ds-commandcenter" \
|
|
ip="10.0.10.6" \
|
|
summary="전체 시스템 최종 테스트" \
|
|
description="이 알림이 도착하고 모든 링크가 올바르게 작동하면 성공이다." \
|
|
value="99" \
|
|
runbook_url="https://grafana.dungeonstalkers.com:8443"
|
|
```
|
|
MS Teams 채널에 알림 카드 도착 및 'Grafana에서 보기' 버튼 링크의 정상 작동 여부를 확인한다.
|
|
|
|
---
|
|
|
|
## 4. 트러블슈팅 (Troubleshooting)
|
|
|
|
본 섹션은 구축 과정에서 발생했던 주요 문제와 해결 과정을 기술한다.
|
|
|
|
| 문제 현상 | 원인 | 해결 방안 |
|
|
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `alertmanager` 서비스 시작 실패 (`address already in use`) | 웹 포트(`:9094`)와 클러스터 포트(기본값 `:9094`)가 충돌함. | `alertmanager.service` 파일에 `--cluster.listen-address=":9095"` 옵션을 추가하여 클러스터 포트를 명시적으로 변경. |
|
|
| `alertmanager` 서비스 시작 실패 (`function "add"/"floor" not defined`) | MS Teams 템플릿 파일(`msteams.tmpl`)에 Alertmanager가 지원하지 않는 함수가 포함됨. | `floor`는 `printf "%.0f"`로 대체하고, `add` 함수를 사용하는 로직은 더 단순하고 호환성 높은 방식으로 수정. |
|
|
| `prometheus` 서비스 시작 실패 (`yaml: did not find expected key`) | `prometheus.yml` 파일의 YAML 문법 오류 (주로 들여쓰기 문제). | `external_url` 설정을 `prometheus.yml`에서 제거하고, 대신 `prometheus.service` 파일의 실행 옵션에 `--web.external-url`을 추가하는 방식으로 변경하여 YAML 파일의 무결성을 보장. |
|
|
| `prometheus-msteams` 컨테이너 Crash Loop (재시작 반복) | 최신 Docker 이미지와 구버전 설정 방식(환경 변수) 간의 비호환성 문제. | 이전에 성공했던 방식이 환경 변수를 사용했음을 확인하고, 해당 방식을 지원하는 구버전 이미지 태그(`v1.5.2`)를 명시적으로 사용하여 컨테이너를 실행. |
|
|
| MS Teams 알림은 실패하는데 템플릿 관련 에러가 발생함. | `amtool`로 보낸 테스트 알림 데이터에 템플릿이 요구하는 필드(`ip`, `runbook_url`)가 누락됨. | Prometheus 설정에서 모든 타겟에 `ip` 라벨을 추가하고, 알림 규칙에 `runbook_url` 어노테이션을 추가하여 실제 알림 데이터에 해당 필드가 포함되도록 구성. `amtool` 테스트 시에도 해당 필드를 직접 포함하여 전송. |
|
|
| `node_exporter` 포트 충돌 (`:9300`) | OpenSearch 등 다른 서비스가 이미 `:9300`번대 포트를 사용하고 있었음. | 모든 서버의 `node_exporter` 포트를 `:9500`으로 변경하고, `prometheus.yml`의 수집 대상 포트도 모두 `:9500`으로 수정. |
|
|
|
|
---
|
|
|
|
## 5. 부록 (Appendix)
|
|
|
|
### 5.1. 주요 컴포넌트 버전
|
|
* **Prometheus:** `3.5.0`
|
|
* **Alertmanager:** `0.28.1`
|
|
* **Node Exporter:** `1.9.1`
|
|
* **Grafana:** `12.1.0`
|
|
* **Prometheus-MSTeams (Docker):** `quay.io/prometheusmsteams/prometheus-msteams:v1.5.2`
|
|
|
|
### 5.2. 보안 권장 사항
|
|
* **Grafana 관리자 비밀번호 변경:** 설치 후 즉시 Grafana의 `admin` 계정 비밀번호를 변경해야 한다.
|
|
```bash
|
|
grafana-cli admin reset-admin-password <새롭고-안전한-비밀번호>
|
|
```
|
|
* **네트워크 방화벽:** LB의 공인 포트 외에, 각 서비스의 내부 포트(`9091`, `9094`, `3001` 등)는 외부에서 직접 접근할 수 없도록 방화벽으로 차단하는 것을 권장한다.
|
|
* **Webhook URL 보안:** MS Teams Webhook URL은 민감 정보이므로, `config.env` 파일의 권한을 제한(`chmod 600`)하고 Git 등 버전 관리 시스템에 포함되지 않도록 주의해야 한다.
|