feat(itsm): G-1~G-12 확장 기능 + 하네스/봇/설치스크립트 구현
G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현 G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건) G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST) G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직 G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트 G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트 G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트 G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트 G-10: core/push_notify.py + routers/push.py + PushSubscription 모델 G-11: approvals 다중승인 (위임/서명/기한초과/마감연장) G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh 하네스: guardia-orchestrator 확장기능 Phase 반영 봇명령어: /sr /status /license /bulk 슬래시 명령어 추가 설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
938b25f286
362
01_분석설계서.md
Normal file
362
01_분석설계서.md
Normal file
@ -0,0 +1,362 @@
|
||||
# GUARDiA ITSM + Messenger — 분석 및 설계서
|
||||
|
||||
> **문서번호**: GUARDIA-DS-001
|
||||
> **버전**: 1.0
|
||||
> **작성일**: 2026-05-25
|
||||
> **작성자**: GUARDiA 개발팀
|
||||
> **보안등급**: 내부용 (대외비)
|
||||
|
||||
---
|
||||
|
||||
## 1. 문서 목적
|
||||
|
||||
본 문서는 GUARDiA ITSM(IT Service Management) 플랫폼과 GUARDiA Messenger 시스템의 요구사항 분석, 시스템 설계, 데이터 설계, API 설계를 기술한다. 본 문서는 개발자, 운영자, 품질관리자가 시스템을 이해하고 유지보수하는 데 활용한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 시스템 개요
|
||||
|
||||
### 2-1. GUARDiA ITSM
|
||||
|
||||
**GUARDiA ITSM**은 공공기관 IT 운영을 위한 온프레미스 서비스관리 플랫폼이다.
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 목적 | 공공기관 IT 인프라 SR(Service Request) 접수·처리·이력 관리 |
|
||||
| 운영 방식 | 온프레미스 (인터넷 완전 차단 환경 대응) |
|
||||
| 주요 사용자 | 운영 엔지니어, PM, 기관 담당자(고객) |
|
||||
| 기술 스택 | FastAPI, SQLAlchemy (Async), SQLite, Python 3.11+ |
|
||||
| 인증 방식 | JWT (RS256), 역할 기반 접근 제어 (RBAC) |
|
||||
|
||||
### 2-2. GUARDiA Messenger
|
||||
|
||||
**GUARDiA Messenger**는 ITSM과 연동되는 사내 메신저로, SR 알림, 봇 명령, 엔지니어 채팅을 지원한다.
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 목적 | 운영팀 실시간 소통 및 ITSM 이벤트 알림 |
|
||||
| 연동 방식 | GUARDiA ITSM Webhook API |
|
||||
| 주요 기능 | 채팅, 봇 명령, SR 알림, 공지 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 요구사항 분석
|
||||
|
||||
### 3-1. 기능적 요구사항 (Functional Requirements)
|
||||
|
||||
| ID | 분류 | 요구사항 | 우선순위 |
|
||||
|----|------|----------|---------|
|
||||
| FR-001 | SR 관리 | SR 접수·파싱·배정·승인·처리·완료 워크플로우 | 필수 |
|
||||
| FR-002 | SR 관리 | SR 상태 실시간 SSE 이벤트 브로드캐스트 | 필수 |
|
||||
| FR-003 | 인증 | JWT 기반 역할별 로그인 (ADMIN/PM/ENGINEER/CUSTOMER) | 필수 |
|
||||
| FR-004 | CMDB | 기관·서버 자산 등록 및 조회 | 필수 |
|
||||
| FR-005 | SSH | 서버 원격 명령 실행 (asyncssh) | 필수 |
|
||||
| FR-006 | SSL | SSL 인증서 만료 모니터링 및 갱신 이력 관리 | 필수 |
|
||||
| FR-007 | PM | 정기 PM 체크리스트 템플릿 및 반복 스케줄 관리 | 필수 |
|
||||
| FR-008 | 장애 | P1~P4 장애 등록·상태 관리·RCA 기록 | 필수 |
|
||||
| FR-009 | 당직 | 온콜/당직 일정 관리 | 권장 |
|
||||
| FR-010 | 배치 | 배치 작업 등록·실행·이력 관리 | 권장 |
|
||||
| FR-011 | CI/CD | Jenkins 파이프라인 연동 (빌드·배포·콜백) | 선택 |
|
||||
| FR-012 | 알림 | 이메일(SMTP) + 메신저 Webhook 알림 | 필수 |
|
||||
| FR-013 | 보고서 | 작업이력·PM결과 Excel 다운로드 | 권장 |
|
||||
| FR-014 | 감사 | 해시 체인 기반 감사 로그 | 필수 |
|
||||
| FR-015 | KB | 장애 처리 지식 베이스 문서 관리 | 권장 |
|
||||
| FR-016 | 상용화 | 라이선스 키 기반 에디션 제어 (COMMUNITY / STANDARD / ENTERPRISE) | 필수 |
|
||||
| FR-017 | 상용화 | 에디션별 기관·사용자·서버 수량 및 기능(LDAP/PAM/AI/CICD 등) 접근 제한 | 필수 |
|
||||
|
||||
### 3-2. 비기능적 요구사항 (Non-Functional Requirements)
|
||||
|
||||
| ID | 분류 | 요구사항 |
|
||||
|----|------|---------|
|
||||
| NFR-001 | 보안 | AES-256-GCM 서버 자격증명 암호화 |
|
||||
| NFR-002 | 보안 | API 응답에 IP·SSH계정·비밀번호 절대 미포함 |
|
||||
| NFR-003 | 보안 | root SSH 직접 접속 차단 |
|
||||
| NFR-004 | 보안 | 명령어 안전성 검증 (위험 패턴 차단) |
|
||||
| NFR-005 | 가용성 | 99% 이상 가용성 목표 |
|
||||
| NFR-006 | 성능 | API 응답 시간 500ms 이하 (DB 조회 기준) |
|
||||
| NFR-007 | 호환성 | RHEL 8/9, CentOS 7/8, Ubuntu 20.04/22.04 |
|
||||
| NFR-008 | 호환성 | Windows Server 2019/2022 |
|
||||
| NFR-009 | 격리 | 외부 인터넷 완전 차단 환경 동작 가능 |
|
||||
| NFR-010 | 감사 | 모든 SR 처리 이력 블록체인형 해시 체인 보존 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 시스템 아키텍처
|
||||
|
||||
### 4-1. 전체 구성도
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 클라이언트 레이어 │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
|
||||
│ │ 웹 브라우저 │ │ 모바일 앱 │ │ GUARDiA Messenger │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────────┬─────────────┘ │
|
||||
└─────────┼─────────────────┼─────────────────────┼───────────────┘
|
||||
│ HTTPS │ HTTPS │ WebSocket/HTTP
|
||||
┌─────────▼─────────────────▼─────────────────────▼───────────────┐
|
||||
│ API 게이트웨이 레이어 │
|
||||
│ Nginx (Reverse Proxy + TLS Termination) │
|
||||
└─────────────────────────────┬───────────────────────────────────┘
|
||||
│ HTTP (내부)
|
||||
┌─────────────────────────────▼───────────────────────────────────┐
|
||||
│ 애플리케이션 레이어 │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ GUARDiA ITSM (FastAPI) │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │ │
|
||||
│ │ │ SR 관리 │ │ SSH 실행 │ │ SSL 감시 │ │ PM 관리 │ │ │
|
||||
│ │ │ tasks │ │ ssh_exec │ │ssl_mgr │ │ pm │ │ │
|
||||
│ │ └──────────┘ └──────────┘ └──────────┘ └───────────┘ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │ │
|
||||
│ │ │ 장애관리 │ │ 배치작업 │ │ CI/CD │ │ Scheduler │ │ │
|
||||
│ │ │incidents │ │ batch │ │ cicd │ │(APSchedul)│ │ │
|
||||
│ │ └──────────┘ └──────────┘ └──────────┘ └───────────┘ │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ GUARDiA Messenger (FastAPI) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────┬──────────────────────────────────┘
|
||||
│ SQLAlchemy (Async)
|
||||
┌──────────────────────────────▼──────────────────────────────────┐
|
||||
│ 데이터 레이어 │
|
||||
│ SQLite (개발/소규모) → PostgreSQL (운영 권장) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│ asyncssh
|
||||
┌──────────────────────────────▼──────────────────────────────────┐
|
||||
│ 대상 서버 레이어 │
|
||||
│ WEB (Nginx/Apache) WAS (Tomcat/JBoss) DB (PostgreSQL/Oracle) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4-2. GUARDiA ITSM 컴포넌트 설명
|
||||
|
||||
| 컴포넌트 | 파일 | 역할 |
|
||||
|---------|------|------|
|
||||
| 진입점 | `main.py` | FastAPI 앱, 라우터 등록, lifespan |
|
||||
| ORM/스키마 | `models.py` | SQLAlchemy 모델 + Pydantic 스키마 |
|
||||
| DB 세션 | `database.py` | AsyncSession 팩토리 |
|
||||
| 인증 | `core/auth.py` | JWT 발급/검증 |
|
||||
| SSH 실행 | `core/ssh_exec.py` | asyncssh 기반 원격 실행 |
|
||||
| 알림 | `core/notify.py` | SMTP + Messenger Webhook |
|
||||
| 스케줄러 | `core/scheduler.py` | SSL/PM/계약 만료 자동 감시 |
|
||||
| Jenkins 클라이언트 | `core/cicd.py` | Jenkins REST API |
|
||||
| 시드 데이터 | `core/seed.py` | 초기 데이터 자동 생성 |
|
||||
| 라이선스 엔진 | `core/license.py` | AES-256-GCM 라이선스 생성·검증·캐싱 |
|
||||
| 라이선스 가드 | `middleware/license_guard.py` | 에디션별 기관·서버·기능 접근 제한 Dependency |
|
||||
|
||||
### 4-3. GUARDiA Messenger 컴포넌트
|
||||
|
||||
```
|
||||
C:\GUARDiA\messenger\
|
||||
├── core/ 실시간 채팅 엔진
|
||||
├── models/ 메시지·사용자·채널 ORM
|
||||
└── main.py WebSocket + REST API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 데이터 설계
|
||||
|
||||
### 5-1. 전체 테이블 목록 (27개)
|
||||
|
||||
| 번호 | 테이블명 | 설명 | 관련 기능 |
|
||||
|-----|---------|------|---------|
|
||||
| 1 | `tb_user` | 시스템 사용자 | 인증 |
|
||||
| 2 | `tb_inst_meta` | 기관 메타정보 | CMDB |
|
||||
| 3 | `tb_inst_contact` | 기관 담당자 연락처 | CMDB |
|
||||
| 4 | `tb_server_info` | 서버 자산 (암호화) | CMDB/SSH |
|
||||
| 5 | `tb_sr_request` | SR 마스터 | SR관리 |
|
||||
| 6 | `tb_sr_approval` | SR 승인 이력 | 승인 |
|
||||
| 7 | `tb_work_log` | 작업 실행 로그 | 작업 |
|
||||
| 8 | `tb_audit_log` | 감사 로그 (해시체인) | 감사 |
|
||||
| 9 | `tb_rating` | 만족도 평가 | 고객 |
|
||||
| 10 | `tb_shell_script` | SM 스크립트 메타 | 자동화 |
|
||||
| 11 | `tb_work_timetable` | 작업 일정표 | 타임테이블 |
|
||||
| 12 | `tb_sr_attachment` | SR 첨부파일 | 첨부 |
|
||||
| 13 | `tb_notification_log` | 알림 이력 | 알림 |
|
||||
| 14 | `tb_kb_document` | 지식 베이스 | KB |
|
||||
| 15 | `tb_engineer_profile` | 엔지니어 스킬 | 자동배정 |
|
||||
| 16 | `tb_project` | CI/CD 프로젝트 | 배포 |
|
||||
| 17 | `tb_vibe_session` | 바이브 코딩 세션 | CI/CD |
|
||||
| 18 | `tb_ssl_history` | SSL 갱신 이력 | SSL |
|
||||
| 19 | `tb_pm_template` | PM 체크리스트 템플릿 | PM |
|
||||
| 20 | `tb_pm_schedule` | PM 반복 스케줄 | PM |
|
||||
| 21 | `tb_pm_result` | PM 점검 결과 | PM |
|
||||
| 22 | `tb_incident` | 장애 마스터 | 장애관리 |
|
||||
| 23 | `tb_incident_sr` | 장애↔SR 연결 | 장애관리 |
|
||||
| 24 | `tb_oncall_schedule` | 온콜/당직 일정 | 당직 |
|
||||
| 25 | `tb_batch_job` | 배치 작업 | 배치 |
|
||||
| 26 | `tb_batch_run` | 배치 실행 이력 | 배치 |
|
||||
| 27 | `tb_license` | 라이선스 등록 이력 | 상용화 라이선스 관리 |
|
||||
|
||||
### 5-2. 핵심 테이블 ERD (텍스트 형식)
|
||||
|
||||
```
|
||||
tb_inst_meta (1) ──< (N) tb_server_info
|
||||
│
|
||||
tb_inst_meta (1) ──< (N) tb_sr_request ──< (N) tb_sr_approval
|
||||
──< (N) tb_work_log
|
||||
──< (N) tb_audit_log
|
||||
──< (N) tb_sr_attachment
|
||||
──< (N) tb_ops_task
|
||||
|
||||
tb_sr_request (N) >──< (N) tb_incident (via tb_incident_sr)
|
||||
|
||||
tb_server_info (1) ──< (N) tb_ssl_history
|
||||
tb_server_info (1) ──< (N) tb_batch_job
|
||||
|
||||
tb_pm_template (1) ──< (N) tb_pm_result
|
||||
tb_work_timetable (1) ──< (N) tb_pm_result
|
||||
```
|
||||
|
||||
### 5-3. SR 상태 머신
|
||||
|
||||
```
|
||||
RECEIVED ──→ PARSED ──→ PENDING_APPROVAL ──→ APPROVED ──→ IN_PROGRESS
|
||||
└──→ REJECTED
|
||||
↓
|
||||
IN_PROGRESS ──→ PENDING_PM_VALIDATION ──→ COMPLETED
|
||||
└──→ FAILED_ROLLBACK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. API 설계
|
||||
|
||||
### 6-1. 인증 방식
|
||||
|
||||
```
|
||||
POST /api/auth/login
|
||||
요청: { "username": "admin", "password": "****" }
|
||||
응답: { "access_token": "eyJ...", "token_type": "bearer" }
|
||||
|
||||
이후 모든 API 요청 헤더:
|
||||
Authorization: Bearer eyJ...
|
||||
```
|
||||
|
||||
### 6-2. 주요 API 목록
|
||||
|
||||
| 분류 | 메서드 | 경로 | 역할 |
|
||||
|------|--------|------|------|
|
||||
| 인증 | POST | `/api/auth/login` | 로그인 |
|
||||
| SR | GET/POST | `/api/tasks` | SR 목록/생성 |
|
||||
| SR | PATCH | `/api/tasks/{id}/status` | 상태 변경 |
|
||||
| SSL | GET | `/api/ssl/expiring` | 만료 임박 목록 |
|
||||
| SSL | POST | `/api/ssl/check/{id}` | SSH 점검 |
|
||||
| SSL | POST | `/api/ssl/renew/{id}` | 갱신 기록 |
|
||||
| PM | GET/POST | `/api/pm/templates` | 체크리스트 템플릿 |
|
||||
| PM | POST | `/api/pm/schedules/{id}/trigger` | PM 실행 |
|
||||
| 장애 | GET/POST | `/api/incidents` | 장애 관리 |
|
||||
| 당직 | GET | `/api/oncall/today` | 오늘 당직자 |
|
||||
| 배치 | POST | `/api/batch/jobs/{id}/run` | 배치 실행 |
|
||||
|
||||
### 6-3. 오류 응답 형식
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "오류 요약 메시지 (IP, 스택트레이스 미포함)"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 보안 설계
|
||||
|
||||
### 7-1. 자격증명 암호화
|
||||
|
||||
```
|
||||
평문 비밀번호
|
||||
│
|
||||
▼ AES-256-GCM (12바이트 nonce + 암호문)
|
||||
│
|
||||
▼ Base64 인코딩
|
||||
│
|
||||
tb_server_info.os_pw_enc 컬럼 저장
|
||||
```
|
||||
|
||||
암호화 키: `GUARDIA_ENC_KEY` 환경변수 (32바이트)
|
||||
|
||||
### 7-2. JWT 보안
|
||||
|
||||
- 알고리즘: HS256
|
||||
- 만료: 8시간 (기본값, 환경변수로 조정 가능)
|
||||
- 저장: 클라이언트 메모리 (localStorage 사용 금지 권고)
|
||||
|
||||
### 7-3. SSH 보안
|
||||
|
||||
- root 계정 직접 접속 금지 (`ssh_user ≠ root` 검증)
|
||||
- known_hosts 검증 (운영 환경 필수)
|
||||
- 위험 명령어 패턴 차단:
|
||||
- `rm -rf /`, `mkfs`, `dd if=`, `shutdown`, `reboot` 등
|
||||
|
||||
### 7-4. API 응답 보안
|
||||
|
||||
`ServerOut` 스키마에서 민감정보 제외:
|
||||
- `ip_addr` — 미포함
|
||||
- `ssh_user` — 미포함
|
||||
- `os_pw_enc` — 미포함
|
||||
|
||||
### 7-5. 감사 로그 무결성
|
||||
|
||||
각 감사 로그 항목은 이전 항목의 해시값을 포함하는 체인 구조:
|
||||
```
|
||||
log_hash = SHA256(prev_hash + actor + action + detail + timestamp)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 배포 구성
|
||||
|
||||
### 8-1. 운영 환경 권장 구성
|
||||
|
||||
```
|
||||
인터넷
|
||||
│ (차단)
|
||||
▼
|
||||
[Nginx] :443 ──TLS──→ [GUARDiA ITSM] :8000
|
||||
──TLS──→ [GUARDiA Messenger] :8001
|
||||
|
||||
[GUARDiA ITSM] ──asyncssh──→ [관리 대상 서버들]
|
||||
[GUARDiA ITSM] ──SMTP──→ [내부 메일 서버]
|
||||
```
|
||||
|
||||
### 8-2. 최소 서버 사양
|
||||
|
||||
| 항목 | 최소 | 권장 |
|
||||
|------|------|------|
|
||||
| CPU | 2 코어 | 4 코어 |
|
||||
| 메모리 | 4 GB | 8 GB |
|
||||
| 디스크 | 50 GB | 100 GB |
|
||||
| OS | RHEL 8 이상 / Ubuntu 20.04 이상 | RHEL 9 / Ubuntu 22.04 |
|
||||
| Python | 3.10 이상 | 3.11 |
|
||||
|
||||
---
|
||||
|
||||
## 9. GUARDiA Messenger 설계
|
||||
|
||||
### 9-1. 주요 기능
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| 채팅 | 1:1 및 그룹 채팅 |
|
||||
| 봇 명령 | `!vibe`, `!build`, `!deploy`, `!sr`, `!health` 등 |
|
||||
| SR 알림 | ITSM SR 상태 변경 시 자동 알림 |
|
||||
| 파일 전송 | 이미지·문서 공유 |
|
||||
|
||||
### 9-2. 봇 명령어 목록
|
||||
|
||||
| 명령어 | 설명 | 예시 |
|
||||
|--------|------|------|
|
||||
| `!sr <내용>` | SR 빠른 접수 | `!sr WAS 응답 없음` |
|
||||
| `!status <SR번호>` | SR 상태 조회 | `!status SR-20260525-AA1234` |
|
||||
| `!health <서버명>` | 서버 헬스체크 | `!health MOF-WAS-01` |
|
||||
| `!oncall` | 오늘 당직자 조회 | `!oncall` |
|
||||
| `!pm <스케줄명>` | PM 즉시 실행 | `!pm 기재부 월간점검` |
|
||||
| `!ssl <도메인>` | SSL 만료 확인 | `!ssl csv.culture.go.kr` |
|
||||
|
||||
---
|
||||
|
||||
## 10. 변경 이력
|
||||
|
||||
| 버전 | 날짜 | 내용 | 작성자 |
|
||||
|------|------|------|--------|
|
||||
| 1.0 | 2026-05-25 | 최초 작성 | 개발팀 |
|
||||
593
02_단위통합테스트계획서.md
Normal file
593
02_단위통합테스트계획서.md
Normal file
@ -0,0 +1,593 @@
|
||||
# GUARDiA ITSM + Messenger — 단위·통합 테스트 계획서
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**대상 시스템**: GUARDiA ITSM v1.0, GUARDiA Messenger v1.0
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [개요](#1-개요)
|
||||
2. [테스트 범위 및 전략](#2-테스트-범위-및-전략)
|
||||
3. [테스트 환경](#3-테스트-환경)
|
||||
4. [단위 테스트 계획](#4-단위-테스트-계획)
|
||||
5. [통합 테스트 계획](#5-통합-테스트-계획)
|
||||
6. [비기능 테스트 계획](#6-비기능-테스트-계획)
|
||||
7. [테스트 케이스 목록](#7-테스트-케이스-목록)
|
||||
8. [결함 관리](#8-결함-관리)
|
||||
9. [완료 기준](#9-완료-기준)
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 목적
|
||||
|
||||
본 문서는 GUARDiA ITSM 및 GUARDiA Messenger 시스템의 품질 보증을 위한 단위 테스트 및 통합 테스트 계획을 정의한다. 개별 컴포넌트의 정확성 검증(단위)과 컴포넌트 간 상호작용 검증(통합)을 통해 출시 전 결함을 조기에 발견하고 수정한다.
|
||||
|
||||
### 1.2 테스트 대상
|
||||
|
||||
| 구분 | 대상 모듈 |
|
||||
|------|-----------|
|
||||
| ITSM 코어 | auth, dashboard, tasks, approvals, audit, cmdb |
|
||||
| ITSM 운영 | ssl_manager, pm, incidents, oncall, batch |
|
||||
| ITSM 지원 | kb, shell_scripts, ssh, attachments, notifications |
|
||||
| ITSM 고객 | rating, institutions, timetable, work |
|
||||
| Messenger | bot dispatcher, GUARDiA_ITSM bot, command handlers |
|
||||
| 공통 | scheduler, seed, database, JWT auth |
|
||||
|
||||
### 1.3 테스트 유형 정의
|
||||
|
||||
- **단위 테스트(Unit Test)**: 함수/클래스 단위의 독립 검증. 외부 의존성은 mock 처리.
|
||||
- **통합 테스트(Integration Test)**: DB·Redis·SSH·Messenger 등 실제 의존성과의 연동 검증.
|
||||
- **E2E 시나리오 테스트**: 사용자 시나리오 전 구간 검증 (API 호출 → DB 저장 → 알림 발송).
|
||||
|
||||
---
|
||||
|
||||
## 2. 테스트 범위 및 전략
|
||||
|
||||
### 2.1 단위 테스트 범위
|
||||
|
||||
```
|
||||
범위 내 (In Scope)
|
||||
├── 모든 라우터 엔드포인트 (130개 API)
|
||||
├── core/scheduler.py — 스케줄 함수
|
||||
├── core/seed.py — 시드 데이터 생성
|
||||
├── utils/crypto.py — AES-256-GCM 암/복호화
|
||||
├── utils/ssh_runner.py — SSH 명령 검증
|
||||
└── schemas/*.py — Pydantic 직렬화/역직렬화
|
||||
|
||||
범위 외 (Out of Scope)
|
||||
├── 3rd party 라이브러리 내부 (FastAPI, SQLAlchemy)
|
||||
└── OS 수준 시스템 콜
|
||||
```
|
||||
|
||||
### 2.2 통합 테스트 범위
|
||||
|
||||
```
|
||||
범위 내
|
||||
├── ITSM API ↔ SQLite DB 연동
|
||||
├── ITSM API ↔ GUARDiA Messenger 알림
|
||||
├── SSH 실행 → 원격 서버 응답 처리
|
||||
├── SSL 점검 스크립트 → JSON 파싱
|
||||
├── 스케줄러 → DB 변경 → 알림 체인
|
||||
└── PM 스케줄 → WorkTimetable 자동 생성
|
||||
|
||||
범위 외
|
||||
├── 외부 LLM API (완전 금지)
|
||||
├── 실제 고객 운영 서버 대상 SSH
|
||||
└── 이메일/SMS 외부 발송
|
||||
```
|
||||
|
||||
### 2.3 테스트 전략
|
||||
|
||||
- **TDD 기반**: 핵심 비즈니스 로직은 테스트 먼저 작성
|
||||
- **AAA 패턴**: Arrange → Act → Assert
|
||||
- **픽스처 격리**: 각 테스트는 독립 SQLite in-memory DB 사용
|
||||
- **커버리지 목표**: 단위 80% 이상, 통합 70% 이상
|
||||
|
||||
---
|
||||
|
||||
## 3. 테스트 환경
|
||||
|
||||
### 3.1 단위 테스트 환경
|
||||
|
||||
```
|
||||
OS: 동일 (Linux/Windows 무관)
|
||||
Python: 3.11+
|
||||
프레임워크: pytest 8.x + pytest-asyncio
|
||||
Mock: unittest.mock, respx (httpx mock)
|
||||
DB: SQLite in-memory (aiosqlite)
|
||||
의존성: requirements-dev.txt 참조
|
||||
```
|
||||
|
||||
**requirements-dev.txt**:
|
||||
```
|
||||
pytest>=8.0
|
||||
pytest-asyncio>=0.23
|
||||
pytest-cov>=5.0
|
||||
httpx>=0.27 # TestClient
|
||||
respx>=0.21 # httpx mock
|
||||
faker>=25.0 # 테스트 데이터
|
||||
freezegun>=1.4 # 시간 고정
|
||||
```
|
||||
|
||||
### 3.2 통합 테스트 환경
|
||||
|
||||
```
|
||||
OS: Ubuntu 22.04 LTS (권장) 또는 Windows Server 2022
|
||||
Python: 3.11+
|
||||
DB: SQLite 파일 기반 (테스트 전용)
|
||||
Messenger: 테스트 채널 (별도 구성)
|
||||
SSH 대상: localhost SSH 서버 (테스트 전용)
|
||||
```
|
||||
|
||||
### 3.3 디렉토리 구조
|
||||
|
||||
```
|
||||
C:\GUARDiA\itsm\
|
||||
└── tests\
|
||||
├── conftest.py # 공통 픽스처
|
||||
├── unit\
|
||||
│ ├── test_auth.py
|
||||
│ ├── test_tasks.py
|
||||
│ ├── test_ssl_manager.py
|
||||
│ ├── test_pm.py
|
||||
│ ├── test_incidents.py
|
||||
│ ├── test_oncall.py
|
||||
│ ├── test_batch.py
|
||||
│ ├── test_scheduler.py
|
||||
│ └── test_crypto.py
|
||||
└── integration\
|
||||
├── test_sr_workflow.py
|
||||
├── test_approval_chain.py
|
||||
├── test_pm_workflow.py
|
||||
├── test_incident_workflow.py
|
||||
└── test_notification_chain.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 단위 테스트 계획
|
||||
|
||||
### 4.1 conftest.py 공통 픽스처
|
||||
|
||||
```python
|
||||
# tests/conftest.py
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from httpx import AsyncClient, ASGITransport
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from database import Base, get_db
|
||||
from main import app
|
||||
|
||||
TEST_DB = "sqlite+aiosqlite:///:memory:"
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_session():
|
||||
engine = create_async_engine(TEST_DB)
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
SessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
async with SessionLocal() as session:
|
||||
yield session
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def client(db_session):
|
||||
app.dependency_overrides[get_db] = lambda: db_session
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=app), base_url="http://test"
|
||||
) as ac:
|
||||
yield ac
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def auth_headers(client):
|
||||
r = await client.post("/auth/login", json={"username":"admin","password":"admin1234!"})
|
||||
token = r.json()["access_token"]
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
```
|
||||
|
||||
### 4.2 인증 모듈 (auth) 단위 테스트
|
||||
|
||||
**파일**: `tests/unit/test_auth.py`
|
||||
|
||||
| 테스트 ID | 테스트명 | 설명 | 기대 결과 |
|
||||
|-----------|----------|------|-----------|
|
||||
| UT-AUTH-001 | 정상 로그인 | 올바른 계정으로 로그인 | 200 + access_token 반환 |
|
||||
| UT-AUTH-002 | 잘못된 비밀번호 | 틀린 비밀번호 시도 | 401 Unauthorized |
|
||||
| UT-AUTH-003 | 존재하지 않는 계정 | 없는 사용자명 | 401 Unauthorized |
|
||||
| UT-AUTH-004 | 비밀번호 변경 | 현재 PW 확인 후 변경 | 200 + message |
|
||||
| UT-AUTH-005 | JWT 토큰 만료 | 만료된 토큰으로 API 호출 | 401 Unauthorized |
|
||||
| UT-AUTH-006 | RBAC — CUSTOMER 권한 제한 | CUSTOMER가 admin API 호출 | 403 Forbidden |
|
||||
| UT-AUTH-007 | RBAC — ENGINEER 권한 | ENGINEER가 SR 처리 | 200 OK |
|
||||
| UT-AUTH-008 | 빈 토큰 | Authorization 헤더 없음 | 401 Unauthorized |
|
||||
|
||||
```python
|
||||
# 예시 테스트 코드
|
||||
@pytest.mark.asyncio
|
||||
async def test_login_success(client, db_session):
|
||||
# Arrange: seed admin user
|
||||
from core.seed import seed_all
|
||||
await seed_all(db_session)
|
||||
# Act
|
||||
r = await client.post("/auth/login", json={"username":"admin","password":"admin1234!"})
|
||||
# Assert
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert "access_token" in data
|
||||
assert data["token_type"] == "bearer"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_login_wrong_password(client, db_session):
|
||||
from core.seed import seed_all
|
||||
await seed_all(db_session)
|
||||
r = await client.post("/auth/login", json={"username":"admin","password":"wrong!"})
|
||||
assert r.status_code == 401
|
||||
```
|
||||
|
||||
### 4.3 SR 처리 (tasks) 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-TASK-001 | SR 생성 (INCIDENT 유형) | 201 + sr_id 반환 |
|
||||
| UT-TASK-002 | SR 생성 (CHANGE 유형) | 201 + sr_id 반환 |
|
||||
| UT-TASK-003 | SR 목록 페이징 | 200 + items/total 구조 |
|
||||
| UT-TASK-004 | SR 상태 변경 OPEN→IN_PROGRESS | 200 + 상태 업데이트 |
|
||||
| UT-TASK-005 | SR 상태 — 잘못된 전환 | 400 Bad Request |
|
||||
| UT-TASK-006 | SR 첨부파일 업로드 | 201 + file_id |
|
||||
| UT-TASK-007 | SR 첨부파일 경로 순회 방지 | 400 Bad Request |
|
||||
| UT-TASK-008 | 고객(CUSTOMER) SR 자기 것만 조회 | 200 + 본인 SR만 |
|
||||
| UT-TASK-009 | SR 검색 (제목 키워드) | 200 + 필터 결과 |
|
||||
| UT-TASK-010 | SR 만족도 평가 | 200 + rating 저장 |
|
||||
|
||||
### 4.4 SSL 관리 (ssl_manager) 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-SSL-001 | SSL 도메인 등록 | 201 + ssl_id |
|
||||
| UT-SSL-002 | 중복 도메인 등록 | 409 Conflict |
|
||||
| UT-SSL-003 | SSL 즉시 점검 — 정상 응답 파싱 | 200 + days_left 계산 |
|
||||
| UT-SSL-004 | SSL 즉시 점검 — SSH 실패 시 | 200 + error_msg 기록 |
|
||||
| UT-SSL-005 | 만료 임박 알림 등급 (EXPIRED ≤0) | level=EXPIRED |
|
||||
| UT-SSL-006 | 만료 임박 알림 등급 (URGENT ≤7) | level=URGENT |
|
||||
| UT-SSL-007 | 만료 임박 알림 등급 (WARN ≤30) | level=WARN |
|
||||
| UT-SSL-008 | 정상 인증서 (>30일) | level=OK |
|
||||
| UT-SSL-009 | SSL 수동 갱신 완료 기록 | 200 + renewed_at 업데이트 |
|
||||
| UT-SSL-010 | SSL 도메인 삭제 | 204 No Content |
|
||||
|
||||
### 4.5 PM 정기점검 (pm) 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-PM-001 | PM 템플릿 생성 | 201 + template_id |
|
||||
| UT-PM-002 | PM 템플릿 목록 조회 | 200 + 카테고리별 목록 |
|
||||
| UT-PM-003 | PM 스케줄 생성 (MONTHLY) | 201 + next_scheduled 계산 |
|
||||
| UT-PM-004 | PM 스케줄 생성 (WEEKLY) | 201 + 7일 후 next_scheduled |
|
||||
| UT-PM-005 | PM 스케줄 생성 (QUARTERLY) | 201 + 90일 후 next_scheduled |
|
||||
| UT-PM-006 | PM 작업 생성 (WorkTimetable 연동) | 201 + timetable_id |
|
||||
| UT-PM-007 | PM 결과 저장 — PASS | 200 + result=PASS |
|
||||
| UT-PM-008 | PM 결과 저장 — FAIL (비고 필수) | 400 if note 없음 |
|
||||
| UT-PM-009 | PM 완료 처리 | 200 + completed_at 기록 |
|
||||
| UT-PM-010 | PM Excel 보고서 다운로드 | 200 + Content-Type: xlsx |
|
||||
| UT-PM-011 | next_scheduled 계산 — MONTHLY 말일 | 말일 처리 정확성 |
|
||||
| UT-PM-012 | PM 템플릿 소프트 삭제 | 200 + is_active=False |
|
||||
|
||||
### 4.6 장애 관리 (incidents) 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-INC-001 | 장애 생성 (P1) | 201 + INC-YYYYMMDD-XXXXXX 형식 |
|
||||
| UT-INC-002 | 장애 생성 (P4) | 201 + 알림 없음 |
|
||||
| UT-INC-003 | P1/P2 생성 → Messenger 알림 | 알림 함수 호출 확인 (mock) |
|
||||
| UT-INC-004 | 상태 전환 OPEN→INVESTIGATING | 200 + 타임라인 기록 |
|
||||
| UT-INC-005 | 잘못된 상태 전환 CLOSED→OPEN | 400 Bad Request |
|
||||
| UT-INC-006 | 장애 타임라인 조회 | 200 + events 목록 |
|
||||
| UT-INC-007 | 장애 종결 (RCA 필수) | 400 if rca 없음 |
|
||||
| UT-INC-008 | 장애 목록 — P1 필터 | 200 + P1 only |
|
||||
| UT-INC-009 | 장애 통계 (기간별) | 200 + count by priority |
|
||||
| UT-INC-010 | 장애 SR 연동 | 200 + related_sr_ids 업데이트 |
|
||||
|
||||
### 4.7 온콜/당직 (oncall) 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-OC-001 | 당직 단건 등록 | 201 + duty_id |
|
||||
| UT-OC-002 | 중복 당직 등록 (같은 날+교대) | 409 Conflict |
|
||||
| UT-OC-003 | 당직 일괄 등록 (최대 62건) | 201 + count |
|
||||
| UT-OC-004 | 당직 일괄 — 초과 (63건) | 422 Validation Error |
|
||||
| UT-OC-005 | 오늘 당직 조회 | 200 + 교대별 그룹 |
|
||||
| UT-OC-006 | 월별 당직 조회 | 200 + 해당 월 목록 |
|
||||
| UT-OC-007 | 당직 삭제 | 204 No Content |
|
||||
|
||||
### 4.8 배치 작업 (batch) 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-BAT-001 | 배치 작업 생성 | 201 + batch_id |
|
||||
| UT-BAT-002 | 위험 명령 차단 (rm -rf /) | 400 Bad Request |
|
||||
| UT-BAT-003 | 위험 명령 차단 (shutdown) | 400 Bad Request |
|
||||
| UT-BAT-004 | 위험 명령 차단 (fork bomb) | 400 Bad Request |
|
||||
| UT-BAT-005 | 배치 수동 실행 요청 | 202 Accepted |
|
||||
| UT-BAT-006 | 배치 실행 이력 조회 | 200 + runs 목록 |
|
||||
| UT-BAT-007 | 배치 실패 시 SR 자동 생성 | SR 생성 확인 (mock) |
|
||||
| UT-BAT-008 | 비활성 배치 스케줄 — 실행 안 함 | is_active=False 체크 |
|
||||
| UT-BAT-009 | 배치 작업 삭제 | 204 No Content |
|
||||
| UT-BAT-010 | TIMEOUT 처리 (asyncio timeout) | 실행 상태=TIMEOUT |
|
||||
|
||||
### 4.9 암호화 유틸 단위 테스트
|
||||
|
||||
| 테스트 ID | 테스트명 | 기대 결과 |
|
||||
|-----------|----------|-----------|
|
||||
| UT-CRYPTO-001 | AES-256-GCM 암호화/복호화 왕복 | 원문 복원 성공 |
|
||||
| UT-CRYPTO-002 | 서로 다른 평문 → 다른 암호문 | 결정론적 아님 확인 |
|
||||
| UT-CRYPTO-003 | 잘못된 키로 복호화 | 예외 발생 |
|
||||
| UT-CRYPTO-004 | 빈 문자열 암호화 | 정상 처리 |
|
||||
| UT-CRYPTO-005 | 한국어 포함 문자열 암호화 | UTF-8 왕복 성공 |
|
||||
|
||||
### 4.10 스케줄러 단위 테스트
|
||||
|
||||
```python
|
||||
# tests/unit/test_scheduler.py
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from freezegun import freeze_time
|
||||
from core.scheduler import _calc_next
|
||||
from models import PmSchedule, PmFrequency
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_calc_next_weekly():
|
||||
with freeze_time("2026-01-01"):
|
||||
sched = PmSchedule(frequency=PmFrequency.WEEKLY)
|
||||
result = _calc_next(sched, datetime(2026, 1, 1))
|
||||
assert result == datetime(2026, 1, 8)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_calc_next_monthly_end_of_month():
|
||||
# 1월 31일에서 +1달 → 2월 28일 (말일 처리)
|
||||
sched = PmSchedule(frequency=PmFrequency.MONTHLY, day_of_month=31)
|
||||
result = _calc_next(sched, datetime(2026, 1, 31))
|
||||
assert result.day == 28 # 2026년 2월 말일
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 통합 테스트 계획
|
||||
|
||||
### 5.1 SR 처리 전체 워크플로우
|
||||
|
||||
**시나리오**: 고객 SR 접수 → 담당자 배정 → 처리 → 완료 → 만족도 평가
|
||||
|
||||
```
|
||||
TC-INT-SR-001: SR 접수부터 완료까지 전체 흐름
|
||||
1. POST /tasks/ — SR 생성 (CUSTOMER 역할)
|
||||
2. GET /tasks/{id} — 생성 확인
|
||||
3. PATCH /assign/{sr_id} — 담당자 배정 (PM 역할)
|
||||
4. PATCH /tasks/{id}/status — IN_PROGRESS 전환
|
||||
5. POST /work/ — 처리 내역 등록
|
||||
6. PATCH /tasks/{id}/status — RESOLVED 전환
|
||||
7. POST /rating/ — 만족도 5점 등록
|
||||
8. GET /dashboard/summary — 통계 반영 확인
|
||||
|
||||
기대 결과: 전 단계 성공, 최종 SR 상태=RESOLVED, rating=5
|
||||
```
|
||||
|
||||
### 5.2 결재 워크플로우
|
||||
|
||||
**시나리오**: CHANGE 유형 SR → 결재 요청 → 승인 → 실행
|
||||
|
||||
```
|
||||
TC-INT-APV-001: 결재 승인 워크플로우
|
||||
1. POST /tasks/ — CHANGE SR 생성
|
||||
2. POST /approvals/ — 결재 요청 (결재자 2단계 설정)
|
||||
3. PATCH /approvals/{id}/approve — 1단계 승인
|
||||
4. PATCH /approvals/{id}/approve — 2단계 승인
|
||||
5. GET /tasks/{sr_id} — SR 상태 = APPROVED 확인
|
||||
6. PATCH /tasks/{id}/status — IMPLEMENTING 전환
|
||||
|
||||
TC-INT-APV-002: 결재 거부 워크플로우
|
||||
1. POST /tasks/ + POST /approvals/
|
||||
2. PATCH /approvals/{id}/reject — 거부 (사유 필수)
|
||||
3. GET /tasks/{sr_id} — SR 상태 = REJECTED 확인
|
||||
```
|
||||
|
||||
### 5.3 PM 정기점검 워크플로우
|
||||
|
||||
```
|
||||
TC-INT-PM-001: PM 스케줄 → 자동 작업 생성
|
||||
1. POST /pm/templates/ — 체크리스트 템플릿 생성
|
||||
2. POST /pm/schedules/ — MONTHLY 스케줄 등록
|
||||
3. (스케줄러 _auto_generate_pm 함수 직접 호출)
|
||||
4. GET /timetable/ — WorkTimetable 생성 확인
|
||||
5. GET /pm/works/{id} — PM 작업 목록 확인
|
||||
|
||||
TC-INT-PM-002: PM 결과 저장 및 Excel 보고서
|
||||
1. (PM 작업 생성 선행)
|
||||
2. POST /pm/works/{id}/results — 각 항목 PASS/FAIL 저장
|
||||
3. PATCH /pm/works/{id}/complete — 완료 처리
|
||||
4. GET /pm/works/{id}/report — Excel 다운로드
|
||||
5. openpyxl로 파일 열기 → PASS/FAIL 색상 확인
|
||||
```
|
||||
|
||||
### 5.4 장애 처리 워크플로우
|
||||
|
||||
```
|
||||
TC-INT-INC-001: P1 장애 발생 → 처리 → 종결
|
||||
1. POST /incidents/ — P1 장애 등록
|
||||
→ Messenger 알림 mock 확인
|
||||
2. POST /incidents/{id}/timeline — "장애 감지 완료" 기록
|
||||
3. PATCH /incidents/{id}/status — INVESTIGATING
|
||||
4. PATCH /incidents/{id}/status — MITIGATED (조치 내역 포함)
|
||||
5. PATCH /incidents/{id}/status — RESOLVED (임시 해결)
|
||||
6. POST /incidents/{id}/close — RCA 문서 포함 종결
|
||||
7. GET /incidents/{id} — 전체 타임라인 확인
|
||||
```
|
||||
|
||||
### 5.5 배치 실행 및 실패 알림
|
||||
|
||||
```
|
||||
TC-INT-BAT-001: 배치 실행 실패 → SR 자동 생성
|
||||
1. POST /batch/jobs — 의도적 실패 명령으로 배치 등록
|
||||
(예: "exit 1" 명령)
|
||||
2. POST /batch/jobs/{id}/run — 수동 실행
|
||||
3. (BackgroundTask 완료 대기)
|
||||
4. GET /batch/jobs/{id}/runs — 상태 = FAILED 확인
|
||||
5. GET /tasks/ — 자동 생성된 SR 확인 (sr_type=LOG, priority=HIGH)
|
||||
```
|
||||
|
||||
### 5.6 Messenger 알림 통합 테스트
|
||||
|
||||
```
|
||||
TC-INT-MSG-001: ITSM 이벤트 → Messenger 채널 발송
|
||||
1. mock Messenger API (respx)
|
||||
2. P1 장애 생성 → /incidents/ POST
|
||||
3. respx 캡처된 요청에서 채널 ID, 메시지 내용 검증
|
||||
4. 메시지에 incident_id, priority, title 포함 확인
|
||||
5. 서버 IP/비밀번호 미포함 확인 (보안)
|
||||
|
||||
TC-INT-MSG-002: Bot 명령 처리
|
||||
1. POST /messenger/webhook — "/itsm sr list" 명령
|
||||
2. 응답 메시지에 SR 목록 포함 확인
|
||||
3. POST /messenger/webhook — "/itsm help"
|
||||
4. 응답 메시지에 명령어 목록 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 비기능 테스트 계획
|
||||
|
||||
### 6.1 성능 테스트
|
||||
|
||||
| 항목 | 목표 | 측정 방법 |
|
||||
|------|------|-----------|
|
||||
| API 응답 시간 (평균) | ≤ 500ms | locust or k6 |
|
||||
| API 응답 시간 (P99) | ≤ 2000ms | locust or k6 |
|
||||
| 동시 사용자 | 50명 이상 정상 처리 | locust 동시 접속 |
|
||||
| DB 트랜잭션 | 100 TPS 이상 | pytest-benchmark |
|
||||
| 파일 업로드 | 10MB 이하 ≤ 3초 | httpx 직접 테스트 |
|
||||
|
||||
### 6.2 보안 테스트
|
||||
|
||||
| 항목 | 테스트 방법 | 기대 결과 |
|
||||
|------|------------|-----------|
|
||||
| SQL 인젝션 | 검색 파라미터에 SQL 구문 주입 | 에러 없이 빈 결과 |
|
||||
| XSS | SR 제목에 `<script>alert(1)</script>` | HTML 이스케이프 |
|
||||
| JWT 위조 | 임의 서명 토큰 사용 | 401 |
|
||||
| 경로 순회 | `../../../etc/passwd` 첨부파일 | 400 |
|
||||
| SSH 명령 인젝션 | `; rm -rf /` 포함 명령 | 400 |
|
||||
| 서버 정보 노출 | API 응답에 IP/PW 포함 여부 | 미포함 확인 |
|
||||
| 스택트레이스 노출 | 의도적 오류 발생 | SR ID + 요약만 반환 |
|
||||
|
||||
### 6.3 감사 로그 무결성 테스트
|
||||
|
||||
```
|
||||
TC-AUDIT-001: 해시 체인 무결성
|
||||
1. 여러 감사 로그 생성 (API 호출 10회)
|
||||
2. GET /audit/logs — 전체 조회
|
||||
3. 각 로그의 prev_hash → 이전 로그 hash 일치 확인
|
||||
4. DB에서 중간 로그 직접 수정
|
||||
5. GET /audit/verify — 무결성 깨짐 감지 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 테스트 케이스 목록 요약
|
||||
|
||||
| 모듈 | 단위 TC | 통합 TC | 합계 |
|
||||
|------|---------|---------|------|
|
||||
| auth | 8 | 2 | 10 |
|
||||
| tasks/SR | 10 | 4 | 14 |
|
||||
| approvals | 6 | 3 | 9 |
|
||||
| ssl_manager | 10 | 2 | 12 |
|
||||
| pm | 12 | 3 | 15 |
|
||||
| incidents | 10 | 2 | 12 |
|
||||
| oncall | 7 | 1 | 8 |
|
||||
| batch | 10 | 2 | 12 |
|
||||
| scheduler | 6 | 2 | 8 |
|
||||
| crypto/security | 5 | 3 | 8 |
|
||||
| messenger | 4 | 3 | 7 |
|
||||
| audit | 4 | 2 | 6 |
|
||||
| **합계** | **92** | **29** | **121** |
|
||||
|
||||
---
|
||||
|
||||
## 8. 결함 관리
|
||||
|
||||
### 8.1 결함 등급
|
||||
|
||||
| 등급 | 정의 | 처리 기한 |
|
||||
|------|------|-----------|
|
||||
| Critical | 시스템 중단, 데이터 손실, 보안 취약점 | 즉시 (24시간 내) |
|
||||
| Major | 핵심 기능 비정상, 결과 오류 | 3일 내 |
|
||||
| Minor | 비핵심 기능 문제, UI 오류 | 7일 내 |
|
||||
| Trivial | 오타, 경미한 표시 오류 | 다음 버전 |
|
||||
|
||||
### 8.2 결함 생명주기
|
||||
|
||||
```
|
||||
NEW → ASSIGNED → IN_PROGRESS → FIXED → VERIFIED → CLOSED
|
||||
↓
|
||||
REOPENED (재현 시)
|
||||
```
|
||||
|
||||
### 8.3 결함 보고서 양식
|
||||
|
||||
```
|
||||
결함 ID: BUG-YYYY-NNN
|
||||
제목: [모듈] 결함 요약
|
||||
심각도: Critical/Major/Minor/Trivial
|
||||
재현 단계:
|
||||
1. ...
|
||||
2. ...
|
||||
실제 결과: ...
|
||||
기대 결과: ...
|
||||
스크린샷/로그: (첨부)
|
||||
발견일: YYYY-MM-DD
|
||||
발견자: ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 완료 기준
|
||||
|
||||
### 9.1 단계별 완료 기준
|
||||
|
||||
**단위 테스트 완료**:
|
||||
- [ ] 모든 단위 TC 실행 완료
|
||||
- [ ] 코드 커버리지 80% 이상
|
||||
- [ ] Critical/Major 결함 0건
|
||||
|
||||
**통합 테스트 완료**:
|
||||
- [ ] 모든 통합 시나리오 TC 실행 완료
|
||||
- [ ] 주요 워크플로우 E2E 검증 완료
|
||||
- [ ] Critical/Major 결함 0건
|
||||
|
||||
**보안 테스트 완료**:
|
||||
- [ ] 보안 TC 전 항목 통과
|
||||
- [ ] API 응답에 민감 정보 미포함 확인
|
||||
- [ ] 감사 로그 무결성 확인
|
||||
|
||||
### 9.2 테스트 실행 명령
|
||||
|
||||
```bash
|
||||
# 전체 단위 테스트
|
||||
cd C:\GUARDiA\itsm
|
||||
pytest tests/unit/ -v --cov=. --cov-report=html
|
||||
|
||||
# 통합 테스트
|
||||
pytest tests/integration/ -v --timeout=30
|
||||
|
||||
# 특정 모듈
|
||||
pytest tests/unit/test_incidents.py -v
|
||||
|
||||
# 커버리지 HTML 보고서 확인
|
||||
open htmlcov/index.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*본 테스트 계획서는 GUARDiA ITSM v1.0 기준으로 작성되었으며, 버전 업그레이드 시 갱신이 필요합니다.*
|
||||
951
03_개발자지침서.md
Normal file
951
03_개발자지침서.md
Normal file
@ -0,0 +1,951 @@
|
||||
# GUARDiA ITSM + Messenger — 개발자 지침서
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**대상**: GUARDiA 플랫폼 개발자
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [개발 환경 설정](#1-개발-환경-설정)
|
||||
2. [프로젝트 구조](#2-프로젝트-구조)
|
||||
3. [코딩 컨벤션](#3-코딩-컨벤션)
|
||||
4. [API 개발 가이드](#4-api-개발-가이드)
|
||||
5. [데이터베이스 가이드](#5-데이터베이스-가이드)
|
||||
6. [보안 필수 규칙](#6-보안-필수-규칙)
|
||||
7. [테스트 작성 가이드](#7-테스트-작성-가이드)
|
||||
8. [Messenger Bot 개발 가이드](#8-messenger-bot-개발-가이드)
|
||||
9. [스케줄러 확장 가이드](#9-스케줄러-확장-가이드)
|
||||
10. [배포 및 CI/CD](#10-배포-및-cicd)
|
||||
11. [자주 하는 실수 및 주의사항](#11-자주-하는-실수-및-주의사항)
|
||||
|
||||
---
|
||||
|
||||
## 1. 개발 환경 설정
|
||||
|
||||
### 1.1 필수 도구
|
||||
|
||||
```
|
||||
Python 3.11+
|
||||
Git
|
||||
VS Code (권장) 또는 PyCharm
|
||||
SQLite Browser (DB 확인용)
|
||||
httpie 또는 Postman (API 테스트)
|
||||
```
|
||||
|
||||
### 1.2 로컬 개발 환경 구성
|
||||
|
||||
```bash
|
||||
# 1. 레포지토리 클론
|
||||
git clone <repo-url>
|
||||
cd GUARDiA/itsm
|
||||
|
||||
# 2. 가상환경 생성 (Python 3.11 사용)
|
||||
python -m venv .venv
|
||||
|
||||
# Windows
|
||||
.venv\Scripts\activate
|
||||
# Linux/Mac
|
||||
source .venv/bin/activate
|
||||
|
||||
# 3. 의존성 설치
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-dev.txt # 개발/테스트용
|
||||
|
||||
# 4. 환경 변수 설정
|
||||
cp .env.example .env
|
||||
# .env 파일 편집 (SECRET_KEY, DB_PATH 등)
|
||||
|
||||
# 5. 앱 실행
|
||||
uvicorn main:app --reload --port 8000
|
||||
|
||||
# 6. API 문서 확인
|
||||
# http://localhost:8000/docs
|
||||
```
|
||||
|
||||
### 1.3 .env 파일 설정
|
||||
|
||||
```ini
|
||||
# .env.example
|
||||
SECRET_KEY=your-256-bit-secret-key-here-change-in-production
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=480
|
||||
DB_URL=sqlite+aiosqlite:///./guardia_itsm.db
|
||||
|
||||
# 암호화 키 (AES-256-GCM, 32바이트 hex)
|
||||
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
# 라이선스 마스터 키 (64자리 hex, 32바이트)
|
||||
# 생성: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# 운영 환경에서는 반드시 실제 값으로 교체 — 분실 시 기발급 라이선스 전부 무효화됨
|
||||
GUARDIA_LICENSE_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
# Messenger 연동
|
||||
MESSENGER_BASE_URL=http://localhost:8002
|
||||
MESSENGER_BOT_TOKEN=dev-token
|
||||
|
||||
# SSH 실행 타임아웃 (초)
|
||||
SSH_TIMEOUT=30
|
||||
|
||||
# 파일 업로드 경로
|
||||
UPLOAD_ROOT=./uploads
|
||||
MAX_FILE_SIZE_MB=10
|
||||
```
|
||||
|
||||
> **주의**: `.env` 파일은 절대 Git에 커밋하지 마세요. `.gitignore`에 포함되어 있습니다.
|
||||
|
||||
### 1.4 VS Code 권장 설정
|
||||
|
||||
`.vscode/settings.json`:
|
||||
```json
|
||||
{
|
||||
"python.defaultInterpreterPath": ".venv/Scripts/python.exe",
|
||||
"editor.formatOnSave": true,
|
||||
"python.formatting.provider": "black",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.flake8Args": ["--max-line-length=120"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 프로젝트 구조
|
||||
|
||||
```
|
||||
C:\GUARDiA\itsm\
|
||||
├── main.py # FastAPI 앱 진입점
|
||||
├── database.py # DB 엔진, SessionLocal, get_db
|
||||
├── models.py # SQLAlchemy ORM 모델 (전체)
|
||||
├── schemas.py # Pydantic 스키마 (전체)
|
||||
├── requirements.txt # 운영 의존성
|
||||
├── requirements-dev.txt # 개발/테스트 의존성
|
||||
├── .env # 환경 변수 (git 제외)
|
||||
│
|
||||
├── core\
|
||||
│ ├── seed.py # 초기 데이터 시드
|
||||
│ ├── scheduler.py # APScheduler 백그라운드 작업
|
||||
│ └── security.py # JWT, 비밀번호 해싱
|
||||
│
|
||||
├── routers\
|
||||
│ ├── auth.py # 인증/권한
|
||||
│ ├── tasks.py # SR 처리
|
||||
│ ├── approvals.py # 결재
|
||||
│ ├── ssl_manager.py # SSL 인증서 관리
|
||||
│ ├── pm.py # 정기점검
|
||||
│ ├── incidents.py # 장애 관리
|
||||
│ ├── oncall.py # 온콜/당직
|
||||
│ ├── batch.py # 배치 작업
|
||||
│ └── ... # 기타 라우터
|
||||
│
|
||||
├── utils\
|
||||
│ ├── crypto.py # AES-256-GCM 암/복호화
|
||||
│ └── ssh_runner.py # SSH 명령 실행
|
||||
│
|
||||
├── static\ # 프론트엔드 (HTML/CSS/JS)
|
||||
├── uploads\ # 업로드 파일 저장소
|
||||
│ ├── sr_files\
|
||||
│ └── workspaces\
|
||||
│
|
||||
├── scripts\sm\ssl\
|
||||
│ └── ssl_expiry_check.sh # 배포용 SSL 점검 스크립트
|
||||
│
|
||||
└── tests\
|
||||
├── conftest.py
|
||||
├── unit\
|
||||
└── integration\
|
||||
|
||||
C:\GUARDiA\messenger\
|
||||
├── main.py # Messenger 서버
|
||||
├── models\ # DB 모델
|
||||
├── core\ # 봇 로직
|
||||
└── routers\ # API 라우터
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 코딩 컨벤션
|
||||
|
||||
### 3.1 Python 스타일
|
||||
|
||||
```python
|
||||
# 파일 맨 위: 임포트 순서 (isort 준수)
|
||||
# 1) 표준 라이브러리
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# 2) 서드파티
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
# 3) 로컬 모듈
|
||||
from database import get_db
|
||||
from models import SRRequest
|
||||
from schemas import SRCreate, SROut
|
||||
|
||||
# 줄 최대 길이: 120자
|
||||
# 들여쓰기: 4 space (탭 금지)
|
||||
# 문자열: 큰따옴표(") 사용
|
||||
```
|
||||
|
||||
### 3.2 라우터 파일 구조 패턴
|
||||
|
||||
새 라우터를 만들 때 반드시 이 구조를 따르세요:
|
||||
|
||||
```python
|
||||
# routers/example.py
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database import get_db
|
||||
from models import ExampleModel
|
||||
from schemas import ExampleCreate, ExampleOut, ExampleUpdate
|
||||
from core.security import get_current_user
|
||||
from models import User, Role
|
||||
|
||||
router = APIRouter(prefix="/example", tags=["example"])
|
||||
|
||||
|
||||
def _require_role(*roles: Role):
|
||||
"""역할 기반 접근 제어 의존성"""
|
||||
async def checker(current_user: User = Depends(get_current_user)):
|
||||
if current_user.role not in roles:
|
||||
raise HTTPException(status_code=403, detail="권한이 없습니다.")
|
||||
return current_user
|
||||
return checker
|
||||
|
||||
|
||||
@router.get("/", response_model=list[ExampleOut])
|
||||
async def list_examples(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(select(ExampleModel).order_by(ExampleModel.created_at.desc()))
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
@router.post("/", response_model=ExampleOut, status_code=status.HTTP_201_CREATED)
|
||||
async def create_example(
|
||||
body: ExampleCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(_require_role(Role.ADMIN, Role.PM)),
|
||||
):
|
||||
obj = ExampleModel(**body.model_dump(), created_by=current_user.id)
|
||||
db.add(obj)
|
||||
await db.commit()
|
||||
await db.refresh(obj)
|
||||
return obj
|
||||
```
|
||||
|
||||
### 3.3 모델 정의 패턴
|
||||
|
||||
```python
|
||||
# models.py 에 추가할 때
|
||||
class ExampleModel(Base):
|
||||
__tablename__ = "tb_example"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
title = Column(String(200), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
created_by = Column(Integer, ForeignKey("tb_user.id"), nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
```
|
||||
|
||||
### 3.4 스키마 정의 패턴
|
||||
|
||||
```python
|
||||
# schemas.py 에 추가할 때
|
||||
class ExampleCreate(BaseModel):
|
||||
title: str = Field(..., min_length=1, max_length=200)
|
||||
description: Optional[str] = None
|
||||
|
||||
class ExampleUpdate(BaseModel):
|
||||
title: Optional[str] = Field(None, min_length=1, max_length=200)
|
||||
description: Optional[str] = None
|
||||
|
||||
class ExampleOut(BaseModel):
|
||||
id: int
|
||||
title: str
|
||||
description: Optional[str]
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
```
|
||||
|
||||
### 3.5 명명 규칙
|
||||
|
||||
| 대상 | 규칙 | 예시 |
|
||||
|------|------|------|
|
||||
| 파일명 | snake_case | `ssl_manager.py` |
|
||||
| 클래스명 | PascalCase | `SslDomain`, `PmSchedule` |
|
||||
| 함수/변수 | snake_case | `get_ssl_domains()`, `days_left` |
|
||||
| DB 테이블 | `tb_` 접두사 | `tb_ssl_domain` |
|
||||
| 상수 | UPPER_SNAKE | `MAX_FILE_SIZE`, `SSL_WARN_DAYS` |
|
||||
| 비공개 함수 | `_` 접두사 | `_notify_incident()` |
|
||||
| API 경로 | kebab-case | `/ssl-manager/domains` |
|
||||
|
||||
---
|
||||
|
||||
## 4. API 개발 가이드
|
||||
|
||||
### 4.1 응답 형식 규칙
|
||||
|
||||
```python
|
||||
# 성공 응답: HTTP 상태 코드로 구분
|
||||
# 200 OK — 조회, 수정
|
||||
# 201 Created — 생성
|
||||
# 202 Accepted — 비동기 실행 (배치, 백그라운드)
|
||||
# 204 No Content — 삭제
|
||||
|
||||
# 에러 응답: 반드시 이 형식 사용
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="사용자 친화적인 한국어 메시지"
|
||||
)
|
||||
|
||||
# 절대 금지: 스택트레이스 노출
|
||||
# raise Exception(traceback.format_exc()) # ❌
|
||||
```
|
||||
|
||||
### 4.2 페이징 구현 패턴
|
||||
|
||||
```python
|
||||
from fastapi import Query
|
||||
|
||||
@router.get("/", response_model=dict)
|
||||
async def list_items(
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
offset = (page - 1) * size
|
||||
count_q = select(func.count()).select_from(Item)
|
||||
total = (await db.execute(count_q)).scalar()
|
||||
|
||||
items_q = select(Item).offset(offset).limit(size).order_by(Item.created_at.desc())
|
||||
items = (await db.execute(items_q)).scalars().all()
|
||||
|
||||
return {
|
||||
"items": items,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"size": size,
|
||||
"pages": (total + size - 1) // size,
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 비동기 DB 쿼리 패턴
|
||||
|
||||
```python
|
||||
# ✅ 올바른 패턴: asyncio 방식
|
||||
async def get_item(item_id: int, db: AsyncSession) -> Item:
|
||||
result = await db.execute(select(Item).where(Item.id == item_id))
|
||||
item = result.scalar_one_or_none()
|
||||
if item is None:
|
||||
raise HTTPException(status_code=404, detail="항목을 찾을 수 없습니다.")
|
||||
return item
|
||||
|
||||
# ✅ 여러 조건 필터
|
||||
query = select(Item).where(
|
||||
Item.is_active == True,
|
||||
Item.category == category,
|
||||
).order_by(Item.created_at.desc())
|
||||
|
||||
# ✅ JOIN
|
||||
query = select(Item).join(User, Item.created_by == User.id)
|
||||
|
||||
# ❌ 잘못된 패턴: 동기 방식 사용 금지
|
||||
# db.query(Item).filter(Item.id == item_id).first() # ❌
|
||||
```
|
||||
|
||||
### 4.4 백그라운드 작업 패턴
|
||||
|
||||
```python
|
||||
from fastapi import BackgroundTasks
|
||||
|
||||
@router.post("/{id}/run", status_code=202)
|
||||
async def run_job(
|
||||
id: int,
|
||||
background_tasks: BackgroundTasks,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
job = await _get_job(id, db)
|
||||
background_tasks.add_task(_execute_job, job.id)
|
||||
return {"message": "실행이 요청되었습니다.", "job_id": id}
|
||||
|
||||
|
||||
async def _execute_job(job_id: int):
|
||||
# 중요: 새 DB 세션 생성 (request-scope 세션 재사용 금지!)
|
||||
from database import SessionLocal
|
||||
async with SessionLocal() as db:
|
||||
job = await _get_job(job_id, db)
|
||||
# ... 실행 로직
|
||||
await db.commit()
|
||||
```
|
||||
|
||||
### 4.5 main.py 라우터 등록
|
||||
|
||||
새 라우터를 추가할 때:
|
||||
|
||||
```python
|
||||
# main.py
|
||||
from routers import (
|
||||
...
|
||||
new_module, # ← 여기에 추가
|
||||
)
|
||||
|
||||
# lifespan 함수 안에 필요한 초기화 추가
|
||||
|
||||
# 라우터 등록 (알파벳 순 권장)
|
||||
app.include_router(new_module.router)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 데이터베이스 가이드
|
||||
|
||||
### 5.1 마이그레이션 정책
|
||||
|
||||
현재 GUARDiA ITSM은 **Alembic 없이** `init_db()`로 테이블을 자동 생성합니다.
|
||||
|
||||
```python
|
||||
# database.py
|
||||
async def init_db():
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
```
|
||||
|
||||
> **주의**: 운영 DB에서는 테이블 삭제/재생성이 일어나지 않습니다. 컬럼 추가/변경 시 수동 ALTER TABLE이 필요합니다.
|
||||
|
||||
**신규 컬럼 추가 절차**:
|
||||
1. `models.py`에 컬럼 추가
|
||||
2. 운영 DB에 수동 ALTER: `ALTER TABLE tb_xxx ADD COLUMN new_col TEXT;`
|
||||
3. 기본값 업데이트: `UPDATE tb_xxx SET new_col = 'default' WHERE new_col IS NULL;`
|
||||
|
||||
### 5.2 관계 설정 패턴
|
||||
|
||||
```python
|
||||
# 1:N 관계 (부모→자식)
|
||||
class SRRequest(Base):
|
||||
__tablename__ = "tb_sr_request"
|
||||
# ...
|
||||
work_logs = relationship("WorkLog", back_populates="sr", lazy="select")
|
||||
|
||||
class WorkLog(Base):
|
||||
__tablename__ = "tb_work_log"
|
||||
sr_id = Column(Integer, ForeignKey("tb_sr_request.id"), nullable=False)
|
||||
sr = relationship("SRRequest", back_populates="work_logs")
|
||||
|
||||
# 쿼리 시 eagerly load (N+1 문제 방지)
|
||||
from sqlalchemy.orm import selectinload
|
||||
query = select(SRRequest).options(selectinload(SRRequest.work_logs))
|
||||
```
|
||||
|
||||
### 5.3 enum 정의 패턴
|
||||
|
||||
```python
|
||||
import enum
|
||||
|
||||
class SRStatus(str, enum.Enum):
|
||||
OPEN = "OPEN"
|
||||
IN_PROGRESS = "IN_PROGRESS"
|
||||
RESOLVED = "RESOLVED"
|
||||
CLOSED = "CLOSED"
|
||||
|
||||
# 모델에서 사용
|
||||
class SRRequest(Base):
|
||||
status = Column(Enum(SRStatus), default=SRStatus.OPEN, nullable=False)
|
||||
```
|
||||
|
||||
### 5.4 트랜잭션 처리
|
||||
|
||||
```python
|
||||
# 여러 테이블 수정 시 트랜잭션 보장
|
||||
async def complex_operation(db: AsyncSession):
|
||||
try:
|
||||
obj1 = Model1(...)
|
||||
obj2 = Model2(...)
|
||||
db.add(obj1)
|
||||
db.add(obj2)
|
||||
await db.commit() # 모두 성공 시 커밋
|
||||
except Exception:
|
||||
await db.rollback() # 하나라도 실패 시 롤백
|
||||
raise
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 보안 필수 규칙
|
||||
|
||||
> **이 규칙들은 절대 예외가 없습니다. 위반 시 코드 리뷰에서 반려됩니다.**
|
||||
|
||||
### 6.1 서버 자격증명 보호
|
||||
|
||||
```python
|
||||
# ✅ 서버 조회 응답에서 민감 필드 제외
|
||||
class ServerOut(BaseModel):
|
||||
id: int
|
||||
hostname: str
|
||||
server_role: str
|
||||
# ip_addr, ssh_user, os_pw_enc 절대 포함 금지!
|
||||
|
||||
# ❌ 절대 하지 말 것
|
||||
class ServerOut(BaseModel):
|
||||
ip_addr: str # ❌ IP 노출 금지
|
||||
ssh_user: str # ❌ SSH 계정 노출 금지
|
||||
os_pw_enc: str # ❌ 암호화된 비밀번호도 노출 금지
|
||||
```
|
||||
|
||||
### 6.2 SSH 명령 안전성 검증
|
||||
|
||||
```python
|
||||
_DANGEROUS_PATTERNS = [
|
||||
"rm -rf /", "rm -rf /*", "mkfs", "dd if=",
|
||||
"shutdown", "reboot", "halt", "poweroff",
|
||||
":(){:|:&};:", ">()", "chmod 777 /",
|
||||
"chown -R root /", "> /dev/sda",
|
||||
]
|
||||
|
||||
def _validate_command(cmd: str) -> None:
|
||||
for pattern in _DANGEROUS_PATTERNS:
|
||||
if pattern in cmd:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"안전하지 않은 명령어 패턴이 포함되어 있습니다: {pattern}"
|
||||
)
|
||||
```
|
||||
|
||||
### 6.3 파일 경로 순회 방지
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
UPLOAD_ROOT = Path(settings.UPLOAD_ROOT).resolve()
|
||||
|
||||
def _safe_path(filename: str, subdir: str) -> Path:
|
||||
target = (UPLOAD_ROOT / subdir / filename).resolve()
|
||||
if not str(target).startswith(str(UPLOAD_ROOT)):
|
||||
raise HTTPException(status_code=400, detail="잘못된 파일 경로입니다.")
|
||||
return target
|
||||
```
|
||||
|
||||
### 6.4 AES-256-GCM 암호화 사용
|
||||
|
||||
```python
|
||||
# utils/crypto.py
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
import os, base64
|
||||
|
||||
def encrypt(plaintext: str, key_hex: str) -> str:
|
||||
key = bytes.fromhex(key_hex)
|
||||
nonce = os.urandom(12)
|
||||
aesgcm = AESGCM(key)
|
||||
ct = aesgcm.encrypt(nonce, plaintext.encode(), None)
|
||||
return base64.b64encode(nonce + ct).decode()
|
||||
|
||||
def decrypt(ciphertext: str, key_hex: str) -> str:
|
||||
key = bytes.fromhex(key_hex)
|
||||
data = base64.b64decode(ciphertext)
|
||||
nonce, ct = data[:12], data[12:]
|
||||
aesgcm = AESGCM(key)
|
||||
return aesgcm.decrypt(nonce, ct, None).decode()
|
||||
```
|
||||
|
||||
### 6.5 Messenger 발송 메시지 보안
|
||||
|
||||
```python
|
||||
# ✅ 올바른 알림 메시지 (서버 정보 제외)
|
||||
message = f"[장애] {incident.title}\n등급: {incident.priority}\nID: {incident.incident_id}"
|
||||
|
||||
# ❌ 절대 금지 (서버 정보 포함)
|
||||
message = f"서버 {server.ip_addr}에서 장애 발생" # ❌ IP 노출
|
||||
message = f"SSH 계정: {server.ssh_user}" # ❌ 계정 노출
|
||||
```
|
||||
|
||||
### 6.6 JWT 토큰 처리
|
||||
|
||||
```python
|
||||
# 토큰 생성 시 만료 시간 필수
|
||||
def create_access_token(data: dict) -> str:
|
||||
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
return jwt.encode({**data, "exp": expire}, settings.SECRET_KEY, algorithm="HS256")
|
||||
|
||||
# 의존성에서 자동 검증
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
|
||||
except JWTError:
|
||||
raise HTTPException(status_code=401, detail="유효하지 않은 토큰입니다.")
|
||||
```
|
||||
|
||||
### 6.7 root SSH 접속 금지
|
||||
|
||||
```python
|
||||
# 서버 등록/수정 시 검증
|
||||
if body.ssh_user == "root":
|
||||
raise HTTPException(status_code=400, detail="root SSH 직접 접속은 허용되지 않습니다.")
|
||||
```
|
||||
|
||||
### 6.8 라이선스 제한 강제
|
||||
|
||||
에디션별 자원 한도(기관·사용자·서버 수)를 초과하는 생성 API에는 반드시 Dependency를 추가해야 한다.
|
||||
|
||||
```python
|
||||
from middleware.license_guard import (
|
||||
check_institution_limit,
|
||||
check_server_limit,
|
||||
check_user_limit,
|
||||
require_feature,
|
||||
)
|
||||
|
||||
# 기관 등록 엔드포인트 예시
|
||||
@router.post("/", response_model=InstitutionOut, status_code=201)
|
||||
async def create_institution(
|
||||
payload: InstitutionCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
_: None = Depends(check_institution_limit), # 에디션 한도 초과 시 HTTP 403
|
||||
):
|
||||
...
|
||||
|
||||
# 서버 등록 엔드포인트 예시
|
||||
@router.post("/servers", response_model=ServerOut, status_code=201)
|
||||
async def create_server(
|
||||
payload: dict,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
_: None = Depends(check_server_limit), # 에디션 한도 초과 시 HTTP 403
|
||||
):
|
||||
...
|
||||
|
||||
# 특정 기능이 에디션에 포함되는지 체크 (STANDARD 이상 필요한 LDAP 예시)
|
||||
@router.post("/ldap/sync")
|
||||
async def ldap_sync(
|
||||
_: None = Depends(require_feature("LDAP")),
|
||||
):
|
||||
...
|
||||
```
|
||||
|
||||
에디션별 기본 한도:
|
||||
| 에디션 | 기관 | 사용자 | 서버 |
|
||||
|--------|------|-------|------|
|
||||
| COMMUNITY | 1 | 10 | 20 |
|
||||
| STANDARD | 50 | 200 | 500 |
|
||||
| ENTERPRISE | 무제한 | 무제한 | 무제한 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 테스트 작성 가이드
|
||||
|
||||
### 7.1 단위 테스트 기본 구조
|
||||
|
||||
```python
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
class TestSslManager:
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_domain_success(self, client, auth_headers):
|
||||
# Arrange
|
||||
payload = {"domain": "example.com", "port": 443, "server_id": 1}
|
||||
|
||||
# Act
|
||||
r = await client.post("/ssl-manager/domains", json=payload, headers=auth_headers)
|
||||
|
||||
# Assert
|
||||
assert r.status_code == 201
|
||||
data = r.json()
|
||||
assert data["domain"] == "example.com"
|
||||
assert "id" in data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_domain_duplicate(self, client, auth_headers):
|
||||
payload = {"domain": "example.com", "port": 443, "server_id": 1}
|
||||
await client.post("/ssl-manager/domains", json=payload, headers=auth_headers)
|
||||
|
||||
r = await client.post("/ssl-manager/domains", json=payload, headers=auth_headers)
|
||||
assert r.status_code == 409
|
||||
```
|
||||
|
||||
### 7.2 mock 사용 패턴
|
||||
|
||||
```python
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_p1_incident_triggers_notification(client, auth_headers):
|
||||
with patch("routers.incidents._notify_incident", new_callable=AsyncMock) as mock_notify:
|
||||
r = await client.post("/incidents/", json={
|
||||
"title": "DB 서버 다운",
|
||||
"priority": "P1",
|
||||
"description": "운영 DB 응답 없음",
|
||||
}, headers=auth_headers)
|
||||
|
||||
assert r.status_code == 201
|
||||
mock_notify.assert_called_once()
|
||||
call_args = mock_notify.call_args
|
||||
assert call_args[0][0].priority == "P1" # 첫 번째 인자 확인
|
||||
```
|
||||
|
||||
### 7.3 시간 고정 테스트
|
||||
|
||||
```python
|
||||
from freezegun import freeze_time
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@freeze_time("2026-01-15 00:00:00")
|
||||
async def test_pm_schedule_monthly_next_date(client, auth_headers):
|
||||
r = await client.post("/pm/schedules/", json={
|
||||
"template_id": 1,
|
||||
"frequency": "MONTHLY",
|
||||
"day_of_month": 15,
|
||||
"server_id": 1,
|
||||
}, headers=auth_headers)
|
||||
assert r.status_code == 201
|
||||
# 1월 15일 기준 → 다음 실행은 2월 15일
|
||||
assert "2026-02-15" in r.json()["next_scheduled"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Messenger Bot 개발 가이드
|
||||
|
||||
### 8.1 GUARDiA_ITSM 봇 명령 추가
|
||||
|
||||
명령어 추가는 `C:\GUARDiA\messenger\core\itsm_bot.py` 에서 수행합니다.
|
||||
|
||||
```python
|
||||
# 새 명령어 핸들러 추가 패턴
|
||||
COMMAND_HANDLERS = {
|
||||
"/itsm sr list": handle_sr_list,
|
||||
"/itsm sr create": handle_sr_create,
|
||||
"/itsm help": handle_help,
|
||||
"/itsm incident p1": handle_p1_alert, # 새 명령어 추가
|
||||
}
|
||||
|
||||
async def handle_p1_alert(args: list[str], user_id: int) -> str:
|
||||
"""P1 장애 목록 조회"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(
|
||||
f"{ITSM_BASE_URL}/incidents/?priority=P1&status=OPEN",
|
||||
headers={"Authorization": f"Bearer {ITSM_BOT_TOKEN}"},
|
||||
)
|
||||
if r.status_code != 200:
|
||||
return "장애 목록 조회 실패"
|
||||
incidents = r.json()["items"]
|
||||
if not incidents:
|
||||
return "현재 P1 장애 없음"
|
||||
lines = [f"[{i['incident_id']}] {i['title']}" for i in incidents[:5]]
|
||||
return "P1 장애 목록:\n" + "\n".join(lines)
|
||||
```
|
||||
|
||||
### 8.2 ITSM → Messenger 알림 발송
|
||||
|
||||
```python
|
||||
# routers/incidents.py 내 알림 함수
|
||||
async def _notify_incident(incident: IncidentModel) -> None:
|
||||
"""장애 발생 시 Messenger 채널 알림"""
|
||||
try:
|
||||
channel_id = settings.INCIDENT_CHANNEL_ID
|
||||
# 보안: 서버 IP/PW 미포함
|
||||
msg = (
|
||||
f"🚨 **{incident.priority} 장애 발생**\n"
|
||||
f"ID: {incident.incident_id}\n"
|
||||
f"제목: {incident.title}\n"
|
||||
f"등록: {incident.created_at.strftime('%Y-%m-%d %H:%M')}"
|
||||
)
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
await client.post(
|
||||
f"{settings.MESSENGER_BASE_URL}/api/messages",
|
||||
json={"channel_id": channel_id, "content": msg},
|
||||
headers={"Authorization": f"Bearer {settings.MESSENGER_BOT_TOKEN}"},
|
||||
)
|
||||
except Exception:
|
||||
# 알림 실패가 비즈니스 로직을 방해하면 안 됨
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 스케줄러 확장 가이드
|
||||
|
||||
### 9.1 새 스케줄 작업 추가
|
||||
|
||||
`core/scheduler.py`에 새 작업을 추가합니다:
|
||||
|
||||
```python
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
async def _new_scheduled_task() -> None:
|
||||
"""매주 월요일 오전 9시 실행 예시"""
|
||||
from database import SessionLocal
|
||||
async with SessionLocal() as db:
|
||||
# 작업 수행
|
||||
pass
|
||||
|
||||
def start_scheduler():
|
||||
# 기존 작업들 ...
|
||||
_scheduler.add_job(
|
||||
_new_scheduled_task,
|
||||
CronTrigger(day_of_week="mon", hour=9, minute=0),
|
||||
id="new_task",
|
||||
replace_existing=True,
|
||||
)
|
||||
_scheduler.start()
|
||||
```
|
||||
|
||||
### 9.2 스케줄러 테스트
|
||||
|
||||
스케줄러는 직접 함수 호출로 테스트합니다:
|
||||
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_ssl_expiry_scan(db_session):
|
||||
from core.scheduler import _scan_ssl_expiry
|
||||
# 테스트 데이터 준비
|
||||
# ...
|
||||
await _scan_ssl_expiry() # 직접 호출
|
||||
# DB 상태 확인
|
||||
# ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 배포 및 CI/CD
|
||||
|
||||
### 10.1 Jenkins 파이프라인 단계
|
||||
|
||||
```groovy
|
||||
pipeline {
|
||||
stages {
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh 'pytest tests/unit/ --cov=. --cov-report=xml'
|
||||
}
|
||||
}
|
||||
stage('Quality Gate') {
|
||||
steps {
|
||||
// SonarQube 분석
|
||||
sh 'sonar-scanner -Dsonar.projectKey=guardia-itsm'
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
when { branch 'main' }
|
||||
steps {
|
||||
sh './scripts/deploy.sh production'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 운영 서버 배포 체크리스트
|
||||
|
||||
```
|
||||
배포 전:
|
||||
□ 모든 단위 테스트 통과
|
||||
□ 커버리지 80% 이상
|
||||
□ SonarQube 품질 게이트 통과
|
||||
□ .env 운영 설정 확인 (SECRET_KEY, ENCRYPTION_KEY)
|
||||
□ DB 백업 완료
|
||||
|
||||
배포 중:
|
||||
□ 서비스 점검 공지
|
||||
□ 현재 버전 백업
|
||||
□ 새 버전 배포
|
||||
□ DB 마이그레이션 (필요 시)
|
||||
□ 서비스 재시작
|
||||
|
||||
배포 후:
|
||||
□ /docs 페이지 접근 확인
|
||||
□ 로그인 테스트
|
||||
□ 주요 API 스모크 테스트
|
||||
□ 스케줄러 동작 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 자주 하는 실수 및 주의사항
|
||||
|
||||
### 11.1 비동기 함수 실수
|
||||
|
||||
```python
|
||||
# ❌ 실수: async 함수에서 동기 DB 호출
|
||||
async def bad_function(db: AsyncSession):
|
||||
result = db.execute(select(Item)) # ❌ await 누락
|
||||
|
||||
# ✅ 올바른 방법
|
||||
async def good_function(db: AsyncSession):
|
||||
result = await db.execute(select(Item)) # ✅
|
||||
```
|
||||
|
||||
### 11.2 DB 세션 누수
|
||||
|
||||
```python
|
||||
# ❌ 실수: 백그라운드 태스크에서 request 세션 사용
|
||||
@router.post("/")
|
||||
async def create(background_tasks: BackgroundTasks, db: AsyncSession = Depends(get_db)):
|
||||
background_tasks.add_task(some_task, db) # ❌ 세션이 request 후 닫힘
|
||||
|
||||
# ✅ 올바른 방법: 백그라운드에서 새 세션 생성
|
||||
async def some_task():
|
||||
from database import SessionLocal
|
||||
async with SessionLocal() as db: # ✅ 새 세션
|
||||
pass
|
||||
```
|
||||
|
||||
### 11.3 N+1 쿼리 문제
|
||||
|
||||
```python
|
||||
# ❌ N+1 쿼리 발생
|
||||
srs = (await db.execute(select(SRRequest))).scalars().all()
|
||||
for sr in srs:
|
||||
print(sr.work_logs) # 각 SR마다 추가 쿼리 발생!
|
||||
|
||||
# ✅ selectinload 사용
|
||||
srs = (await db.execute(
|
||||
select(SRRequest).options(selectinload(SRRequest.work_logs))
|
||||
)).scalars().all()
|
||||
```
|
||||
|
||||
### 11.4 환경 변수 검증
|
||||
|
||||
```python
|
||||
# ✅ 앱 시작 시 필수 환경 변수 검증
|
||||
class Settings(BaseSettings):
|
||||
SECRET_KEY: str
|
||||
ENCRYPTION_KEY: str
|
||||
|
||||
@validator("ENCRYPTION_KEY")
|
||||
def encryption_key_must_be_64_hex(cls, v):
|
||||
if len(v) != 64:
|
||||
raise ValueError("ENCRYPTION_KEY는 64자리 hex 문자열이어야 합니다.")
|
||||
return v
|
||||
```
|
||||
|
||||
### 11.5 외부 API 완전 금지
|
||||
|
||||
```python
|
||||
# ❌ 절대 금지: 외부 LLM/AI API 호출
|
||||
import openai
|
||||
client = openai.OpenAI(api_key="...") # ❌ 외부 API 사용 금지
|
||||
|
||||
# ✅ 내부 sLLM만 허용
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.post(f"{settings.SLLM_BASE_URL}/v1/chat/completions", ...)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*본 지침서를 준수하지 않은 코드는 코드 리뷰에서 반려될 수 있습니다.*
|
||||
*보안 관련 규칙(6장)은 특히 엄격히 적용됩니다.*
|
||||
935
04_운영자지침서.md
Normal file
935
04_운영자지침서.md
Normal file
@ -0,0 +1,935 @@
|
||||
# GUARDiA ITSM + Messenger — 운영자 지침서
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**대상**: 시스템 운영자, IT 관리자
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [시스템 개요](#1-시스템-개요)
|
||||
2. [일상 운영 절차](#2-일상-운영-절차)
|
||||
3. [사용자 계정 관리](#3-사용자-계정-관리)
|
||||
4. [SR(서비스요청) 관리](#4-sr서비스요청-관리)
|
||||
5. [SSL 인증서 관리](#5-ssl-인증서-관리)
|
||||
6. [정기점검(PM) 관리](#6-정기점검pm-관리)
|
||||
7. [장애 관리](#7-장애-관리)
|
||||
8. [온콜/당직 관리](#8-온콜당직-관리)
|
||||
9. [배치 작업 관리](#9-배치-작업-관리)
|
||||
10. [백업 및 복구](#10-백업-및-복구)
|
||||
11. [모니터링 및 알림](#11-모니터링-및-알림)
|
||||
12. [보안 운영 지침](#12-보안-운영-지침)
|
||||
13. [장애 대응 절차](#13-장애-대응-절차)
|
||||
14. [로그 관리](#14-로그-관리)
|
||||
15. [주요 설정 파일](#15-주요-설정-파일)
|
||||
16. [라이선스 관리](#16-라이선스-관리)
|
||||
|
||||
---
|
||||
|
||||
## 1. 시스템 개요
|
||||
|
||||
### 1.1 구성 요소
|
||||
|
||||
| 구성요소 | 역할 | 기본 포트 |
|
||||
|----------|------|-----------|
|
||||
| GUARDiA ITSM | IT 서비스 관리 플랫폼 | 8000 |
|
||||
| GUARDiA Messenger | 내부 메신저 + 봇 | 8001 |
|
||||
| SQLite DB | 데이터 저장소 | — |
|
||||
| APScheduler | 백그라운드 작업 (SSL 점검, PM) | — |
|
||||
|
||||
### 1.2 서비스 URL
|
||||
|
||||
```
|
||||
운영자/엔지니어 화면: http://<서버IP>:8000/
|
||||
고객 화면: http://<서버IP>:8000/customer
|
||||
로그인: http://<서버IP>:8000/login
|
||||
API 문서: http://<서버IP>:8000/docs (운영: 비활성화 권장)
|
||||
Messenger: http://<서버IP>:8001/
|
||||
```
|
||||
|
||||
### 1.3 프로세스 구성
|
||||
|
||||
```
|
||||
systemd (Linux):
|
||||
guardia-itsm.service — ITSM 메인 서버
|
||||
guardia-messenger.service — Messenger 서버
|
||||
|
||||
Windows Service:
|
||||
GUARDiA-ITSM — ITSM 메인 서버
|
||||
GUARDiA-Messenger — Messenger 서버
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 일상 운영 절차
|
||||
|
||||
### 2.1 매일 아침 점검 체크리스트
|
||||
|
||||
```
|
||||
시간: 매일 09:00
|
||||
|
||||
□ 1. 서비스 상태 확인
|
||||
→ curl http://localhost:8000/ (200 응답 확인)
|
||||
|
||||
□ 2. 야간 스케줄러 실행 결과 확인
|
||||
→ /var/log/guardia/scheduler.log 최신 항목 확인
|
||||
→ SSL 점검 결과 확인 (00:10 실행)
|
||||
→ 계약 만료 점검 결과 확인 (01:00 실행)
|
||||
→ PM 자동 생성 결과 확인 (06:00 실행)
|
||||
|
||||
□ 3. 신규 SR 확인
|
||||
→ ITSM 로그인 → 대시보드 → 미배정 SR 확인
|
||||
→ P1/P2 긴급 건 우선 배정
|
||||
|
||||
□ 4. SSL 만료 임박 도메인 확인
|
||||
→ ITSM → SSL 관리 → CRITICAL/URGENT 상태 도메인 갱신 조치
|
||||
|
||||
□ 5. 오늘 PM 일정 확인
|
||||
→ ITSM → 정기점검 → 오늘 예정 작업 확인
|
||||
|
||||
□ 6. 라이선스 상태 확인
|
||||
→ ITSM → 사이드바 🔏 라이선스 관리 → 상태 배너 확인
|
||||
→ 노랑 배너(만료 30일 이내): 갱신 준비 시작
|
||||
→ 빨강 배너(만료): 즉시 갱신 키 적용
|
||||
```
|
||||
|
||||
### 2.2 매주 점검 체크리스트
|
||||
|
||||
```
|
||||
시간: 매주 월요일 10:00
|
||||
|
||||
□ DB 파일 크기 확인 (100MB 이상 시 정리 검토)
|
||||
□ 로그 파일 로테이션 확인
|
||||
□ 업로드 파일 디렉토리 크기 확인
|
||||
□ 미해결 SR 현황 검토 (7일 이상 미처리 건)
|
||||
□ 만족도 낮은 SR 원인 분석
|
||||
□ 이번 주 당직 배정 확인 및 알림
|
||||
□ 만료 예정 SSL 인증서 30일 이내 갱신 계획 수립
|
||||
```
|
||||
|
||||
### 2.3 매월 점검 체크리스트
|
||||
|
||||
```
|
||||
시간: 매월 1일 11:00
|
||||
|
||||
□ 월간 SR 통계 보고서 작성
|
||||
□ PM 점검 완료율 확인 및 미완료 건 추적
|
||||
□ 보안 패치 현황 확인
|
||||
□ 사용자 계정 감사 (퇴직자 계정 비활성화 확인)
|
||||
□ DB 백업 파일 무결성 확인
|
||||
□ 장애 발생 현황 분석 및 재발 방지 대책 검토
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 사용자 계정 관리
|
||||
|
||||
### 3.1 계정 역할(Role) 정의
|
||||
|
||||
| 역할 | 권한 | 사용 대상 |
|
||||
|------|------|-----------|
|
||||
| ADMIN | 전체 관리 | 시스템 관리자 |
|
||||
| PM | SR 배정, 결재, PM 관리 | 프로젝트/서비스 매니저 |
|
||||
| ENGINEER | SR 처리, 작업 등록 | IT 엔지니어 |
|
||||
| CUSTOMER | 자신의 SR 조회/생성 | 고객/사용자 |
|
||||
|
||||
### 3.2 신규 사용자 등록
|
||||
|
||||
**방법 1: API 직접 등록 (ADMIN)**
|
||||
```
|
||||
POST /auth/register
|
||||
{
|
||||
"username": "user001",
|
||||
"password": "초기비밀번호!123",
|
||||
"full_name": "홍길동",
|
||||
"email": "hong@example.com",
|
||||
"role": "ENGINEER"
|
||||
}
|
||||
```
|
||||
|
||||
**방법 2: seed.py 수정 (초기 구성 시)**
|
||||
```python
|
||||
# core/seed.py — _seed_users() 함수에 추가
|
||||
{"username": "new_user", "password": "초기PW!", "role": Role.ENGINEER}
|
||||
```
|
||||
|
||||
### 3.3 비밀번호 정책
|
||||
|
||||
```
|
||||
최소 길이: 8자
|
||||
필수 조합: 영문 + 숫자 + 특수문자
|
||||
금지 비밀번호: admin1234, password, 12345678 등
|
||||
변경 주기: 90일마다 강제 변경 (구현 예정)
|
||||
초기 비밀번호: 등록 후 첫 로그인 시 변경 강제
|
||||
```
|
||||
|
||||
### 3.4 계정 잠금 및 비활성화
|
||||
|
||||
```
|
||||
# 퇴직자/이탈자 계정 비활성화
|
||||
PATCH /auth/users/{id}
|
||||
{"is_active": false}
|
||||
|
||||
# 계정 삭제 (데이터 보존 위해 비활성화 권장, 삭제 비권장)
|
||||
DELETE /auth/users/{id}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. SR(서비스요청) 관리
|
||||
|
||||
### 4.1 SR 유형 및 처리 기한
|
||||
|
||||
| SR 유형 | 설명 | 목표 처리 시간 |
|
||||
|---------|------|----------------|
|
||||
| INCIDENT | 긴급 장애, 서비스 중단 | P1: 1시간, P2: 4시간 |
|
||||
| SERVICE | 일반 서비스 요청 | 2 영업일 |
|
||||
| CHANGE | 변경 요청 (결재 필요) | 5 영업일 |
|
||||
| LOG | 작업 기록, 배치 결과 | 정보성 (SLA 없음) |
|
||||
|
||||
### 4.2 SR 상태 흐름
|
||||
|
||||
```
|
||||
OPEN → IN_PROGRESS → RESOLVED → CLOSED
|
||||
↓ ↑
|
||||
REJECTED ← (결재 거부) REOPENED (재오픈)
|
||||
```
|
||||
|
||||
### 4.3 담당자 배정 방법
|
||||
|
||||
```
|
||||
자동 배정:
|
||||
PATCH /assign/{sr_id}
|
||||
{"assignee_id": null} → 시스템이 자동 배정
|
||||
|
||||
수동 배정:
|
||||
PATCH /assign/{sr_id}
|
||||
{"assignee_id": 5} → 특정 엔지니어(ID:5)에게 배정
|
||||
```
|
||||
|
||||
### 4.4 SLA 위반 모니터링
|
||||
|
||||
```
|
||||
SLA 위반 기준:
|
||||
P1 INCIDENT: 1시간 이내 미처리
|
||||
P2 INCIDENT: 4시간 이내 미처리
|
||||
|
||||
점검 방법:
|
||||
GET /dashboard/sla-violations
|
||||
|
||||
Messenger 알림: 위반 30분 전 자동 알림 (스케줄러)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. SSL 인증서 관리
|
||||
|
||||
### 5.1 SSL 도메인 등록
|
||||
|
||||
```
|
||||
새 도메인 등록:
|
||||
POST /ssl-manager/domains
|
||||
{
|
||||
"domain": "www.example.com",
|
||||
"port": 443,
|
||||
"server_id": 1,
|
||||
"memo": "메인 홈페이지"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 자동 점검 일정
|
||||
|
||||
```
|
||||
매일 00:10 — 모든 등록 도메인 SSL 점검 자동 실행
|
||||
점검 방법: 관리 서버의 ssl_expiry_check.sh 스크립트 실행
|
||||
|
||||
알림 발송 조건:
|
||||
EXPIRED (만료됨) → 즉시 Critical 알림
|
||||
URGENT (7일 이내) → 매일 알림
|
||||
WARN (30일 이내) → 최초 발견 시 알림
|
||||
OK (30일 초과) → 알림 없음
|
||||
```
|
||||
|
||||
### 5.3 SSL 갱신 처리 절차
|
||||
|
||||
```
|
||||
1단계: 만료 임박 도메인 확인
|
||||
GET /ssl-manager/domains?level=CRITICAL
|
||||
GET /ssl-manager/domains?level=URGENT
|
||||
|
||||
2단계: 인증서 갱신 (외부 CA에서 수행)
|
||||
- Let's Encrypt: certbot renew
|
||||
- 상용 CA: CA 포털에서 갱신 신청
|
||||
|
||||
3단계: 서버에 인증서 적용
|
||||
- Nginx/Apache 인증서 파일 교체
|
||||
- 웹서버 재시작
|
||||
|
||||
4단계: GUARDiA ITSM에 갱신 완료 기록
|
||||
POST /ssl-manager/domains/{id}/renew
|
||||
{
|
||||
"renewed_at": "2026-05-25T10:00:00",
|
||||
"new_expiry": "2027-05-25T10:00:00",
|
||||
"memo": "Let's Encrypt 자동 갱신"
|
||||
}
|
||||
|
||||
5단계: 즉시 재점검으로 확인
|
||||
POST /ssl-manager/domains/{id}/check
|
||||
```
|
||||
|
||||
### 5.4 SSL 스크립트 배포
|
||||
|
||||
서버에 점검 스크립트를 배포해야 SSH 점검이 가능합니다:
|
||||
|
||||
```bash
|
||||
# 관리 대상 서버에 스크립트 배포
|
||||
scp scripts/sm/ssl/ssl_expiry_check.sh user@서버IP:/opt/guardia/scripts/ssl/
|
||||
ssh user@서버IP "chmod +x /opt/guardia/scripts/ssl/ssl_expiry_check.sh"
|
||||
|
||||
# 테스트
|
||||
ssh user@서버IP "bash /opt/guardia/scripts/ssl/ssl_expiry_check.sh www.example.com"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 정기점검(PM) 관리
|
||||
|
||||
### 6.1 PM 스케줄 설정
|
||||
|
||||
```
|
||||
PM 주기 옵션:
|
||||
WEEKLY — 매주
|
||||
BIWEEKLY — 격주
|
||||
MONTHLY — 매월
|
||||
QUARTERLY — 분기별 (3개월)
|
||||
SEMIANNUAL — 반기별 (6개월)
|
||||
ANNUAL — 연간
|
||||
CUSTOM — 커스텀 cron 식
|
||||
|
||||
스케줄 등록:
|
||||
POST /pm/schedules/
|
||||
{
|
||||
"name": "운영 서버 월간 점검",
|
||||
"server_id": 1,
|
||||
"template_ids": [1, 2, 3],
|
||||
"frequency": "MONTHLY",
|
||||
"day_of_month": 15,
|
||||
"assigned_to": 5
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 PM 작업 자동 생성
|
||||
|
||||
```
|
||||
매일 06:00 — 스케줄러가 당일 PM 작업 자동 생성
|
||||
자동 생성된 작업: GET /pm/works/
|
||||
|
||||
수동 생성:
|
||||
POST /pm/works/
|
||||
{
|
||||
"schedule_id": 1,
|
||||
"planned_date": "2026-06-01",
|
||||
"notes": "6월 월간 점검"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 PM 결과 입력
|
||||
|
||||
```
|
||||
각 체크리스트 항목 결과 입력:
|
||||
PATCH /pm/works/{work_id}/results/{result_id}
|
||||
{
|
||||
"result": "PASS", // PASS / FAIL / WARNING / NA
|
||||
"value": "85%", // 측정값
|
||||
"note": "" // FAIL인 경우 조치 내역 필수
|
||||
}
|
||||
|
||||
PM 완료 처리:
|
||||
PATCH /pm/works/{work_id}/complete
|
||||
```
|
||||
|
||||
### 6.4 PM 보고서 출력
|
||||
|
||||
```
|
||||
Excel 보고서 다운로드:
|
||||
GET /pm/works/{work_id}/report
|
||||
→ Excel 파일 다운로드 (PASS: 녹색, FAIL: 빨간색, WARNING: 노란색)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 장애 관리
|
||||
|
||||
### 7.1 장애 등급 정의
|
||||
|
||||
| 등급 | 영향 | 대응 시간 | 예시 |
|
||||
|------|------|-----------|------|
|
||||
| P1 | 전체 서비스 중단 | 1시간 | 운영 DB Down |
|
||||
| P2 | 주요 기능 장애 | 4시간 | 로그인 불가 |
|
||||
| P3 | 부분 기능 장애 | 8시간 | 특정 기능 오류 |
|
||||
| P4 | 경미한 장애 | 2영업일 | UI 오류 |
|
||||
|
||||
### 7.2 P1/P2 장애 대응 절차
|
||||
|
||||
```
|
||||
즉시 조치 (15분 이내):
|
||||
1. ITSM에 장애 등록 (POST /incidents/)
|
||||
→ Messenger 긴급 알림 자동 발송
|
||||
2. 온콜 담당자에게 연락
|
||||
3. 장애 타임라인 기록 시작
|
||||
|
||||
초기 조사 (30분 이내):
|
||||
4. PATCH /incidents/{id}/status — INVESTIGATING
|
||||
5. 원인 파악 시작
|
||||
6. 임시 우회 방안 검토
|
||||
|
||||
조치 및 복구:
|
||||
7. PATCH /incidents/{id}/status — MITIGATED (임시 조치 완료)
|
||||
8. 근본 원인 해결
|
||||
9. PATCH /incidents/{id}/status — RESOLVED
|
||||
|
||||
사후 처리:
|
||||
10. RCA(근본원인분석) 작성
|
||||
11. POST /incidents/{id}/close (RCA 포함 필수)
|
||||
12. 재발 방지 대책 SR 등록
|
||||
```
|
||||
|
||||
### 7.3 장애 타임라인 기록
|
||||
|
||||
```
|
||||
장애 진행 중 모든 주요 활동 기록:
|
||||
POST /incidents/{id}/timeline
|
||||
{
|
||||
"event_type": "INVESTIGATION",
|
||||
"description": "DB 서버 CPU 100% 확인, 쿼리 분석 중"
|
||||
}
|
||||
|
||||
event_type 옵션:
|
||||
DETECTION — 최초 감지
|
||||
INVESTIGATION — 조사 활동
|
||||
ACTION — 조치 수행
|
||||
UPDATE — 현황 업데이트
|
||||
RECOVERY — 복구 완료
|
||||
POSTMORTEM — 사후 분석
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 온콜/당직 관리
|
||||
|
||||
### 8.1 당직 배정
|
||||
|
||||
```
|
||||
단건 배정:
|
||||
POST /oncall/duties
|
||||
{
|
||||
"duty_date": "2026-06-01",
|
||||
"shift": "ALL_DAY", // ALL_DAY / DAYTIME / NIGHTTIME
|
||||
"user_id": 5,
|
||||
"note": "야간 비상 연락"
|
||||
}
|
||||
|
||||
월간 일괄 배정:
|
||||
POST /oncall/duties/bulk
|
||||
{
|
||||
"duties": [
|
||||
{"duty_date": "2026-06-01", "shift": "ALL_DAY", "user_id": 5},
|
||||
{"duty_date": "2026-06-02", "shift": "ALL_DAY", "user_id": 6}
|
||||
],
|
||||
"overwrite": false // true: 중복 시 덮어쓰기
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 오늘 당직 확인
|
||||
|
||||
```
|
||||
GET /oncall/today
|
||||
→ 오늘 날짜의 당직자 목록 (교대별)
|
||||
|
||||
응답 예시:
|
||||
{
|
||||
"date": "2026-06-01",
|
||||
"duties": {
|
||||
"ALL_DAY": [{"user": "홍길동", "phone": "010-XXXX-XXXX"}],
|
||||
"NIGHTTIME": [{"user": "김철수", "phone": "010-XXXX-XXXX"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 월간 당직 조회
|
||||
|
||||
```
|
||||
GET /oncall/calendar?year=2026&month=6
|
||||
→ 해당 월 전체 당직 일정
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 배치 작업 관리
|
||||
|
||||
### 9.1 배치 작업 등록
|
||||
|
||||
```
|
||||
POST /batch/jobs
|
||||
{
|
||||
"name": "일일 DB 백업",
|
||||
"server_id": 1,
|
||||
"command": "bash /opt/scripts/backup.sh",
|
||||
"cron_expr": "0 2 * * *", // 매일 오전 2시
|
||||
"timeout_sec": 3600,
|
||||
"alert_on_fail": true,
|
||||
"is_active": true
|
||||
}
|
||||
```
|
||||
|
||||
> **주의**: 다음 명령어 패턴은 등록 자체가 거부됩니다:
|
||||
> `rm -rf /`, `shutdown`, `reboot`, `mkfs`, `dd if=`, Fork Bomb 등
|
||||
|
||||
### 9.2 배치 작업 모니터링
|
||||
|
||||
```
|
||||
최근 실행 이력:
|
||||
GET /batch/jobs/{id}/runs?limit=20
|
||||
|
||||
실행 상태:
|
||||
RUNNING — 실행 중
|
||||
SUCCESS — 성공
|
||||
FAILED — 실패 (alert_on_fail=true 시 SR 자동 생성)
|
||||
TIMEOUT — 타임아웃 초과
|
||||
|
||||
실패 시 자동 SR: GET /tasks/?sr_type=LOG&priority=HIGH
|
||||
```
|
||||
|
||||
### 9.3 수동 실행
|
||||
|
||||
```
|
||||
POST /batch/jobs/{id}/run
|
||||
→ 202 Accepted (백그라운드 실행 시작)
|
||||
|
||||
실행 결과 확인:
|
||||
GET /batch/jobs/{id}/runs?limit=1 (최신 1건)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 백업 및 복구
|
||||
|
||||
### 10.1 DB 백업 절차
|
||||
|
||||
```bash
|
||||
# Linux — 수동 백업
|
||||
cp /opt/guardia/itsm/guardia_itsm.db /backup/guardia_itsm_$(date +%Y%m%d).db
|
||||
cp /opt/guardia/messenger/guardia_messenger.db /backup/guardia_messenger_$(date +%Y%m%d).db
|
||||
|
||||
# 자동 백업 스크립트 (cron 등록 권장)
|
||||
# crontab -e
|
||||
0 3 * * * /opt/guardia/scripts/backup.sh >> /var/log/guardia/backup.log 2>&1
|
||||
|
||||
# Windows — PowerShell
|
||||
$date = Get-Date -Format "yyyyMMdd"
|
||||
Copy-Item "C:\GUARDiA\itsm\guardia_itsm.db" "D:\Backup\guardia_itsm_$date.db"
|
||||
```
|
||||
|
||||
### 10.2 업로드 파일 백업
|
||||
|
||||
```bash
|
||||
# 업로드 파일 디렉토리 전체 백업
|
||||
tar -czf /backup/uploads_$(date +%Y%m%d).tar.gz /opt/guardia/itsm/uploads/
|
||||
```
|
||||
|
||||
### 10.3 복구 절차
|
||||
|
||||
```bash
|
||||
# 1. 서비스 중지
|
||||
systemctl stop guardia-itsm
|
||||
systemctl stop guardia-messenger
|
||||
|
||||
# 2. 현재 DB 보존
|
||||
mv guardia_itsm.db guardia_itsm.db.broken
|
||||
|
||||
# 3. 백업 복원
|
||||
cp /backup/guardia_itsm_20260525.db guardia_itsm.db
|
||||
|
||||
# 4. 서비스 재시작
|
||||
systemctl start guardia-itsm
|
||||
systemctl start guardia-messenger
|
||||
|
||||
# 5. 서비스 상태 확인
|
||||
curl http://localhost:8000/
|
||||
```
|
||||
|
||||
### 10.4 백업 보관 정책
|
||||
|
||||
```
|
||||
일간 백업: 30일 보관
|
||||
주간 백업: 12주 보관
|
||||
월간 백업: 12개월 보관
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 모니터링 및 알림
|
||||
|
||||
### 11.1 시스템 모니터링 지점
|
||||
|
||||
| 모니터링 항목 | 임계값 | 조치 |
|
||||
|--------------|--------|------|
|
||||
| ITSM 서비스 응답 | 5초 이상 → 경고 | 서비스 재시작 |
|
||||
| DB 파일 크기 | 500MB 이상 → 경고 | 정리/이관 검토 |
|
||||
| 디스크 사용률 | 80% 이상 → 경고 | 파일 정리 |
|
||||
| 업로드 디렉토리 | 10GB 이상 → 경고 | 오래된 파일 정리 |
|
||||
| 스케줄러 실행 | 오전 06:30까지 미실행 → 경고 | 재시작 |
|
||||
|
||||
### 11.2 Messenger 알림 채널
|
||||
|
||||
```
|
||||
장애 알림: #guardia-incidents
|
||||
SSL 만료 알림: #guardia-ssl
|
||||
PM 일정 알림: #guardia-pm
|
||||
배치 실패: #guardia-ops
|
||||
```
|
||||
|
||||
### 11.3 서비스 상태 확인
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
systemctl status guardia-itsm
|
||||
systemctl status guardia-messenger
|
||||
|
||||
# 로그 실시간 확인
|
||||
journalctl -u guardia-itsm -f
|
||||
|
||||
# Windows
|
||||
Get-Service -Name "GUARDiA-ITSM"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 보안 운영 지침
|
||||
|
||||
### 12.1 계정 보안
|
||||
|
||||
```
|
||||
□ 기본 admin 비밀번호 즉시 변경 (초기 설치 후)
|
||||
□ 퇴직자 계정 당일 비활성화
|
||||
□ 공용 계정 사용 금지 (1인 1계정 원칙)
|
||||
□ 분기마다 불필요 계정 감사
|
||||
□ CUSTOMER 역할 계정은 자신의 SR만 조회 가능 확인
|
||||
```
|
||||
|
||||
### 12.2 API 접근 제어
|
||||
|
||||
```
|
||||
□ 운영 환경에서 /docs 페이지 비활성화 권장
|
||||
main.py: app = FastAPI(docs_url=None, redoc_url=None)
|
||||
|
||||
□ CORS 설정 확인 (운영 환경)
|
||||
allow_origins: 실제 도메인만 허가 (localhost 제거)
|
||||
|
||||
□ 로그인 실패 횟수 모니터링
|
||||
GET /audit/logs?action=LOGIN_FAIL
|
||||
```
|
||||
|
||||
### 12.3 서버 자격증명 관리
|
||||
|
||||
```
|
||||
□ SSH 비밀번호는 AES-256-GCM 암호화 저장 확인
|
||||
□ 평문 비밀번호 어디에도 저장 금지
|
||||
□ ENCRYPTION_KEY는 .env에만 저장, Git 제외
|
||||
□ API 응답에 IP/PW 포함 여부 정기 검사
|
||||
□ root SSH 직접 접속 금지 설정 확인
|
||||
```
|
||||
|
||||
### 12.4 감사 로그 관리
|
||||
|
||||
```
|
||||
감사 로그 확인:
|
||||
GET /audit/logs?start=2026-05-01&end=2026-05-31
|
||||
|
||||
무결성 검증:
|
||||
GET /audit/verify
|
||||
→ "integrity": true 확인
|
||||
|
||||
의심 활동 모니터링:
|
||||
- 새벽 시간대 로그인
|
||||
- 다수 SR 한 번에 변경
|
||||
- 서버 자격증명 접근
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 장애 대응 절차
|
||||
|
||||
### 13.1 ITSM 서비스 응답 없음
|
||||
|
||||
```
|
||||
증상: curl http://localhost:8000/ 타임아웃
|
||||
|
||||
1단계 — 프로세스 확인
|
||||
Linux: ps aux | grep uvicorn
|
||||
Windows: Get-Process -Name python
|
||||
|
||||
2단계 — 로그 확인
|
||||
Linux: journalctl -u guardia-itsm --since "10 min ago"
|
||||
Linux: cat /var/log/guardia/itsm.log | tail -100
|
||||
|
||||
3단계 — 서비스 재시작
|
||||
Linux: systemctl restart guardia-itsm
|
||||
Windows: Restart-Service "GUARDiA-ITSM"
|
||||
|
||||
4단계 — 재시작 후 확인
|
||||
curl http://localhost:8000/
|
||||
→ 200 응답 확인
|
||||
```
|
||||
|
||||
### 13.2 DB 잠금(Lock) 오류
|
||||
|
||||
```
|
||||
증상: "database is locked" 에러 로그 발생
|
||||
|
||||
1단계 — 연결 프로세스 확인
|
||||
Linux: fuser guardia_itsm.db
|
||||
|
||||
2단계 — 임시 잠금 파일 삭제 (서비스 중지 후)
|
||||
rm guardia_itsm.db-shm guardia_itsm.db-wal
|
||||
|
||||
3단계 — 서비스 재시작
|
||||
systemctl restart guardia-itsm
|
||||
```
|
||||
|
||||
### 13.3 스케줄러 미동작
|
||||
|
||||
```
|
||||
증상: SSL 점검, PM 자동 생성이 실행되지 않음
|
||||
|
||||
1단계 — 로그 확인
|
||||
grep "scheduler" /var/log/guardia/itsm.log
|
||||
|
||||
2단계 — APScheduler 설치 확인
|
||||
pip show apscheduler
|
||||
|
||||
3단계 — 서비스 재시작
|
||||
systemctl restart guardia-itsm
|
||||
|
||||
4단계 — 수동 실행으로 대체
|
||||
ITSM → SSL 관리 → "전체 점검" 버튼
|
||||
ITSM → 정기점검 → 수동 작업 생성
|
||||
```
|
||||
|
||||
### 13.4 Messenger 연결 실패
|
||||
|
||||
```
|
||||
증상: ITSM 알림이 Messenger에 미전달
|
||||
|
||||
1단계 — Messenger 서비스 상태 확인
|
||||
systemctl status guardia-messenger
|
||||
|
||||
2단계 — 연결 설정 확인
|
||||
.env 파일의 MESSENGER_BASE_URL, MESSENGER_BOT_TOKEN 확인
|
||||
|
||||
3단계 — 연결 테스트
|
||||
curl http://localhost:8001/api/health
|
||||
|
||||
4단계 — Messenger 재시작
|
||||
systemctl restart guardia-messenger
|
||||
|
||||
주의: Messenger 알림 실패는 ITSM 기능에 영향 없음 (알림만 안 됨)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 로그 관리
|
||||
|
||||
### 14.1 로그 파일 위치
|
||||
|
||||
```
|
||||
Linux:
|
||||
/var/log/guardia/itsm.log — ITSM 애플리케이션 로그
|
||||
/var/log/guardia/scheduler.log — 스케줄러 실행 로그
|
||||
/var/log/guardia/messenger.log — Messenger 로그
|
||||
/var/log/guardia/backup.log — 백업 실행 로그
|
||||
|
||||
Windows:
|
||||
C:\GUARDiA\logs\itsm.log
|
||||
C:\GUARDiA\logs\scheduler.log
|
||||
C:\GUARDiA\logs\messenger.log
|
||||
```
|
||||
|
||||
### 14.2 로그 로테이션 설정 (Linux)
|
||||
|
||||
```
|
||||
# /etc/logrotate.d/guardia
|
||||
/var/log/guardia/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 640 guardia guardia
|
||||
postrotate
|
||||
systemctl reload guardia-itsm
|
||||
endscript
|
||||
}
|
||||
```
|
||||
|
||||
### 14.3 감사 로그 (DB 내 저장)
|
||||
|
||||
```
|
||||
ITSM 내 모든 주요 활동은 tb_audit_log 테이블에 저장됩니다.
|
||||
해시 체인으로 무결성 보호.
|
||||
|
||||
조회:
|
||||
GET /audit/logs?limit=100
|
||||
GET /audit/logs?user_id=5&start=2026-05-01
|
||||
|
||||
무결성 검증:
|
||||
GET /audit/verify
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. 주요 설정 파일
|
||||
|
||||
### 15.1 .env 운영 설정
|
||||
|
||||
```ini
|
||||
# 운영 환경 .env
|
||||
SECRET_KEY=<64자 이상 랜덤 문자열>
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=480
|
||||
DB_URL=sqlite+aiosqlite:///./guardia_itsm.db
|
||||
ENCRYPTION_KEY=<64자리 hex 문자열>
|
||||
|
||||
MESSENGER_BASE_URL=http://localhost:8001
|
||||
MESSENGER_BOT_TOKEN=<봇 토큰>
|
||||
|
||||
SSH_TIMEOUT=30
|
||||
UPLOAD_ROOT=./uploads
|
||||
MAX_FILE_SIZE_MB=10
|
||||
|
||||
# 운영: API 문서 비활성화
|
||||
DOCS_DISABLED=true
|
||||
```
|
||||
|
||||
### 15.2 서비스 파일 (Linux systemd)
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/guardia-itsm.service
|
||||
[Unit]
|
||||
Description=GUARDiA ITSM Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=guardia
|
||||
WorkingDirectory=/opt/guardia/itsm
|
||||
EnvironmentFile=/opt/guardia/itsm/.env
|
||||
ExecStart=/opt/guardia/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 --workers 2
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:/var/log/guardia/itsm.log
|
||||
StandardError=append:/var/log/guardia/itsm.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 15.3 방화벽 설정
|
||||
|
||||
```bash
|
||||
# Linux (firewalld)
|
||||
firewall-cmd --permanent --add-port=8000/tcp # ITSM
|
||||
firewall-cmd --permanent --add-port=8001/tcp # Messenger
|
||||
firewall-cmd --reload
|
||||
|
||||
# Windows (PowerShell)
|
||||
New-NetFirewallRule -DisplayName "GUARDiA ITSM" -Direction Inbound -Protocol TCP -LocalPort 8000 -Action Allow
|
||||
New-NetFirewallRule -DisplayName "GUARDiA Messenger" -Direction Inbound -Protocol TCP -LocalPort 8001 -Action Allow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 부록 A: 운영 명령어 빠른 참조
|
||||
|
||||
```bash
|
||||
# 서비스 시작/중지/재시작
|
||||
systemctl start|stop|restart guardia-itsm
|
||||
systemctl start|stop|restart guardia-messenger
|
||||
|
||||
# 상태 확인
|
||||
systemctl status guardia-itsm
|
||||
curl http://localhost:8000/
|
||||
|
||||
# 로그 확인
|
||||
journalctl -u guardia-itsm -f --since "1 hour ago"
|
||||
|
||||
# DB 백업
|
||||
cp guardia_itsm.db guardia_itsm_$(date +%Y%m%d%H%M).db
|
||||
|
||||
# 업로드 용량 확인
|
||||
du -sh uploads/
|
||||
|
||||
# 활성 연결 확인
|
||||
ss -tlnp | grep :8000
|
||||
```
|
||||
|
||||
## 부록 B: 비상 연락망
|
||||
|
||||
```
|
||||
ITSM 운영팀: ...
|
||||
보안팀: ...
|
||||
인프라팀: ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 16. 라이선스 관리
|
||||
|
||||
### 16.1 라이선스 등록 절차
|
||||
|
||||
1. ITSM 관리자 계정으로 로그인
|
||||
2. 사이드바 하단 **🔏 라이선스 관리** 클릭 (`/license` 페이지)
|
||||
3. **라이선스 키 등록/갱신** 섹션에 `GRD-...` 형식의 키 붙여넣기
|
||||
4. **라이선스 등록** 버튼 클릭
|
||||
5. 상단 배너에서 에디션·만료일 확인
|
||||
|
||||
**API로 등록 (자동화)**:
|
||||
```bash
|
||||
curl -X POST http://localhost:8001/api/license/activate \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"license_key": "GRD-..."}'
|
||||
```
|
||||
|
||||
### 16.2 라이선스 상태 배너 의미
|
||||
|
||||
| 배너 색상 | 상태 | 조치 |
|
||||
|---------|------|------|
|
||||
| ✅ 초록 | 정상 활성 | — |
|
||||
| ⚠️ 노랑 | 만료 30일 이내 경고 | 갱신 라이선스 요청 |
|
||||
| ❌ 빨강 | 만료됨 | 즉시 갱신 키 적용 필요 |
|
||||
| ⬜ 회색 | 라이선스 미등록 | Community 제한 모드로 동작 |
|
||||
|
||||
### 16.3 에디션별 자원 제한
|
||||
|
||||
| 에디션 | 기관 수 | 사용자 수 | 서버 수 | 주요 기능 |
|
||||
|--------|--------|---------|--------|---------|
|
||||
| COMMUNITY | 1 | 10 | 20 | MFA |
|
||||
| STANDARD | 50 | 200 | 500 | MFA, LDAP, PAM, AI 에이전트 |
|
||||
| ENTERPRISE | 무제한 | 무제한 | 무제한 | 전체 기능 |
|
||||
|
||||
> **주의**: 한도 초과 시 신규 등록 API가 HTTP 403을 반환한다.
|
||||
> 기존 등록 데이터는 만료·초과 후에도 삭제되지 않는다.
|
||||
|
||||
### 16.4 라이선스 갱신
|
||||
|
||||
갱신 라이선스 키를 받은 후 16.1과 동일하게 등록하면 기존 라이선스가 자동으로 비활성화되고 새 라이선스로 교체된다.
|
||||
|
||||
자세한 발급·갱신 절차는 **`manual/14_라이선스_키_발급_가이드.md`** 참조.
|
||||
|
||||
---
|
||||
|
||||
*본 운영자 지침서는 GUARDiA ITSM v1.0 기준으로 작성되었습니다.*
|
||||
837
05_설치가이드_리눅스.md
Normal file
837
05_설치가이드_리눅스.md
Normal file
@ -0,0 +1,837 @@
|
||||
# GUARDiA ITSM + Messenger — 설치 가이드 (Linux)
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**지원 OS**: Ubuntu 22.04 LTS, Ubuntu 20.04 LTS, CentOS 8+, RHEL 8+
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [설치 전 요구사항 확인](#1-설치-전-요구사항-확인)
|
||||
2. [시스템 사전 준비](#2-시스템-사전-준비)
|
||||
3. [Python 환경 설정](#3-python-환경-설정)
|
||||
4. [GUARDiA ITSM 설치](#4-guardia-itsm-설치)
|
||||
5. [GUARDiA Messenger 설치](#5-guardia-messenger-설치)
|
||||
6. [서비스 등록 (systemd)](#6-서비스-등록-systemd)
|
||||
7. [SSL 점검 스크립트 배포](#7-ssl-점검-스크립트-배포)
|
||||
8. [방화벽 설정](#8-방화벽-설정)
|
||||
9. [Nginx 리버스 프록시 설정 (선택)](#9-nginx-리버스-프록시-설정-선택)
|
||||
10. [설치 검증](#10-설치-검증)
|
||||
11. [보안 강화 설정](#11-보안-강화-설정)
|
||||
12. [문제 해결](#12-문제-해결)
|
||||
|
||||
---
|
||||
|
||||
## 1. 설치 전 요구사항 확인
|
||||
|
||||
### 1.1 하드웨어 최소 요구사항
|
||||
|
||||
| 항목 | 최소 | 권장 |
|
||||
|------|------|------|
|
||||
| CPU | 2코어 | 4코어 이상 |
|
||||
| RAM | 2GB | 4GB 이상 |
|
||||
| 디스크 | 20GB | 50GB 이상 |
|
||||
| 네트워크 | 100Mbps | 1Gbps |
|
||||
|
||||
### 1.2 소프트웨어 요구사항
|
||||
|
||||
```
|
||||
OS: Ubuntu 22.04 LTS (권장)
|
||||
Python: 3.11 이상
|
||||
openssl: 1.1.1 이상 (SSL 점검에 필요)
|
||||
sqlite3: 3.35 이상
|
||||
```
|
||||
|
||||
### 1.3 요구사항 사전 확인
|
||||
|
||||
```bash
|
||||
# OS 버전 확인
|
||||
lsb_release -a
|
||||
uname -r
|
||||
|
||||
# Python 버전 확인
|
||||
python3 --version
|
||||
# → Python 3.11.x 이상이어야 함
|
||||
|
||||
# openssl 버전 확인
|
||||
openssl version
|
||||
# → OpenSSL 1.1.1 이상
|
||||
|
||||
# SQLite 버전 확인
|
||||
sqlite3 --version
|
||||
# → 3.35.0 이상
|
||||
|
||||
# 디스크 여유 공간 확인
|
||||
df -h /
|
||||
# → Available 20GB 이상 확인
|
||||
|
||||
# 포트 사용 여부 확인 (8000, 8001이 비어 있어야 함)
|
||||
ss -tlnp | grep -E ':8000|:8001'
|
||||
# → 아무것도 출력되지 않아야 함
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 시스템 사전 준비
|
||||
|
||||
### 2.1 시스템 업데이트 (Ubuntu)
|
||||
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
sudo apt install -y git curl wget vim unzip
|
||||
```
|
||||
|
||||
### 2.2 시스템 업데이트 (CentOS/RHEL)
|
||||
|
||||
```bash
|
||||
sudo dnf update -y
|
||||
sudo dnf install -y git curl wget vim unzip
|
||||
```
|
||||
|
||||
### 2.3 전용 사용자 생성
|
||||
|
||||
보안을 위해 root가 아닌 전용 사용자로 실행합니다.
|
||||
|
||||
```bash
|
||||
# guardia 사용자 생성 (로그인 불가 설정)
|
||||
sudo useradd -r -m -d /opt/guardia -s /bin/bash guardia
|
||||
sudo passwd guardia # 비밀번호 설정 (관리자만 알아야 함)
|
||||
|
||||
# 사용자 확인
|
||||
id guardia
|
||||
# → uid=998(guardia) gid=998(guardia) groups=998(guardia)
|
||||
```
|
||||
|
||||
### 2.4 디렉토리 구조 생성
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/guardia/{itsm,messenger}
|
||||
sudo mkdir -p /opt/guardia/scripts/ssl
|
||||
sudo mkdir -p /var/log/guardia
|
||||
sudo mkdir -p /backup/guardia
|
||||
|
||||
sudo chown -R guardia:guardia /opt/guardia
|
||||
sudo chown -R guardia:guardia /var/log/guardia
|
||||
sudo chown -R guardia:guardia /backup/guardia
|
||||
|
||||
# 확인
|
||||
ls -la /opt/guardia/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Python 환경 설정
|
||||
|
||||
### 3.1 Python 3.11 설치 (Ubuntu 22.04)
|
||||
|
||||
Ubuntu 22.04는 기본적으로 Python 3.10이 설치됩니다. 3.11을 설치합니다.
|
||||
|
||||
```bash
|
||||
# deadsnakes PPA 추가
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa -y
|
||||
sudo apt update
|
||||
|
||||
# Python 3.11 설치
|
||||
sudo apt install -y python3.11 python3.11-venv python3.11-dev
|
||||
|
||||
# 버전 확인
|
||||
python3.11 --version
|
||||
# → Python 3.11.x
|
||||
```
|
||||
|
||||
### 3.2 Python 3.11 설치 (CentOS/RHEL 8)
|
||||
|
||||
```bash
|
||||
# EPEL 및 개발 도구 설치
|
||||
sudo dnf install -y epel-release
|
||||
sudo dnf install -y python3.11 python3.11-pip
|
||||
|
||||
python3.11 --version
|
||||
```
|
||||
|
||||
### 3.3 pip 업그레이드
|
||||
|
||||
```bash
|
||||
python3.11 -m pip install --upgrade pip
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. GUARDiA ITSM 설치
|
||||
|
||||
### 4.1 소스 코드 배포
|
||||
|
||||
```bash
|
||||
# guardia 사용자로 전환
|
||||
sudo -u guardia bash
|
||||
|
||||
# 소스 코드 복사 (파일 서버에서 복사 또는 git clone)
|
||||
# 방법 1: scp로 복사
|
||||
# scp -r itsm/ guardia@서버IP:/opt/guardia/itsm/
|
||||
|
||||
# 방법 2: git clone (내부 Git 서버)
|
||||
cd /opt/guardia
|
||||
git clone http://내부git서버/guardia/itsm.git itsm
|
||||
|
||||
# 방법 3: 직접 파일 복사 (zip 사용)
|
||||
# unzip guardia_itsm_v1.0.zip -d /opt/guardia/itsm/
|
||||
|
||||
# 설치 확인
|
||||
ls /opt/guardia/itsm/
|
||||
# → main.py database.py models.py schemas.py routers/ core/ utils/ static/ 등
|
||||
```
|
||||
|
||||
### 4.2 가상환경 생성 및 의존성 설치
|
||||
|
||||
```bash
|
||||
# /opt/guardia/itsm 디렉토리에서
|
||||
cd /opt/guardia/itsm
|
||||
|
||||
# 가상환경 생성 (공유 위치)
|
||||
python3.11 -m venv /opt/guardia/.venv
|
||||
|
||||
# 가상환경 활성화
|
||||
source /opt/guardia/.venv/bin/activate
|
||||
|
||||
# pip 업그레이드
|
||||
pip install --upgrade pip
|
||||
|
||||
# 의존성 설치
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 설치 확인
|
||||
pip list | grep -E "fastapi|uvicorn|sqlalchemy|apscheduler"
|
||||
# → 패키지 목록 출력 확인
|
||||
|
||||
# 가상환경 비활성화
|
||||
deactivate
|
||||
```
|
||||
|
||||
**requirements.txt 주요 패키지**:
|
||||
```
|
||||
fastapi>=0.110
|
||||
uvicorn[standard]>=0.29
|
||||
sqlalchemy>=2.0
|
||||
aiosqlite>=0.20
|
||||
python-jose[cryptography]>=3.3
|
||||
passlib[bcrypt]>=1.7
|
||||
python-multipart>=0.0.9
|
||||
apscheduler>=3.10
|
||||
asyncssh>=2.14
|
||||
openpyxl>=3.1
|
||||
httpx>=0.27
|
||||
cryptography>=42.0
|
||||
croniter>=2.0
|
||||
```
|
||||
|
||||
### 4.3 환경 변수 설정
|
||||
|
||||
```bash
|
||||
# .env 파일 생성
|
||||
cat > /opt/guardia/itsm/.env << 'EOF'
|
||||
SECRET_KEY=여기에_64자이상의_랜덤문자열_입력_반드시_변경하세요
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=480
|
||||
DB_URL=sqlite+aiosqlite:////opt/guardia/itsm/guardia_itsm.db
|
||||
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
# 라이선스 마스터 키 (64자리 hex = 32바이트)
|
||||
# 생성: python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
# ⚠️ 분실 시 기발급 라이선스 전부 무효화 — 안전하게 별도 보관 필수
|
||||
GUARDIA_LICENSE_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
MESSENGER_BASE_URL=http://localhost:8001
|
||||
MESSENGER_BOT_TOKEN=변경하세요
|
||||
SSH_TIMEOUT=30
|
||||
UPLOAD_ROOT=/opt/guardia/itsm/uploads
|
||||
MAX_FILE_SIZE_MB=10
|
||||
EOF
|
||||
|
||||
# 권한 설정 (소유자만 읽기/쓰기)
|
||||
chmod 600 /opt/guardia/itsm/.env
|
||||
chown guardia:guardia /opt/guardia/itsm/.env
|
||||
```
|
||||
|
||||
> **중요**: `SECRET_KEY`, `ENCRYPTION_KEY`, `GUARDIA_LICENSE_KEY`는 반드시 강력한 랜덤 값으로 변경하세요!
|
||||
|
||||
**랜덤 키 생성 방법**:
|
||||
```bash
|
||||
# SECRET_KEY 생성 (64자)
|
||||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
|
||||
# ENCRYPTION_KEY 생성 (64자 hex = AES-256)
|
||||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
```
|
||||
|
||||
### 4.4 업로드 디렉토리 생성
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/guardia/itsm/uploads/sr_files
|
||||
mkdir -p /opt/guardia/itsm/uploads/workspaces
|
||||
chown -R guardia:guardia /opt/guardia/itsm/uploads/
|
||||
```
|
||||
|
||||
### 4.5 초기 실행 및 DB 초기화
|
||||
|
||||
```bash
|
||||
cd /opt/guardia/itsm
|
||||
source /opt/guardia/.venv/bin/activate
|
||||
|
||||
# 테스트 실행 (초기 DB 생성 및 시드 데이터 삽입)
|
||||
python -m uvicorn main:app --host 127.0.0.1 --port 8000 &
|
||||
sleep 5
|
||||
|
||||
# 정상 동작 확인
|
||||
curl http://127.0.0.1:8000/
|
||||
# → {"detail":"Not Found"} 또는 HTML 응답이면 정상
|
||||
|
||||
# 로그인 테스트
|
||||
curl -X POST http://127.0.0.1:8000/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin1234!"}'
|
||||
# → {"access_token":"...","token_type":"bearer"} 출력 확인
|
||||
|
||||
# 프로세스 종료 (systemd 서비스로 실행 예정)
|
||||
kill %1
|
||||
deactivate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. GUARDiA Messenger 설치
|
||||
|
||||
### 5.1 소스 코드 배포
|
||||
|
||||
```bash
|
||||
sudo -u guardia bash
|
||||
cd /opt/guardia
|
||||
git clone http://내부git서버/guardia/messenger.git messenger
|
||||
# 또는 파일 복사
|
||||
```
|
||||
|
||||
### 5.2 의존성 설치
|
||||
|
||||
```bash
|
||||
cd /opt/guardia/messenger
|
||||
source /opt/guardia/.venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
deactivate
|
||||
```
|
||||
|
||||
### 5.3 환경 변수 설정
|
||||
|
||||
```bash
|
||||
cat > /opt/guardia/messenger/.env << 'EOF'
|
||||
SECRET_KEY=ITSM과_동일한_키_또는_별도_키
|
||||
ALGORITHM=HS256
|
||||
DB_URL=sqlite+aiosqlite:////opt/guardia/messenger/guardia_messenger.db
|
||||
ITSM_BASE_URL=http://localhost:8000
|
||||
PORT=8001
|
||||
EOF
|
||||
|
||||
chmod 600 /opt/guardia/messenger/.env
|
||||
chown guardia:guardia /opt/guardia/messenger/.env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 서비스 등록 (systemd)
|
||||
|
||||
### 6.1 ITSM 서비스 파일 생성
|
||||
|
||||
```bash
|
||||
sudo tee /etc/systemd/system/guardia-itsm.service > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=GUARDiA ITSM Service
|
||||
Documentation=https://내부문서URL
|
||||
After=network.target network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=guardia
|
||||
Group=guardia
|
||||
WorkingDirectory=/opt/guardia/itsm
|
||||
EnvironmentFile=/opt/guardia/itsm/.env
|
||||
ExecStart=/opt/guardia/.venv/bin/uvicorn main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
--workers 2 \
|
||||
--log-level info
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StartLimitInterval=60
|
||||
StartLimitBurst=3
|
||||
|
||||
# 보안 강화
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/opt/guardia/itsm/uploads /var/log/guardia
|
||||
|
||||
StandardOutput=append:/var/log/guardia/itsm.log
|
||||
StandardError=append:/var/log/guardia/itsm.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
```
|
||||
|
||||
### 6.2 Messenger 서비스 파일 생성
|
||||
|
||||
```bash
|
||||
sudo tee /etc/systemd/system/guardia-messenger.service > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=GUARDiA Messenger Service
|
||||
After=network.target guardia-itsm.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=guardia
|
||||
Group=guardia
|
||||
WorkingDirectory=/opt/guardia/messenger
|
||||
EnvironmentFile=/opt/guardia/messenger/.env
|
||||
ExecStart=/opt/guardia/.venv/bin/uvicorn main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port 8001 \
|
||||
--workers 1 \
|
||||
--log-level info
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
StandardOutput=append:/var/log/guardia/messenger.log
|
||||
StandardError=append:/var/log/guardia/messenger.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
```
|
||||
|
||||
### 6.3 서비스 등록 및 시작
|
||||
|
||||
```bash
|
||||
# systemd 재로드
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 서비스 활성화 (부팅 시 자동 시작)
|
||||
sudo systemctl enable guardia-itsm
|
||||
sudo systemctl enable guardia-messenger
|
||||
|
||||
# 서비스 시작
|
||||
sudo systemctl start guardia-itsm
|
||||
sudo systemctl start guardia-messenger
|
||||
|
||||
# 상태 확인
|
||||
sudo systemctl status guardia-itsm
|
||||
sudo systemctl status guardia-messenger
|
||||
|
||||
# 기대 출력:
|
||||
# ● guardia-itsm.service - GUARDiA ITSM Service
|
||||
# Active: active (running) since ...
|
||||
```
|
||||
|
||||
### 6.4 로그 확인
|
||||
|
||||
```bash
|
||||
# 실시간 로그 확인
|
||||
sudo journalctl -u guardia-itsm -f
|
||||
|
||||
# 최근 50줄
|
||||
sudo journalctl -u guardia-itsm -n 50
|
||||
|
||||
# 파일 직접 확인
|
||||
tail -f /var/log/guardia/itsm.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. SSL 점검 스크립트 배포
|
||||
|
||||
GUARDiA ITSM이 관리하는 모든 서버에 SSL 점검 스크립트를 배포합니다.
|
||||
|
||||
```bash
|
||||
# ITSM 서버에서 관리 대상 서버로 스크립트 복사
|
||||
# (ITSM 서버에서 실행)
|
||||
|
||||
TARGET_SERVERS=("192.168.1.10" "192.168.1.11" "192.168.1.12")
|
||||
|
||||
for SERVER in "${TARGET_SERVERS[@]}"; do
|
||||
echo "배포 중: $SERVER"
|
||||
ssh guardia@${SERVER} "mkdir -p /opt/guardia/scripts/ssl"
|
||||
scp /opt/guardia/itsm/scripts/sm/ssl/ssl_expiry_check.sh \
|
||||
guardia@${SERVER}:/opt/guardia/scripts/ssl/
|
||||
ssh guardia@${SERVER} "chmod +x /opt/guardia/scripts/ssl/ssl_expiry_check.sh"
|
||||
|
||||
# 동작 테스트
|
||||
ssh guardia@${SERVER} "bash /opt/guardia/scripts/ssl/ssl_expiry_check.sh google.com"
|
||||
echo "배포 완료: $SERVER"
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 방화벽 설정
|
||||
|
||||
### 8.1 UFW (Ubuntu)
|
||||
|
||||
```bash
|
||||
# UFW 활성화 (이미 활성화된 경우 skip)
|
||||
sudo ufw enable
|
||||
|
||||
# GUARDiA 포트 허용
|
||||
sudo ufw allow 8000/tcp comment "GUARDiA ITSM"
|
||||
sudo ufw allow 8001/tcp comment "GUARDiA Messenger"
|
||||
|
||||
# SSH 허용 (원격 접속용, 이미 설정된 경우 skip)
|
||||
sudo ufw allow 22/tcp
|
||||
|
||||
# 상태 확인
|
||||
sudo ufw status verbose
|
||||
```
|
||||
|
||||
### 8.2 firewalld (CentOS/RHEL)
|
||||
|
||||
```bash
|
||||
# GUARDiA 포트 허용
|
||||
sudo firewall-cmd --permanent --add-port=8000/tcp
|
||||
sudo firewall-cmd --permanent --add-port=8001/tcp
|
||||
sudo firewall-cmd --reload
|
||||
|
||||
# 확인
|
||||
sudo firewall-cmd --list-ports
|
||||
```
|
||||
|
||||
### 8.3 특정 IP만 허용 (보안 강화)
|
||||
|
||||
```bash
|
||||
# 내부 네트워크(192.168.1.0/24)에서만 접근 허용
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 8000
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 8001
|
||||
sudo ufw deny 8000
|
||||
sudo ufw deny 8001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Nginx 리버스 프록시 설정 (선택)
|
||||
|
||||
HTTPS를 적용하거나 표준 포트(80/443)를 사용하려면 Nginx를 앞단에 배치합니다.
|
||||
|
||||
### 9.1 Nginx 설치
|
||||
|
||||
```bash
|
||||
# Ubuntu
|
||||
sudo apt install -y nginx
|
||||
|
||||
# CentOS/RHEL
|
||||
sudo dnf install -y nginx
|
||||
```
|
||||
|
||||
### 9.2 Nginx 설정
|
||||
|
||||
```bash
|
||||
sudo tee /etc/nginx/conf.d/guardia.conf > /dev/null << 'EOF'
|
||||
# HTTP → HTTPS 리다이렉트
|
||||
server {
|
||||
listen 80;
|
||||
server_name itsm.example.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# ITSM HTTPS
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name itsm.example.com;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/itsm.example.com.crt;
|
||||
ssl_certificate_key /etc/ssl/private/itsm.example.com.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# 업로드 용량 제한
|
||||
client_max_body_size 20M;
|
||||
|
||||
# ITSM API/UI
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300;
|
||||
proxy_connect_timeout 10;
|
||||
}
|
||||
}
|
||||
|
||||
# Messenger HTTPS
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name messenger.example.com;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/messenger.example.com.crt;
|
||||
ssl_certificate_key /etc/ssl/private/messenger.example.com.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade"; # WebSocket 지원
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 설정 검증
|
||||
sudo nginx -t
|
||||
# → configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
|
||||
# Nginx 재시작
|
||||
sudo systemctl restart nginx
|
||||
sudo systemctl enable nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 설치 검증
|
||||
|
||||
### 10.1 서비스 상태 확인
|
||||
|
||||
```bash
|
||||
# 서비스 실행 상태
|
||||
sudo systemctl is-active guardia-itsm guardia-messenger
|
||||
# → active
|
||||
# → active
|
||||
|
||||
# 포트 리스닝 확인
|
||||
ss -tlnp | grep -E ':8000|:8001'
|
||||
# → 8000, 8001 포트 출력 확인
|
||||
```
|
||||
|
||||
### 10.2 API 기능 테스트
|
||||
|
||||
```bash
|
||||
# 1. 홈페이지 응답 확인
|
||||
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/
|
||||
# → 200
|
||||
|
||||
# 2. 로그인 테스트
|
||||
curl -s -X POST http://localhost:8000/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin1234!"}' | python3 -m json.tool
|
||||
# → {"access_token": "eyJ...", "token_type": "bearer"} 출력 확인
|
||||
|
||||
# 3. 토큰으로 API 호출
|
||||
TOKEN=$(curl -s -X POST http://localhost:8000/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin1234!"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
||||
|
||||
curl -s http://localhost:8000/dashboard/summary \
|
||||
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
|
||||
# → 대시보드 통계 JSON 출력 확인
|
||||
|
||||
# 4. Messenger 확인
|
||||
curl -s http://localhost:8001/
|
||||
# → 200 응답 확인
|
||||
```
|
||||
|
||||
### 10.3 스케줄러 동작 확인
|
||||
|
||||
```bash
|
||||
# 로그에서 스케줄러 시작 메시지 확인
|
||||
grep -i "scheduler" /var/log/guardia/itsm.log | tail -5
|
||||
# → "scheduler started" 또는 "APScheduler started" 메시지 확인
|
||||
```
|
||||
|
||||
### 10.4 설치 검증 스크립트
|
||||
|
||||
```bash
|
||||
cat > /tmp/verify_install.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo "=== GUARDiA 설치 검증 ==="
|
||||
|
||||
check() {
|
||||
if $1; then echo " [ok] $2"; else echo " [FAIL] $2"; fi
|
||||
}
|
||||
|
||||
# 서비스 상태
|
||||
systemctl is-active --quiet guardia-itsm
|
||||
check "[ $? -eq 0 ]" "ITSM 서비스 실행 중"
|
||||
|
||||
systemctl is-active --quiet guardia-messenger
|
||||
check "[ $? -eq 0 ]" "Messenger 서비스 실행 중"
|
||||
|
||||
# 포트 확인
|
||||
ss -tlnp | grep -q ':8000'
|
||||
check "[ $? -eq 0 ]" "ITSM 포트 8000 리스닝"
|
||||
|
||||
ss -tlnp | grep -q ':8001'
|
||||
check "[ $? -eq 0 ]" "Messenger 포트 8001 리스닝"
|
||||
|
||||
# API 응답
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/)
|
||||
check "[ '$CODE' = '200' ]" "ITSM API 응답 (200)"
|
||||
|
||||
# DB 파일
|
||||
[ -f /opt/guardia/itsm/guardia_itsm.db ]
|
||||
check "[ $? -eq 0 ]" "ITSM DB 파일 존재"
|
||||
|
||||
echo "=== 검증 완료 ==="
|
||||
EOF
|
||||
|
||||
chmod +x /tmp/verify_install.sh
|
||||
bash /tmp/verify_install.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 보안 강화 설정
|
||||
|
||||
### 11.1 초기 관리자 비밀번호 변경
|
||||
|
||||
```bash
|
||||
# 로그인 후 즉시 비밀번호 변경
|
||||
TOKEN=$(curl -s -X POST http://localhost:8000/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin1234!"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
||||
|
||||
curl -X POST http://localhost:8000/auth/change-password \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"current_password":"admin1234!","new_password":"새로운강력한비밀번호!@#"}'
|
||||
```
|
||||
|
||||
### 11.2 API 문서 비활성화 (운영)
|
||||
|
||||
```python
|
||||
# main.py 수정
|
||||
app = FastAPI(
|
||||
title="GUARDiA ITSM",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan,
|
||||
docs_url=None, # /docs 비활성화
|
||||
redoc_url=None, # /redoc 비활성화
|
||||
)
|
||||
```
|
||||
|
||||
### 11.3 DB 파일 권한 설정
|
||||
|
||||
```bash
|
||||
# DB 파일은 guardia 사용자만 접근 가능
|
||||
chmod 600 /opt/guardia/itsm/guardia_itsm.db
|
||||
chmod 600 /opt/guardia/messenger/guardia_messenger.db
|
||||
```
|
||||
|
||||
### 11.4 자동 백업 설정
|
||||
|
||||
```bash
|
||||
# 백업 스크립트 생성
|
||||
cat > /opt/guardia/scripts/backup.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
BACKUP_DIR=/backup/guardia
|
||||
DATE=$(date +%Y%m%d_%H%M)
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
cp /opt/guardia/itsm/guardia_itsm.db $BACKUP_DIR/itsm_$DATE.db
|
||||
cp /opt/guardia/messenger/guardia_messenger.db $BACKUP_DIR/messenger_$DATE.db
|
||||
|
||||
# 30일 이전 백업 삭제
|
||||
find $BACKUP_DIR -name "*.db" -mtime +30 -delete
|
||||
|
||||
echo "[$DATE] 백업 완료" >> /var/log/guardia/backup.log
|
||||
EOF
|
||||
|
||||
chmod +x /opt/guardia/scripts/backup.sh
|
||||
|
||||
# cron 등록 (매일 오전 3시)
|
||||
echo "0 3 * * * guardia /opt/guardia/scripts/backup.sh" | sudo tee -a /etc/cron.d/guardia
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 문제 해결
|
||||
|
||||
### 12.1 서비스 시작 실패
|
||||
|
||||
```bash
|
||||
# 상세 에러 확인
|
||||
sudo journalctl -u guardia-itsm -n 50 --no-pager
|
||||
|
||||
# 가상환경 경로 확인
|
||||
ls -la /opt/guardia/.venv/bin/uvicorn
|
||||
# → 파일이 없으면: source /opt/guardia/.venv/bin/activate && pip install uvicorn
|
||||
|
||||
# .env 파일 권한 확인
|
||||
ls -la /opt/guardia/itsm/.env
|
||||
# → -rw------- 1 guardia guardia 이어야 함
|
||||
```
|
||||
|
||||
### 12.2 포트 충돌
|
||||
|
||||
```bash
|
||||
# 포트를 사용 중인 프로세스 확인
|
||||
sudo lsof -i :8000
|
||||
sudo lsof -i :8001
|
||||
|
||||
# 해당 프로세스 종료 후 재시작
|
||||
sudo kill -9 <PID>
|
||||
sudo systemctl start guardia-itsm
|
||||
```
|
||||
|
||||
### 12.3 Python 패키지 설치 오류
|
||||
|
||||
```bash
|
||||
# 개발 헤더 설치 (컴파일이 필요한 패키지)
|
||||
# Ubuntu
|
||||
sudo apt install -y python3.11-dev libssl-dev libffi-dev build-essential
|
||||
|
||||
# CentOS/RHEL
|
||||
sudo dnf install -y python3.11-devel openssl-devel libffi-devel gcc
|
||||
|
||||
# 재설치
|
||||
source /opt/guardia/.venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r /opt/guardia/itsm/requirements.txt
|
||||
```
|
||||
|
||||
### 12.4 DB 권한 오류
|
||||
|
||||
```bash
|
||||
# DB 파일 소유자 및 권한 확인
|
||||
ls -la /opt/guardia/itsm/guardia_itsm.db
|
||||
|
||||
# 수정
|
||||
sudo chown guardia:guardia /opt/guardia/itsm/guardia_itsm.db
|
||||
sudo chmod 660 /opt/guardia/itsm/guardia_itsm.db
|
||||
```
|
||||
|
||||
### 12.5 첫 로그인 실패 (seed 데이터 미생성)
|
||||
|
||||
```bash
|
||||
# 수동으로 DB 초기화
|
||||
sudo -u guardia bash -c "
|
||||
source /opt/guardia/.venv/bin/activate
|
||||
cd /opt/guardia/itsm
|
||||
python -c \"
|
||||
import asyncio
|
||||
from database import init_db, SessionLocal
|
||||
from core.seed import seed_all
|
||||
|
||||
async def main():
|
||||
await init_db()
|
||||
async with SessionLocal() as db:
|
||||
await seed_all(db)
|
||||
print('DB 초기화 완료')
|
||||
|
||||
asyncio.run(main())
|
||||
\"
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*설치 중 문제가 발생하면 `/var/log/guardia/itsm.log`를 확인하거나 개발팀에 문의하세요.*
|
||||
873
06_설치가이드_윈도우서버.md
Normal file
873
06_설치가이드_윈도우서버.md
Normal file
@ -0,0 +1,873 @@
|
||||
# GUARDiA ITSM + Messenger — 설치 가이드 (Windows Server)
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**지원 OS**: Windows Server 2019, Windows Server 2022
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [설치 전 요구사항 확인](#1-설치-전-요구사항-확인)
|
||||
2. [사전 소프트웨어 설치](#2-사전-소프트웨어-설치)
|
||||
3. [GUARDiA ITSM 설치](#3-guardia-itsm-설치)
|
||||
4. [GUARDiA Messenger 설치](#4-guardia-messenger-설치)
|
||||
5. [Windows 서비스 등록 (NSSM)](#5-windows-서비스-등록-nssm)
|
||||
6. [방화벽 설정](#6-방화벽-설정)
|
||||
7. [IIS 리버스 프록시 설정 (선택)](#7-iis-리버스-프록시-설정-선택)
|
||||
8. [설치 검증](#8-설치-검증)
|
||||
9. [보안 강화 설정](#9-보안-강화-설정)
|
||||
10. [자동 백업 설정](#10-자동-백업-설정)
|
||||
11. [문제 해결](#11-문제-해결)
|
||||
|
||||
---
|
||||
|
||||
## 1. 설치 전 요구사항 확인
|
||||
|
||||
### 1.1 하드웨어 최소 요구사항
|
||||
|
||||
| 항목 | 최소 | 권장 |
|
||||
|------|------|------|
|
||||
| CPU | 2코어 | 4코어 이상 |
|
||||
| RAM | 4GB | 8GB 이상 |
|
||||
| 디스크 | 30GB | 60GB 이상 |
|
||||
| 네트워크 | 100Mbps | 1Gbps |
|
||||
|
||||
### 1.2 소프트웨어 요구사항
|
||||
|
||||
```
|
||||
OS: Windows Server 2022 (권장) 또는 Windows Server 2019
|
||||
Python: 3.11 이상
|
||||
OpenSSL: Win64 OpenSSL v3.x (SSL 점검 기능 사용 시)
|
||||
NSSM: 서비스 등록 도구 (선택, Windows Service로 등록 시 필요)
|
||||
```
|
||||
|
||||
### 1.3 설치 전 확인 (PowerShell)
|
||||
|
||||
PowerShell을 **관리자 권한**으로 실행하여 확인합니다.
|
||||
|
||||
```powershell
|
||||
# OS 버전 확인
|
||||
[System.Environment]::OSVersion.Version
|
||||
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion
|
||||
|
||||
# PowerShell 버전 확인
|
||||
$PSVersionTable.PSVersion
|
||||
# → Major 5 이상
|
||||
|
||||
# 디스크 여유 공간 확인
|
||||
Get-PSDrive C | Select-Object Used, Free
|
||||
# → Free 30GB 이상 확인
|
||||
|
||||
# 포트 사용 여부 확인
|
||||
netstat -an | findstr ":8000"
|
||||
netstat -an | findstr ":8001"
|
||||
# → 아무것도 출력되지 않아야 함
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 사전 소프트웨어 설치
|
||||
|
||||
### 2.1 Python 3.11 설치
|
||||
|
||||
1. 웹브라우저에서 `python.org/downloads` 접속
|
||||
2. **Python 3.11.x** (Windows installer 64-bit) 다운로드
|
||||
3. 설치 시 반드시 체크: **"Add Python to PATH"**
|
||||
|
||||
```powershell
|
||||
# 설치 후 확인
|
||||
python --version
|
||||
# → Python 3.11.x
|
||||
|
||||
python -m pip --version
|
||||
# → pip 24.x from ...
|
||||
```
|
||||
|
||||
> 설치 후 PowerShell을 새로 열어야 PATH가 반영됩니다.
|
||||
|
||||
### 2.2 Git 설치 (선택)
|
||||
|
||||
내부 Git 서버에서 코드를 받는 경우 필요합니다.
|
||||
|
||||
1. `git-scm.com/download/win` 에서 설치 파일 다운로드
|
||||
2. 기본 설정으로 설치
|
||||
3. 확인: `git --version`
|
||||
|
||||
### 2.3 OpenSSL 설치 (SSL 점검 기능 사용 시)
|
||||
|
||||
```
|
||||
1. 웹에서 "Win64 OpenSSL" 검색 → slproweb.com 또는 공식 배포처에서 다운로드
|
||||
(Win64 OpenSSL v3.x.x Light 권장)
|
||||
2. 기본 경로 C:\Program Files\OpenSSL-Win64 에 설치
|
||||
3. 시스템 환경변수 PATH에 추가: C:\Program Files\OpenSSL-Win64\bin
|
||||
```
|
||||
|
||||
```powershell
|
||||
# 설치 확인
|
||||
openssl version
|
||||
# → OpenSSL 3.x.x
|
||||
```
|
||||
|
||||
### 2.4 NSSM 설치 (서비스 등록용)
|
||||
|
||||
NSSM은 Python 앱을 Windows Service로 등록하는 도구입니다.
|
||||
|
||||
```
|
||||
1. nssm.cc/download 에서 최신 버전 다운로드
|
||||
2. 압축 해제 후 nssm.exe (win64 폴더)를 C:\Windows\System32\ 에 복사
|
||||
```
|
||||
|
||||
```powershell
|
||||
# 확인
|
||||
nssm version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. GUARDiA ITSM 설치
|
||||
|
||||
### 3.1 설치 디렉토리 생성
|
||||
|
||||
PowerShell을 **관리자 권한**으로 실행합니다.
|
||||
|
||||
```powershell
|
||||
# 디렉토리 생성
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\itsm"
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\messenger"
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\logs"
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\backup"
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\scripts\ssl"
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\itsm\uploads\sr_files"
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\itsm\uploads\workspaces"
|
||||
|
||||
Write-Host "디렉토리 생성 완료"
|
||||
```
|
||||
|
||||
### 3.2 소스 코드 배포
|
||||
|
||||
```powershell
|
||||
# 방법 1: 파일 복사 (zip 파일로 배포 시)
|
||||
Expand-Archive -Path "C:\temp\guardia_itsm_v1.0.zip" -DestinationPath "C:\GUARDiA\itsm" -Force
|
||||
|
||||
# 방법 2: Git clone (내부 Git 서버)
|
||||
cd C:\GUARDiA
|
||||
git clone http://내부git서버/guardia/itsm.git itsm
|
||||
|
||||
# 배포 확인
|
||||
Get-ChildItem C:\GUARDiA\itsm\
|
||||
# → main.py database.py models.py schemas.py 등 표시
|
||||
```
|
||||
|
||||
### 3.3 가상환경 생성 및 의존성 설치
|
||||
|
||||
```powershell
|
||||
# C:\GUARDiA\itsm 으로 이동
|
||||
cd C:\GUARDiA\itsm
|
||||
|
||||
# 가상환경 생성
|
||||
python -m venv C:\GUARDiA\.venv
|
||||
|
||||
# 가상환경 활성화
|
||||
C:\GUARDiA\.venv\Scripts\Activate.ps1
|
||||
|
||||
# 활성화 오류 시 실행 정책 변경
|
||||
# Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
# pip 업그레이드
|
||||
python -m pip install --upgrade pip
|
||||
|
||||
# 의존성 설치
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 설치 확인
|
||||
pip show fastapi uvicorn sqlalchemy apscheduler
|
||||
# → Name, Version 정보 출력 확인
|
||||
|
||||
# 가상환경 비활성화
|
||||
deactivate
|
||||
```
|
||||
|
||||
**설치 중 오류 발생 시**:
|
||||
```powershell
|
||||
# Visual C++ 빌드 도구가 필요한 패키지 오류 시
|
||||
# Microsoft C++ Build Tools 설치 필요
|
||||
# visualstudio.microsoft.com/visual-cpp-build-tools/ 에서 다운로드 후 설치
|
||||
```
|
||||
|
||||
### 3.4 환경 변수 파일 (.env) 생성
|
||||
|
||||
메모장 또는 VS Code로 `C:\GUARDiA\itsm\.env` 파일을 생성합니다.
|
||||
|
||||
```powershell
|
||||
# PowerShell로 .env 파일 생성
|
||||
$envContent = @"
|
||||
SECRET_KEY=여기에_64자이상의_랜덤문자열_반드시_변경하세요
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=480
|
||||
DB_URL=sqlite+aiosqlite:///C:/GUARDiA/itsm/guardia_itsm.db
|
||||
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
# 라이선스 마스터 키 (64자리 hex = 32바이트)
|
||||
# 생성: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# 분실 시 기발급 라이선스 전부 무효화 — 안전하게 별도 보관 필수
|
||||
GUARDIA_LICENSE_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
MESSENGER_BASE_URL=http://localhost:8001
|
||||
MESSENGER_BOT_TOKEN=변경하세요
|
||||
SSH_TIMEOUT=30
|
||||
UPLOAD_ROOT=C:/GUARDiA/itsm/uploads
|
||||
MAX_FILE_SIZE_MB=10
|
||||
"@
|
||||
$envContent | Out-File -FilePath "C:\GUARDiA\itsm\.env" -Encoding utf8
|
||||
Write-Host ".env 파일 생성 완료"
|
||||
```
|
||||
|
||||
> **중요**: `SECRET_KEY`, `ENCRYPTION_KEY`, `GUARDIA_LICENSE_KEY`를 반드시 강력한 랜덤 값으로 변경하세요!
|
||||
|
||||
```powershell
|
||||
# GUARDIA_LICENSE_KEY 생성
|
||||
python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# → 출력된 64자리 hex를 GUARDIA_LICENSE_KEY에 복사
|
||||
```
|
||||
|
||||
```powershell
|
||||
# 랜덤 키 생성
|
||||
python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# → 출력된 값을 SECRET_KEY에 복사
|
||||
|
||||
python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# → 출력된 값을 ENCRYPTION_KEY에 복사
|
||||
```
|
||||
|
||||
### 3.5 초기 실행 및 DB 초기화 테스트
|
||||
|
||||
```powershell
|
||||
# 가상환경 활성화
|
||||
C:\GUARDiA\.venv\Scripts\Activate.ps1
|
||||
|
||||
# 디렉토리 이동
|
||||
cd C:\GUARDiA\itsm
|
||||
|
||||
# 임시 실행 (초기 DB 생성 확인)
|
||||
$process = Start-Process -FilePath "C:\GUARDiA\.venv\Scripts\uvicorn.exe" `
|
||||
-ArgumentList "main:app --host 127.0.0.1 --port 8000" `
|
||||
-PassThru -NoNewWindow
|
||||
|
||||
Start-Sleep -Seconds 8
|
||||
|
||||
# 정상 동작 확인
|
||||
$response = Invoke-WebRequest -Uri "http://127.0.0.1:8000/" -ErrorAction SilentlyContinue
|
||||
Write-Host "HTTP 응답 코드: $($response.StatusCode)"
|
||||
# → 200이면 정상
|
||||
|
||||
# 로그인 테스트
|
||||
$body = '{"username":"admin","password":"admin1234!"}'
|
||||
$result = Invoke-RestMethod -Uri "http://127.0.0.1:8000/auth/login" `
|
||||
-Method Post -Body $body -ContentType "application/json"
|
||||
Write-Host "토큰: $($result.access_token.Substring(0,20))..."
|
||||
# → 토큰 앞부분 출력 확인
|
||||
|
||||
# 프로세스 종료
|
||||
Stop-Process -Id $process.Id
|
||||
|
||||
deactivate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. GUARDiA Messenger 설치
|
||||
|
||||
### 4.1 소스 코드 배포
|
||||
|
||||
```powershell
|
||||
# 파일 복사
|
||||
Expand-Archive -Path "C:\temp\guardia_messenger_v1.0.zip" `
|
||||
-DestinationPath "C:\GUARDiA\messenger" -Force
|
||||
|
||||
# 또는 git clone
|
||||
cd C:\GUARDiA
|
||||
git clone http://내부git서버/guardia/messenger.git messenger
|
||||
```
|
||||
|
||||
### 4.2 의존성 설치
|
||||
|
||||
```powershell
|
||||
C:\GUARDiA\.venv\Scripts\Activate.ps1
|
||||
cd C:\GUARDiA\messenger
|
||||
pip install -r requirements.txt
|
||||
deactivate
|
||||
```
|
||||
|
||||
### 4.3 환경 변수 설정
|
||||
|
||||
```powershell
|
||||
$envContent = @"
|
||||
SECRET_KEY=ITSM과_동일한_키_또는_별도_키
|
||||
ALGORITHM=HS256
|
||||
DB_URL=sqlite+aiosqlite:///C:/GUARDiA/messenger/guardia_messenger.db
|
||||
ITSM_BASE_URL=http://localhost:8000
|
||||
PORT=8001
|
||||
"@
|
||||
$envContent | Out-File -FilePath "C:\GUARDiA\messenger\.env" -Encoding utf8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Windows 서비스 등록 (NSSM)
|
||||
|
||||
NSSM을 사용하여 GUARDiA를 Windows Service로 등록합니다.
|
||||
|
||||
### 5.1 ITSM 서비스 등록
|
||||
|
||||
PowerShell을 **관리자 권한**으로 실행합니다.
|
||||
|
||||
```powershell
|
||||
# ITSM 서비스 등록
|
||||
nssm install GUARDiA-ITSM C:\GUARDiA\.venv\Scripts\uvicorn.exe
|
||||
|
||||
# 서비스 파라미터 설정
|
||||
nssm set GUARDiA-ITSM Application C:\GUARDiA\.venv\Scripts\uvicorn.exe
|
||||
nssm set GUARDiA-ITSM AppParameters "main:app --host 0.0.0.0 --port 8000 --workers 2"
|
||||
nssm set GUARDiA-ITSM AppDirectory "C:\GUARDiA\itsm"
|
||||
|
||||
# 로그 설정
|
||||
nssm set GUARDiA-ITSM AppStdout "C:\GUARDiA\logs\itsm.log"
|
||||
nssm set GUARDiA-ITSM AppStderr "C:\GUARDiA\logs\itsm_error.log"
|
||||
nssm set GUARDiA-ITSM AppRotateFiles 1
|
||||
nssm set GUARDiA-ITSM AppRotateBytes 10485760
|
||||
|
||||
# 서비스 설명
|
||||
nssm set GUARDiA-ITSM Description "GUARDiA ITSM IT Service Management"
|
||||
nssm set GUARDiA-ITSM DisplayName "GUARDiA ITSM"
|
||||
|
||||
# 환경 변수 설정 (경로 구분은 \t 탭 문자 사용)
|
||||
nssm set GUARDiA-ITSM AppEnvironmentExtra "PYTHONDONTWRITEBYTECODE=1"
|
||||
|
||||
# 자동 시작 설정
|
||||
nssm set GUARDiA-ITSM Start SERVICE_AUTO_START
|
||||
```
|
||||
|
||||
### 5.2 Messenger 서비스 등록
|
||||
|
||||
```powershell
|
||||
nssm install GUARDiA-Messenger C:\GUARDiA\.venv\Scripts\uvicorn.exe
|
||||
|
||||
nssm set GUARDiA-Messenger Application C:\GUARDiA\.venv\Scripts\uvicorn.exe
|
||||
nssm set GUARDiA-Messenger AppParameters "main:app --host 0.0.0.0 --port 8001 --workers 1"
|
||||
nssm set GUARDiA-Messenger AppDirectory "C:\GUARDiA\messenger"
|
||||
nssm set GUARDiA-Messenger AppStdout "C:\GUARDiA\logs\messenger.log"
|
||||
nssm set GUARDiA-Messenger AppStderr "C:\GUARDiA\logs\messenger_error.log"
|
||||
nssm set GUARDiA-Messenger AppRotateFiles 1
|
||||
nssm set GUARDiA-Messenger Description "GUARDiA Messenger Service"
|
||||
nssm set GUARDiA-Messenger Start SERVICE_AUTO_START
|
||||
```
|
||||
|
||||
### 5.3 서비스 시작
|
||||
|
||||
```powershell
|
||||
# 서비스 시작
|
||||
Start-Service GUARDiA-ITSM
|
||||
Start-Service GUARDiA-Messenger
|
||||
|
||||
# 상태 확인
|
||||
Get-Service GUARDiA-ITSM, GUARDiA-Messenger
|
||||
|
||||
# 기대 출력:
|
||||
# Status Name DisplayName
|
||||
# ------ ---- -----------
|
||||
# Running GUARDiA-ITSM GUARDiA ITSM
|
||||
# Running GUARDiA-Messenger GUARDiA Messenger
|
||||
```
|
||||
|
||||
### 5.4 서비스 관리 명령어
|
||||
|
||||
```powershell
|
||||
# 시작
|
||||
Start-Service GUARDiA-ITSM
|
||||
|
||||
# 중지
|
||||
Stop-Service GUARDiA-ITSM
|
||||
|
||||
# 재시작
|
||||
Restart-Service GUARDiA-ITSM
|
||||
|
||||
# 상태 확인
|
||||
Get-Service GUARDiA-ITSM | Select-Object Name, Status, StartType
|
||||
|
||||
# 로그 확인 (최근 50줄)
|
||||
Get-Content "C:\GUARDiA\logs\itsm.log" -Tail 50
|
||||
|
||||
# 실시간 로그 확인
|
||||
Get-Content "C:\GUARDiA\logs\itsm.log" -Wait -Tail 20
|
||||
```
|
||||
|
||||
### 5.5 서비스 제거 (재설치 시)
|
||||
|
||||
```powershell
|
||||
# 서비스 제거
|
||||
nssm remove GUARDiA-ITSM confirm
|
||||
nssm remove GUARDiA-Messenger confirm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 방화벽 설정
|
||||
|
||||
### 6.1 Windows Defender 방화벽 규칙 추가
|
||||
|
||||
```powershell
|
||||
# ITSM 포트 허용
|
||||
New-NetFirewallRule `
|
||||
-DisplayName "GUARDiA ITSM" `
|
||||
-Direction Inbound `
|
||||
-Protocol TCP `
|
||||
-LocalPort 8000 `
|
||||
-Action Allow `
|
||||
-Profile Domain, Private
|
||||
|
||||
# Messenger 포트 허용
|
||||
New-NetFirewallRule `
|
||||
-DisplayName "GUARDiA Messenger" `
|
||||
-Direction Inbound `
|
||||
-Protocol TCP `
|
||||
-LocalPort 8001 `
|
||||
-Action Allow `
|
||||
-Profile Domain, Private
|
||||
|
||||
# 규칙 확인
|
||||
Get-NetFirewallRule -DisplayName "GUARDiA*" | Select-Object DisplayName, Enabled, Action
|
||||
```
|
||||
|
||||
### 6.2 특정 IP 대역만 허용 (보안 강화)
|
||||
|
||||
```powershell
|
||||
# 내부 네트워크 192.168.1.0/24 에서만 접근 허용
|
||||
New-NetFirewallRule `
|
||||
-DisplayName "GUARDiA ITSM (내부망)" `
|
||||
-Direction Inbound `
|
||||
-Protocol TCP `
|
||||
-LocalPort 8000 `
|
||||
-RemoteAddress "192.168.1.0/24" `
|
||||
-Action Allow
|
||||
|
||||
# 기존 전체 허용 규칙 비활성화
|
||||
Disable-NetFirewallRule -DisplayName "GUARDiA ITSM"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. IIS 리버스 프록시 설정 (선택)
|
||||
|
||||
HTTPS를 적용하거나 80/443 포트를 사용하려면 IIS를 앞단에 배치합니다.
|
||||
|
||||
### 7.1 IIS 및 ARR 설치
|
||||
|
||||
```powershell
|
||||
# IIS 설치
|
||||
Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole -All
|
||||
Install-WindowsFeature -Name Web-Server -IncludeManagementTools
|
||||
|
||||
# ARR (Application Request Routing) 설치
|
||||
# Web Platform Installer에서 "Application Request Routing 3.0" 설치
|
||||
# 또는: https://www.iis.net/downloads/microsoft/application-request-routing
|
||||
```
|
||||
|
||||
### 7.2 IIS 역방향 프록시 설정
|
||||
|
||||
IIS 관리자를 열어 다음을 설정합니다:
|
||||
|
||||
```
|
||||
1. IIS 관리자 → 서버 노드 → Application Request Routing Cache
|
||||
→ "Enable proxy" 체크
|
||||
|
||||
2. 웹사이트 → "URL 재작성" → 규칙 추가
|
||||
→ 역방향 프록시 → 서버: localhost:8000
|
||||
|
||||
3. 바인딩 추가:
|
||||
- 포트 80 (HTTP)
|
||||
- 포트 443 (HTTPS, SSL 인증서 선택)
|
||||
```
|
||||
|
||||
**web.config 예시**:
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="GUARDiA ITSM" stopProcessing="true">
|
||||
<match url="(.*)" />
|
||||
<action type="Rewrite" url="http://localhost:8000/{R:1}" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 설치 검증
|
||||
|
||||
### 8.1 서비스 상태 확인
|
||||
|
||||
```powershell
|
||||
# 서비스 상태
|
||||
Get-Service GUARDiA-ITSM, GUARDiA-Messenger
|
||||
|
||||
# 포트 리스닝 확인
|
||||
netstat -an | findstr ":8000"
|
||||
netstat -an | findstr ":8001"
|
||||
# → TCP 0.0.0.0:8000 ... LISTENING
|
||||
```
|
||||
|
||||
### 8.2 API 기능 테스트
|
||||
|
||||
```powershell
|
||||
# 1. 홈페이지 응답 확인
|
||||
$response = Invoke-WebRequest -Uri "http://localhost:8000/"
|
||||
Write-Host "HTTP 상태: $($response.StatusCode)"
|
||||
# → 200
|
||||
|
||||
# 2. 로그인 테스트
|
||||
$loginBody = '{"username":"admin","password":"admin1234!"}'
|
||||
$loginResult = Invoke-RestMethod -Uri "http://localhost:8000/auth/login" `
|
||||
-Method Post -Body $loginBody -ContentType "application/json"
|
||||
$token = $loginResult.access_token
|
||||
Write-Host "로그인 성공: 토큰 앞 20자 = $($token.Substring(0,20))..."
|
||||
|
||||
# 3. 대시보드 API 호출
|
||||
$headers = @{ Authorization = "Bearer $token" }
|
||||
$dashboard = Invoke-RestMethod -Uri "http://localhost:8000/dashboard/summary" `
|
||||
-Headers $headers
|
||||
Write-Host "대시보드 응답:"
|
||||
$dashboard | ConvertTo-Json
|
||||
|
||||
# 4. Messenger 확인
|
||||
$msgResponse = Invoke-WebRequest -Uri "http://localhost:8001/"
|
||||
Write-Host "Messenger HTTP 상태: $($msgResponse.StatusCode)"
|
||||
```
|
||||
|
||||
### 8.3 설치 검증 스크립트
|
||||
|
||||
```powershell
|
||||
# C:\GUARDiA\verify_install.ps1 생성
|
||||
$script = @'
|
||||
Write-Host "=== GUARDiA 설치 검증 ===" -ForegroundColor Cyan
|
||||
|
||||
function Check-Item($condition, $label) {
|
||||
if ($condition) {
|
||||
Write-Host " [ok] $label" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " [FAIL] $label" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# 서비스 상태
|
||||
$itsmSvc = Get-Service GUARDiA-ITSM -ErrorAction SilentlyContinue
|
||||
Check-Item ($itsmSvc -and $itsmSvc.Status -eq "Running") "ITSM 서비스 실행 중"
|
||||
|
||||
$msgSvc = Get-Service GUARDiA-Messenger -ErrorAction SilentlyContinue
|
||||
Check-Item ($msgSvc -and $msgSvc.Status -eq "Running") "Messenger 서비스 실행 중"
|
||||
|
||||
# 포트 확인
|
||||
$itsm8000 = netstat -an | findstr ":8000" | findstr "LISTENING"
|
||||
Check-Item ($itsm8000 -ne $null) "ITSM 포트 8000 리스닝"
|
||||
|
||||
$msg8001 = netstat -an | findstr ":8001" | findstr "LISTENING"
|
||||
Check-Item ($msg8001 -ne $null) "Messenger 포트 8001 리스닝"
|
||||
|
||||
# API 응답
|
||||
try {
|
||||
$r = Invoke-WebRequest "http://localhost:8000/" -TimeoutSec 5
|
||||
Check-Item ($r.StatusCode -eq 200) "ITSM API 응답 (200)"
|
||||
} catch {
|
||||
Check-Item $false "ITSM API 응답 (오류: $_)"
|
||||
}
|
||||
|
||||
# DB 파일
|
||||
Check-Item (Test-Path "C:\GUARDiA\itsm\guardia_itsm.db") "ITSM DB 파일 존재"
|
||||
|
||||
# 로그 파일
|
||||
Check-Item (Test-Path "C:\GUARDiA\logs\itsm.log") "ITSM 로그 파일 존재"
|
||||
|
||||
Write-Host "=== 검증 완료 ===" -ForegroundColor Cyan
|
||||
'@
|
||||
|
||||
$script | Out-File -FilePath "C:\GUARDiA\verify_install.ps1" -Encoding utf8
|
||||
|
||||
# 검증 실행
|
||||
PowerShell -ExecutionPolicy Bypass -File "C:\GUARDiA\verify_install.ps1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 보안 강화 설정
|
||||
|
||||
### 9.1 초기 관리자 비밀번호 변경
|
||||
|
||||
```powershell
|
||||
# 로그인
|
||||
$loginBody = '{"username":"admin","password":"admin1234!"}'
|
||||
$loginResult = Invoke-RestMethod -Uri "http://localhost:8000/auth/login" `
|
||||
-Method Post -Body $loginBody -ContentType "application/json"
|
||||
$token = $loginResult.access_token
|
||||
|
||||
# 비밀번호 변경
|
||||
$pwBody = @{
|
||||
current_password = "admin1234!"
|
||||
new_password = "새로운강력한비밀번호!@#456"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Invoke-RestMethod -Uri "http://localhost:8000/auth/change-password" `
|
||||
-Method Post -Body $pwBody -ContentType "application/json" `
|
||||
-Headers @{ Authorization = "Bearer $token" }
|
||||
Write-Host "비밀번호 변경 완료"
|
||||
```
|
||||
|
||||
### 9.2 .env 파일 접근 권한 제한
|
||||
|
||||
```powershell
|
||||
# .env 파일 소유자 확인
|
||||
Get-Acl "C:\GUARDiA\itsm\.env"
|
||||
|
||||
# 서비스 계정(또는 시스템)만 읽기 가능하도록 권한 설정
|
||||
$acl = Get-Acl "C:\GUARDiA\itsm\.env"
|
||||
$acl.SetAccessRuleProtection($true, $false) # 상속 차단
|
||||
|
||||
# 현재 사용자 전체 제어
|
||||
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
||||
$env:USERNAME, "FullControl", "Allow"
|
||||
)
|
||||
$acl.AddAccessRule($rule)
|
||||
Set-Acl "C:\GUARDiA\itsm\.env" $acl
|
||||
|
||||
Write-Host "파일 권한 설정 완료"
|
||||
```
|
||||
|
||||
### 9.3 이벤트 로그 설정
|
||||
|
||||
```powershell
|
||||
# 이벤트 소스 등록 (최초 1회)
|
||||
New-EventLog -LogName Application -Source "GUARDiA-ITSM" -ErrorAction SilentlyContinue
|
||||
|
||||
# 이벤트 로그 크기 설정
|
||||
Limit-EventLog -LogName Application -MaximumSize 50MB
|
||||
```
|
||||
|
||||
### 9.4 보안 정책 확인
|
||||
|
||||
```powershell
|
||||
# TLS 1.2 이상만 허용 (Windows Server 2019+는 기본 설정)
|
||||
# 레지스트리 확인
|
||||
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -ErrorAction SilentlyContinue
|
||||
# DisabledByDefault = 1 이면 비활성화
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 자동 백업 설정
|
||||
|
||||
### 10.1 백업 스크립트 생성
|
||||
|
||||
```powershell
|
||||
# C:\GUARDiA\scripts\backup.ps1 생성
|
||||
$backupScript = @'
|
||||
$date = Get-Date -Format "yyyyMMdd_HHmm"
|
||||
$backupDir = "C:\GUARDiA\backup"
|
||||
|
||||
# 백업 디렉토리 생성
|
||||
if (-not (Test-Path $backupDir)) {
|
||||
New-Item -ItemType Directory -Path $backupDir -Force
|
||||
}
|
||||
|
||||
# ITSM DB 백업
|
||||
$itsmDb = "C:\GUARDiA\itsm\guardia_itsm.db"
|
||||
if (Test-Path $itsmDb) {
|
||||
Copy-Item $itsmDb "$backupDir\itsm_$date.db"
|
||||
Write-Host "$date - ITSM DB 백업 완료"
|
||||
}
|
||||
|
||||
# Messenger DB 백업
|
||||
$msgDb = "C:\GUARDiA\messenger\guardia_messenger.db"
|
||||
if (Test-Path $msgDb) {
|
||||
Copy-Item $msgDb "$backupDir\messenger_$date.db"
|
||||
Write-Host "$date - Messenger DB 백업 완료"
|
||||
}
|
||||
|
||||
# 업로드 파일 백업 (선택)
|
||||
# Compress-Archive -Path "C:\GUARDiA\itsm\uploads" -DestinationPath "$backupDir\uploads_$date.zip"
|
||||
|
||||
# 30일 이전 백업 삭제
|
||||
Get-ChildItem $backupDir -Filter "*.db" |
|
||||
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
|
||||
Remove-Item -Force
|
||||
|
||||
Write-Host "$date - 백업 작업 완료"
|
||||
"@
|
||||
|
||||
New-Item -ItemType Directory -Force -Path "C:\GUARDiA\scripts"
|
||||
$backupScript | Out-File -FilePath "C:\GUARDiA\scripts\backup.ps1" -Encoding utf8
|
||||
```
|
||||
|
||||
### 10.2 작업 스케줄러 등록
|
||||
|
||||
```powershell
|
||||
# 매일 새벽 3시 자동 실행
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute "PowerShell.exe" `
|
||||
-Argument "-NonInteractive -ExecutionPolicy Bypass -File C:\GUARDiA\scripts\backup.ps1 >> C:\GUARDiA\logs\backup.log 2>&1"
|
||||
|
||||
$trigger = New-ScheduledTaskTrigger -Daily -At "03:00"
|
||||
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
|
||||
-RunOnlyIfNetworkAvailable $false
|
||||
|
||||
Register-ScheduledTask `
|
||||
-TaskName "GUARDiA-Backup" `
|
||||
-Action $action `
|
||||
-Trigger $trigger `
|
||||
-Settings $settings `
|
||||
-RunLevel Highest `
|
||||
-Force
|
||||
|
||||
Write-Host "백업 작업 스케줄 등록 완료"
|
||||
|
||||
# 즉시 실행으로 테스트
|
||||
Start-ScheduledTask -TaskName "GUARDiA-Backup"
|
||||
Start-Sleep -Seconds 5
|
||||
Get-ChildItem "C:\GUARDiA\backup\" | Sort-Object LastWriteTime -Descending | Select-Object -First 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 문제 해결
|
||||
|
||||
### 11.1 서비스 시작 실패
|
||||
|
||||
```powershell
|
||||
# 1. 서비스 상태 확인
|
||||
Get-Service GUARDiA-ITSM | Format-List *
|
||||
|
||||
# 2. 이벤트 로그 확인
|
||||
Get-EventLog -LogName Application -Source "GUARDiA-ITSM" -Newest 10 |
|
||||
Select-Object TimeGenerated, EntryType, Message
|
||||
|
||||
# 3. NSSM 로그 확인
|
||||
Get-Content "C:\GUARDiA\logs\itsm_error.log" -Tail 30
|
||||
|
||||
# 4. 수동 실행으로 오류 확인
|
||||
cd C:\GUARDiA\itsm
|
||||
C:\GUARDiA\.venv\Scripts\uvicorn.exe main:app --host 127.0.0.1 --port 8000
|
||||
# → 직접 오류 메시지 확인
|
||||
```
|
||||
|
||||
### 11.2 Python 패키지 누락 오류
|
||||
|
||||
```
|
||||
오류: ModuleNotFoundError: No module named 'xxx'
|
||||
|
||||
해결:
|
||||
C:\GUARDiA\.venv\Scripts\Activate.ps1
|
||||
pip install xxx
|
||||
deactivate
|
||||
Restart-Service GUARDiA-ITSM
|
||||
```
|
||||
|
||||
### 11.3 포트 충돌
|
||||
|
||||
```powershell
|
||||
# 8000 포트 사용 프로세스 확인
|
||||
netstat -ano | findstr ":8000"
|
||||
# → 마지막 컬럼이 PID
|
||||
|
||||
# 프로세스 확인
|
||||
Get-Process -Id <PID>
|
||||
|
||||
# 프로세스 종료 (필요 시)
|
||||
Stop-Process -Id <PID> -Force
|
||||
```
|
||||
|
||||
### 11.4 PowerShell 실행 정책 오류
|
||||
|
||||
```
|
||||
오류: running scripts is disabled on this system
|
||||
|
||||
해결:
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine
|
||||
# 또는 현재 사용자만:
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
|
||||
### 11.5 DB 파일 잠금 오류
|
||||
|
||||
```
|
||||
오류: database is locked
|
||||
|
||||
해결:
|
||||
1. 서비스 중지
|
||||
Stop-Service GUARDiA-ITSM
|
||||
|
||||
2. 잠금 파일 삭제
|
||||
Remove-Item "C:\GUARDiA\itsm\guardia_itsm.db-shm" -ErrorAction SilentlyContinue
|
||||
Remove-Item "C:\GUARDiA\itsm\guardia_itsm.db-wal" -ErrorAction SilentlyContinue
|
||||
|
||||
3. 서비스 재시작
|
||||
Start-Service GUARDiA-ITSM
|
||||
```
|
||||
|
||||
### 11.6 인코딩 오류 (한글 깨짐)
|
||||
|
||||
```powershell
|
||||
# .env 파일을 UTF-8 without BOM으로 저장
|
||||
[System.IO.File]::WriteAllText(
|
||||
"C:\GUARDiA\itsm\.env",
|
||||
(Get-Content "C:\GUARDiA\itsm\.env" -Raw),
|
||||
[System.Text.UTF8Encoding]::new($false) # $false = no BOM
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 부록 A: Windows 서비스 빠른 참조
|
||||
|
||||
```powershell
|
||||
# 서비스 시작/중지/재시작
|
||||
Start-Service GUARDiA-ITSM
|
||||
Stop-Service GUARDiA-ITSM
|
||||
Restart-Service GUARDiA-ITSM
|
||||
|
||||
Start-Service GUARDiA-Messenger
|
||||
Stop-Service GUARDiA-Messenger
|
||||
Restart-Service GUARDiA-Messenger
|
||||
|
||||
# 상태 확인
|
||||
Get-Service GUARDiA-ITSM, GUARDiA-Messenger
|
||||
|
||||
# 로그 확인 (최근 50줄)
|
||||
Get-Content "C:\GUARDiA\logs\itsm.log" -Tail 50
|
||||
Get-Content "C:\GUARDiA\logs\itsm_error.log" -Tail 20
|
||||
|
||||
# 실시간 로그 모니터링
|
||||
Get-Content "C:\GUARDiA\logs\itsm.log" -Wait -Tail 10
|
||||
|
||||
# 수동 백업
|
||||
PowerShell -File "C:\GUARDiA\scripts\backup.ps1"
|
||||
```
|
||||
|
||||
## 부록 B: 환경 변수 참조
|
||||
|
||||
| 변수명 | 설명 | 예시 값 |
|
||||
|--------|------|---------|
|
||||
| SECRET_KEY | JWT 서명 키 (필수 변경) | 랜덤 64자 이상 문자열 |
|
||||
| ENCRYPTION_KEY | AES-256 암호화 키 (필수 변경) | 랜덤 64자 hex 문자열 |
|
||||
| ACCESS_TOKEN_EXPIRE_MINUTES | 토큰 만료 시간(분) | 480 (8시간) |
|
||||
| DB_URL | SQLite DB 경로 | sqlite+aiosqlite:///C:/... |
|
||||
| MESSENGER_BASE_URL | Messenger 서버 URL | http://localhost:8001 |
|
||||
| SSH_TIMEOUT | SSH 실행 타임아웃(초) | 30 |
|
||||
| UPLOAD_ROOT | 파일 업로드 경로 | C:/GUARDiA/itsm/uploads |
|
||||
| MAX_FILE_SIZE_MB | 최대 업로드 파일 크기(MB) | 10 |
|
||||
|
||||
---
|
||||
|
||||
*설치 중 문제가 발생하면 `C:\GUARDiA\logs\itsm_error.log`를 확인하거나 개발팀에 문의하세요.*
|
||||
432
07_SI프로젝트관리_분석설계서.md
Normal file
432
07_SI프로젝트관리_분석설계서.md
Normal file
@ -0,0 +1,432 @@
|
||||
# GUARDiA ITSM — SI 프로젝트 관리 모듈 분석·설계서
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**대상 독자**: 개발자, PM, 이해관계자
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [개요 및 목적](#1-개요-및-목적)
|
||||
2. [기능 범위](#2-기능-범위)
|
||||
3. [도메인 모델 설계](#3-도메인-모델-설계)
|
||||
4. [API 엔드포인트 설계](#4-api-엔드포인트-설계)
|
||||
5. [프로세스 흐름](#5-프로세스-흐름)
|
||||
6. [추가 고려사항 및 확장 방향](#6-추가-고려사항-및-확장-방향)
|
||||
7. [SM ↔ SI 통합 연계](#7-sm--si-통합-연계)
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요 및 목적
|
||||
|
||||
### 1.1 현재 vs 확장 범위
|
||||
|
||||
```
|
||||
현재 (SM 모드) 확장 (SI 모드)
|
||||
──────────────── ────────────────────────────
|
||||
SR 접수·처리 RFP 요구사항 등록·관리
|
||||
SSL 인증서 점검 WBS 작성·진척 관리
|
||||
정기 PM 점검 프로젝트 이슈·리스크 관리
|
||||
장애 관리 (Incident) 마일스톤·산출물 관리
|
||||
온콜·당직 관리 변경 요청(CR) 관리
|
||||
배치 작업 관리 테스트 관리 (계획→실행→결함)
|
||||
요구사항 추적성 매트릭스(RTM)
|
||||
Gantt 차트 데이터 제공
|
||||
안정화 체크리스트
|
||||
```
|
||||
|
||||
### 1.2 SI 프로젝트 생명주기
|
||||
|
||||
```
|
||||
착수 → 분석 → 설계 → 개발 → 테스트 → 구축 → 안정화 → 종료
|
||||
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
|
||||
RFP 요구사 설계 WBS 테스트 시스템 헬스체크 최종
|
||||
등록 항분류 산출 진척 실행 이관 SR자동 보고서
|
||||
물 관리 결함 체크 생성
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 기능 범위
|
||||
|
||||
### 2.1 핵심 기능 8개
|
||||
|
||||
| # | 기능 | 설명 |
|
||||
|---|------|------|
|
||||
| F1 | **SI 프로젝트 관리** | 프로젝트 CRUD, 단계(Phase) 전환, 진행률 집계 |
|
||||
| F2 | **RFP 요구사항 관리** | 요구사항 등록·분류·추적, RTM 생성, Excel 일괄 업로드 |
|
||||
| F3 | **WBS 관리** | 계층형 WBS 작성, Gantt 데이터, 진척률·지연 감지 |
|
||||
| F4 | **이슈 관리** | 기술/일정/자원/품질 이슈, 이슈→SR 연동 |
|
||||
| F5 | **리스크 관리** | 확률×영향 점수, 대응 계획, 리스크→이슈 전환 |
|
||||
| F6 | **마일스톤·산출물 관리** | 마일스톤 달성 여부, 산출물 제출·승인 워크플로우 |
|
||||
| F7 | **변경 요청(CR) 관리** | 범위·일정·예산 변경, 결재 연동 |
|
||||
| F8 | **테스트 관리** | 테스트 계획, 케이스, 실행, 결함, 결과 보고서 |
|
||||
|
||||
### 2.2 고려사항 (구현 시 주의)
|
||||
|
||||
```
|
||||
① 요구사항 추적성 (Traceability)
|
||||
RFP 요구사항 → WBS 항목 → 테스트 케이스 → 결함 → 검증
|
||||
req_id FK 체인으로 연결, RTM Excel 자동 생성
|
||||
|
||||
② WBS 계층 구조
|
||||
재귀 Self-Join (parent_id → id)
|
||||
최대 4레벨: 1 프로젝트 > 1.1 단계 > 1.1.1 업무 > 1.1.1.1 작업
|
||||
진척률 = 자식 노드 평균 (Leaf 노드만 수동 입력)
|
||||
|
||||
③ Gantt 데이터 포맷
|
||||
WBS 항목의 planned_start/end, actual_start/end를 JSON 배열로 반환
|
||||
프론트엔드에서 D3.js 또는 다른 Gantt 라이브러리로 렌더링
|
||||
|
||||
④ 지연 감지 자동화
|
||||
스케줄러: 매일 07:00 — planned_end < today AND completion_pct < 100 → 이슈 자동 생성 + 알림
|
||||
|
||||
⑤ SM 통합
|
||||
SI 안정화 단계 종료 후 → SM 모드로 자동 전환
|
||||
SM 서버(CMDB), SR, PM 스케줄에 연동
|
||||
|
||||
⑥ 대용량 파일
|
||||
산출물 파일 업로드는 기존 attachments 모듈 재사용 (uploads/si_deliverables/)
|
||||
|
||||
⑦ 보안
|
||||
프로젝트별 접근 권한: ADMIN > PM(해당 프로젝트) > ENGINEER(참여자) > 조회 불가
|
||||
계약금액 등 민감 필드는 ADMIN/PM만 조회
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 도메인 모델 설계
|
||||
|
||||
### 3.1 ER 다이어그램 (핵심 테이블)
|
||||
|
||||
```
|
||||
tb_si_project (SI 프로젝트)
|
||||
├── tb_si_requirement (요구사항) ─── req_id FK ──► tb_wbs_item
|
||||
├── tb_wbs_item (WBS 항목) ←── parent_id (self-join)
|
||||
│ └── tb_wbs_item ←── predecessor FK
|
||||
├── tb_project_issue (이슈)
|
||||
│ └── tb_sr_request (SR 연동, nullable FK)
|
||||
├── tb_project_risk (리스크)
|
||||
│ └── tb_project_issue (실현 시 이슈 생성)
|
||||
├── tb_project_milestone (마일스톤)
|
||||
│ └── tb_project_deliverable (산출물)
|
||||
├── tb_change_request (변경 요청)
|
||||
│ └── tb_approval_flow (결재 연동)
|
||||
└── tb_si_test_plan (테스트 계획)
|
||||
├── tb_si_test_case (테스트 케이스)
|
||||
│ └── tb_si_requirement (요구사항 FK)
|
||||
└── tb_si_test_execution (실행 결과)
|
||||
└── tb_si_defect (결함)
|
||||
```
|
||||
|
||||
### 3.2 Enum 정의
|
||||
|
||||
```python
|
||||
class ProjectPhase(str, Enum):
|
||||
INITIATION = "INITIATION" # 착수
|
||||
ANALYSIS = "ANALYSIS" # 분석
|
||||
DESIGN = "DESIGN" # 설계
|
||||
DEVELOPMENT = "DEVELOPMENT" # 개발
|
||||
TESTING = "TESTING" # 테스트
|
||||
DEPLOYMENT = "DEPLOYMENT" # 구축/이관
|
||||
STABILIZATION = "STABILIZATION" # 안정화
|
||||
CLOSED = "CLOSED" # 종료
|
||||
|
||||
class ReqType(str, Enum):
|
||||
FUNCTIONAL = "FUNCTIONAL" # 기능 요구사항
|
||||
NON_FUNCTIONAL = "NON_FUNCTIONAL" # 비기능 요구사항
|
||||
CONSTRAINT = "CONSTRAINT" # 제약 사항
|
||||
INTERFACE = "INTERFACE" # 인터페이스 요구사항
|
||||
|
||||
class ReqStatus(str, Enum):
|
||||
DRAFT = "DRAFT" # 초안
|
||||
REVIEWED = "REVIEWED" # 검토 완료
|
||||
APPROVED = "APPROVED" # 승인
|
||||
IMPLEMENTED = "IMPLEMENTED" # 구현 완료
|
||||
VERIFIED = "VERIFIED" # 검증 완료
|
||||
DEFERRED = "DEFERRED" # 보류
|
||||
DELETED = "DELETED" # 삭제
|
||||
|
||||
class WbsStatus(str, Enum):
|
||||
NOT_STARTED = "NOT_STARTED"
|
||||
IN_PROGRESS = "IN_PROGRESS"
|
||||
COMPLETED = "COMPLETED"
|
||||
DELAYED = "DELAYED"
|
||||
ON_HOLD = "ON_HOLD"
|
||||
|
||||
class RiskLevel(str, Enum):
|
||||
HIGH = "HIGH" # 3
|
||||
MEDIUM = "MEDIUM" # 2
|
||||
LOW = "LOW" # 1
|
||||
|
||||
class IssueType(str, Enum):
|
||||
TECHNICAL = "TECHNICAL" # 기술
|
||||
SCHEDULE = "SCHEDULE" # 일정
|
||||
RESOURCE = "RESOURCE" # 자원
|
||||
QUALITY = "QUALITY" # 품질
|
||||
SCOPE = "SCOPE" # 범위
|
||||
EXTERNAL = "EXTERNAL" # 외부
|
||||
|
||||
class CrType(str, Enum):
|
||||
SCOPE = "SCOPE" # 범위 변경
|
||||
SCHEDULE = "SCHEDULE" # 일정 변경
|
||||
BUDGET = "BUDGET" # 예산 변경
|
||||
QUALITY = "QUALITY" # 품질 기준 변경
|
||||
|
||||
class TestResult(str, Enum):
|
||||
PASS = "PASS"
|
||||
FAIL = "FAIL"
|
||||
BLOCKED = "BLOCKED"
|
||||
SKIP = "SKIP"
|
||||
|
||||
class DefectSeverity(str, Enum):
|
||||
CRITICAL = "CRITICAL"
|
||||
MAJOR = "MAJOR"
|
||||
MINOR = "MINOR"
|
||||
TRIVIAL = "TRIVIAL"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API 엔드포인트 설계
|
||||
|
||||
### 4.1 SI 프로젝트 (`/api/si/projects`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/` | 프로젝트 목록 |
|
||||
| POST | `/` | 프로젝트 생성 |
|
||||
| GET | `/{id}` | 프로젝트 상세 (진행률 포함) |
|
||||
| PATCH | `/{id}` | 프로젝트 수정 |
|
||||
| PATCH | `/{id}/phase` | 단계 전환 |
|
||||
| GET | `/{id}/dashboard` | 프로젝트 대시보드 (이슈수, 리스크수, WBS 진척률) |
|
||||
| GET | `/{id}/gantt` | Gantt 차트 데이터 (WBS 전체) |
|
||||
| POST | `/{id}/members` | 프로젝트 멤버 추가 |
|
||||
|
||||
### 4.2 요구사항 (`/api/si/projects/{pid}/requirements`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/` | 요구사항 목록 (타입/상태 필터) |
|
||||
| POST | `/` | 요구사항 단건 등록 |
|
||||
| POST | `/bulk` | Excel 일괄 업로드 (openpyxl 파싱) |
|
||||
| GET | `/{id}` | 요구사항 상세 |
|
||||
| PATCH | `/{id}` | 요구사항 수정 |
|
||||
| PATCH | `/{id}/status` | 상태 변경 |
|
||||
| GET | `/rtm` | 추적성 매트릭스(RTM) JSON |
|
||||
| GET | `/rtm/excel` | RTM Excel 다운로드 |
|
||||
|
||||
### 4.3 WBS (`/api/si/projects/{pid}/wbs`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/` | WBS 트리 구조 조회 |
|
||||
| POST | `/` | WBS 항목 추가 |
|
||||
| POST | `/bulk` | Excel WBS 일괄 업로드 |
|
||||
| PATCH | `/{id}` | WBS 항목 수정 |
|
||||
| PATCH | `/{id}/progress` | 진척률 업데이트 |
|
||||
| DELETE | `/{id}` | WBS 항목 삭제 (자식 있으면 거부) |
|
||||
| GET | `/gantt` | Gantt 렌더링용 JSON |
|
||||
| GET | `/delayed` | 지연 항목 목록 |
|
||||
|
||||
### 4.4 이슈 (`/api/si/projects/{pid}/issues`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/` | 이슈 목록 (타입/상태 필터) |
|
||||
| POST | `/` | 이슈 등록 |
|
||||
| GET | `/{id}` | 이슈 상세 |
|
||||
| PATCH | `/{id}` | 이슈 수정 |
|
||||
| PATCH | `/{id}/resolve` | 이슈 해결 처리 |
|
||||
| POST | `/{id}/convert-sr` | ITSM SR로 변환 등록 |
|
||||
|
||||
### 4.5 리스크 (`/api/si/projects/{pid}/risks`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/` | 리스크 목록 (점수 정렬) |
|
||||
| POST | `/` | 리스크 등록 |
|
||||
| PATCH | `/{id}` | 리스크 수정 |
|
||||
| PATCH | `/{id}/occur` | 리스크 실현 → 이슈 자동 생성 |
|
||||
| GET | `/matrix` | 리스크 매트릭스 데이터 (3×3 격자) |
|
||||
|
||||
### 4.6 마일스톤·산출물
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/api/si/projects/{pid}/milestones` | 마일스톤 목록 |
|
||||
| POST | `/api/si/projects/{pid}/milestones` | 마일스톤 등록 |
|
||||
| PATCH | `/api/si/projects/{pid}/milestones/{id}/achieve` | 달성 처리 |
|
||||
| GET | `/api/si/projects/{pid}/deliverables` | 산출물 목록 |
|
||||
| POST | `/api/si/projects/{pid}/deliverables` | 산출물 등록 |
|
||||
| POST | `/api/si/projects/{pid}/deliverables/{id}/submit` | 제출 처리 |
|
||||
| PATCH | `/api/si/projects/{pid}/deliverables/{id}/approve` | 승인 처리 |
|
||||
|
||||
### 4.7 변경 요청 (`/api/si/projects/{pid}/change-requests`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| GET | `/` | CR 목록 |
|
||||
| POST | `/` | CR 등록 |
|
||||
| PATCH | `/{id}/approve` | CR 승인 |
|
||||
| PATCH | `/{id}/reject` | CR 거부 |
|
||||
| PATCH | `/{id}/implement` | CR 구현 완료 |
|
||||
|
||||
### 4.8 테스트 관리 (`/api/si/projects/{pid}/tests`)
|
||||
|
||||
| Method | Path | 설명 |
|
||||
|--------|------|------|
|
||||
| POST | `/plans` | 테스트 계획 생성 |
|
||||
| POST | `/plans/{plan_id}/cases` | 테스트 케이스 등록 |
|
||||
| POST | `/plans/{plan_id}/cases/bulk` | 케이스 일괄 업로드 |
|
||||
| POST | `/plans/{plan_id}/execute` | 테스트 실행 결과 저장 |
|
||||
| POST | `/defects` | 결함 등록 |
|
||||
| PATCH | `/defects/{id}/fix` | 결함 수정 완료 |
|
||||
| GET | `/plans/{plan_id}/report` | 테스트 결과 보고서 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 프로세스 흐름
|
||||
|
||||
### 5.1 RFP 입력 → WBS 자동 생성 흐름
|
||||
|
||||
```
|
||||
1. RFP 문서 업로드 (PDF/Word/Excel)
|
||||
2. 요구사항 수동 입력 또는 Excel 일괄 업로드
|
||||
→ req_id 자동 채번: REQ-F-001 (기능), REQ-NF-001 (비기능)
|
||||
3. 요구사항 검토 → APPROVED 상태 전환
|
||||
4. WBS 자동 생성 트리거:
|
||||
APPROVED 요구사항 카테고리 기준으로 WBS 골격 생성
|
||||
(분석→설계→개발→테스트 단계별 노드 자동 생성)
|
||||
5. PM이 WBS 상세 조정 (기간, 담당자 배정)
|
||||
6. Gantt 차트 확인 → 일정 확정
|
||||
```
|
||||
|
||||
### 5.2 WBS 진척 관리 흐름
|
||||
|
||||
```
|
||||
매일 07:00 스케줄러 실행:
|
||||
└── planned_end < today AND completion_pct < 100
|
||||
└── status = DELAYED 자동 변경
|
||||
└── ProjectIssue 자동 생성 (issue_type=SCHEDULE)
|
||||
└── Messenger 알림 → PM/담당자
|
||||
|
||||
엔지니어: completion_pct 업데이트
|
||||
└── PATCH /wbs/{id}/progress {"completion_pct": 75}
|
||||
└── 부모 노드 진척률 자동 재계산 (평균)
|
||||
└── 프로젝트 전체 진척률 갱신
|
||||
```
|
||||
|
||||
### 5.3 리스크 → 이슈 전환 흐름
|
||||
|
||||
```
|
||||
리스크 생성 (확률×영향 점수 HIGH×HIGH = 9)
|
||||
└── 점수 6 이상: 관리 필요 알림
|
||||
└── 점수 9: 즉시 PM 알림
|
||||
|
||||
리스크 실현 (PATCH /risks/{id}/occur):
|
||||
└── risk.status = OCCURRED
|
||||
└── ProjectIssue 자동 생성
|
||||
- title: f"[리스크 실현] {risk.title}"
|
||||
- issue_type: 리스크 타입 매핑
|
||||
- priority: HIGH
|
||||
└── Messenger P2 알림
|
||||
```
|
||||
|
||||
### 5.4 테스트 → SM 전환 흐름
|
||||
|
||||
```
|
||||
테스트 완료 조건:
|
||||
- 전체 TC Pass율 ≥ 95%
|
||||
- Critical/Major 결함 0건
|
||||
- 마일스톤 "테스트 완료" 달성
|
||||
|
||||
구축(DEPLOYMENT) 단계:
|
||||
- 서버 정보 → CMDB 자동 등록 (Server 테이블)
|
||||
- SSL 도메인 → SslDomain 자동 등록
|
||||
- PM 스케줄 → PmSchedule 자동 생성
|
||||
|
||||
안정화(STABILIZATION) 단계:
|
||||
- 안정화 SR 자동 생성 (sr_type=INCIDENT, priority=HIGH)
|
||||
- 온콜 당직 스케줄 생성
|
||||
|
||||
종료(CLOSED):
|
||||
- 프로젝트 최종 보고서 Excel 생성
|
||||
- SM 모드로 전환 완료 플래그
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 추가 고려사항 및 확장 방향
|
||||
|
||||
### 6.1 즉시 구현 필요 (Must)
|
||||
|
||||
```
|
||||
① RTM(Requirements Traceability Matrix) Excel 자동 생성
|
||||
요구사항 ID → WBS 코드 → 테스트 케이스 ID → 결함 수 → 검증 상태
|
||||
|
||||
② WBS Excel 업로드/다운로드
|
||||
표준 WBS 양식(xlsx) 업로드 → 자동 파싱 → DB 저장
|
||||
|
||||
③ 지연 자동 감지 스케줄러
|
||||
scheduler.py에 _scan_wbs_delay() 추가
|
||||
|
||||
④ 안정화→SM 전환 트리거
|
||||
DEPLOYMENT 완료 시 CMDB/SSL/PM 자동 생성
|
||||
```
|
||||
|
||||
### 6.2 중기 확장 (Should)
|
||||
|
||||
```
|
||||
① Gantt 차트 프론트엔드
|
||||
WBS Gantt API → static/si_gantt.html 구현
|
||||
|
||||
② 공수 관리 (Man-day)
|
||||
WbsItem에 planned_md, actual_md 추가
|
||||
|
||||
③ 예산 관리
|
||||
SiProject에 budget_total, budget_used 연동
|
||||
|
||||
④ 외부 협력사 포털
|
||||
CUSTOMER Role 확장: 협력사 계정에 특정 WBS만 접근
|
||||
|
||||
⑤ sLLM 연동
|
||||
RFP 텍스트 → 요구사항 자동 추출 (내부 sLLM API)
|
||||
```
|
||||
|
||||
### 6.3 장기 확장 (Nice)
|
||||
|
||||
```
|
||||
① 프로젝트 템플릿
|
||||
표준 SI 프로젝트 WBS 템플릿 (전자정부, ERP 등)
|
||||
|
||||
② 시뮬레이션
|
||||
일정 변경 시 종료일 영향 시뮬레이션 (Critical Path)
|
||||
|
||||
③ 유사 프로젝트 비교
|
||||
과거 SI 프로젝트 실적 기반 일정/공수 추정 보조
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. SM ↔ SI 통합 연계
|
||||
|
||||
```
|
||||
SI 프로젝트 종료 후 SM 자산 자동 등록:
|
||||
|
||||
SiProject.phase = CLOSED
|
||||
└── 각 서버 → Server(CMDB) 자동 등록
|
||||
└── 각 도메인 → SslDomain 자동 등록
|
||||
└── PM 스케줄 → PmSchedule 자동 생성 (MONTHLY)
|
||||
└── 배치 작업 → BatchJob 이관 (있는 경우)
|
||||
└── SM SRType.INQUIRY → 안정화 SR 자동 생성
|
||||
|
||||
연계 API: POST /api/si/projects/{id}/convert-to-sm
|
||||
→ 위 자동 등록 트리거
|
||||
→ 결과 요약 반환 (등록된 서버 수, SSL 수, PM 수)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*본 설계서는 구현 진행에 따라 갱신됩니다.*
|
||||
669
08_AI에이전트_Paperclip_설계서.md
Normal file
669
08_AI에이전트_Paperclip_설계서.md
Normal file
@ -0,0 +1,669 @@
|
||||
# GUARDiA ITSM — AI 에이전트 (Paperclip × GUARDiA) 설계서
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**작성일**: 2026-05-25
|
||||
**대상 독자**: 개발자, DevOps 엔지니어, 운영 관리자
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [개요 및 목적](#1-개요-및-목적)
|
||||
2. [아키텍처 설계](#2-아키텍처-설계)
|
||||
3. [Phase 1 — Paperclip 개발 도구 설정](#3-phase-1--paperclip-개발-도구-설정)
|
||||
4. [Phase 2 — Ollama 로컬 LLM 설정](#4-phase-2--ollama-로컬-llm-설정)
|
||||
5. [Phase 3 — GUARDiA 에이전트 엔진](#5-phase-3--guardia-에이전트-엔진)
|
||||
6. [Phase 4 — 자율 운영 대시보드](#6-phase-4--자율-운영-대시보드)
|
||||
7. [API 엔드포인트 설계](#7-api-엔드포인트-설계)
|
||||
8. [보안 제약사항](#8-보안-제약사항)
|
||||
9. [스케줄러 잡 설계](#9-스케줄러-잡-설계)
|
||||
10. [테스트 결과](#10-테스트-결과)
|
||||
11. [운영 가이드](#11-운영-가이드)
|
||||
12. [향후 로드맵](#12-향후-로드맵)
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요 및 목적
|
||||
|
||||
### 1.1 배경
|
||||
|
||||
GUARDiA ITSM은 온프레미스 IT 서비스 관리 플랫폼으로, 서비스 요청(SR), 장애 관리, SSL 인증서 모니터링, PM 일정 관리, SI 프로젝트 관리를 제공합니다.
|
||||
|
||||
AI 에이전트 기능 추가를 통해 다음 목표를 달성합니다:
|
||||
|
||||
- **반복 업무 자동화**: 장애 분류, KB 등록, SSL 갱신 SR 생성 등 반복적인 운영 업무를 AI가 자동 처리
|
||||
- **능동적 모니터링**: 에이전트가 주기적으로 시스템 상태를 확인하고 이상 징후를 감지
|
||||
- **사람-AI 협업**: 고위험 작업은 사람의 승인을 거쳐 실행하는 거버넌스 체계
|
||||
|
||||
### 1.2 Paperclip 프레임워크 채택
|
||||
|
||||
[Paperclip](https://github.com/paperclipai/paperclip)은 AI 에이전트 오케스트레이션 오픈소스 프레임워크입니다.
|
||||
|
||||
| 특징 | 설명 |
|
||||
|------|------|
|
||||
| 조직도 구조 | CEO → CTO → 개발자/QA 계층적 에이전트 관리 |
|
||||
| 하트비트 시스템 | 에이전트가 주기적으로 깨어나 작업 수행 후 대기 |
|
||||
| 이슈 추적 | GitHub 스타일의 태스크/이슈 관리 |
|
||||
| 거버넌스 | 위험 수준에 따른 사람 승인 게이트 |
|
||||
|
||||
### 1.3 보안 원칙
|
||||
|
||||
> **외부 LLM/AI API 완전 금지** — 모든 AI 추론은 Ollama (localhost:11434) 를 통해 온프레미스에서 처리
|
||||
|
||||
---
|
||||
|
||||
## 2. 아키텍처 설계
|
||||
|
||||
### 2.1 전체 구조
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ GUARDiA ITSM │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ FastAPI Layer │ │
|
||||
│ │ /api/agents/* ←──────────────────── agents.html (SPA) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────▼─────────────────────────────────┐ │
|
||||
│ │ AgentEngine (core) │ │
|
||||
│ │ │ │
|
||||
│ │ INCIDENT_TRIAGE KB_CURATOR SSL_WATCHER │ │
|
||||
│ │ WBS_MONITOR PM_SUGGESTER DEVELOPER │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────┐ ┌─────▼───────────────────────────────┐ │
|
||||
│ │ APScheduler │──▶│ OllamaClient (LLM 추론) │ │
|
||||
│ │ (9 cron jobs) │ │ localhost:11434 │ │
|
||||
│ └────────────────┘ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ SQLite DB: tb_agent_config | tb_agent_task | tb_agent_approval│
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
외부(개발 시에만):
|
||||
Paperclip CLI ──── paperclip.config.json ──── agents/*.md
|
||||
```
|
||||
|
||||
### 2.2 에이전트 조직도
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ CEO │ 전략·결재
|
||||
└──────┬──────┘
|
||||
┌────────┴────────┐
|
||||
┌──────▼──────┐ ┌────▼──────┐
|
||||
│ CTO │ │ PM_AGENT │ 프로젝트 관리
|
||||
└──────┬──────┘ └───────────┘
|
||||
┌──────┴────────┐
|
||||
┌──▼──────┐ ┌────▼───┐
|
||||
│DEVELOPER│ │ QA │
|
||||
└─────────┘ └────────┘
|
||||
|
||||
운영 자동화 에이전트 (Ops):
|
||||
INCIDENT_TRIAGE ← 장애 자동 분류
|
||||
KB_CURATOR ← 지식베이스 자동 등록
|
||||
SSL_WATCHER ← SSL 만료 감시
|
||||
WBS_MONITOR ← WBS 지연 감지
|
||||
PM_SUGGESTER ← PM 일정 제안
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Phase 1 — Paperclip 개발 도구 설정
|
||||
|
||||
### 3.1 파일 구조
|
||||
|
||||
```
|
||||
C:\GUARDiA\
|
||||
└── paperclip\
|
||||
├── paperclip.config.json # 조직도 + 거버넌스 설정
|
||||
├── README.md # 설치/사용 가이드
|
||||
└── agents\
|
||||
├── ceo.md # CEO 에이전트 페르소나
|
||||
├── cto.md # CTO 에이전트 페르소나
|
||||
├── developer.md # 개발자 에이전트 페르소나
|
||||
└── qa.md # QA 에이전트 페르소나
|
||||
```
|
||||
|
||||
### 3.2 설치
|
||||
|
||||
```bash
|
||||
npm install -g @paperclipai/paperclip
|
||||
|
||||
# Paperclip 초기화 (프로젝트 루트에서)
|
||||
cd C:\GUARDiA
|
||||
paperclip init
|
||||
|
||||
# 에이전트 시작
|
||||
paperclip start
|
||||
```
|
||||
|
||||
### 3.3 조직도 구성 (paperclip.config.json 요약)
|
||||
|
||||
```json
|
||||
{
|
||||
"org_chart": {
|
||||
"ceo": { "reports_to": null, "can_approve": ["deploy", "code_commit"] },
|
||||
"cto": { "reports_to": "ceo" },
|
||||
"developer": { "reports_to": "cto" },
|
||||
"qa": { "reports_to": "cto" },
|
||||
"pm_agent": { "reports_to": "ceo" }
|
||||
},
|
||||
"llm": {
|
||||
"provider": "ollama",
|
||||
"base_url": "http://localhost:11434",
|
||||
"models": {
|
||||
"ceo": "guardia-agent",
|
||||
"developer": "codellama:7b",
|
||||
"qa": "codellama:7b"
|
||||
}
|
||||
},
|
||||
"governance": {
|
||||
"require_human_approval": ["code_commit", "deploy", "delete_data"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 거버넌스 규칙
|
||||
|
||||
| 액션 | 승인 방식 | 승인자 |
|
||||
|------|-----------|--------|
|
||||
| code_commit | 사람 승인 필수 | CTO 또는 CEO |
|
||||
| deploy | 사람 승인 필수 | CEO |
|
||||
| delete_data | 사람 승인 필수 | CEO |
|
||||
| 장애 분류 (일반) | 자동 승인 | — |
|
||||
| 장애 분류 (CRITICAL) | 사람 승인 필수 | 담당자 |
|
||||
| KB 등록 | 자동 승인 | — |
|
||||
| SSL 갱신 SR 생성 | 자동 승인 | — |
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase 2 — Ollama 로컬 LLM 설정
|
||||
|
||||
### 4.1 파일 구조
|
||||
|
||||
```
|
||||
C:\GUARDiA\
|
||||
└── ollama\
|
||||
├── setup.ps1 # 자동 설치 스크립트
|
||||
└── Modelfile.guardia # 커스텀 모델 정의
|
||||
```
|
||||
|
||||
### 4.2 자동 설치 (setup.ps1)
|
||||
|
||||
```powershell
|
||||
# 실행 방법:
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
.\setup.ps1
|
||||
|
||||
# 스크립트 동작:
|
||||
# 1. OllamaSetup.exe 다운로드
|
||||
# 2. 설치 후 ollama serve 시작
|
||||
# 3. 헬스체크 (10회 재시도)
|
||||
# 4. llama3.1:8b + codellama:7b 풀링
|
||||
# 5. guardia-agent 커스텀 모델 생성
|
||||
```
|
||||
|
||||
### 4.3 guardia-agent 모델 (Modelfile.guardia)
|
||||
|
||||
```
|
||||
FROM llama3.1:8b
|
||||
SYSTEM """당신은 GUARDiA ITSM AI 운영 에이전트입니다.
|
||||
한국어로 응답하며, IT 서비스 관리(ITSM)에 특화되어 있습니다.
|
||||
보안 원칙: 외부 API 호출 금지, 민감 정보 노출 금지."""
|
||||
PARAMETER temperature 0.2
|
||||
PARAMETER num_predict 2048
|
||||
```
|
||||
|
||||
### 4.4 OllamaClient API
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `health_check()` | Ollama 서버 상태 확인 |
|
||||
| `list_models()` | 설치된 모델 목록 조회 |
|
||||
| `resolve_model(preferred)` | 선호 모델이 없으면 fallback 모델 반환 |
|
||||
| `chat(messages, model)` | 대화형 추론 |
|
||||
| `generate(prompt, model)` | 단일 프롬프트 추론 |
|
||||
| `json_generate(prompt, model)` | JSON 응답 추출 (코드블록 자동 제거) |
|
||||
| `pull_model(model)` | 모델 다운로드 |
|
||||
|
||||
---
|
||||
|
||||
## 5. Phase 3 — GUARDiA 에이전트 엔진
|
||||
|
||||
### 5.1 파일 구조
|
||||
|
||||
```
|
||||
C:\GUARDiA\itsm\
|
||||
├── models.py # AgentConfig, AgentTask, AgentApproval 모델 추가
|
||||
├── core\
|
||||
│ ├── llm_client.py # OllamaClient 구현
|
||||
│ └── agents.py # AgentEngine 구현
|
||||
└── routers\
|
||||
└── agents.py # 16개 REST API 엔드포인트
|
||||
```
|
||||
|
||||
### 5.2 데이터 모델
|
||||
|
||||
#### AgentConfig (tb_agent_config)
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | INTEGER PK | 에이전트 ID |
|
||||
| name | VARCHAR | 에이전트 이름 |
|
||||
| role | VARCHAR | AgentRole enum |
|
||||
| llm_provider | VARCHAR | ollama (고정) |
|
||||
| llm_model | VARCHAR | 사용할 LLM 모델 |
|
||||
| system_prompt | TEXT | 역할 설명 프롬프트 |
|
||||
| heartbeat_cron | VARCHAR | 크론 표현식 |
|
||||
| is_active | BOOLEAN | 활성화 여부 |
|
||||
| status | VARCHAR | IDLE/ACTIVE/WORKING/ERROR/PAUSED |
|
||||
| last_heartbeat | DATETIME | 마지막 실행 시각 |
|
||||
| total_tasks | INTEGER | 누적 처리 태스크 수 |
|
||||
| total_tokens | INTEGER | 누적 사용 토큰 수 |
|
||||
|
||||
#### AgentTask (tb_agent_task)
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | INTEGER PK | 태스크 ID |
|
||||
| agent_id | INTEGER FK | 에이전트 참조 |
|
||||
| title | VARCHAR | 태스크 제목 |
|
||||
| status | VARCHAR | PENDING/IN_PROGRESS/COMPLETED/FAILED |
|
||||
| input_data | JSON | 입력 데이터 |
|
||||
| output_data | JSON | LLM 출력 결과 |
|
||||
| tokens_used | INTEGER | 사용된 토큰 수 |
|
||||
|
||||
#### AgentApproval (tb_agent_approval)
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | INTEGER PK | 승인 ID |
|
||||
| agent_id | INTEGER FK | 에이전트 참조 |
|
||||
| task_id | INTEGER FK nullable | 연관 태스크 |
|
||||
| action_type | VARCHAR | 액션 유형 |
|
||||
| action_data | JSON | 액션 상세 데이터 |
|
||||
| status | VARCHAR | PENDING/APPROVED/REJECTED/AUTO_APPROVED |
|
||||
| reviewed_by | INTEGER FK nullable | 검토자 |
|
||||
|
||||
### 5.3 에이전트별 동작
|
||||
|
||||
#### INCIDENT_TRIAGE (15분마다 실행)
|
||||
|
||||
```
|
||||
1. 미배정 장애(assigned_to=None, status=OPEN/RECEIVED) 조회
|
||||
2. LLM JSON 분류 요청:
|
||||
{ severity: CRITICAL/HIGH/MEDIUM/LOW,
|
||||
category: HARDWARE/SOFTWARE/NETWORK/...,
|
||||
reason: "분류 근거" }
|
||||
3. CRITICAL → AgentApproval(PENDING) 생성
|
||||
4. 그 외 → AUTO_APPROVED + Incident 등급 즉시 반영
|
||||
```
|
||||
|
||||
#### KB_CURATOR (매시간 정각)
|
||||
|
||||
```
|
||||
1. SR 완료 건 중 KB가 없는 건 조회
|
||||
2. LLM KB 초안 생성:
|
||||
{ kb_title, symptom, cause, solution, tags }
|
||||
3. KBDocument 생성 (published=False, 검토 대기)
|
||||
4. AgentApproval(AUTO_APPROVED) 기록
|
||||
```
|
||||
|
||||
#### SSL_WATCHER (매일 08:30)
|
||||
|
||||
```
|
||||
1. ssl_expire_date가 0~30일 이내인 서버 조회
|
||||
2. 기존 SSL 갱신 SR이 없는 경우에만 SR 자동 생성
|
||||
3. 긴급도: 7일 미만=CRITICAL, 30일 미만=HIGH
|
||||
```
|
||||
|
||||
#### WBS_MONITOR (매일 08:00)
|
||||
|
||||
```
|
||||
1. 진행 중 SI 프로젝트의 WBS 지연 항목 조회
|
||||
2. 3일+ 지연: 주의, 10일+ 지연: CRITICAL
|
||||
3. LLM 리스크 분석:
|
||||
{ risk_level, probability, impact, title, mitigation_plan }
|
||||
4. ProjectRisk 자동 등록
|
||||
5. CRITICAL 리스크 → 사람 승인 필요
|
||||
```
|
||||
|
||||
#### PM_SUGGESTER (매일 09:00)
|
||||
|
||||
```
|
||||
1. PM 일정이 없는 서버 조회
|
||||
2. 권장 PM 일정 제안 (AgentTask 기록)
|
||||
```
|
||||
|
||||
#### DEVELOPER (수동 트리거 or 이슈 등록 시)
|
||||
|
||||
```
|
||||
1. PENDING AgentTask 조회
|
||||
2. LLM 코드/응답 생성
|
||||
3. CODE_CHANGE 태스크 → AgentApproval(PENDING) 생성
|
||||
```
|
||||
|
||||
### 5.4 하트비트 사이클
|
||||
|
||||
```
|
||||
에이전트 등록 (AgentConfig 생성)
|
||||
│
|
||||
▼
|
||||
APScheduler Cron 잡 등록
|
||||
│
|
||||
▼ (스케줄 도달)
|
||||
status = ACTIVE
|
||||
│
|
||||
▼
|
||||
Ollama health_check()
|
||||
│ (실패)──────────────────► status = ERROR
|
||||
│ (성공)
|
||||
▼
|
||||
_handler(db, agent) 실행
|
||||
│
|
||||
├─ 작업 완료 ──► AgentTask(COMPLETED) + status = IDLE
|
||||
└─ 오류 발생 ──► AgentTask(FAILED) + status = ERROR
|
||||
last_error 기록
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase 4 — 자율 운영 대시보드
|
||||
|
||||
### 6.1 접근 경로
|
||||
|
||||
```
|
||||
http://localhost:8001/agents
|
||||
```
|
||||
|
||||
### 6.2 대시보드 구성
|
||||
|
||||
| 영역 | 설명 |
|
||||
|------|------|
|
||||
| LLM 상태 배너 | Ollama 온라인/오프라인 상태 실시간 표시 |
|
||||
| 통계 카드 | 총 에이전트 수, 활성, 오늘 태스크, 오늘 토큰, 승인 대기 |
|
||||
| 에이전트 탭 | 에이전트 카드 (역할 배지, 상태 펄스 애니메이션) |
|
||||
| 조직도 탭 | 계층적 트리 렌더링 |
|
||||
| 승인 대기 탭 | 보류 중 승인 목록 (CRITICAL 강조) |
|
||||
| 태스크 피드 탭 | 전체 에이전트 태스크 실시간 피드 |
|
||||
|
||||
### 6.3 에이전트 상태 색상 코드
|
||||
|
||||
| 상태 | 색상 | 설명 |
|
||||
|------|------|------|
|
||||
| IDLE | 회색 | 대기 중 |
|
||||
| ACTIVE | 파랑 | 심장박동 시작 |
|
||||
| WORKING | 주황 (펄스) | 작업 진행 중 |
|
||||
| ERROR | 빨강 | 오류 발생 |
|
||||
| PAUSED | 노랑 | 일시 중지 |
|
||||
|
||||
### 6.4 역할별 색상 배지
|
||||
|
||||
```
|
||||
CEO → 보라색 (#6C5CE7)
|
||||
CTO → 파랑색 (#0984E3)
|
||||
DEVELOPER → 초록색 (#00B894)
|
||||
QA → 노랑색 (#FDCB6E)
|
||||
PM_AGENT → 청록색 (#00CEC9)
|
||||
|
||||
INCIDENT_TRIAGE → 빨강 (#E17055)
|
||||
KB_CURATOR → 민트 (#55EFC4)
|
||||
SSL_WATCHER → 주황 (#FD79A8)
|
||||
WBS_MONITOR → 남색 (#74B9FF)
|
||||
PM_SUGGESTER → 연두 (#A29BFE)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. API 엔드포인트 설계
|
||||
|
||||
### 7.1 전체 목록 (16개)
|
||||
|
||||
| HTTP | 경로 | 설명 | 권한 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/agents | 에이전트 목록 | USER+ |
|
||||
| POST | /api/agents | 에이전트 생성 | ADMIN |
|
||||
| GET | /api/agents/stats | 통계 요약 | USER+ |
|
||||
| GET | /api/agents/orgchart | 조직도 | USER+ |
|
||||
| GET | /api/agents/approvals | 승인 대기 목록 | USER+ |
|
||||
| PATCH | /api/agents/approvals/{id}/review | 승인/거부 | USER+ |
|
||||
| GET | /api/agents/llm/health | LLM 상태 확인 | USER+ |
|
||||
| POST | /api/agents/llm/pull | 모델 다운로드 | ADMIN |
|
||||
| GET | /api/agents/{id} | 에이전트 상세 | USER+ |
|
||||
| PATCH | /api/agents/{id} | 에이전트 수정 | ADMIN |
|
||||
| DELETE | /api/agents/{id} | 에이전트 삭제 | ADMIN |
|
||||
| POST | /api/agents/{id}/heartbeat | 수동 하트비트 | USER+ |
|
||||
| POST | /api/agents/{id}/pause | 에이전트 일시정지 | ADMIN |
|
||||
| POST | /api/agents/{id}/resume | 에이전트 재개 | ADMIN |
|
||||
| GET | /api/agents/{id}/tasks | 태스크 목록 | USER+ |
|
||||
| POST | /api/agents/{id}/tasks | 태스크 생성 | USER+ |
|
||||
|
||||
> CUSTOMER 역할: 모든 에이전트 엔드포인트 접근 불가
|
||||
|
||||
### 7.2 주요 스키마
|
||||
|
||||
#### AgentConfigOut
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "장애 분류 에이전트",
|
||||
"role": "INCIDENT_TRIAGE",
|
||||
"description": "미배정 장애를 자동으로 분류합니다",
|
||||
"llm_model": "guardia-agent",
|
||||
"heartbeat_cron": "*/15 * * * *",
|
||||
"is_active": true,
|
||||
"status": "IDLE",
|
||||
"last_heartbeat": "2026-05-25T08:15:00",
|
||||
"total_tasks": 42,
|
||||
"total_tokens": 15300
|
||||
}
|
||||
```
|
||||
|
||||
#### AgentApprovalReview
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "APPROVED",
|
||||
"notes": "내용 확인 후 승인합니다"
|
||||
}
|
||||
```
|
||||
|
||||
#### AgentStatsOut
|
||||
|
||||
```json
|
||||
{
|
||||
"total_agents": 6,
|
||||
"active_agents": 4,
|
||||
"today_tasks": 12,
|
||||
"today_tokens": 5240,
|
||||
"pending_approvals": 2
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 보안 제약사항
|
||||
|
||||
### 8.1 런타임 LLM 보안
|
||||
|
||||
| 규칙 | 내용 |
|
||||
|------|------|
|
||||
| 외부 API 금지 | 모든 LLM 호출은 localhost:11434 (Ollama) 만 허용 |
|
||||
| 토큰 최대값 | num_predict: 2048 (무한 루프 방지) |
|
||||
| 온도 고정 | temperature: 0.2 (결정론적 응답) |
|
||||
| 타임아웃 | HTTP 요청 30초 타임아웃 |
|
||||
|
||||
### 8.2 에이전트 액션 보안
|
||||
|
||||
| 규칙 | 내용 |
|
||||
|------|------|
|
||||
| CRITICAL 액션 | 반드시 사람 승인 후 실행 |
|
||||
| 위험 명령어 | `rm -rf /`, `shutdown`, `mkfs` 등 차단 |
|
||||
| 서버 정보 | `ip_addr`, `ssh_user`, `os_pw_enc` API 응답 미포함 |
|
||||
| 파일 경로 | `file_path` 컬럼 API 응답 절대 미노출 |
|
||||
|
||||
### 8.3 접근 제어
|
||||
|
||||
| 역할 | 에이전트 조회 | 에이전트 생성/수정/삭제 | 승인 처리 |
|
||||
|------|--------------|----------------------|----------|
|
||||
| CUSTOMER | ✗ | ✗ | ✗ |
|
||||
| USER | ✓ | ✗ | ✓ |
|
||||
| OPERATOR | ✓ | ✗ | ✓ |
|
||||
| ADMIN | ✓ | ✓ | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## 9. 스케줄러 잡 설계
|
||||
|
||||
### 9.1 전체 잡 목록 (9개)
|
||||
|
||||
| ID | 잡 이름 | 스케줄 | 설명 |
|
||||
|----|---------|--------|------|
|
||||
| 1 | cert_check | 매일 09:00 | SSL 인증서 만료 확인 |
|
||||
| 2 | pm_check | 매일 09:05 | PM 점검 일정 확인 |
|
||||
| 3 | on_call_notify | 매일 08:55 | 온콜 교대 알림 |
|
||||
| 4 | batch_cleanup | 매일 02:00 | 배치 결과 정리 |
|
||||
| 5 | agent_incident_triage | 매 15분 | 장애 자동 분류 |
|
||||
| 6 | agent_kb_curator | 매시간 정각 | KB 자동 생성 |
|
||||
| 7 | agent_ssl_watcher | 매일 08:30 | SSL 만료 감시 |
|
||||
| 8 | agent_wbs_monitor | 매일 08:00 | WBS 지연 감지 |
|
||||
| 9 | agent_pm_suggester | 매일 09:00 | PM 일정 제안 |
|
||||
|
||||
### 9.2 에이전트 하트비트 흐름
|
||||
|
||||
```
|
||||
APScheduler → _agent_heartbeat_by_role(role)
|
||||
│
|
||||
▼
|
||||
AgentConfig WHERE role=? AND is_active=True 조회
|
||||
│
|
||||
┌─────────┴───────────┐
|
||||
없음│ │있음
|
||||
▼ ▼
|
||||
skip for each agent:
|
||||
get_agent_engine().run_heartbeat(id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 테스트 결과
|
||||
|
||||
### 10.1 구문 검사 (Syntax Check)
|
||||
|
||||
| 파일 | 결과 |
|
||||
|------|------|
|
||||
| `models.py` | ✅ 통과 |
|
||||
| `main.py` | ✅ 통과 |
|
||||
| `core/llm_client.py` | ✅ 통과 |
|
||||
| `core/agents.py` | ✅ 통과 |
|
||||
| `core/scheduler.py` | ✅ 통과 |
|
||||
| `routers/agents.py` | ✅ 통과 |
|
||||
|
||||
### 10.2 심볼 검증
|
||||
|
||||
| 항목 | 검증 내용 | 결과 |
|
||||
|------|-----------|------|
|
||||
| AgentRole enum | 10가지 역할 정의 | ✅ |
|
||||
| AgentEngine handlers | 6개 핸들러 구현 | ✅ |
|
||||
| OllamaClient | 7개 메서드 구현 | ✅ |
|
||||
| API 엔드포인트 | 16개 라우터 등록 | ✅ |
|
||||
| 스케줄러 잡 | 9개 (기존 4 + 신규 5) | ✅ |
|
||||
| main.py 라우터 | 34개 라우터 등록 | ✅ |
|
||||
|
||||
### 10.3 보안 검증
|
||||
|
||||
| 항목 | 결과 |
|
||||
|------|------|
|
||||
| 외부 LLM API 호출 없음 | ✅ |
|
||||
| ServerOut에 ip_addr 미포함 | ✅ |
|
||||
| CUSTOMER 역할 차단 | ✅ |
|
||||
| CRITICAL 승인 게이트 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 11. 운영 가이드
|
||||
|
||||
### 11.1 에이전트 등록
|
||||
|
||||
```bash
|
||||
# 장애 분류 에이전트 등록
|
||||
curl -X POST http://localhost:8001/api/agents \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "장애 분류 에이전트",
|
||||
"role": "INCIDENT_TRIAGE",
|
||||
"description": "미배정 장애를 자동으로 분류하고 우선순위를 설정합니다",
|
||||
"llm_model": "guardia-agent",
|
||||
"system_prompt": "당신은 IT 장애 분류 전문가입니다...",
|
||||
"heartbeat_cron": "*/15 * * * *",
|
||||
"is_active": true
|
||||
}'
|
||||
```
|
||||
|
||||
### 11.2 수동 하트비트 실행
|
||||
|
||||
```bash
|
||||
# 에이전트 즉시 실행
|
||||
curl -X POST http://localhost:8001/api/agents/1/heartbeat \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
### 11.3 승인 처리
|
||||
|
||||
```bash
|
||||
# 승인
|
||||
curl -X PATCH http://localhost:8001/api/agents/approvals/5/review \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "APPROVED", "notes": "내용 확인 후 승인"}'
|
||||
|
||||
# 거부
|
||||
curl -X PATCH http://localhost:8001/api/agents/approvals/5/review \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "REJECTED", "notes": "추가 검토 필요"}'
|
||||
```
|
||||
|
||||
### 11.4 LLM 상태 확인
|
||||
|
||||
```bash
|
||||
# Ollama 서버 상태
|
||||
curl http://localhost:8001/api/agents/llm/health
|
||||
|
||||
# 응답 예시
|
||||
{
|
||||
"healthy": true,
|
||||
"models": ["guardia-agent:latest", "llama3.1:8b", "codellama:7b"],
|
||||
"version": "0.1.x"
|
||||
}
|
||||
```
|
||||
|
||||
### 11.5 모니터링 대시보드
|
||||
|
||||
```
|
||||
브라우저 접속: http://localhost:8001/agents
|
||||
자동 갱신: 30초 간격
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 향후 로드맵
|
||||
|
||||
| 단계 | 기능 | 예상 시기 |
|
||||
|------|------|----------|
|
||||
| v1.1 | 에이전트 간 메시지 전달 (CEO→CTO 위임) | 2026 Q3 |
|
||||
| v1.2 | 에이전트 학습 (이전 태스크 기반 파인튜닝) | 2026 Q3 |
|
||||
| v1.3 | 멀티모달 지원 (스크린샷 분석) | 2026 Q4 |
|
||||
| v2.0 | 에이전트 마켓플레이스 (커뮤니티 에이전트) | 2027 Q1 |
|
||||
| v2.1 | 엣지 배포 (경량 모델: llama3.2:1b) | 2027 Q1 |
|
||||
|
||||
---
|
||||
|
||||
*이 문서는 GUARDiA ITSM Paperclip × GUARDiA Phase 1~4 구현 내용을 담고 있습니다.*
|
||||
*Ollama 공식 문서: https://ollama.com*
|
||||
*Paperclip GitHub: https://github.com/paperclipai/paperclip*
|
||||
206
09_확장개발_Priority1_UI구현.md
Normal file
206
09_확장개발_Priority1_UI구현.md
Normal file
@ -0,0 +1,206 @@
|
||||
# GUARDiA ITSM — Priority 1: UI 없는 백엔드 모듈 화면 구현
|
||||
|
||||
**문서 버전**: 1.0 | **작성일**: 2026-05-25
|
||||
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
백엔드 API는 완성되어 있으나 프론트엔드 HTML 페이지가 없는 7개 모듈의 SPA 구현.
|
||||
각 페이지는 `/static/style.css` 공유 테마를 사용하며 독립 경로로 접근한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 목록
|
||||
|
||||
| 페이지 | URL 경로 | 파일 | 백엔드 라우터 |
|
||||
|--------|---------|------|-------------|
|
||||
| 장애 관리 | /incidents | static/incidents.html | routers/incidents.py |
|
||||
| SSL 관리 | /ssl | static/ssl.html | routers/ssl_manager.py |
|
||||
| PM 점검 | /pm | static/pm.html | routers/pm.py |
|
||||
| 온콜 관리 | /oncall | static/oncall.html | routers/oncall.py |
|
||||
| 배치 작업 | /batch | static/batch.html | routers/batch.py |
|
||||
| 바이브 코딩 | /vibe | static/vibe.html | routers/vibe.py |
|
||||
| SI 프로젝트 | /si | static/si.html | si_projects/wbs/requirements/issues/risks/milestones/change_requests/tests.py |
|
||||
| 라이선스 관리 | /license | static/license.html | routers/license.py |
|
||||
|
||||
---
|
||||
|
||||
## 공통 설계 원칙
|
||||
|
||||
### 인증
|
||||
```javascript
|
||||
const token = localStorage.getItem('guardia_token');
|
||||
if (!token) { location.href = '/login'; return; }
|
||||
|
||||
async function api(method, path, body) {
|
||||
const res = await fetch(path, {
|
||||
method,
|
||||
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
});
|
||||
if (res.status === 401) { localStorage.removeItem('guardia_token'); location.href = '/login'; }
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
### 테마 복원
|
||||
```javascript
|
||||
document.body.dataset.theme = localStorage.getItem('guardia_theme') || 'dark';
|
||||
```
|
||||
|
||||
### 상단 네비게이션 바
|
||||
모든 외부 페이지는 공통 topnav를 가진다:
|
||||
- GUARDiA ITSM 로고 (/ 링크)
|
||||
- 페이지 링크: 대시보드, 장애관리, SSL, PM점검, 온콜, 배치, 바이브, SI, AI에이전트
|
||||
- 우측: 사용자명, 로그아웃 버튼
|
||||
|
||||
### 자동 새로고침
|
||||
```javascript
|
||||
setInterval(loadData, 30000);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 장애 관리 (incidents.html)
|
||||
|
||||
### 화면 구성
|
||||
1. **통계 카드 행** (4개): 전체, OPEN, P1/P2, 평균 MTTR
|
||||
2. **필터 툴바**: 상태 / 등급 / 키워드 검색
|
||||
3. **장애 테이블**: INC번호, 제목, 등급배지, 상태, 발생시각, 담당자, MTTR
|
||||
4. **등록 모달**: 제목, 설명, grade, assigned_to, affected_services, occurred_at
|
||||
5. **상세 모달**: 전체 정보 + 상태 전환 버튼 + SR 연결 + RCA 종료
|
||||
|
||||
### 등급 배지
|
||||
| 등급 | 색상 | 의미 |
|
||||
|------|------|------|
|
||||
| P1 | 빨강 #f87171 | 서비스 전체 중단 |
|
||||
| P2 | 주황 #fb923c | 주요 기능 영향 |
|
||||
| P3 | 노랑 #fcd34d | 일부 기능 저하 |
|
||||
| P4 | 초록 #4ade80 | 경미한 영향 |
|
||||
|
||||
### 상태 전환 규칙
|
||||
```
|
||||
OPEN → INVESTIGATING | CLOSED
|
||||
INVESTIGATING → MITIGATED | RESOLVED
|
||||
MITIGATED → INVESTIGATING | RESOLVED
|
||||
RESOLVED → CLOSED
|
||||
CLOSED → (전환 불가)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. SSL 인증서 관리 (ssl.html)
|
||||
|
||||
### 화면 구성
|
||||
1. **현황 카드** (4개): OK / WARN / URGENT / EXPIRED — 클릭 시 필터
|
||||
2. **만료 현황 테이블**: 서버명, 만료일, 남은일수 게이지, 경고레벨 배지, 점검/갱신 버튼
|
||||
3. **days 슬라이더**: 0~90일 범위 조회
|
||||
4. **갱신 기록 모달**: new_expire_date, renewed_by, notes
|
||||
5. **갱신 이력 모달**: 서버별 이력 테이블
|
||||
|
||||
### 경고 레벨 기준
|
||||
| 레벨 | 조건 | 배지 색상 |
|
||||
|------|------|----------|
|
||||
| OK | days_left > 30 | 초록 |
|
||||
| WARN | 7 < days_left ≤ 30 | 노랑 |
|
||||
| URGENT | 0 < days_left ≤ 7 | 주황 |
|
||||
| EXPIRED | days_left ≤ 0 | 빨강 |
|
||||
|
||||
---
|
||||
|
||||
## 3. PM 정기점검 (pm.html)
|
||||
|
||||
### 탭 구성
|
||||
- **탭 1 — 점검 스케줄**: 스케줄 목록, 즉시 실행, 스케줄 등록
|
||||
- **탭 2 — 점검 결과**: 타임테이블 선택 → 체크리스트 항목별 결과 입력 + Excel 다운로드
|
||||
- **탭 3 — 체크리스트 템플릿**: 서버역할별 템플릿 CRUD
|
||||
|
||||
### 점검 주기 옵션
|
||||
WEEKLY / BIWEEKLY / MONTHLY / QUARTERLY / SEMIANNUAL / ANNUAL / CUSTOM
|
||||
|
||||
### 결과 상태
|
||||
| 상태 | 의미 | 색상 |
|
||||
|------|------|------|
|
||||
| PASS | 정상 | 초록 |
|
||||
| FAIL | 실패 | 빨강 |
|
||||
| WARNING | 경고 | 노랑 |
|
||||
| NA | 해당없음 | 회색 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 온콜/당직 관리 (oncall.html)
|
||||
|
||||
### 탭 구성
|
||||
- **탭 1 — 월간 캘린더**: 연/월 네비게이션, 날짜 클릭 시 당직 등록 모달
|
||||
- **탭 2 — 목록 & 일괄 등록**: 당직 목록 테이블 + JSON 일괄 등록
|
||||
|
||||
### 상단 배너
|
||||
```
|
||||
📞 오늘 당직: [이름] ([시프트]) | 백업: [백업담당자]
|
||||
```
|
||||
|
||||
### 시프트 종류
|
||||
| 값 | 시간 | 색상 |
|
||||
|----|------|------|
|
||||
| ALL_DAY | 24시간 | 파랑 |
|
||||
| DAYTIME | 09:00~18:00 | 초록 |
|
||||
| NIGHTTIME | 18:00~익일09:00 | 보라 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 배치 작업 관리 (batch.html)
|
||||
|
||||
### 탭 구성
|
||||
- **탭 1 — 배치 작업**: 작업 목록, 활성/비활성 토글, 즉시 실행, 등록
|
||||
- **탭 2 — 실행 이력**: 최근 실행 이력 통합, 상세 모달 (stdout 전체)
|
||||
|
||||
### 작업 등록 필드
|
||||
| 필드 | 설명 |
|
||||
|------|------|
|
||||
| job_name | 작업명 |
|
||||
| server_id | 대상 서버 |
|
||||
| cron_expr | cron 표현식 (예: `0 2 * * *`) |
|
||||
| command | 실행 명령어 |
|
||||
| timeout_sec | 타임아웃 (초) |
|
||||
| alert_on_fail | 실패 시 SR 자동 생성 여부 |
|
||||
|
||||
### 실행 결과 상태
|
||||
SUCCESS(초록) / FAILED(빨강) / TIMEOUT(주황) / RUNNING(파랑 깜빡)
|
||||
|
||||
---
|
||||
|
||||
## 6. 바이브 코딩 세션 (vibe.html)
|
||||
|
||||
### 탭 구성
|
||||
- **탭 1 — 활성 세션**: 세션 카드 그리드, 파이프라인 스텝 바
|
||||
- **탭 2 — 이력**: 전체 세션 이력 테이블
|
||||
- **탭 3 — 프로젝트**: 등록된 프로젝트 목록 + 등록 모달
|
||||
|
||||
### 파이프라인 단계
|
||||
```
|
||||
PENDING → CODING → BUILDING → TESTING → DEPLOYING → COMPLETED
|
||||
```
|
||||
|
||||
### Jenkins 연결 상태
|
||||
- 상단 배너: `GET /api/vibe/jenkins/health` 결과 표시
|
||||
- 연결됨(초록) / 오프라인(빨강)
|
||||
|
||||
---
|
||||
|
||||
## 7. SI 프로젝트 관리 (si.html)
|
||||
|
||||
### 탭 구성 (7탭)
|
||||
- **프로젝트**: SI 프로젝트 목록, 등록, 단계 전환
|
||||
- **WBS**: Gantt 차트 스타일 WBS 트리, 진척률 시각화
|
||||
- **요구사항**: RFP → 확정 요구사항 관리
|
||||
- **이슈**: 프로젝트 이슈 목록, 상태 전환
|
||||
- **리스크**: 위험 매트릭스 (확률 × 영향도), 이슈 전환
|
||||
- **마일스톤**: 마일스톤 타임라인, 산출물 목록
|
||||
- **변경요청(CR)**: CR 등록, 영향도 분석, 승인 워크플로우
|
||||
- **테스트**: 테스트 계획 → 케이스 → 실행 → 결함 관리
|
||||
|
||||
### SI 프로젝트 상태 전환
|
||||
```
|
||||
PLANNING → ANALYSIS → DESIGN → DEVELOPMENT → TESTING → DEPLOYMENT → COMPLETED
|
||||
```
|
||||
159
10_확장개발_Priority2_코어모듈.md
Normal file
159
10_확장개발_Priority2_코어모듈.md
Normal file
@ -0,0 +1,159 @@
|
||||
# GUARDiA ITSM — Priority 2: 코어 모듈 구현
|
||||
|
||||
**문서 버전**: 1.0 | **작성일**: 2026-05-25
|
||||
|
||||
---
|
||||
|
||||
## 1. core/vibe_bridge.py — Claude CLI SDK 연동
|
||||
|
||||
### 목적
|
||||
`subprocess` 방식 대신 Python SDK를 직접 연동하여 Claude CLI 세션을 관리한다.
|
||||
|
||||
### 주요 클래스
|
||||
|
||||
```python
|
||||
class VibeBridge:
|
||||
"""Claude CLI SDK 비동기 브리지"""
|
||||
|
||||
async def start_session(self, sr_id: str, project_path: str) -> str:
|
||||
"""새 Claude 세션 시작 → session_id 반환"""
|
||||
|
||||
async def send_message(self, session_id: str, message: str) -> str:
|
||||
"""세션에 메시지 전송 → 응답 반환"""
|
||||
|
||||
async def get_session_status(self, session_id: str) -> dict:
|
||||
"""세션 상태 조회 {active, last_response, tokens_used}"""
|
||||
|
||||
async def close_session(self, session_id: str) -> bool:
|
||||
"""세션 종료"""
|
||||
|
||||
async def resume_session(self, session_id: str, message: str) -> str:
|
||||
"""기존 세션 재개"""
|
||||
```
|
||||
|
||||
### 환경 변수
|
||||
```bash
|
||||
CLAUDE_CLI_PATH=/usr/local/bin/claude
|
||||
CLAUDE_WORKSPACE_ROOT=/opt/guardia/workspaces
|
||||
CLAUDE_SESSION_TIMEOUT=3600 # 세션 타임아웃 (초)
|
||||
```
|
||||
|
||||
### 사용 예시
|
||||
```python
|
||||
bridge = VibeBridge()
|
||||
session_id = await bridge.start_session(
|
||||
sr_id="SR-20260525-000001",
|
||||
project_path="/opt/src/myproject"
|
||||
)
|
||||
response = await bridge.send_message(session_id, "버그를 수정해주세요")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. core/deploy_pipeline.py — 배포 파이프라인 오케스트레이터
|
||||
|
||||
### 목적
|
||||
빌드 → 테스트 → 배포 → 헬스체크 → ITSM 콜백의 전체 파이프라인을 관리한다.
|
||||
|
||||
### 파이프라인 단계
|
||||
|
||||
| 단계 | 함수 | 설명 |
|
||||
|------|------|------|
|
||||
| pre_check | `_pre_check()` | 배포 환경 사전 점검 (서버 연결, 디스크 용량) |
|
||||
| build | `_build()` | tb_project.build_cmd 실행 |
|
||||
| test | `_test()` | tb_project.test_cmd 실행, 실패 시 중단 |
|
||||
| backup | `_backup()` | 현재 배포본 백업 (rollback 대비) |
|
||||
| deploy | `_deploy()` | SSH로 파일 전송 → deploy_path |
|
||||
| restart | `_restart()` | was_restart_cmd 실행 |
|
||||
| health_check | `_health_check()` | HTTP GET → health_check_url (10회 재시도) |
|
||||
| notify | `_notify()` | WorkLog 등록 + 메신저 알림 |
|
||||
|
||||
### 클래스 구조
|
||||
|
||||
```python
|
||||
class DeployPipeline:
|
||||
async def run(self, session_id: int) -> PipelineResult:
|
||||
"""전체 파이프라인 실행"""
|
||||
|
||||
async def rollback(self, session_id: int) -> bool:
|
||||
"""이전 버전으로 롤백"""
|
||||
|
||||
async def get_status(self, session_id: int) -> PipelineStatus:
|
||||
"""현재 진행 단계 조회"""
|
||||
```
|
||||
|
||||
### 오류 처리
|
||||
- 각 단계 실패 시 `tb_vibe_session.error_msg` 업데이트
|
||||
- test 단계 실패 → 배포 중단, SR 상태 = FAILED_ROLLBACK
|
||||
- health_check 10회 실패 → 자동 rollback 실행
|
||||
|
||||
---
|
||||
|
||||
## 3. 배치 잡 동적 APScheduler 등록
|
||||
|
||||
### 목적
|
||||
`tb_batch_job` 테이블의 cron_expr을 APScheduler에 동적으로 등록/제거한다.
|
||||
|
||||
### 구현 위치
|
||||
`core/scheduler.py` 확장
|
||||
|
||||
### API 연동 흐름
|
||||
|
||||
```
|
||||
POST /api/batch/jobs/{id}/enable
|
||||
→ scheduler.add_job(
|
||||
run_batch_job,
|
||||
CronTrigger.from_crontab(job.cron_expr),
|
||||
id=f"batch_{job.id}",
|
||||
args=[job.id],
|
||||
replace_existing=True
|
||||
)
|
||||
|
||||
POST /api/batch/jobs/{id}/disable
|
||||
→ scheduler.remove_job(f"batch_{job.id}")
|
||||
|
||||
서버 시작 시 (lifespan):
|
||||
→ 모든 is_active=True 배치 잡 자동 등록
|
||||
```
|
||||
|
||||
### run_batch_job 함수
|
||||
```python
|
||||
async def run_batch_job(job_id: int):
|
||||
async with SessionLocal() as db:
|
||||
job = await db.get(BatchJob, job_id)
|
||||
run = BatchRun(job_id=job_id, status="RUNNING", started_at=datetime.utcnow())
|
||||
db.add(run)
|
||||
await db.commit()
|
||||
|
||||
try:
|
||||
result = await execute_ssh_command(job.server_id, job.command, job.timeout_sec)
|
||||
run.status = "SUCCESS" if result.exit_code == 0 else "FAILED"
|
||||
run.stdout = result.stdout[-5000:] # 마지막 5000자
|
||||
run.exit_code = result.exit_code
|
||||
except asyncio.TimeoutError:
|
||||
run.status = "TIMEOUT"
|
||||
finally:
|
||||
run.completed_at = datetime.utcnow()
|
||||
await db.commit()
|
||||
|
||||
# alert_on_fail 처리
|
||||
if run.status != "SUCCESS" and job.alert_on_fail:
|
||||
await create_sr_for_batch_failure(job, run, db)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. SM 스크립트 실행 이력 UI
|
||||
|
||||
### 목적
|
||||
SSH 명령 실행 결과를 `tb_work_log` 기반으로 조회하여 index.html에 표시한다.
|
||||
|
||||
### API
|
||||
```
|
||||
GET /api/work-logs?sr_id={id}&work_type=SSH_EXEC
|
||||
```
|
||||
|
||||
### index.html 추가 뷰
|
||||
- 스크립트 관리 뷰에 "실행 이력" 탭 추가
|
||||
- 테이블: 서버명, 스크립트명, 실행시각, 결과(PASS/FAIL), 소요시간
|
||||
- 행 클릭 시 stdout/stderr 상세 모달
|
||||
197
11_확장개발_Priority3_AI에이전트.md
Normal file
197
11_확장개발_Priority3_AI에이전트.md
Normal file
@ -0,0 +1,197 @@
|
||||
# GUARDiA ITSM — Priority 3: AI 에이전트 확장
|
||||
|
||||
**문서 버전**: 1.0 | **작성일**: 2026-05-25
|
||||
|
||||
---
|
||||
|
||||
## 1. 장애 RCA 자동 초안 생성
|
||||
|
||||
### 목적
|
||||
INCIDENT_TRIAGE 에이전트 확장. RESOLVED 상태 장애에 대해 Ollama로 RCA 문서 초안을 자동 생성한다.
|
||||
|
||||
### 동작 흐름
|
||||
```
|
||||
1. tb_incident WHERE status='RESOLVED' AND rca_draft IS NULL 조회
|
||||
2. LLM 프롬프트 구성:
|
||||
- 장애 제목, 설명, 등급, 발생~해소 시각, 조치 이력
|
||||
3. Ollama json_generate() 호출
|
||||
4. 결과 구조:
|
||||
{
|
||||
"root_cause": "근본 원인",
|
||||
"timeline": "사건 타임라인",
|
||||
"impact": "영향 범위",
|
||||
"resolution": "해결 과정",
|
||||
"preventive_measures": "재발 방지 조치",
|
||||
"lessons_learned": "교훈"
|
||||
}
|
||||
5. tb_incident.rca_draft 컬럼에 저장 (JSON)
|
||||
6. AgentApproval(PENDING) 생성 — 담당자 검토 후 최종 확정
|
||||
```
|
||||
|
||||
### 구현 위치
|
||||
`core/agents.py` → `_incident_triage()` 핸들러 하단 추가
|
||||
```python
|
||||
# RESOLVED 장애 RCA 초안 생성
|
||||
resolved_incidents = await db.execute(
|
||||
select(Incident).where(
|
||||
Incident.status == "RESOLVED",
|
||||
Incident.rca_draft == None
|
||||
).limit(5)
|
||||
)
|
||||
for inc in resolved_incidents.scalars():
|
||||
rca = await llm.json_generate(rca_prompt(inc), agent.llm_model)
|
||||
inc.rca_draft = rca
|
||||
await db.commit()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 에이전트 간 메시지 전달
|
||||
|
||||
### 목적
|
||||
CEO 에이전트가 CTO/PM_AGENT에게 태스크를 위임할 수 있도록 에이전트 간 메시지 전달 체계를 구현한다.
|
||||
|
||||
### 신규 DB 테이블: tb_agent_message
|
||||
|
||||
```sql
|
||||
CREATE TABLE tb_agent_message (
|
||||
id INTEGER PRIMARY KEY,
|
||||
from_agent_id INTEGER REFERENCES tb_agent_config(id),
|
||||
to_agent_id INTEGER REFERENCES tb_agent_config(id),
|
||||
message_type VARCHAR(30), -- TASK_DELEGATION / STATUS_UPDATE / ESCALATION
|
||||
subject VARCHAR(200),
|
||||
body TEXT,
|
||||
metadata JSON,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
read_at DATETIME
|
||||
);
|
||||
```
|
||||
|
||||
### 메시지 타입
|
||||
|
||||
| 타입 | 설명 | 트리거 |
|
||||
|------|------|--------|
|
||||
| TASK_DELEGATION | 태스크 위임 | CEO → CTO: 개발 태스크 |
|
||||
| STATUS_UPDATE | 상태 보고 | CTO/Dev → CEO: 완료 보고 |
|
||||
| ESCALATION | 에스컬레이션 | 하위 에이전트 → 상위 에이전트 |
|
||||
|
||||
### API 엔드포인트 추가
|
||||
```
|
||||
GET /api/agents/{id}/messages 수신 메시지 목록
|
||||
POST /api/agents/{id}/messages 메시지 전송
|
||||
PATCH /api/agents/messages/{msg_id}/read 읽음 처리
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. KB 품질 검토 자동화
|
||||
|
||||
### 목적
|
||||
KB_CURATOR가 생성한 초안에 자동으로 품질 점수를 부여하고, 기준 이상이면 자동 발행한다.
|
||||
|
||||
### 품질 평가 기준
|
||||
| 항목 | 가중치 | 설명 |
|
||||
|------|--------|------|
|
||||
| completeness | 40% | 증상/원인/해결책 3섹션 완성도 |
|
||||
| clarity | 30% | 문장 명확성 및 가독성 |
|
||||
| actionability | 30% | 실제 조치 가능한 구체적 내용 여부 |
|
||||
|
||||
### 동작 흐름
|
||||
```python
|
||||
# kb_curator 핸들러 확장
|
||||
quality = await llm.json_generate(
|
||||
quality_prompt(kb_doc),
|
||||
agent.llm_model
|
||||
)
|
||||
# quality = {"completeness": 85, "clarity": 78, "actionability": 82, "overall": 82}
|
||||
|
||||
kb_doc.quality_score = quality.get("overall", 0)
|
||||
if kb_doc.quality_score >= 80:
|
||||
kb_doc.published = True # 자동 발행
|
||||
approval.status = "AUTO_APPROVED"
|
||||
else:
|
||||
approval.status = "PENDING" # 수동 검토 필요
|
||||
```
|
||||
|
||||
### 모델 변경 (models.py)
|
||||
`KBDocument`에 `quality_score: int` 컬럼 추가
|
||||
|
||||
---
|
||||
|
||||
## 4. WBS 지연 완료 예측
|
||||
|
||||
### 목적
|
||||
WBS_MONITOR 핸들러 확장. 완료율 추이를 분석하여 프로젝트 완료 예상일을 계산한다.
|
||||
|
||||
### 예측 알고리즘
|
||||
```python
|
||||
# 최근 7일간 완료율 기울기 계산
|
||||
prev_rate = wbs_snapshot_7days_ago.completion_rate # 예: 45%
|
||||
curr_rate = current_completion_rate # 예: 52%
|
||||
daily_delta = (curr_rate - prev_rate) / 7 # 1%/일
|
||||
|
||||
remaining = 100 - curr_rate # 48%
|
||||
est_days = remaining / daily_delta if daily_delta > 0 else 999
|
||||
est_completion = date.today() + timedelta(days=est_days)
|
||||
|
||||
# planned_end_date와 비교 → 초과 시 리스크 등록
|
||||
if est_completion > project.planned_end_date:
|
||||
delay_days = (est_completion - project.planned_end_date).days
|
||||
# WBS 리스크 자동 등록
|
||||
```
|
||||
|
||||
### 저장 위치
|
||||
`si_project.estimated_completion` 컬럼 업데이트 (또는 AgentTask에 기록)
|
||||
|
||||
---
|
||||
|
||||
## 5. 에이전트 파인튜닝 파이프라인
|
||||
|
||||
### 목적
|
||||
누적된 `tb_agent_task(COMPLETED)` 데이터를 기반으로 Ollama 커스텀 모델을 파인튜닝한다.
|
||||
|
||||
### 파이프라인 단계
|
||||
|
||||
```
|
||||
1. 데이터 수집
|
||||
SELECT input_data, output_data FROM tb_agent_task
|
||||
WHERE status='COMPLETED' AND tokens_used > 0
|
||||
LIMIT 1000
|
||||
|
||||
2. JSONL 파일 생성 (Ollama fine-tune 포맷)
|
||||
{"prompt": "...", "response": "..."}
|
||||
→ /opt/guardia/finetune/guardia-agent-v2.jsonl
|
||||
|
||||
3. Modelfile 생성
|
||||
FROM guardia-agent
|
||||
TRAIN /opt/guardia/finetune/guardia-agent-v2.jsonl
|
||||
|
||||
4. ollama create 실행
|
||||
ollama create guardia-agent-v2 -f Modelfile.guardia-v2
|
||||
|
||||
5. 헬스체크 후 active model 전환
|
||||
AgentConfig.llm_model = "guardia-agent-v2"
|
||||
```
|
||||
|
||||
### API 엔드포인트
|
||||
```
|
||||
POST /api/agents/finetune/start 파인튜닝 시작 (ADMIN only)
|
||||
GET /api/agents/finetune/status 진행 상태 조회
|
||||
```
|
||||
|
||||
### 구현 위치
|
||||
`core/llm_client.py`에 `fine_tune()` 메서드 추가
|
||||
|
||||
```python
|
||||
async def fine_tune(self, dataset_path: str, model_name: str) -> bool:
|
||||
"""Ollama 모델 파인튜닝 실행"""
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
"ollama", "create", model_name,
|
||||
"-f", f"Modelfile.{model_name}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await proc.communicate()
|
||||
return proc.returncode == 0
|
||||
```
|
||||
175
12_확장개발_Priority4_인프라.md
Normal file
175
12_확장개발_Priority4_인프라.md
Normal file
@ -0,0 +1,175 @@
|
||||
# GUARDiA ITSM — Priority 4: 인프라 / 플랫폼
|
||||
|
||||
**문서 버전**: 1.0 | **작성일**: 2026-05-25
|
||||
|
||||
---
|
||||
|
||||
## 1. PostgreSQL 마이그레이션
|
||||
|
||||
### database.py 변경
|
||||
|
||||
```python
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
# 환경변수 기반 DB 자동 선택
|
||||
DATABASE_URL = os.getenv(
|
||||
"DATABASE_URL",
|
||||
"sqlite+aiosqlite:///./guardia.db" # 기본: SQLite (개발)
|
||||
)
|
||||
# PostgreSQL 예시:
|
||||
# postgresql+asyncpg://guardia:password@localhost:5432/guardia
|
||||
|
||||
engine = create_async_engine(
|
||||
DATABASE_URL,
|
||||
echo=False,
|
||||
pool_pre_ping=True,
|
||||
pool_size=10 if "postgresql" in DATABASE_URL else 5,
|
||||
max_overflow=20 if "postgresql" in DATABASE_URL else 0,
|
||||
)
|
||||
SessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
```
|
||||
|
||||
### Alembic 설정
|
||||
|
||||
```bash
|
||||
# 설치
|
||||
pip install alembic asyncpg
|
||||
|
||||
# 초기화
|
||||
cd C:\GUARDiA\itsm
|
||||
alembic init migrations
|
||||
|
||||
# alembic.ini 수정
|
||||
# sqlalchemy.url = postgresql+asyncpg://user:pass@localhost:5432/guardia
|
||||
|
||||
# 첫 마이그레이션 생성
|
||||
alembic revision --autogenerate -m "initial_schema"
|
||||
|
||||
# 적용
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
### 마이그레이션 파일 위치
|
||||
```
|
||||
itsm/
|
||||
├── alembic.ini
|
||||
└── migrations/
|
||||
├── env.py
|
||||
└── versions/
|
||||
└── 001_initial_schema.py
|
||||
```
|
||||
|
||||
### SQLite → PostgreSQL 차이점 주의사항
|
||||
|
||||
| 항목 | SQLite | PostgreSQL |
|
||||
|------|--------|-----------|
|
||||
| JSON 컬럼 | 문자열 저장 | JSONB (인덱싱 가능) |
|
||||
| 날짜 함수 | `strftime()` | `date_trunc()`, `extract()` |
|
||||
| 자동 증가 | `INTEGER PRIMARY KEY` | `SERIAL` 또는 `BIGSERIAL` |
|
||||
| 동시성 | 파일 잠금 | MVCC (완전 동시 지원) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 멀티테넌트 지원
|
||||
|
||||
### 설계 방식: 공유 스키마 + tenant_id 컬럼
|
||||
|
||||
```python
|
||||
# models.py — 주요 테이블에 tenant_id 추가
|
||||
class Institution(Base):
|
||||
tenant_id: str = Column(String(20), nullable=False, index=True)
|
||||
|
||||
class SRRequest(Base):
|
||||
tenant_id: str = Column(String(20), nullable=False, index=True)
|
||||
|
||||
class Server(Base):
|
||||
tenant_id: str = Column(String(20), nullable=False, index=True)
|
||||
```
|
||||
|
||||
### JWT에 tenant_id 포함
|
||||
|
||||
```python
|
||||
# core/auth.py
|
||||
def create_access_token(user: User) -> str:
|
||||
payload = {
|
||||
"sub": str(user.id),
|
||||
"role": user.role,
|
||||
"tenant_id": user.inst_code, # 기관코드를 tenant_id로 사용
|
||||
"exp": ...
|
||||
}
|
||||
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
|
||||
```
|
||||
|
||||
### 자동 필터링 미들웨어
|
||||
|
||||
```python
|
||||
# 모든 조회 쿼리에 tenant_id 자동 추가
|
||||
async def get_tenant_db(current_user: User = Depends(get_current_user)):
|
||||
async with SessionLocal() as db:
|
||||
db.tenant_id = current_user.tenant_id
|
||||
yield db
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. RBAC 세분화
|
||||
|
||||
### 신규 역할: DEPLOY_ENGINEER
|
||||
|
||||
```python
|
||||
class UserRole(str, Enum):
|
||||
ADMIN = "ADMIN"
|
||||
PM = "PM"
|
||||
ENGINEER = "ENGINEER"
|
||||
DEPLOY_ENGINEER = "DEPLOY_ENGINEER" # 신규
|
||||
CUSTOMER = "CUSTOMER"
|
||||
```
|
||||
|
||||
### 권한 매핑
|
||||
|
||||
| 기능 | ADMIN | PM | ENGINEER | DEPLOY_ENGINEER | CUSTOMER |
|
||||
|------|-------|-----|---------|-----------------|---------|
|
||||
| SR 조회 | ✅ | ✅ | ✅ | ✅ | ✅ (자신만) |
|
||||
| SR 생성 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| 배포 트리거 | ✅ | ✅ | ❌ | ✅ | ❌ |
|
||||
| 배치 즉시 실행 | ✅ | ❌ | ❌ | ✅ | ❌ |
|
||||
| 서버 자격증명 조회 | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| 에이전트 관리 | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| 사용자 관리 | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
### 구현
|
||||
|
||||
```python
|
||||
# core/auth.py
|
||||
DEPLOY_ROLES = {UserRole.ADMIN, UserRole.PM, UserRole.DEPLOY_ENGINEER}
|
||||
|
||||
def require_deploy_role(current_user: User = Depends(get_current_user)):
|
||||
if current_user.role not in DEPLOY_ROLES:
|
||||
raise HTTPException(403, "배포 권한이 없습니다")
|
||||
return current_user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 배포 이력 UI
|
||||
|
||||
### vibe.html 내 배포 이력 탭 추가
|
||||
|
||||
- Jenkins 빌드 번호별 조회
|
||||
- 아티팩트 목록 (다운로드 링크)
|
||||
- 롤백 버튼 (이전 빌드로 롤백)
|
||||
|
||||
### Jenkins 빌드 이력 API (core/cicd.py)
|
||||
|
||||
```python
|
||||
async def get_build_history(project_name: str, limit: int = 10) -> list[BuildInfo]:
|
||||
"""Jenkins 빌드 이력 조회"""
|
||||
url = f"{JENKINS_URL}/job/{project_name}/api/json?tree=builds[number,result,timestamp,duration,artifacts[*]]"
|
||||
...
|
||||
|
||||
async def get_artifact_url(project_name: str, build_number: int, artifact: str) -> str:
|
||||
"""빌드 아티팩트 다운로드 URL 반환"""
|
||||
return f"{JENKINS_URL}/job/{project_name}/{build_number}/artifact/{artifact}"
|
||||
```
|
||||
243
13_확장개발_Priority5_외부연동.md
Normal file
243
13_확장개발_Priority5_외부연동.md
Normal file
@ -0,0 +1,243 @@
|
||||
# GUARDiA ITSM — Priority 5: 외부 시스템 연동 고도화
|
||||
|
||||
**문서 버전**: 1.0 | **작성일**: 2026-05-25
|
||||
|
||||
---
|
||||
|
||||
## 1. Jenkins 운영 배포 승인 연동
|
||||
|
||||
### 목적
|
||||
Jenkins Declarative Pipeline의 `input()` 단계와 ITSM 승인 API를 양방향으로 연동한다.
|
||||
운영(prd) 배포 시 ITSM에서 PM이 승인해야만 Jenkins 파이프라인이 진행된다.
|
||||
|
||||
### 흐름
|
||||
|
||||
```
|
||||
GUARDiA Vibe 세션 → "배포(prd)" 버튼 클릭
|
||||
│
|
||||
▼ POST /api/vibe/{id}/deploy { environment: "prd" }
|
||||
│
|
||||
▼ Jenkins Job 트리거 (core/cicd.py)
|
||||
│
|
||||
Jenkins Pipeline:
|
||||
stage('Request ITSM Approval') {
|
||||
steps {
|
||||
sh """
|
||||
curl -X POST ${ITSM_URL}/api/vibe/${SESSION_ID}/request-approval \
|
||||
-H "Authorization: Bearer ${ITSM_TOKEN}" \
|
||||
-d '{"environment": "prd", "build_number": ${BUILD_NUMBER}}'
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Wait for ITSM Approval') {
|
||||
steps {
|
||||
timeout(time: 60, unit: 'MINUTES') {
|
||||
waitUntil {
|
||||
def result = sh(
|
||||
script: "curl -s ${ITSM_URL}/api/vibe/${SESSION_ID}/approval-status",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
return result == '"APPROVED"'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Deploy to Production') { ... }
|
||||
```
|
||||
|
||||
### ITSM API 신규 엔드포인트
|
||||
|
||||
```
|
||||
POST /api/vibe/{id}/request-approval
|
||||
→ VibeDeploy 승인 요청 생성 (SRApproval 또는 AgentApproval 재사용)
|
||||
→ PM에게 메신저 알림
|
||||
|
||||
GET /api/vibe/{id}/approval-status
|
||||
→ "PENDING" | "APPROVED" | "REJECTED"
|
||||
|
||||
PATCH /api/vibe/{id}/approve
|
||||
→ PM이 ITSM UI에서 승인 처리
|
||||
→ Jenkins 폴링 응답에 "APPROVED" 반환
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. SonarQube Quality Gate 연동
|
||||
|
||||
### 목적
|
||||
Jenkins 빌드 완료 후 SonarQube 분석 결과를 ITSM에 자동 등록한다.
|
||||
Quality Gate 실패 시 SR을 생성하여 개발팀에 통보한다.
|
||||
|
||||
### core/cicd.py 추가 함수
|
||||
|
||||
```python
|
||||
async def get_sonarqube_result(project_key: str) -> SonarResult:
|
||||
"""SonarQube Quality Gate 결과 조회"""
|
||||
url = f"{SONARQUBE_URL}/api/qualitygates/project_status?projectKey={project_key}"
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(url, headers={"Authorization": f"Bearer {SONARQUBE_TOKEN}"})
|
||||
data = r.json()
|
||||
status = data["projectStatus"]["status"] # OK | WARN | ERROR
|
||||
|
||||
metrics = {
|
||||
c["metricKey"]: c["value"]
|
||||
for c in data["projectStatus"]["conditions"]
|
||||
}
|
||||
return SonarResult(
|
||||
status=status,
|
||||
coverage=metrics.get("coverage", "N/A"),
|
||||
bugs=metrics.get("bugs", "0"),
|
||||
vulnerabilities=metrics.get("vulnerabilities", "0"),
|
||||
code_smells=metrics.get("code_smells", "0"),
|
||||
)
|
||||
|
||||
async def handle_sonar_gate_failure(session_id: int, result: SonarResult, db: AsyncSession):
|
||||
"""Quality Gate 실패 시 SR 자동 생성"""
|
||||
if result.status == "ERROR":
|
||||
sr = SRRequest(
|
||||
title=f"SonarQube Quality Gate 실패 — 세션 {session_id}",
|
||||
description=f"취약점: {result.vulnerabilities}, 버그: {result.bugs}, 커버리지: {result.coverage}%",
|
||||
sr_type="DEPLOY",
|
||||
priority="HIGH",
|
||||
)
|
||||
db.add(sr)
|
||||
await db.commit()
|
||||
```
|
||||
|
||||
### Jenkins 연동 (Jenkinsfile에 추가)
|
||||
|
||||
```groovy
|
||||
stage('SonarQube Analysis') {
|
||||
steps {
|
||||
withSonarQubeEnv('SonarQube') {
|
||||
sh 'mvn sonar:sonar -Dsonar.projectKey=${PROJECT_KEY}'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Quality Gate') {
|
||||
steps {
|
||||
timeout(time: 5, unit: 'MINUTES') {
|
||||
waitForQualityGate abortPipeline: false
|
||||
}
|
||||
// ITSM에 결과 전송
|
||||
sh """
|
||||
curl -X POST ${ITSM_URL}/api/vibe/sonar-result \
|
||||
-d '{"session_id": ${SESSION_ID}, "project_key": "${PROJECT_KEY}"}'
|
||||
"""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 환경 변수
|
||||
```bash
|
||||
SONARQUBE_URL=http://sonar.agency.go.kr:9000
|
||||
SONARQUBE_TOKEN=<SonarQube API Token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. SSL 자동 갱신 (Let's Encrypt)
|
||||
|
||||
### 목적
|
||||
certbot과 연동하여 SSL 인증서를 자동으로 갱신하고, 결과를 ITSM에 기록한다.
|
||||
|
||||
### scripts/sm/ssl/ssl_auto_renew.sh
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# SSL 자동 갱신 스크립트 (서버에서 실행)
|
||||
set -euo pipefail
|
||||
|
||||
ITSM_URL="${ITSM_URL:-http://localhost:8001}"
|
||||
SERVER_ID="${SERVER_ID:-}"
|
||||
ITSM_TOKEN="${ITSM_TOKEN:-}"
|
||||
|
||||
# certbot 갱신
|
||||
if certbot renew --quiet --non-interactive \
|
||||
--deploy-hook "systemctl reload nginx 2>/dev/null || systemctl reload apache2 2>/dev/null"; then
|
||||
|
||||
# 새 만료일 조회
|
||||
NEW_EXPIRE=$(openssl x509 -in /etc/letsencrypt/live/*/cert.pem -noout -enddate \
|
||||
| cut -d= -f2)
|
||||
NEW_EXPIRE_ISO=$(date -d "$NEW_EXPIRE" +"%Y-%m-%d")
|
||||
|
||||
# ITSM에 갱신 기록
|
||||
curl -s -X POST "${ITSM_URL}/api/ssl/renew/${SERVER_ID}" \
|
||||
-H "Authorization: Bearer ${ITSM_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"new_expire_date\": \"${NEW_EXPIRE_ISO}\", \"renewed_by\": \"certbot-auto\", \"notes\": \"Let's Encrypt 자동 갱신\"}"
|
||||
|
||||
echo "[OK] SSL 갱신 완료: ${NEW_EXPIRE_ISO}"
|
||||
else
|
||||
echo "[WARN] SSL 갱신 불필요 (아직 유효기간 충분)"
|
||||
fi
|
||||
```
|
||||
|
||||
### 배치 작업 등록
|
||||
|
||||
`tb_batch_job`에 자동 갱신 배치 잡 등록:
|
||||
- `cron_expr`: `0 3 * * *` (매일 03:00)
|
||||
- `command`: `SSL_WATCHER=true bash /opt/guardia/scripts/ssl/ssl_auto_renew.sh`
|
||||
- `alert_on_fail`: true
|
||||
|
||||
---
|
||||
|
||||
## 4. SSE 스트리밍 확장
|
||||
|
||||
### 목적
|
||||
배치 실행 로그와 빌드 로그를 SSE로 실시간 스트리밍한다.
|
||||
|
||||
### core/events.py 확장
|
||||
|
||||
```python
|
||||
# 배치 실행 로그 스트리밍
|
||||
async def stream_batch_log(run_id: int) -> AsyncGenerator[str, None]:
|
||||
"""tb_batch_run.stdout 청크 단위 SSE 전송"""
|
||||
last_len = 0
|
||||
for _ in range(300): # 최대 5분 (1초 간격)
|
||||
async with SessionLocal() as db:
|
||||
run = await db.get(BatchRun, run_id)
|
||||
if run.stdout and len(run.stdout) > last_len:
|
||||
new_data = run.stdout[last_len:]
|
||||
last_len = len(run.stdout)
|
||||
yield f"data: {json.dumps({'chunk': new_data})}\n\n"
|
||||
if run.status not in ("RUNNING",):
|
||||
yield f"data: {json.dumps({'done': True, 'status': run.status})}\n\n"
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Jenkins 빌드 로그 스트리밍
|
||||
async def stream_build_log(session_id: int) -> AsyncGenerator[str, None]:
|
||||
"""Jenkins Build Log API 폴링 → SSE 전송"""
|
||||
# GET {JENKINS_URL}/job/{name}/{number}/logText/progressiveText?start=0
|
||||
offset = 0
|
||||
while True:
|
||||
log_chunk, next_offset = await cicd.get_progressive_log(session_id, offset)
|
||||
if log_chunk:
|
||||
yield f"data: {json.dumps({'chunk': log_chunk, 'offset': next_offset})}\n\n"
|
||||
offset = next_offset
|
||||
done = await cicd.is_build_complete(session_id)
|
||||
if done:
|
||||
yield f"data: {json.dumps({'done': True})}\n\n"
|
||||
break
|
||||
await asyncio.sleep(2)
|
||||
```
|
||||
|
||||
### 신규 엔드포인트
|
||||
|
||||
```
|
||||
GET /api/batch/runs/{run_id}/stream 배치 로그 실시간 스트리밍 (SSE)
|
||||
GET /api/vibe/{id}/build/stream 빌드 로그 실시간 스트리밍 (SSE)
|
||||
```
|
||||
|
||||
### 프론트엔드 연결
|
||||
|
||||
```javascript
|
||||
// batch.html — 실행 로그 스트리밍
|
||||
const es = new EventSource(`/api/batch/runs/${runId}/stream?token=${token}`);
|
||||
es.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data.chunk) logBox.textContent += data.chunk;
|
||||
if (data.done) { es.close(); updateRunStatus(data.status); }
|
||||
};
|
||||
```
|
||||
647
14_라이선스_키_발급_가이드.md
Normal file
647
14_라이선스_키_발급_가이드.md
Normal file
@ -0,0 +1,647 @@
|
||||
# GUARDiA ITSM — 라이선스 키 발급 및 적용 가이드
|
||||
|
||||
> **문서번호**: GUARDIA-LIC-001
|
||||
> **버전**: 1.0
|
||||
> **작성일**: 2026-05-28
|
||||
> **작성자**: GUARDiA 개발팀
|
||||
> **보안등급**: 내부용 (대외비) — 마스터 키 및 생성 절차는 벤더 내부 한정
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [라이선스 시스템 개요](#1-라이선스-시스템-개요)
|
||||
2. [에디션별 제한 및 기능](#2-에디션별-제한-및-기능)
|
||||
3. [마스터 키 생성 및 보관](#3-마스터-키-생성-및-보관)
|
||||
4. [라이선스 키 발급 (벤더 전용)](#4-라이선스-키-발급-벤더-전용)
|
||||
5. [라이선스 키 고객 적용 절차](#5-라이선스-키-고객-적용-절차)
|
||||
6. [라이선스 갱신 절차](#6-라이선스-갱신-절차)
|
||||
7. [라이선스 상태 모니터링](#7-라이선스-상태-모니터링)
|
||||
8. [CLI 레퍼런스](#8-cli-레퍼런스)
|
||||
9. [API 레퍼런스](#9-api-레퍼런스)
|
||||
10. [문제 해결](#10-문제-해결)
|
||||
|
||||
---
|
||||
|
||||
## 1. 라이선스 시스템 개요
|
||||
|
||||
### 1.1 보안 설계
|
||||
|
||||
GUARDiA 라이선스는 **완전 오프라인 검증** 방식으로, 외부 인터넷 연결 없이 검증된다.
|
||||
|
||||
```
|
||||
라이선스 키 구조:
|
||||
GRD-{base64url(iv[12B] + ciphertext + gcm_tag[16B] + hmac_sig[8B])}
|
||||
|
||||
암호화: AES-256-GCM (iv 12B + GCM tag 16B)
|
||||
서명: HMAC-SHA256 (앞 8B prefix — 위변조 즉시 탐지)
|
||||
키 파생: SHA-256(master_key + "guardia-aes-v1") → AES 키
|
||||
SHA-256(master_key + "guardia-license-hmac-v1") → HMAC 키
|
||||
```
|
||||
|
||||
**검증 흐름:**
|
||||
```
|
||||
고객 서버가 키 수신
|
||||
│
|
||||
▼ HMAC 서명 검증 (위변조 즉시 차단)
|
||||
│
|
||||
▼ AES-256-GCM 복호화 (마스터 키 불일치 시 실패)
|
||||
│
|
||||
▼ JSON 페이로드 파싱 (만료일, 에디션, 고객명 등)
|
||||
│
|
||||
▼ 만료일 비교 → valid: true / expired: true
|
||||
```
|
||||
|
||||
### 1.2 라이선스 페이로드 구성
|
||||
|
||||
```json
|
||||
{
|
||||
"license_id": "GRD-A1B2C3",
|
||||
"edition": "ENTERPRISE",
|
||||
"customer": "서울특별시청",
|
||||
"issued_at": "2026-05-28T00:00:00+00:00",
|
||||
"expires_at": "2027-05-28T00:00:00+00:00",
|
||||
"limits": {
|
||||
"max_institutions": -1,
|
||||
"max_users": -1,
|
||||
"max_servers": -1,
|
||||
"features": ["MFA", "LDAP", "PAM", "AI_AGENTS",
|
||||
"VULN_SCAN", "CICD", "ANALYTICS", "FINOPS"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 에디션별 제한 및 기능
|
||||
|
||||
| 에디션 | 기관 수 | 사용자 수 | 서버 수 | 활성화 기능 | 대상 |
|
||||
|--------|--------|---------|--------|-----------|------|
|
||||
| **COMMUNITY** | 1 | 10 | 20 | MFA | 소규모 단일 기관, 평가판 |
|
||||
| **STANDARD** | 50 | 200 | 500 | MFA, LDAP, PAM, AI_AGENTS | 중규모 기관·지자체 |
|
||||
| **ENTERPRISE** | 무제한(-1) | 무제한(-1) | 무제한(-1) | 전체 기능 | 광역단체, 대규모 멀티테넌트 |
|
||||
|
||||
### 기능별 에디션 요구사항
|
||||
|
||||
| 기능 코드 | 기능명 | 최소 에디션 |
|
||||
|----------|-------|-----------|
|
||||
| `MFA` | 2단계 인증 (TOTP/OTP) | COMMUNITY |
|
||||
| `LDAP` | LDAP/AD 디렉토리 연동 | STANDARD |
|
||||
| `PAM` | 특권 접근 관리 | STANDARD |
|
||||
| `AI_AGENTS` | AI 에이전트 오케스트레이션 | STANDARD |
|
||||
| `VULN_SCAN` | 보안 취약점 자동 스캔 | ENTERPRISE |
|
||||
| `CICD` | CI/CD Jenkins 연동 | ENTERPRISE |
|
||||
| `ANALYTICS` | 고급 분석 대시보드 | ENTERPRISE |
|
||||
| `FINOPS` | 비용 분석 (FinOps) | ENTERPRISE |
|
||||
|
||||
---
|
||||
|
||||
## 3. 마스터 키 생성 및 보관
|
||||
|
||||
> **경고**: 마스터 키는 절대 외부 유출 금지. 분실 시 기존 발급 라이선스 모두 무효화됨.
|
||||
|
||||
### 3.1 마스터 키 생성
|
||||
|
||||
마스터 키는 GUARDiA 벤더 내부 시스템에서 **1회 생성**하여 안전하게 보관한다.
|
||||
|
||||
```bash
|
||||
# 방법 1: Python (권장)
|
||||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
|
||||
# 방법 2: OpenSSL
|
||||
openssl rand -hex 32
|
||||
|
||||
# 결과 예시 (64자리 hex = 32바이트):
|
||||
# a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607
|
||||
```
|
||||
|
||||
### 3.2 마스터 키 보관 정책
|
||||
|
||||
| 보관 방법 | 설명 |
|
||||
|---------|------|
|
||||
| **운영 서버** | 환경변수 `GUARDIA_LICENSE_KEY` — systemd 서비스 파일 또는 .env (권한 600) |
|
||||
| **비상 백업** | 오프라인 금고 (USB 암호화 드라이브) |
|
||||
| **접근 권한** | 벤더 최고 관리자 2인 이상 알 고리즘 — 단독 접근 금지 |
|
||||
|
||||
### 3.3 .env 파일 설정 (운영 서버)
|
||||
|
||||
```ini
|
||||
# C:\GUARDiA\itsm\.env (권한: 소유자 읽기/쓰기만)
|
||||
GUARDIA_LICENSE_KEY=a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607
|
||||
```
|
||||
|
||||
```bash
|
||||
# Linux: 파일 권한 보호
|
||||
chmod 600 /opt/guardia/itsm/.env
|
||||
chown guardia:guardia /opt/guardia/itsm/.env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 라이선스 키 발급 (벤더 전용)
|
||||
|
||||
> 이 절차는 GUARDiA 벤더(개발사) 내부 운영자만 실행한다.
|
||||
|
||||
### 4.1 발급 전 확인 사항
|
||||
|
||||
```
|
||||
□ 고객명 (정식 기관명, 계약서 표기 명칭과 동일)
|
||||
□ 에디션 (COMMUNITY / STANDARD / ENTERPRISE)
|
||||
□ 유효 기간 (일수) 예: 365일(1년), 730일(2년)
|
||||
□ 커스텀 한도 여부 (특수 계약 시: max_institutions, max_users, max_servers 별도 지정)
|
||||
□ GUARDIA_LICENSE_KEY 환경변수 설정 확인
|
||||
```
|
||||
|
||||
### 4.2 CLI를 이용한 발급
|
||||
|
||||
```bash
|
||||
# 발급 서버(벤더 내부 PC)에서 실행
|
||||
cd C:\GUARDiA\itsm
|
||||
|
||||
# 방법 1: 환경변수에 마스터 키 설정 후 발급
|
||||
$env:GUARDIA_LICENSE_KEY = "a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607"
|
||||
python -m core.license --customer "서울특별시청" --edition ENTERPRISE --days 365
|
||||
|
||||
# 방법 2: --key 인수로 직접 지정 (환경변수 없어도 가능)
|
||||
python -m core.license \
|
||||
--customer "인천광역시청" \
|
||||
--edition STANDARD \
|
||||
--days 730 \
|
||||
--key a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607
|
||||
|
||||
# 커스텀 라이선스 ID 지정 (선택)
|
||||
python -m core.license --customer "부산광역시청" --edition ENTERPRISE --days 365 --lid GRD-BUSAN2026
|
||||
```
|
||||
|
||||
### 4.3 발급 결과 예시
|
||||
|
||||
```
|
||||
============================================================
|
||||
GUARDiA 라이선스 키 생성 완료
|
||||
============================================================
|
||||
고객명 : 서울특별시청
|
||||
에디션 : ENTERPRISE
|
||||
라이선스ID: GRD-A1B2C3
|
||||
발급일시 : 2026-05-28T00:00:00+00:00
|
||||
만료일시 : 2027-05-28T00:00:00+00:00
|
||||
유효기간 : 365일
|
||||
============================================================
|
||||
|
||||
라이선스 키:
|
||||
GRD-eyJsaWNlbnNlX2lkIjoiR1JELUExQjJDMyIsImVkaXRpb24iOiJFTlRFUlBSSVNFIn0...
|
||||
```
|
||||
|
||||
### 4.4 Python 코드를 이용한 발급 (자동화)
|
||||
|
||||
```python
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from core.license import generate_license_key, validate_license, LicenseEdition
|
||||
|
||||
MASTER_KEY = "a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607"
|
||||
|
||||
# 1년짜리 ENTERPRISE 라이선스 생성
|
||||
lic_key = generate_license_key(
|
||||
customer = "서울특별시청",
|
||||
edition = LicenseEdition.ENTERPRISE,
|
||||
expires_at = datetime.now(timezone.utc) + timedelta(days=365),
|
||||
master_key_hex = MASTER_KEY,
|
||||
)
|
||||
print(f"키: {lic_key}")
|
||||
|
||||
# 검증
|
||||
status = validate_license(lic_key, master_key_hex=MASTER_KEY)
|
||||
print(f"유효: {status['valid']}, 남은 일수: {status['days_remaining']}")
|
||||
```
|
||||
|
||||
### 4.5 커스텀 한도 지정 (특수 계약)
|
||||
|
||||
```python
|
||||
custom_limits = {
|
||||
"max_institutions": 10, # 기관 10개
|
||||
"max_users": 100, # 사용자 100명
|
||||
"max_servers": 200, # 서버 200대
|
||||
"features": ["MFA", "LDAP", "PAM"],
|
||||
}
|
||||
|
||||
lic_key = generate_license_key(
|
||||
customer = "경기도청",
|
||||
edition = LicenseEdition.STANDARD,
|
||||
expires_at = datetime.now(timezone.utc) + timedelta(days=365),
|
||||
custom_limits = custom_limits,
|
||||
master_key_hex = MASTER_KEY,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 라이선스 키 고객 적용 절차
|
||||
|
||||
### 5.1 방법 A: 웹 UI (권장)
|
||||
|
||||
1. ITSM 관리자 계정으로 로그인
|
||||
2. 사이드바 **🔏 라이선스 관리** 클릭 → `/license`
|
||||
3. **라이선스 키 등록/갱신** 섹션에 `GRD-...` 키 붙여넣기
|
||||
4. **라이선스 등록** 버튼 클릭
|
||||
5. 상단 배너에서 에디션·만료일 확인
|
||||
|
||||
```
|
||||
[상태 배너 예시]
|
||||
✅ ENTERPRISE 라이선스 활성 — 365일 남음 (서울특별시청)
|
||||
```
|
||||
|
||||
### 5.2 방법 B: API (자동화/스크립트)
|
||||
|
||||
```bash
|
||||
# 1. 관리자 로그인 후 토큰 획득
|
||||
TOKEN=$(curl -s -X POST http://localhost:8001/api/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"Admin!1234"}' \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
||||
|
||||
# 2. 라이선스 등록
|
||||
curl -X POST http://localhost:8001/api/license/activate \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"license_key": "GRD-eyJsaWNlbnNlX2lkIjoiR1JELUExQjJDMyI..."
|
||||
}'
|
||||
|
||||
# 성공 응답 예시:
|
||||
# {
|
||||
# "message": "ENTERPRISE 라이선스가 활성화되었습니다 (365일 남음).",
|
||||
# "license_id": "GRD-A1B2C3",
|
||||
# "edition": "ENTERPRISE",
|
||||
# "customer": "서울특별시청",
|
||||
# "expires_at": "2027-05-28T00:00:00+00:00",
|
||||
# "days_remaining": 365,
|
||||
# "valid": true
|
||||
# }
|
||||
```
|
||||
|
||||
### 5.3 방법 C: .env 파일 사전 구성 (설치 시점 적용)
|
||||
|
||||
배포 자동화 파이프라인에서 설치 시점에 라이선스를 미리 구성할 때:
|
||||
|
||||
```ini
|
||||
# C:\GUARDiA\itsm\.env
|
||||
SECRET_KEY=<jwt-secret>
|
||||
GUARDIA_LICENSE_KEY=<마스터_키_64자리_hex>
|
||||
```
|
||||
|
||||
서버 기동 시 ITSM이 자동으로 라이선스 상태를 출력한다:
|
||||
```
|
||||
[LICENSE] ENTERPRISE 라이선스 활성 (365일 남음) — 서울특별시청
|
||||
```
|
||||
|
||||
단, `.env`에 마스터 키만 있고 DB에 라이선스 레코드가 없으면 활성화는 안 된다.
|
||||
**DB에 키를 등록하는 것은 API 호출(방법 A/B) 필수.**
|
||||
|
||||
---
|
||||
|
||||
## 6. 라이선스 갱신 절차
|
||||
|
||||
### 6.1 갱신 타이밍
|
||||
|
||||
| 시점 | 시스템 반응 | 권장 조치 |
|
||||
|------|-----------|---------|
|
||||
| 만료 30일 전 | 웹 UI 황색 배너 경고, 서버 시작 시 경고 로그 | 갱신 라이선스 발급 준비 |
|
||||
| 만료 7일 전 | 매일 경고 | 즉시 갱신 신청 |
|
||||
| 만료 당일 | `expired: true`, 기능 제한 시작 | 긴급 갱신 |
|
||||
| 만료 후 | 기존 데이터 보존, 신규 등록만 차단 | 갱신 라이선스 즉시 적용 |
|
||||
|
||||
> **중요**: 만료 후에도 기존 등록된 기관·서버·사용자 데이터는 삭제되지 않는다.
|
||||
> 신규 생성(기관 추가, 서버 추가)만 한도 초과 시 차단된다.
|
||||
|
||||
### 6.2 갱신 라이선스 발급
|
||||
|
||||
동일 CLI로 새 키를 발급하면 된다. 라이선스 ID는 새로 자동 생성된다:
|
||||
|
||||
```bash
|
||||
python -m core.license \
|
||||
--customer "서울특별시청" \
|
||||
--edition ENTERPRISE \
|
||||
--days 365 \
|
||||
--key <마스터_키>
|
||||
|
||||
# 기존 라이선스는 자동 비활성화되고 새 라이선스로 교체된다.
|
||||
```
|
||||
|
||||
### 6.3 갱신 적용
|
||||
|
||||
**웹 UI**: `/license` → 새 `GRD-...` 키 입력 → **라이선스 등록**
|
||||
(기존 활성 라이선스는 자동으로 비활성화됨)
|
||||
|
||||
**API**:
|
||||
```bash
|
||||
curl -X POST http://localhost:8001/api/license/activate \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{"license_key": "GRD-<새_키>"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 라이선스 상태 모니터링
|
||||
|
||||
### 7.1 웹 UI 상태 배너
|
||||
|
||||
`/license` 페이지의 상단 배너로 즉시 확인:
|
||||
|
||||
| 배너 색상 | 의미 |
|
||||
|---------|------|
|
||||
| 초록 | 정상 활성 |
|
||||
| 노랑 | 만료 30일 이내 경고 |
|
||||
| 빨강 | 만료됨 |
|
||||
| 회색 | 라이선스 미등록 (Community 제한 모드) |
|
||||
|
||||
### 7.2 사용량 현황
|
||||
|
||||
`/license` 페이지 **사용량 현황** 섹션에서 기관/사용자/서버 현재 수 vs 한도를 확인할 수 있다:
|
||||
|
||||
```
|
||||
기관 [██████████░] 48/50
|
||||
사용자 [████░░░░░░░] 80/200
|
||||
서버 [███░░░░░░░░] 150/500
|
||||
```
|
||||
|
||||
### 7.3 API로 상태 확인
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/api/license/status \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"activated": true,
|
||||
"valid": true,
|
||||
"expired": false,
|
||||
"expiry_warning": false,
|
||||
"license_id": "GRD-A1B2C3",
|
||||
"edition": "ENTERPRISE",
|
||||
"customer": "서울특별시청",
|
||||
"issued_at": "2026-05-28T00:00:00+00:00",
|
||||
"expires_at": "2027-05-28T00:00:00+00:00",
|
||||
"days_remaining": 365,
|
||||
"limits": {
|
||||
"max_institutions": -1,
|
||||
"max_users": -1,
|
||||
"max_servers": -1,
|
||||
"features": ["MFA","LDAP","PAM","AI_AGENTS","VULN_SCAN","CICD","ANALYTICS","FINOPS"]
|
||||
},
|
||||
"message": "ENTERPRISE 라이선스 활성 (365일 남음)"
|
||||
}
|
||||
```
|
||||
|
||||
### 7.4 서버 로그 모니터링
|
||||
|
||||
```bash
|
||||
# 시스템 기동 시 라이선스 상태 로그 확인
|
||||
journalctl -u guardia-itsm -n 50 | grep LICENSE
|
||||
|
||||
# 정상:
|
||||
# [LICENSE] ENTERPRISE 라이선스 활성 (365일 남음) — 서울특별시청
|
||||
|
||||
# 경고:
|
||||
# [LICENSE] 경고: 라이선스 만료 25일 남음
|
||||
|
||||
# 만료:
|
||||
# [LICENSE] 경고: 라이선스가 만료되었습니다. 갱신이 필요합니다.
|
||||
|
||||
# 미설정:
|
||||
# [LICENSE] GUARDIA_LICENSE_KEY 미설정 — Community 모드로 실행됩니다.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. CLI 레퍼런스
|
||||
|
||||
```
|
||||
usage: python -m core.license [-h] --customer CUSTOMER
|
||||
--edition {COMMUNITY,STANDARD,ENTERPRISE}
|
||||
--days DAYS
|
||||
[--lid LID]
|
||||
[--key KEY]
|
||||
|
||||
GUARDiA 라이선스 키 생성 도구
|
||||
|
||||
필수 인수:
|
||||
--customer 고객/기관 이름 (예: "서울특별시청")
|
||||
--edition 라이선스 에디션: COMMUNITY | STANDARD | ENTERPRISE
|
||||
--days 유효 기간 (일, 예: 365)
|
||||
|
||||
선택 인수:
|
||||
--lid 라이선스 ID 직접 지정 (기본: GRD-{6자리랜덤hex})
|
||||
--key 마스터 키 hex 64자리 (기본: GUARDIA_LICENSE_KEY 환경변수)
|
||||
-h, --help 도움말 출력
|
||||
```
|
||||
|
||||
### 사용 예시
|
||||
|
||||
```bash
|
||||
# ENTERPRISE 1년
|
||||
python -m core.license --customer "서울특별시청" --edition ENTERPRISE --days 365
|
||||
|
||||
# STANDARD 2년
|
||||
python -m core.license --customer "경기도청" --edition STANDARD --days 730
|
||||
|
||||
# COMMUNITY 30일 평가판
|
||||
python -m core.license --customer "테스트기관" --edition COMMUNITY --days 30
|
||||
|
||||
# 마스터 키 직접 지정 (환경변수 없을 때)
|
||||
python -m core.license --customer "부산광역시청" --edition ENTERPRISE --days 365 \
|
||||
--key a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607
|
||||
|
||||
# 커스텀 라이선스 ID
|
||||
python -m core.license --customer "인천광역시청" --edition STANDARD --days 365 \
|
||||
--lid GRD-INCHEON26
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. API 레퍼런스
|
||||
|
||||
모든 API는 `Authorization: Bearer <JWT_TOKEN>` 헤더 필요.
|
||||
ADMIN 전용 API는 ADMIN 역할 계정만 호출 가능.
|
||||
|
||||
### GET /api/license/status
|
||||
|
||||
현재 라이선스 상태 조회 (로그인한 모든 사용자 접근 가능).
|
||||
|
||||
**응답:**
|
||||
```json
|
||||
{
|
||||
"activated": true,
|
||||
"valid": true,
|
||||
"expired": false,
|
||||
"expiry_warning": false,
|
||||
"license_id": "GRD-A1B2C3",
|
||||
"edition": "ENTERPRISE",
|
||||
"customer": "서울특별시청",
|
||||
"issued_at": "2026-05-28T00:00:00+00:00",
|
||||
"expires_at": "2027-05-28T00:00:00+00:00",
|
||||
"days_remaining": 365,
|
||||
"limits": { ... },
|
||||
"message": "ENTERPRISE 라이선스 활성 (365일 남음)"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/license/activate
|
||||
|
||||
라이선스 키 등록 및 활성화 **(ADMIN 전용)**.
|
||||
|
||||
**요청 본문:**
|
||||
```json
|
||||
{ "license_key": "GRD-eyJ..." }
|
||||
```
|
||||
|
||||
**응답 (성공):**
|
||||
```json
|
||||
{
|
||||
"message": "ENTERPRISE 라이선스가 활성화되었습니다 (365일 남음).",
|
||||
"license_id": "GRD-A1B2C3",
|
||||
"edition": "ENTERPRISE",
|
||||
"customer": "서울특별시청",
|
||||
"expires_at": "2027-05-28T00:00:00+00:00",
|
||||
"days_remaining": 365,
|
||||
"valid": true
|
||||
}
|
||||
```
|
||||
|
||||
**오류:**
|
||||
| 코드 | 원인 |
|
||||
|------|------|
|
||||
| 400 | 키 형식 오류, 서명 불일치, 복호화 실패 |
|
||||
| 403 | ADMIN 권한 없음 |
|
||||
| 500 | GUARDIA_LICENSE_KEY 환경변수 미설정 |
|
||||
|
||||
---
|
||||
|
||||
### POST /api/license/verify
|
||||
|
||||
등록 없이 키 검증만 수행 **(ADMIN 전용)**.
|
||||
|
||||
**요청 본문:** `{ "license_key": "GRD-eyJ..." }`
|
||||
|
||||
**응답:** 라이선스 페이로드 전체 반환 (status와 동일 구조)
|
||||
|
||||
---
|
||||
|
||||
### DELETE /api/license/
|
||||
|
||||
활성 라이선스 비활성화 **(ADMIN 전용)**.
|
||||
시스템이 Community 제한 모드로 전환된다.
|
||||
|
||||
**응답:** `{ "message": "라이선스가 비활성화되었습니다..." }`
|
||||
|
||||
---
|
||||
|
||||
### GET /api/license/history
|
||||
|
||||
등록 이력 전체 조회 **(ADMIN 전용)**.
|
||||
|
||||
**응답:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"license_id": "GRD-A1B2C3",
|
||||
"edition": "ENTERPRISE",
|
||||
"customer": "서울특별시청",
|
||||
"issued_at": "2026-05-28T00:00:00",
|
||||
"expires_at": "2027-05-28T00:00:00",
|
||||
"is_active": true,
|
||||
"activated_by": "admin",
|
||||
"activated_at": "2026-05-28T09:00:00"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"license_id": "GRD-OLD001",
|
||||
"edition": "STANDARD",
|
||||
"is_active": false,
|
||||
...
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 문제 해결
|
||||
|
||||
### 10.1 "GUARDIA_LICENSE_KEY 환경변수가 설정되지 않았습니다"
|
||||
|
||||
```
|
||||
원인: 서버 .env 파일에 GUARDIA_LICENSE_KEY가 없거나 서비스가 .env를 읽지 못함
|
||||
해결:
|
||||
1. .env 파일에 키 추가:
|
||||
GUARDIA_LICENSE_KEY=<64자리 hex>
|
||||
2. 서비스 재시작:
|
||||
systemctl restart guardia-itsm (Linux)
|
||||
Restart-Service GUARDiA-ITSM (Windows)
|
||||
```
|
||||
|
||||
### 10.2 "라이선스 서명 검증 실패 — 위변조 또는 잘못된 키"
|
||||
|
||||
```
|
||||
원인 1: 키가 다른 마스터 키로 생성됨 (고객사 서버의 GUARDIA_LICENSE_KEY와 발급 시 키가 다름)
|
||||
원인 2: 키 문자열이 복사 중 잘림
|
||||
해결:
|
||||
1. 키를 처음부터 끝까지 정확히 복사했는지 확인 (앞뒤 공백 없이)
|
||||
2. 발급 시 사용한 마스터 키 == 운영 서버의 GUARDIA_LICENSE_KEY 확인
|
||||
3. 다른 환경에서 발급된 키라면 해당 환경의 마스터 키로 서버를 재설정
|
||||
```
|
||||
|
||||
### 10.3 "라이선스 복호화 실패 — 마스터 키 불일치"
|
||||
|
||||
```
|
||||
원인: HMAC 통과했으나 AES 복호화 실패 — 마스터 키 파생 문자가 다름
|
||||
해결: 마스터 키가 정확히 64자리 hex인지 확인
|
||||
python3 -c "print(len('YOUR_KEY'))" # 반드시 64 출력
|
||||
```
|
||||
|
||||
### 10.4 "라이선스 한도 초과: ENTERPRISE 에디션으로 업그레이드하세요"
|
||||
|
||||
```
|
||||
원인: 현재 등록된 기관/서버/사용자 수가 에디션 한도를 초과함
|
||||
해결:
|
||||
방법 1: 상위 에디션 라이선스 발급 후 적용
|
||||
방법 2: 불필요한 기관/서버를 삭제하여 한도 이하로 줄임
|
||||
```
|
||||
|
||||
### 10.5 만료된 라이선스 등록 시도
|
||||
|
||||
```
|
||||
동작: 만료된 키도 DB에 등록 가능하나 valid: false, expired: true 반환됨
|
||||
등록 시 경고 메시지: "경고: 만료된 라이선스입니다 (만료일: ...). 갱신이 필요합니다."
|
||||
해결: 새 라이선스를 발급받아 즉시 교체 등록
|
||||
```
|
||||
|
||||
### 10.6 라이선스 캐시 갱신 안 됨 (변경 내용 미반영)
|
||||
|
||||
```
|
||||
원인: 인메모리 캐시(TTL 1시간)가 남아있어 이전 상태를 반환
|
||||
해결: 라이선스를 activate/deactivate하면 캐시가 자동 무효화됨
|
||||
긴급 시 ITSM 서비스 재시작으로 강제 캐시 초기화
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 부록 A: 라이선스 발급 체크리스트 (벤더용)
|
||||
|
||||
```
|
||||
고객사 발급 전 체크리스트:
|
||||
□ 계약서에 명시된 기관명과 동일한지 확인
|
||||
□ 에디션이 계약 내용과 일치하는지 확인
|
||||
□ 유효 기간이 계약 기간과 일치하는지 확인
|
||||
□ 마스터 키가 해당 고객사 서버에 적용된 키와 동일한지 확인
|
||||
□ 발급된 키를 암호화된 채널(이메일 암호화, 보안 메신저)로 전달
|
||||
□ 발급 이력 대장에 기록 (날짜, 고객명, 에디션, 만료일, 발급자)
|
||||
```
|
||||
|
||||
## 부록 B: 라이선스 발급 이력 대장 양식
|
||||
|
||||
| 번호 | 발급일 | 고객명 | 에디션 | 라이선스 ID | 만료일 | 발급자 | 비고 |
|
||||
|-----|-------|-------|--------|-----------|-------|--------|------|
|
||||
| 001 | 2026-05-28 | 서울특별시청 | ENTERPRISE | GRD-A1B2C3 | 2027-05-28 | 홍길동 | 신규 |
|
||||
| 002 | 2026-05-28 | 경기도청 | STANDARD | GRD-D4E5F6 | 2028-05-28 | 홍길동 | 신규 2년 |
|
||||
BIN
GUARDiA_Paperclip_AI에이전트_구현보고서.pptx
Normal file
BIN
GUARDiA_Paperclip_AI에이전트_구현보고서.pptx
Normal file
Binary file not shown.
1209
gen_ppt.js
Normal file
1209
gen_ppt.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user