# 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" # 재시작 이력 ```