Files
sam-docs/dev/deploys/ops-manual/07-monitoring.md

272 lines
6.9 KiB
Markdown
Raw Normal View History

# 7. 모니터링
[목차로 돌아가기](./README.md)
---
## 아키텍처
```
운영서버 (node_exporter:9100) --스크래핑--> CI/CD (Prometheus:9090) --> Grafana:3100
개발서버 (node_exporter:9100) --스크래핑--> CI/CD (Prometheus:9090) --> Grafana:3100
CI/CD (node_exporter:9100) --스크래핑--> CI/CD (Prometheus:9090) --> Grafana:3100
```
- **Grafana 대시보드:** https://monitor.sam.it.kr
- **Prometheus 쿼리:** CI/CD 서버에서 http://localhost:9090
- **운영서버 메트릭:** 운영서버에서 http://localhost:9100/metrics
- **개발서버 메트릭:** 개발서버에서 http://localhost:9100/metrics
---
## Prometheus 스크래핑 설정
**현재 설정 (/etc/prometheus/prometheus.yml):**
```yaml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'sam-prod'
static_configs:
- targets: ['211.117.60.189:9100']
labels:
server: 'production'
- job_name: 'sam-cicd'
static_configs:
- targets: ['localhost:9100']
labels:
server: 'cicd'
- job_name: 'sam-dev'
static_configs:
- targets: ['114.203.209.83:9100']
labels:
server: 'development'
```
### 스크래핑 대상 추가
```bash
# 1. 대상 서버에 node_exporter 설치 (미설치 시)
# 바이너리: https://github.com/prometheus/node_exporter/releases
# 서비스: /etc/systemd/system/node_exporter.service
# 포트: 9100 (기본)
# 2. 대상 서버 방화벽에서 CI/CD IP 허용
sudo ufw allow from 110.10.147.46 to any port 9100 comment 'Prometheus scraping from CI/CD'
# 3. CI/CD 서버에서 설정 파일 편집
sudo vim /etc/prometheus/prometheus.yml
# 4. 새 대상 추가 예시
# - job_name: 'sam-new'
# static_configs:
# - targets: ['<서버IP>:9100']
# labels:
# server: '<환경명>'
# 5. 문법 검사
promtool check config /etc/prometheus/prometheus.yml
# 6. 서비스 리로드
sudo systemctl restart prometheus
```
### 대상 상태 확인
```bash
curl -s http://localhost:9090/api/v1/targets | python3 -c "
import json, sys
data = json.load(sys.stdin)
for t in data['data']['activeTargets']:
print(f\"{t['labels'].get('job','?'):15} {t['health']:6} {t['scrapeUrl']}\")
"
```
---
## PromQL 쿼리
Prometheus UI (http://localhost:9090) 또는 Grafana에서 사용.
### CPU
```promql
# CPU 사용률 (%) - 서버별
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 유휴 CPU 비율 (5분 평균)
rate(node_cpu_seconds_total{mode="idle"}[5m])
```
### 메모리
```promql
# 사용 가능 메모리 비율
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100
# 사용 중인 메모리 (GB)
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / 1024 / 1024 / 1024
# 전체 메모리 (GB)
node_memory_MemTotal_bytes / 1024 / 1024 / 1024
```
### 디스크
```promql
# 디스크 사용률 (%)
100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100)
# 사용 가능 디스크 (GB)
node_filesystem_avail_bytes{mountpoint="/"} / 1024 / 1024 / 1024
# 디스크 I/O (읽기/쓰기 바이트, 5분 평균)
rate(node_disk_read_bytes_total[5m])
rate(node_disk_written_bytes_total[5m])
```
### 네트워크
```promql
# 수신 (bytes/sec, 5분 평균)
rate(node_network_receive_bytes_total{device="eth0"}[5m])
# 전송 (bytes/sec, 5분 평균)
rate(node_network_transmit_bytes_total{device="eth0"}[5m])
```
### 시스템
```promql
# 서버 업타임 (초)
time() - node_boot_time_seconds
# Load Average (1분)
node_load1
# 열린 파일 디스크립터
node_filefd_allocated
```
---
## Grafana 대시보드
**기본 대시보드:** Node Exporter Full (ID: 1860)
**Data Source:** Prometheus (http://localhost:9090)
### 대시보드 추가 (Import)
1. Grafana 웹 > Dashboards > Import
2. Dashboard ID 입력 (예: 1860)
3. Data Source로 Prometheus 선택
4. Import 클릭
### 알림 규칙 설정
**설정 경로:** Grafana > Alerting > Alert rules
**현재 설정된 알림 규칙 (SAM Alerts 폴더):**
| 규칙명 | 조건 | 대기 시간 | 설명 |
|--------|------|-----------|------|
| CPU 사용률 > 90% | avg(rate(node_cpu_idle[5m])) | 5분 | CPU 과부하 |
| 메모리 사용률 > 85% | MemAvailable/MemTotal | 5분 | 메모리 부족 |
| 디스크 사용률 > 80% | filesystem_avail/size (/) | 5분 | 디스크 공간 부족 |
| 서비스 다운 (스크래핑 실패) | up < 1 | 1분 | Prometheus 타겟 다운 |
**알림 채널:** Grafana > Alerting > Contact points 에서 이메일, Slack 등 설정
**현재 설정:** SAM Slack Contact Point (Incoming Webhook) 연결 완료. Notification Policy에서 SAM Alerts 폴더의 알림이 Slack `#product_infra` 채널로 전송됨.
---
## [운영] 성능 모니터링
### 메모리 사용량 분석
```bash
free -h
ps aux --sort=-%mem | head -16
# MySQL 메모리
sudo mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
sudo mysql -e "SHOW STATUS LIKE 'Innodb_buffer_pool_bytes_data';"
# Redis 메모리
redis-cli info memory | grep -E "used_memory_human|maxmemory_human"
# PHP-FPM 프로세스별 메모리
ps -C php-fpm8.4 -o pid,user,%mem,rss,args --sort=-rss
```
### CPU 모니터링
```bash
htop
uptime # 로드 평균 (1분/5분/15분)
ps aux --sort=-%cpu | head -11 # CPU 상위 프로세스
nproc # CPU 코어 수
```
### 디스크 I/O
```bash
df -h
sudo du -sh /home/webservice/*
sudo du -sh /var/log/*
sudo du -sh /var/lib/mysql/*
sudo iostat -x 1 5 # 실시간 I/O
```
### 네트워크
```bash
sudo ss -tlnp # 열린 포트
ss -s # 연결 상태 요약
sudo ss -tn | awk '{print $4}' | grep -oP ':\d+$' | sort | uniq -c | sort -rn | head -10
```
### PHP-FPM Pool 상태
```bash
ps aux | grep "php-fpm" | grep -v grep | wc -l # 프로세스 수
ps aux | grep "php-fpm" | grep -v grep | awk '{print $NF}' | sort | uniq -c # Pool별
sudo grep "max_children" /var/log/php8.4-fpm.log | tail -10 # max_children 도달 여부
```
### MySQL 성능
```bash
# 연결 상태
sudo mysql -e "SHOW STATUS LIKE 'Threads%';"
# Slow Query 요약
sudo mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# InnoDB Buffer Pool 히트율
sudo mysql -e "
SELECT
ROUND((1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')) * 100, 2) AS buffer_pool_hit_rate_pct;
"
# 테이블 락 대기
sudo mysql -e "SHOW STATUS LIKE 'Table_locks%';"
```
### PM2 모니터링
```bash
pm2 status
pm2 monit # 실시간 CPU/메모리
pm2 describe sam-front # 상세 정보
pm2 describe sam-front | grep -A5 "restart" # 재시작 이력
```