Fix: [main] 리드미 수정 및 cicd md 파일 제외
Some checks failed
baekjoon-bot-cicd / build_push_deploy (push) Failing after 1m34s
Some checks failed
baekjoon-bot-cicd / build_push_deploy (push) Failing after 1m34s
This commit is contained in:
@@ -1,138 +1,94 @@
|
||||
# Operations Guide
|
||||
작성: AI / 수정: nkey
|
||||
|
||||
## Supported Deployment Modes
|
||||
- Dockerfile 기반 단일 컨테이너 실행 (`dockerfile`)
|
||||
- (참고) 로컬에서 Uvicorn 직접 실행
|
||||
## 배포 방식
|
||||
|
||||
### Docker (수동)
|
||||
|
||||
## Production Checklist
|
||||
- Secrets/ENV:
|
||||
- `DATABASE_URL`는 필수입니다.
|
||||
- Admin API를 운영에서 사용할 경우 `ADMIN_PASSWORD`를 반드시 설정하고, 클라이언트에서 `X-Admin-Password` 헤더로 전달해야 합니다.
|
||||
- Networking:
|
||||
- 인바운드: TCP 8000 (API)
|
||||
- 아웃바운드: solved.ac API, acmicpc.net(백준) 접근 필요(문제/문제집 정보 조회).
|
||||
- Storage:
|
||||
- 앱 자체는 로컬 파일 저장을 전제로 하지 않습니다.
|
||||
- DB는 외부 PostgreSQL을 사용합니다. (주의) 레포에 DDL/마이그레이션이 포함되어 있지 않으므로, 운영 DB 스키마는 별도로 준비되어 있어야 합니다.
|
||||
- Observability:
|
||||
- 기본 Uvicorn 로그(stdout) 기반. 별도 로깅/메트릭 구성은 레포에 없습니다.
|
||||
- Scaling:
|
||||
- `/today`의 search 모드는 DB 없이도 동작 가능(단, 코드상 DB 의존성 주입은 존재).
|
||||
- workbook 모드는 DB 상태(workbook_sends)에 의해 결과가 달라지므로, 다중 인스턴스 운영 시 동일 DB를 바라보도록 구성해야 합니다.
|
||||
|
||||
## Deployment
|
||||
### Docker
|
||||
```bash
|
||||
# build (주의: 파일명이 'Dockerfile'이 아니라 'dockerfile' 입니다)
|
||||
docker build -f dockerfile -t baekjoon-bot:prod .
|
||||
|
||||
# run
|
||||
cat > .env <<'EOF'
|
||||
DATABASE_URL=postgresql+asyncpg://USER:PASSWORD@HOST:5432/DBNAME
|
||||
ADMIN_PASSWORD=change-me
|
||||
EOF
|
||||
|
||||
docker run -d --name baekjoon-bot \
|
||||
--env-file .env \
|
||||
-p 8000:8000 \
|
||||
baekjoon-bot:prod
|
||||
|
||||
# verify
|
||||
curl -s http://localhost:8000/ | cat
|
||||
```
|
||||
|
||||
## Operations
|
||||
### Healthcheck
|
||||
- `GET /`
|
||||
- 성공 조건: `{"status":"ok"}` 응답
|
||||
### CI/CD (Gitea Actions)
|
||||
|
||||
Workflow: `.gitea/workflows/cicd.yml`
|
||||
|
||||
**트리거**: `main` 브랜치 push (문서 파일만 변경된 경우 스킵)
|
||||
|
||||
**파이프라인**:
|
||||
1. 수동 checkout (Gitea `/gitea` 서브패스 대응)
|
||||
2. Docker Hub 로그인
|
||||
3. 이미지 빌드/푸시: `${DOCKERHUB_USERNAME}/baekjoon-bot:latest`
|
||||
4. 서버 배포: `/nkeysworld/compose.apps.yml`에서 `baekjoon-bot` 서비스 pull/up
|
||||
5. 정리: `docker image prune -f`
|
||||
6. Discord webhook 알림 (성공/실패)
|
||||
|
||||
**필요 Secrets**:
|
||||
- `NKEY_PAT` - Gitea repo fetch 인증
|
||||
- `DOCKERHUB_USERNAME` / `DOCKERHUB_TOKEN` - Docker Hub 인증
|
||||
- `DISCORD_WEBHOOK` - 알림 전송
|
||||
|
||||
**서버 요구사항**:
|
||||
- `docker`, `docker compose` 실행 가능
|
||||
- `/nkeysworld/compose.apps.yml` 파일 존재
|
||||
- compose 내 서비스명 `baekjoon-bot` 존재
|
||||
|
||||
## 운영 체크리스트
|
||||
|
||||
- `DATABASE_URL` 환경변수 필수
|
||||
- Admin API 사용 시 `ADMIN_PASSWORD` 설정 필수
|
||||
- 인바운드: TCP 8000
|
||||
- 아웃바운드: solved.ac API, acmicpc.net 접근 필요
|
||||
- DB 스키마는 별도 준비 필요 (레포에 DDL 미포함, `docs/DEVELOPMENT.md` 참고)
|
||||
- Uvicorn stdout 기반 로깅
|
||||
|
||||
## 헬스체크
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8000/
|
||||
# {"status": "ok"}
|
||||
```
|
||||
|
||||
## 로그 확인
|
||||
|
||||
### Logs
|
||||
- Docker 실행 시:
|
||||
```bash
|
||||
docker logs -f baekjoon-bot
|
||||
```
|
||||
|
||||
## Security Notes (only with evidence)
|
||||
- Admin API는 `X-Admin-Password` 헤더 기반 단일 비밀번호 체크입니다.
|
||||
- `ADMIN_PASSWORD`가 비어 있으면 admin API 호출 시 500으로 실패합니다(설정 누락).
|
||||
- `.env`는 `.gitignore`에 포함되어 있어 커밋되지 않도록 되어 있습니다.
|
||||
## 장애 대응
|
||||
|
||||
## Incident Runbook (Top 5)
|
||||
1) **env 누락**
|
||||
- Symptom: 앱 시작 시 `DATABASE_URL is required...`로 즉시 종료
|
||||
- Cause: `DATABASE_URL` 미설정
|
||||
- Fix: `.env` 또는 런타임 환경변수에 `DATABASE_URL` 설정
|
||||
- Verify: 컨테이너/프로세스 정상 기동 후 `GET /`가 200
|
||||
### 1. DATABASE_URL 누락
|
||||
- **증상**: 앱 시작 시 `DATABASE_URL is required...`로 즉시 종료
|
||||
- **조치**: `.env` 또는 환경변수에 `DATABASE_URL` 설정
|
||||
|
||||
2) **포트 충돌**
|
||||
- Symptom: 컨테이너 실행 시 `bind: address already in use` 또는 접근 불가
|
||||
- Cause: 호스트 8000 포트 사용 중
|
||||
- Fix: `-p 18000:8000` 같이 호스트 포트 변경
|
||||
- Verify: `curl http://localhost:18000/`
|
||||
### 2. 포트 충돌
|
||||
- **증상**: `bind: address already in use`
|
||||
- **조치**: `-p 18000:8000` 등으로 호스트 포트 변경
|
||||
|
||||
3) **DB 연결 실패**
|
||||
- Symptom: `/today` 또는 admin 호출 시 500, 로그에 DB 커넥션 에러
|
||||
- Cause: `DATABASE_URL` 잘못됨 / 네트워크/방화벽 / DB 다운
|
||||
- Fix: URL 재확인, DB 접근 허용, DB 상태 확인
|
||||
- Verify: `/today?source_mode=workbook&workbook_id=...`가 정상 응답(워크북 모드 사용 시)
|
||||
### 3. DB 연결 실패
|
||||
- **증상**: API 호출 시 500, 로그에 DB 커넥션 에러
|
||||
- **조치**: URL 확인, DB 접근 허용, DB 상태 확인
|
||||
|
||||
4) **Admin 인증 실패**
|
||||
- Symptom: admin API가 403 `invalid admin password`
|
||||
- Cause: `X-Admin-Password` 누락 또는 불일치
|
||||
- Fix: `ADMIN_PASSWORD` 값과 동일한 헤더 전달
|
||||
- Verify: `POST /admin/workbooks/{id}/enrich`가 200
|
||||
### 4. Admin 인증 실패
|
||||
- **증상**: admin API 403 `invalid admin password`
|
||||
- **조치**: `ADMIN_PASSWORD` 값과 `X-Admin-Password` 헤더 일치 확인
|
||||
|
||||
5) **문제집 소진**
|
||||
- Symptom: `GET /today?source_mode=workbook&workbook_id=...`가 409 + `no_more_problems_in_workbook`
|
||||
- Cause: 해당 workbook에서 아직 보내지 않은 문제 후보가 없음(`workbook_sends`에 모두 기록됨)
|
||||
- Fix: 진행상황 초기화 API 호출(주의: 관리자 권한 필요) 또는 다른 workbook 사용
|
||||
- Verify: 초기화 후 workbook 모드 호출 시 정상 추천 반환
|
||||
### 5. 문제집 소진
|
||||
- **증상**: 409 `no_more_problems_in_workbook`
|
||||
- **조치**: `DELETE /admin/workbooks/{id}/reset`으로 초기화하거나 다른 문제집 사용
|
||||
|
||||
## Supported Deployment Modes
|
||||
- (기존) Dockerfile 기반 단일 컨테이너 실행: `dockerfile`
|
||||
- (추가) Gitea Actions 기반 CI/CD: `.gitea/workflows/cicd.yml`
|
||||
- Docker Hub로 이미지 푸시 후,
|
||||
- 러너/서버에서 `docker compose -f /nkeysworld/compose.apps.yml pull/up`으로 배포
|
||||
## 보안
|
||||
|
||||
> 참고: 레포에는 Docker Compose 파일이 포함되어 있지 않습니다. CI/CD 배포는 서버에 존재하는 `/nkeysworld/compose.apps.yml`을 사용합니다.
|
||||
- Admin API는 `X-Admin-Password` 헤더 기반 단일 비밀번호 인증
|
||||
- `ADMIN_PASSWORD` 미설정 시 admin API는 500 반환 (의도적 차단)
|
||||
- `.env` 파일은 `.gitignore`에 포함
|
||||
|
||||
## Deployment
|
||||
### CI/CD (Gitea Actions)
|
||||
Workflow: `.gitea/workflows/cicd.yml`
|
||||
## 롤백
|
||||
|
||||
#### Trigger
|
||||
- `main` 브랜치로 `push` 시 `build_push_deploy` 잡 실행
|
||||
|
||||
#### What it does
|
||||
- (1) Manual checkout (Gitea `/gitea` 서브패스 고려)
|
||||
- (2) Docker Hub 로그인
|
||||
- (3) 이미지 빌드/푸시: `${DOCKERHUB_USERNAME}/baekjoon-bot:latest`
|
||||
- (4) 배포: `docker compose -f /nkeysworld/compose.apps.yml pull baekjoon-bot` + `up -d baekjoon-bot`
|
||||
- (5) 정리: `docker image prune -f`
|
||||
- (6) 알림: Discord webhook (성공/실패 모두 전송)
|
||||
|
||||
#### Required Secrets
|
||||
- `NKEY_PAT`: `https://${ACTOR}:${TOKEN}@nkeystudy.site/gitea/${REPO}.git` fetch에 사용
|
||||
- `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`: `docker login`에 사용
|
||||
- `DISCORD_WEBHOOK`: 결과 알림 전송에 사용
|
||||
|
||||
#### Server/Runner Prerequisites
|
||||
- `docker` 실행 가능
|
||||
- `docker compose` 실행 가능(워크플로우에 설치 보완 단계가 있으나, 기본적으로 compose가 동작해야 함)
|
||||
- `/nkeysworld/compose.apps.yml` 파일이 존재해야 함
|
||||
- compose 내 서비스명이 `baekjoon-bot` 이어야 함(워크플로우가 해당 서비스만 `pull/up` 수행)
|
||||
|
||||
#### Rollback (workflow 기준)
|
||||
- 워크플로우는 `:latest`만 푸시/배포합니다.
|
||||
- 롤백을 하려면(운영 정책에 따라):
|
||||
- compose가 특정 태그로 pinning 되도록 변경하거나
|
||||
- `latest` 대신 태그 전략을 도입해야 합니다.
|
||||
|
||||
## Operations
|
||||
### Notifications
|
||||
- CI/CD 결과는 Discord webhook(`DISCORD_WEBHOOK`)으로 전송됩니다.
|
||||
- 성공: `Build & Deploy Success`
|
||||
- 실패: `Build or Deploy Failed`
|
||||
- 포함 정보: repo, commit sha, actor, timestamp
|
||||
CI/CD는 `:latest` 태그만 사용하므로, 롤백이 필요한 경우:
|
||||
- compose 파일에서 특정 태그로 고정하거나
|
||||
- 태그 전략을 도입해야 한다
|
||||
|
||||
Reference in New Issue
Block a user