docs(DOC): 운영 매뉴얼 구조화 — ops-manual/ 11개 주제별 파일로 재구성
- 운영/CI/CD 서버 셋업 가이드를 ops-manual/에 통합 - 11-server-setup.md: 운영+CI/CD 서버 설치 절차 통합 - 05-deployment.md: Jenkinsfile 코드, Git 동기화 전략, 배포 흐름도 추가 - 원본 파일 삭제: production-server-setup.md, cicd-server-setup.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
522
deploys/ops-manual/08-troubleshooting.md
Normal file
522
deploys/ops-manual/08-troubleshooting.md
Normal file
@@ -0,0 +1,522 @@
|
||||
# 8. 장애 대응 가이드
|
||||
|
||||
[목차로 돌아가기](./README.md)
|
||||
|
||||
---
|
||||
|
||||
## 운영서버 장애
|
||||
|
||||
### Nginx 502 Bad Gateway
|
||||
|
||||
**증상:** 브라우저에서 502 에러. 정적 파일은 정상, 동적 요청만 실패.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo tail -50 /var/log/nginx/api.sam.it.kr.error.log
|
||||
# "connect() failed" 또는 "no live upstreams" 메시지 확인
|
||||
|
||||
# Laravel 사이트인 경우
|
||||
sudo systemctl status php8.4-fpm
|
||||
ls -la /run/php/php8.4-fpm-*.sock
|
||||
|
||||
# Next.js 사이트인 경우
|
||||
pm2 status
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
# PHP-FPM이 죽은 경우
|
||||
sudo systemctl restart php8.4-fpm
|
||||
|
||||
# PM2가 죽은 경우
|
||||
cd /home/webservice && pm2 start ecosystem.config.js
|
||||
pm2 save
|
||||
|
||||
# Nginx 자체 문제
|
||||
sudo nginx -t && sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
**예방:** PHP-FPM과 PM2는 자동 재시작 설정됨. 반복 발생 시 메모리 부족을 의심.
|
||||
|
||||
---
|
||||
|
||||
### Nginx 504 Gateway Timeout
|
||||
|
||||
**증상:** 요청이 오래 걸린 후 504 에러. 무거운 API 호출에서 발생.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo tail -50 /var/log/nginx/api.sam.it.kr.error.log
|
||||
# "upstream timed out" 메시지 확인
|
||||
sudo tail -50 /var/log/mysql/slow.log
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
# 장시간 실행 중인 MySQL 쿼리 kill
|
||||
sudo mysql -e "SHOW PROCESSLIST;" | grep -v Sleep
|
||||
sudo mysql -e "KILL 프로세스_ID;"
|
||||
|
||||
# Nginx timeout 일시적 증가 (필요시)
|
||||
# /etc/nginx/sites-available/api.sam.it.kr 에서 fastcgi_read_timeout 값 조정
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
**예방:** 무거운 작업은 Queue로 처리. 현재 fastcgi_read_timeout은 60초.
|
||||
|
||||
---
|
||||
|
||||
### MySQL 연결 거부 / Too Many Connections
|
||||
|
||||
**증상:** "Connection refused" 또는 "Too many connections" 에러.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo systemctl status mysql
|
||||
sudo mysql -e "SHOW STATUS LIKE 'Threads_connected';"
|
||||
sudo mysql -e "SHOW VARIABLES LIKE 'max_connections';"
|
||||
sudo mysql -e "SHOW PROCESSLIST;"
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
# MySQL이 정지된 경우
|
||||
sudo systemctl start mysql
|
||||
|
||||
# Sleep 연결 정리 (300초 이상 유휴)
|
||||
sudo mysql -e "SELECT id FROM information_schema.processlist WHERE command='Sleep' AND time > 300;" | while read id; do
|
||||
[ "$id" != "id" ] && sudo mysql -e "KILL $id;"
|
||||
done
|
||||
|
||||
# 임시로 max_connections 증가 (재시작 없이)
|
||||
sudo mysql -e "SET GLOBAL max_connections = 150;"
|
||||
```
|
||||
|
||||
**예방:** max_connections(100)은 현재 규모에 적합. 부족 시 sam-tuning.cnf 조정.
|
||||
|
||||
---
|
||||
|
||||
### Redis 메모리 부족
|
||||
|
||||
**증상:** "OOM command not allowed" 메시지.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
redis-cli info memory | grep used_memory_human
|
||||
redis-cli config get maxmemory
|
||||
redis-cli dbsize
|
||||
redis-cli --bigkeys
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
cd /home/webservice/api/current && php artisan cache:clear
|
||||
redis-cli keys "laravel_cache:*" | xargs redis-cli del
|
||||
redis-cli flushall # 전체 초기화 (세션도 삭제 - 주의)
|
||||
redis-cli config set maxmemory 768mb # 임시 증가
|
||||
```
|
||||
|
||||
**예방:** allkeys-lru 정책 설정됨. 512MB 부족 시 redis.conf에서 maxmemory 조정.
|
||||
|
||||
---
|
||||
|
||||
### PM2 프로세스 크래시 / 재시작 반복
|
||||
|
||||
**증상:** sam.it.kr 접속 불가 또는 간헐적 502. PM2 status에서 restart 횟수 급증.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
pm2 status
|
||||
pm2 logs sam-front --err --lines 100
|
||||
pm2 describe sam-front | grep memory
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
pm2 reload sam-front
|
||||
|
||||
# 문제 지속 시 완전 재시작
|
||||
pm2 stop sam-front
|
||||
cd /home/webservice && pm2 start ecosystem.config.js --only sam-front
|
||||
pm2 save
|
||||
|
||||
# 로그 파일이 너무 큰 경우
|
||||
pm2 flush
|
||||
```
|
||||
|
||||
**예방:** max_memory_restart=300M 설정됨. 반복 크래시 시 코드 문제 조사.
|
||||
|
||||
---
|
||||
|
||||
### Queue Worker 정지 / 미처리
|
||||
|
||||
**증상:** 이메일, 알림 등 비동기 작업 미처리.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo supervisorctl status
|
||||
sudo tail -50 /home/webservice/api/shared/storage/logs/queue-worker.log
|
||||
cd /home/webservice/api/current && php artisan queue:monitor redis:default
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
sudo supervisorctl restart sam-queue-worker:*
|
||||
|
||||
cd /home/webservice/api/current
|
||||
php artisan queue:failed # 실패한 작업 확인
|
||||
php artisan queue:retry all # 실패한 작업 재시도
|
||||
php artisan queue:flush # 실패한 작업 전체 삭제
|
||||
```
|
||||
|
||||
**예방:** max-time=3600 설정 (1시간마다 자동 재시작). Supervisor가 프로세스 자동 복구.
|
||||
|
||||
---
|
||||
|
||||
### SSL 인증서 만료
|
||||
|
||||
**증상:** 브라우저에서 "연결이 비공개가 아닙니다" 경고.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo certbot certificates
|
||||
sudo systemctl status certbot.timer
|
||||
echo | openssl s_client -servername api.sam.it.kr -connect 211.117.60.189:443 2>/dev/null | openssl x509 -noout -dates
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
sudo certbot renew
|
||||
sudo certbot certonly --nginx -d api.sam.it.kr # 특정 도메인만
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
**예방:** certbot.timer 정상 작동 시 만료 30일 전 자동 갱신.
|
||||
|
||||
---
|
||||
|
||||
### PHP-FPM Pool 소진 (max_children)
|
||||
|
||||
**증상:** 응답 지연 후 502. PHP-FPM 로그에 "server reached max_children" 경고.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo grep "max_children" /var/log/php8.4-fpm.log
|
||||
ps aux | grep "php-fpm" | grep -v grep | wc -l
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
sudo systemctl restart php8.4-fpm
|
||||
|
||||
# max_children 조정 (예: api pool 10 -> 15)
|
||||
sudo vi /etc/php/8.4/fpm/pool.d/api.conf
|
||||
sudo systemctl reload php8.4-fpm
|
||||
```
|
||||
|
||||
**예방:** 프로세스당 약 80MB. API pool: 10 x 80MB = 800MB. 메모리 여유 시만 증가.
|
||||
|
||||
---
|
||||
|
||||
### Laravel Storage 권한 문제
|
||||
|
||||
**증상:** "Permission denied". 로그 파일 작성 불가. 파일 업로드 실패.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
ls -la /home/webservice/api/shared/storage/
|
||||
ls -la /home/webservice/api/shared/storage/logs/
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
sudo chown -R www-data:webservice /home/webservice/api/shared/storage
|
||||
sudo chmod -R 775 /home/webservice/api/shared/storage
|
||||
sudo chown -R www-data:webservice /home/webservice/api/current/bootstrap/cache
|
||||
sudo chmod -R 775 /home/webservice/api/current/bootstrap/cache
|
||||
```
|
||||
|
||||
**예방:** 배포 스크립트에 권한 설정 포함. shared/storage 심링크 확인.
|
||||
|
||||
---
|
||||
|
||||
## 공통 장애
|
||||
|
||||
### 디스크 공간 부족
|
||||
|
||||
**증상:** 서비스 오류. 로그 기록 실패. MySQL 쓰기 실패.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
df -h
|
||||
sudo du -sh /var/log/*
|
||||
```
|
||||
|
||||
**[운영] 정리:**
|
||||
|
||||
```bash
|
||||
cd /home/webservice/api/releases && ls -1dt */ | tail -n +4 | xargs rm -rf
|
||||
cd /home/webservice/react/releases && ls -1dt */ | tail -n +4 | xargs rm -rf
|
||||
sudo find /var/log -name "*.gz" -mtime +30 -delete
|
||||
sudo truncate -s 0 /home/webservice/api/shared/storage/logs/laravel.log
|
||||
pm2 flush
|
||||
sudo mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
|
||||
sudo apt clean
|
||||
```
|
||||
|
||||
**[CI/CD] 정리:**
|
||||
|
||||
```bash
|
||||
sudo rm -rf /var/lib/jenkins/workspace/*
|
||||
sudo find /var/lib/jenkins/jobs/*/builds -maxdepth 1 -type d -mtime +30 -exec rm -rf {} +
|
||||
sudo journalctl --vacuum-size=500M
|
||||
find /home/hskwon/backups -name "*.sql.gz" -mtime +14 -delete
|
||||
sudo apt clean && sudo apt autoremove -y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 메모리 부족 (OOM)
|
||||
|
||||
**증상:** 프로세스 갑자기 종료. dmesg에 "Out of memory" 메시지.
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
free -h
|
||||
sudo dmesg | grep -i "out of memory"
|
||||
sudo dmesg | grep -i "killed process"
|
||||
ps aux --sort=-%mem | head -15
|
||||
```
|
||||
|
||||
**[운영] 조치:**
|
||||
|
||||
```bash
|
||||
cd /home/webservice/api/current && php artisan cache:clear
|
||||
redis-cli flushall
|
||||
```
|
||||
|
||||
**[운영] 메모리 배분:** MySQL 2GB, Redis 512MB, PHP-FPM ~1.5GB, PM2 ~0.75GB, OS ~3GB
|
||||
|
||||
**[CI/CD] 조치:**
|
||||
|
||||
```bash
|
||||
# Jenkins JVM 메모리 축소 (긴급)
|
||||
# override.conf: -Xmx2048m -> -Xmx1536m
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart jenkins
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 서버 접속 불가 (SSH 타임아웃)
|
||||
|
||||
**진단 (로컬에서):**
|
||||
|
||||
```bash
|
||||
ping 서버_IP
|
||||
nc -zv 서버_IP 22
|
||||
nc -zv 서버_IP 80
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
- ping 응답 없음: IDC 업체에 서버 상태 확인 요청
|
||||
- ping 응답, SSH 불가: fail2ban IP 차단 의심. IDC 콘솔 또는 다른 IP에서 접속하여 `sudo fail2ban-client set sshd unbanip 본인_IP`
|
||||
- 웹은 되나 SSH만 불가: `sudo systemctl restart sshd` (IDC 콘솔)
|
||||
|
||||
**예방:** 관리자 IP를 fail2ban whitelist에 추가.
|
||||
|
||||
---
|
||||
|
||||
### fail2ban 정상 IP 차단
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo fail2ban-client status sshd
|
||||
sudo fail2ban-client get sshd banned | grep 차단의심_IP
|
||||
```
|
||||
|
||||
**조치:**
|
||||
|
||||
```bash
|
||||
sudo fail2ban-client set sshd unbanip 차단된_IP주소
|
||||
sudo systemctl restart fail2ban # 전체 차단 초기화
|
||||
```
|
||||
|
||||
**예방:**
|
||||
|
||||
```bash
|
||||
# /etc/fail2ban/jail.local
|
||||
[DEFAULT]
|
||||
ignoreip = 127.0.0.1/8 관리자_IP_1 관리자_IP_2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD 서버 장애
|
||||
|
||||
### Jenkins 시작 실패
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo journalctl -u jenkins --since "10 minutes ago" --no-pager
|
||||
ps aux | grep java
|
||||
df -h
|
||||
free -h
|
||||
```
|
||||
|
||||
**(a) Java Heap 메모리 부족** (로그: `java.lang.OutOfMemoryError: Java heap space`)
|
||||
|
||||
```bash
|
||||
cat /etc/systemd/system/jenkins.service.d/override.conf
|
||||
# -Xmx 값 조정
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart jenkins
|
||||
```
|
||||
|
||||
**(b) 디스크 공간 부족** (로그: `No space left on device`)
|
||||
|
||||
```bash
|
||||
sudo rm -rf /var/lib/jenkins/workspace/*
|
||||
sudo find /var/lib/jenkins/jobs/*/builds -maxdepth 1 -type d -mtime +30 -exec rm -rf {} +
|
||||
sudo journalctl --vacuum-size=500M
|
||||
sudo systemctl restart jenkins
|
||||
```
|
||||
|
||||
**(c) 플러그인 충돌** (업데이트 후 시작 실패, ClassNotFoundException)
|
||||
|
||||
```bash
|
||||
ls -lt /var/lib/jenkins/plugins/*.jpi | head -10
|
||||
sudo rm /var/lib/jenkins/plugins/문제플러그인.jpi
|
||||
sudo systemctl restart jenkins
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Jenkins 빌드 실패
|
||||
|
||||
**(a) npm/composer 오류:**
|
||||
|
||||
```bash
|
||||
sudo -u jenkins npm cache clean --force
|
||||
sudo rm -rf /var/lib/jenkins/workspace/<JOB>/node_modules
|
||||
```
|
||||
|
||||
**(b) SSH 키 문제:** (`Permission denied`, `Host key verification failed`)
|
||||
|
||||
```bash
|
||||
sudo -u jenkins ssh -i /var/lib/jenkins/.ssh/id_ed25519 hskwon@211.117.60.189 "echo OK"
|
||||
sudo -u jenkins ssh-keyscan -H 211.117.60.189 >> /var/lib/jenkins/.ssh/known_hosts
|
||||
sudo -u jenkins ssh-keyscan -H 114.203.209.83 >> /var/lib/jenkins/.ssh/known_hosts
|
||||
```
|
||||
|
||||
**(c) rsync 실패:** (`connection unexpectedly closed`)
|
||||
|
||||
```bash
|
||||
ssh sam-prod "df -h"
|
||||
ssh sam-prod "ls -la /home/webservice/react/"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Gitea 접속 불가
|
||||
|
||||
**진단:**
|
||||
|
||||
```bash
|
||||
sudo systemctl status gitea
|
||||
curl -I http://localhost:3000
|
||||
sudo ss -tlnp | grep 3000
|
||||
```
|
||||
|
||||
**(a) 포트 충돌:**
|
||||
|
||||
```bash
|
||||
sudo fuser 3000/tcp
|
||||
sudo systemctl restart gitea
|
||||
```
|
||||
|
||||
**(b) DB 연결 실패:**
|
||||
|
||||
```bash
|
||||
sudo systemctl status mysql
|
||||
mysql -u gitea -p gitea -e "SELECT 1;"
|
||||
sudo systemctl restart mysql && sudo systemctl restart gitea
|
||||
```
|
||||
|
||||
**(c) 설정 파일 오류:**
|
||||
|
||||
```bash
|
||||
sudo chown git:git /etc/gitea/app.ini
|
||||
sudo systemctl restart gitea
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Gitea push/pull 느림
|
||||
|
||||
```bash
|
||||
sudo tail -50 /var/lib/gitea/log/gitea.log
|
||||
sudo du -sh /var/lib/gitea/data/repositories/SamProject/*
|
||||
|
||||
# Git GC (저장소 최적화)
|
||||
sudo -u git git -C /var/lib/gitea/data/repositories/SamProject/sam-react-prod.git gc --aggressive
|
||||
sudo systemctl restart gitea
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Prometheus 스크래핑 실패
|
||||
|
||||
**증상:** Grafana에서 데이터 없음.
|
||||
|
||||
```bash
|
||||
sudo systemctl status prometheus
|
||||
curl -s http://localhost:9090/api/v1/targets | python3 -m json.tool | grep -A5 "health"
|
||||
promtool check config /etc/prometheus/prometheus.yml
|
||||
|
||||
# 대상 서버 연결 확인
|
||||
curl -s --connect-timeout 5 http://211.117.60.189:9100/metrics | head -5
|
||||
ssh sam-prod "sudo ufw status | grep 9100"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Grafana 대시보드 로딩 실패
|
||||
|
||||
```bash
|
||||
sudo systemctl status grafana-server
|
||||
curl -I http://localhost:3100
|
||||
sudo systemctl restart grafana-server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 긴급 연락처
|
||||
|
||||
| 역할 | 연락처 | 비고 |
|
||||
|------|--------|------|
|
||||
| 서버 관리 | hskwon | SSH 접속 가능 |
|
||||
| IDC 업체 | (IDC 연락처 기입) | 서버 물리적 장애, 네트워크 |
|
||||
Reference in New Issue
Block a user