diff --git a/README.md b/README.md index 499621f..b8cb3b5 100755 --- a/README.md +++ b/README.md @@ -627,6 +627,69 @@ git config --global user.name "계정명" --- +### 14. Gitea Registry ImagePullBackOff — 토큰 인증 헤어핀 NAT 문제 +**증상** +``` +Failed to pull image: Error response from daemon: +Get "http://gitea-http.gitea.svc.cluster.local:3000/v2/token?...": context deadline exceeded +``` +**원인** Docker Desktop 환경에서 kubelet이 이미지를 Pull할 때 Docker 데몬을 통해 수행하는데, +Docker 데몬은 K8s 내부 DNS(`gitea-http.gitea.svc.cluster.local`)를 해석하지 못함. +Gitea Registry가 토큰 인증을 내부 서비스명으로 리다이렉트하면 Docker 데몬이 접근 불가 → timeout 발생. + +**해결** +이미지 주소와 Registry Secret을 외부 IP로 통일: +```bash +# yaml 이미지 주소 변경 +sed -i "s|gitea-http.gitea.svc.cluster.local:3000|192.168.10.101:30000|g" k8s/04-backend.yaml +sed -i "s|gitea-http.gitea.svc.cluster.local:3000|192.168.10.101:30000|g" k8s/05-frontend.yaml + +# Registry Secret 재생성 (외부 IP 기준) +kubectl delete secret gitea-registry-secret -n web-portal +kubectl create secret docker-registry gitea-registry-secret \ + --namespace=web-portal \ + --docker-server=192.168.10.101:30000 \ + --docker-username=<계정> \ + --docker-password=<패스워드> + +# 적용 +kubectl apply -f k8s/04-backend.yaml +kubectl apply -f k8s/05-frontend.yaml +kubectl rollout restart deployment/backend -n web-portal +kubectl rollout restart deployment/frontend -n web-portal +``` + +> ⚠️ Gitea ROOT_URL을 내부 서비스명으로 변경해도 Docker 데몬은 K8s 내부 DNS를 사용할 수 없으므로 +> Docker Desktop 환경에서는 반드시 외부 IP(`192.168.10.101:30000`)를 사용해야 함. + +--- + +### 15. 모니터링 알림 환경변수 미적용 (notifier skipping) +**증상** +``` +[NOTIFIER] Discord webhook URL not set, skipping +[NOTIFIER] Gmail config not set, skipping +``` +**원인** `kubectl set image` 명령어로 이미지를 변경하면 deployment의 환경변수 설정이 초기화됨. +또는 Secret 이름이 yaml과 다르게 생성된 경우. + +**해결** +yaml의 Secret 이름(`notify-secrets`)과 key 이름을 확인 후 동일하게 Secret 재생성: +```bash +kubectl delete secret notify-secrets -n web-portal +kubectl create secret generic notify-secrets \ + --namespace=web-portal \ + --from-literal=discord-webhook-url="https://discord.com/api/webhooks/..." \ + --from-literal=gmail-user="발송계정@gmail.com" \ + --from-literal=gmail-app-password="xxxx xxxx xxxx xxxx" \ + --from-literal=alert-email-to="수신계정@gmail.com" + +kubectl rollout restart deployment/backend -n web-portal +``` +yaml 변경 후에는 반드시 `kubectl apply -f k8s/04-backend.yaml` 로 재적용할 것. + +--- + ## 📅 변경 이력 ### 2026-04-06 (초기 구축) @@ -661,3 +724,45 @@ git config --global user.name "계정명" - **gitea.cyanburu.com** → Gitea (Let's Encrypt 인증서 자동 발급) - **argo.cyanburu.com** → ArgoCD (Let's Encrypt 인증서 자동 발급) - CoreDNS에 서브도메인 내부 IP 등록 (헤어핀 NAT 우회) + +### 2026-04-27 (모니터링 알림 추가 + 장애 복구) + +#### 기능 추가 +- **Discord 알림**: Pod 이상/복구, 계정 잠금, 임시 비밀번호 발급 시 Discord Webhook 알림 +- **Gmail 알림**: Pod 이상/복구, 인증서 만료 임박 시 이메일 알림 +- **자동 모니터링 스케줄러**: Pod 상태 1분마다 체크, 인증서 만료 24시간마다 체크 +- `notifier.py` — Discord/Gmail 알림 모듈 추가 +- `monitor.py` — APScheduler 기반 모니터링 모듈 추가 +- `main.py` — `asyncio.create_task()` → `await` 방식으로 수정 (동기함수 내 비동기 호출 오류 수정) + +#### 알림 설정 방법 +**1. Discord Webhook URL 발급** +Discord 서버 → 알림받을 채널 → ⚙️ 채널 설정 → 연동 → 웹후크 → 새 웹후크 생성 → URL 복사 + +**2. Gmail 앱 비밀번호 발급** +- Google 계정 → 보안 → 2단계 인증 활성화 (필수) +- `https://myaccount.google.com/apppasswords` → 앱 이름 입력 → 16자리 비밀번호 복사 + +**3. K8s Secret 등록** +```bash +kubectl create secret generic notify-secrets \ + --namespace=web-portal \ + --from-literal=discord-webhook-url="https://discord.com/api/webhooks/..." \ + --from-literal=gmail-user="발송계정@gmail.com" \ + --from-literal=gmail-app-password="xxxx xxxx xxxx xxxx" \ + --from-literal=alert-email-to="수신계정@gmail.com" +``` + +**4. 알림 테스트** +브라우저 콘솔(F12)에서 실행: +```javascript +fetch('/api/admin/notify-test', { + headers: { 'Authorization': 'Bearer ' + localStorage.getItem('portal_token') } +}).then(r => r.json()).then(console.log) +``` + +#### 장애 복구 내용 +- **backend ImagePullBackOff** — Gitea Registry 토큰 인증 문제로 발생, 외부 IP 방식으로 해결 +- **backend/frontend 이미지 주소** — 내부 서비스명(`gitea-http.gitea.svc.cluster.local:3000`) → 외부 IP(`192.168.10.101:30000`)로 변경 +- **notifier.py, monitor.py 누락** — Dockerfile에 파일이 있었으나 `--no-cache` 재빌드로 해결 +- **notify-secrets** — 기존 Secret이 잘못된 값으로 등록되어 있어 재생성