- 모든 .md, .html 파일 권한을 644로 정상화 - .gitignore 파일 권한도 644로 수정 - 문서 파일에 실행 권한은 불필요하고 보안상 바람직하지 않음 - deprecated 아이디어 폴더 생성 및 레벨별 UI 변경 아이디어 이동
646 lines
18 KiB
Markdown
646 lines
18 KiB
Markdown
# Nginx 기반 로빙 컨테이너 아키텍처 설계
|
|
|
|
## 목차
|
|
1. [배경 및 문제점](#배경-및-문제점)
|
|
2. [Docker 이미지 최적화](#docker-이미지-최적화)
|
|
3. [Nginx 리버스 프록시 아키텍처](#nginx-리버스-프록시-아키텍처)
|
|
4. [컨테이너 동적 관리](#컨테이너-동적-관리)
|
|
5. [비용 분석](#비용-분석)
|
|
6. [Docker 유료화 현황](#docker-유료화-현황)
|
|
7. [아키텍처 성능 비교](#아키텍처-성능-비교)
|
|
8. [로빙 컨테이너 아키텍처 설계](#로빙-컨테이너-아키텍처-설계)
|
|
9. [예제 구현](#예제-구현)
|
|
|
|
---
|
|
|
|
## 배경 및 문제점
|
|
|
|
### 초기 상황
|
|
- 각 폴더(docs, frontend, api-base, test_api, test_meta-skill, test_front)의 git 상태 확인 및 동기화 완료
|
|
- api-base 폴더에서 새로운 변경사항 발견:
|
|
- Dockerfile 및 docker-compose.yml 추가
|
|
- static/test.html 파일 추가
|
|
- requirements.txt 업데이트
|
|
|
|
### 발견된 문제점
|
|
- **Docker 이미지 용량 과다**: 1.54GB로 예상보다 크게 빌드됨
|
|
- **고정 파라미터**: run.py에서 모든 설정값이 하드코딩됨
|
|
- **확장성 문제**: 컨테이너 수 증가 시 nginx.conf 수동 관리 필요
|
|
|
|
---
|
|
|
|
## Docker 이미지 최적화
|
|
|
|
### 문제 분석
|
|
기존 Dockerfile에서 불필요한 패키지 설치로 인한 용량 증가:
|
|
|
|
```dockerfile
|
|
# 기존 문제가 있던 Dockerfile
|
|
FROM python:3.11-slim
|
|
|
|
RUN apt-get update && apt-get install -y \
|
|
gcc \
|
|
postgresql-client \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
```
|
|
|
|
### gcc 필요성 검증
|
|
requirements.txt 분석 결과:
|
|
```text
|
|
fastapi>=0.104.0
|
|
uvicorn>=0.24.0
|
|
psycopg2-binary>=2.9.0 # 바이너리 버전 사용
|
|
chromadb>=0.4.0
|
|
pymupdf>=1.23.0
|
|
python-jose[cryptography]>=3.3.0
|
|
# ... 기타 패키지들
|
|
```
|
|
|
|
**테스트 결과**: gcc 없이도 모든 패키지가 정상 설치됨
|
|
- 모든 패키지가 바이너리 wheel로 제공
|
|
- psycopg2-binary 사용으로 컴파일 불필요
|
|
|
|
### 최적화된 Dockerfile
|
|
```dockerfile
|
|
FROM python:3.11-slim
|
|
|
|
WORKDIR /app
|
|
|
|
# 필수 런타임 의존성만 설치
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# requirements 먼저 복사 (캐시 활용)
|
|
COPY requirements.txt .
|
|
RUN pip install --no-cache-dir --no-compile -r requirements.txt
|
|
|
|
# 애플리케이션 코드 복사
|
|
COPY . .
|
|
|
|
# 비루트 사용자 생성
|
|
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
|
USER appuser
|
|
|
|
# 포트 노출
|
|
EXPOSE 8000
|
|
|
|
# 환경변수로 설정 관리
|
|
ENV HOST=0.0.0.0
|
|
ENV PORT=8000
|
|
ENV RELOAD=false
|
|
ENV LOG_LEVEL=info
|
|
|
|
# 파라미터화된 실행
|
|
CMD ["sh", "-c", "python run.py --host $HOST --port $PORT"]
|
|
```
|
|
|
|
### run.py 파라미터 외부화
|
|
기존 하드코딩된 설정:
|
|
```python
|
|
# 기존 run.py
|
|
if __name__ == "__main__":
|
|
uvicorn.run(
|
|
"app.main:app",
|
|
host="0.0.0.0", # 고정값
|
|
port=8000, # 고정값
|
|
reload=True, # 고정값
|
|
log_level="info" # 고정값
|
|
)
|
|
```
|
|
|
|
개선된 환경변수 기반 설정:
|
|
```python
|
|
import os
|
|
import uvicorn
|
|
from app.main import app
|
|
|
|
if __name__ == "__main__":
|
|
uvicorn.run(
|
|
"app.main:app",
|
|
host=os.getenv("HOST", "0.0.0.0"),
|
|
port=int(os.getenv("PORT", "8000")),
|
|
reload=os.getenv("RELOAD", "false").lower() == "true",
|
|
log_level=os.getenv("LOG_LEVEL", "info")
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Nginx 리버스 프록시 아키텍처
|
|
|
|
### 기본 구조
|
|
```
|
|
[Client Request]
|
|
↓
|
|
[Nginx (Reverse Proxy)]
|
|
↓ ↓ ↓
|
|
[Container A] [Container B] [Container C]
|
|
(e.g. FastAPI) (e.g. Node.js) (e.g. LangChain)
|
|
```
|
|
|
|
### Nginx 설정 예시
|
|
```nginx
|
|
http {
|
|
upstream app1 {
|
|
server container-a:8000;
|
|
}
|
|
|
|
upstream app2 {
|
|
server container-b:8001;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
|
|
location /app1/ {
|
|
proxy_pass http://app1/;
|
|
}
|
|
|
|
location /app2/ {
|
|
proxy_pass http://app2/;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Docker Compose 구성
|
|
```yaml
|
|
version: '3.8'
|
|
services:
|
|
nginx:
|
|
image: nginx
|
|
ports:
|
|
- "80:80"
|
|
volumes:
|
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
|
depends_on:
|
|
- container-a
|
|
- container-b
|
|
|
|
container-a:
|
|
build: ./app1
|
|
expose:
|
|
- "8000"
|
|
|
|
container-b:
|
|
build: ./app2
|
|
expose:
|
|
- "8001"
|
|
```
|
|
|
|
### Nginx 컨테이너화 필요성
|
|
**컨테이너로 감싸는 이유:**
|
|
- **이식성**: 어디서든 동일한 환경으로 실행 가능
|
|
- **버전 고정**: Nginx 이미지 버전 명시로 예기치 않은 업데이트 방지
|
|
- **구성 분리**: nginx.conf 등 설정 파일을 외부 볼륨으로 관리
|
|
- **CI/CD 통합**: 자동 배포 및 테스트 환경 구성 용이
|
|
- **DevOps 표준화**: 다른 마이크로서비스와 동일한 방식으로 관리
|
|
|
|
---
|
|
|
|
## 컨테이너 동적 관리
|
|
|
|
### nginx.conf 동적 갱신 문제
|
|
**문제**: 기본 nginx는 정적 설정 파일 기반으로 컨테이너 수 증가 시 자동 갱신 불가
|
|
|
|
**해결 방법:**
|
|
|
|
#### 1. Docker + Nginx + Template 갱신 도구
|
|
```bash
|
|
# nginx-proxy 사용 예시
|
|
docker run -d -p 80:80 \
|
|
-v /var/run/docker.sock:/tmp/docker.sock:ro \
|
|
jwilder/nginx-proxy
|
|
```
|
|
|
|
#### 2. Traefik 사용 (권장)
|
|
- Docker 레이블 기반 동적 서비스 탐지
|
|
- 자동 라우팅 처리
|
|
- Let's Encrypt 자동 적용
|
|
|
|
```yaml
|
|
services:
|
|
traefik:
|
|
image: traefik:v2.10
|
|
command:
|
|
- "--providers.docker=true"
|
|
- "--entrypoints.web.address=:80"
|
|
ports:
|
|
- "80:80"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
```
|
|
|
|
### 조건 기반 컨테이너 자동 제어
|
|
**시나리오**: 로빙 레벨업, 스킬 업데이트 시 자동 컨테이너 관리
|
|
|
|
**구현 방식:**
|
|
```python
|
|
import docker
|
|
|
|
def handle_level_up(robeing_id, new_level):
|
|
client = docker.from_env()
|
|
|
|
# 기존 컨테이너 중단
|
|
old_container = client.containers.get(f"robeing-{robeing_id}")
|
|
old_container.stop()
|
|
old_container.remove()
|
|
|
|
# 새 레벨에 맞는 컨테이너 시작
|
|
client.containers.run(
|
|
f"robeing:level-{new_level}",
|
|
name=f"robeing-{robeing_id}",
|
|
detach=True,
|
|
volumes={
|
|
f"robeing-{robeing_id}-data": {
|
|
"bind": "/app/data",
|
|
"mode": "rw"
|
|
}
|
|
}
|
|
)
|
|
```
|
|
|
|
**자동화 조건 예시:**
|
|
- 로빙 레벨 5 도달 → GPT-4o 컨테이너 시작
|
|
- 스킬X 업그레이드 → 기존 컨테이너 중단 후 새 이미지로 재배포
|
|
- PDF 스킬 7일간 미사용 → 해당 컨테이너 자동 종료
|
|
|
|
---
|
|
|
|
## 비용 분석
|
|
|
|
### 컨테이너 자동화 비용
|
|
**비용 절감 전략:**
|
|
- **이벤트 기반 실행**: 사용 시에만 컨테이너 활성화
|
|
- **레벨 제한**: 고성능 스킬은 상위 레벨에만 허용
|
|
- **경량 컨테이너**: FastAPI, uvicorn 기반 최소화
|
|
- **무료 티어 활용**: Hetzner, Railway, Supabase 등
|
|
|
|
**예산 범위 (월 기준, 1-3 스킬):**
|
|
| 방식 | 예상 비용 | 비고 |
|
|
|------|-----------|------|
|
|
| AWS ECS Fargate + S3 | $3-8 | idle auto-off 조건 |
|
|
| Supabase Edge Function | $0-5 | 기본 무료 + API 통제 |
|
|
| Hetzner VPS + docker-compose | €4-6 | CX11 기준 |
|
|
| Railway (1GB RAM) | $0-5 | 스킬 2-3개 |
|
|
|
|
### 컨테이너 호스팅 서비스 비용
|
|
**내부 전용 플랫폼** (로빙 등):
|
|
- **인력**: 1-2명 (DevOps + 백엔드)
|
|
- **비용**: 월 3-10만원
|
|
- **구성**: Hetzner VPS + Supabase + Docker
|
|
|
|
**상업용 PaaS 플랫폼**:
|
|
- **인력**: 5-10명 이상
|
|
- **비용**: 수천만원 이상
|
|
- **예시**: Railway, Render, Vercel 수준
|
|
|
|
---
|
|
|
|
## Docker 유료화 현황
|
|
|
|
### 정책 요약 (2024-2025)
|
|
- **Docker Engine, Compose**: 계속 무료
|
|
- **Docker Desktop**: 연매출 $1천만 이상 기업만 유료
|
|
- **Docker Hub**: Free 요금제 Pull 제한 (6시간당 100회)
|
|
|
|
### 요금 구조
|
|
| 플랜 | 가격 | 대상 |
|
|
|------|------|------|
|
|
| Docker Personal | 무료 | 개인/소규모 상업용 |
|
|
| Docker Pro | $9/월 | 개발자 |
|
|
| Docker Team | $15/월 | 협업팀 |
|
|
| Docker Business | $24/월 | 엔터프라이즈 |
|
|
|
|
**결론**: 스타트업/MVP 단계에서는 비용 거의 없음
|
|
|
|
---
|
|
|
|
## 아키텍처 성능 비교
|
|
|
|
### 속도 측면 비교
|
|
| 항목 | Docker (컨테이너) | VM (가상머신) | 서버리스 (FaaS) | 베어메탈 |
|
|
|------|------------------|---------------|-----------------|----------|
|
|
| 시작 속도 | 매우 빠름 (초 단위) | 느림 (수십 초~수분) | 보통~빠름 (콜드스타트) | 느림 (OS 부팅) |
|
|
| 처리 속도 (CPU) | 거의 네이티브급 | 약간 느림 | 느림~중간 | 최상 |
|
|
| IO 성능 | 빠름 (호스트 공유) | 느림 (가상화 레이어) | 느림~중간 | 최상 |
|
|
| 성능 일관성 | 중간~높음 | 높음 | 낮음 (콜드스타트) | 최상 |
|
|
| 멀티 컨테이너 처리 | 뛰어남 | 복잡 | 불가 (함수 단위) | 복잡 |
|
|
|
|
**결론**: 로빙 같은 동적 자원 할당과 상태 지속이 필요한 구조에서는 컨테이너 기반이 최적
|
|
|
|
---
|
|
|
|
## 로빙 컨테이너 아키텍처 설계
|
|
|
|
### 핵심 요구사항
|
|
- **레벨업 시 자원 증가**: 컨테이너 재시작으로 max 성능 향상
|
|
- **기억/코드 유지**: 볼륨 마운트로 데이터 지속성 보장
|
|
- **대시보드 연동**: 로그인, 상태, 아이템 권한, API 키 관리
|
|
- **동적 라우팅**: 컨테이너 변화에 따른 자동 프록시 업데이트
|
|
|
|
### MVP 구성 요소
|
|
- **로빙 컨테이너**: 10개
|
|
- **리버스 프록시**: 1개 (Traefik)
|
|
- **컨테이너 오케스트레이션**: Docker Swarm
|
|
- **제어 백엔드**: FastAPI 기반
|
|
- **대시보드**: React/Vue 프론트엔드
|
|
|
|
### 아키텍처 다이어그램
|
|
```
|
|
[ User + 대시보드 ]
|
|
↓ REST/WebSocket
|
|
[ Control API Server ]
|
|
↓ ↓
|
|
[ Docker Engine (Swarm) ] ←→ [ Redis / Postgres ]
|
|
↑
|
|
[ Reverse Proxy (Traefik) ]
|
|
↓
|
|
[ 로빙 컨테이너 1~10개 ]
|
|
```
|
|
|
|
### 레벨업 동작 흐름
|
|
1. 로빙 경험치 증가 → 레벨업 판정 (PostgreSQL 반영)
|
|
2. Control API가 변화 감지 (cron, hook, event)
|
|
3. 기존 robeing-3 컨테이너 stop + remove
|
|
4. 더 큰 자원 설정으로 재시작 (`--memory`, `--cpu-shares` 증가)
|
|
5. 기존 `/data` 볼륨 마운트 유지 (기억/코드 보존)
|
|
6. Traefik이 자동 라우팅 업데이트
|
|
|
|
---
|
|
|
|
## 예제 구현
|
|
|
|
### Docker Swarm 기반 구성
|
|
```yaml
|
|
# docker-compose.yml
|
|
version: '3.8'
|
|
services:
|
|
traefik:
|
|
image: traefik:v2.10
|
|
command:
|
|
- "--providers.docker=true"
|
|
- "--entrypoints.web.address=:80"
|
|
- "--entrypoints.websecure.address=:443"
|
|
- "--api.insecure=true"
|
|
ports:
|
|
- "80:80"
|
|
- "8080:8080" # 관리 대시보드
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
deploy:
|
|
replicas: 1
|
|
resources:
|
|
limits:
|
|
cpus: '0.5'
|
|
memory: '512M'
|
|
|
|
control-api:
|
|
image: ro-being/control-api:latest
|
|
deploy:
|
|
replicas: 1
|
|
environment:
|
|
- DATABASE_URL=postgresql://user:pass@postgres:5432/robeing
|
|
- REDIS_URL=redis://redis:6379/0
|
|
- DOCKER_SOCKET=/var/run/docker.sock
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
networks:
|
|
- traefik-public
|
|
labels:
|
|
- "traefik.http.routers.control-api.rule=Host(`api.robeing.local`)"
|
|
|
|
postgres:
|
|
image: postgres:15
|
|
environment:
|
|
- POSTGRES_DB=robeing
|
|
- POSTGRES_USER=user
|
|
- POSTGRES_PASSWORD=pass
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
networks:
|
|
- traefik-public
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
networks:
|
|
- traefik-public
|
|
|
|
# 로빙 컨테이너 템플릿
|
|
robeing-1:
|
|
image: robeing:level-1
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '0.5'
|
|
memory: '256M'
|
|
volumes:
|
|
- robeing-1-memory:/app/memory
|
|
- robeing-1-code:/app/code
|
|
networks:
|
|
- traefik-public
|
|
labels:
|
|
- "traefik.http.routers.robeing1.rule=Host(`robeing1.local`)"
|
|
- "traefik.http.services.robeing1.loadbalancer.server.port=8000"
|
|
|
|
networks:
|
|
traefik-public:
|
|
external: true
|
|
|
|
volumes:
|
|
postgres_data:
|
|
robeing-1-memory:
|
|
robeing-1-code:
|
|
```
|
|
|
|
### 로빙 컨테이너 제어 API
|
|
```python
|
|
# control_api.py
|
|
import docker
|
|
from fastapi import FastAPI
|
|
from sqlalchemy.orm import Session
|
|
from database import get_db
|
|
from models import Robeing
|
|
|
|
app = FastAPI()
|
|
client = docker.from_env()
|
|
|
|
@app.post("/robeing/{robeing_id}/level-up")
|
|
async def level_up_robeing(robeing_id: int, db: Session = Depends(get_db)):
|
|
# 로빙 정보 조회
|
|
robeing = db.query(Robeing).filter(Robeing.id == robeing_id).first()
|
|
if not robeing:
|
|
raise HTTPException(404, "Robeing not found")
|
|
|
|
# 레벨업 처리
|
|
new_level = robeing.level + 1
|
|
robeing.level = new_level
|
|
db.commit()
|
|
|
|
# 컨테이너 재시작
|
|
await restart_robeing_container(robeing_id, new_level)
|
|
|
|
return {"message": f"Robeing {robeing_id} leveled up to {new_level}"}
|
|
|
|
async def restart_robeing_container(robeing_id: int, level: int):
|
|
container_name = f"robeing-{robeing_id}"
|
|
|
|
try:
|
|
# 기존 컨테이너 중지 및 제거
|
|
old_container = client.containers.get(container_name)
|
|
old_container.stop()
|
|
old_container.remove()
|
|
except docker.errors.NotFound:
|
|
pass
|
|
|
|
# 레벨에 따른 자원 할당
|
|
memory_limit = f"{256 * level}m"
|
|
cpu_quota = 100000 * level # CPU 할당량
|
|
|
|
# 새 컨테이너 시작
|
|
client.containers.run(
|
|
image=f"robeing:level-{level}",
|
|
name=container_name,
|
|
detach=True,
|
|
mem_limit=memory_limit,
|
|
cpu_quota=cpu_quota,
|
|
volumes={
|
|
f"robeing-{robeing_id}-memory": {
|
|
"bind": "/app/memory",
|
|
"mode": "rw"
|
|
},
|
|
f"robeing-{robeing_id}-code": {
|
|
"bind": "/app/code",
|
|
"mode": "rw"
|
|
}
|
|
},
|
|
labels={
|
|
f"traefik.http.routers.robeing{robeing_id}.rule": f"Host(`robeing{robeing_id}.local`)",
|
|
f"traefik.http.services.robeing{robeing_id}.loadbalancer.server.port": "8000"
|
|
},
|
|
network="traefik-public"
|
|
)
|
|
```
|
|
|
|
### 대시보드 연동 예시
|
|
```python
|
|
# dashboard_api.py
|
|
@app.get("/dashboard/robeing/{robeing_id}/status")
|
|
async def get_robeing_status(robeing_id: int):
|
|
container_name = f"robeing-{robeing_id}"
|
|
|
|
try:
|
|
container = client.containers.get(container_name)
|
|
stats = container.stats(stream=False)
|
|
|
|
return {
|
|
"id": robeing_id,
|
|
"status": container.status,
|
|
"cpu_usage": calculate_cpu_percent(stats),
|
|
"memory_usage": stats['memory_stats']['usage'],
|
|
"memory_limit": stats['memory_stats']['limit'],
|
|
"level": get_robeing_level(robeing_id),
|
|
"uptime": container.attrs['State']['StartedAt']
|
|
}
|
|
except docker.errors.NotFound:
|
|
return {"status": "not_found"}
|
|
|
|
@app.post("/dashboard/robeing/{robeing_id}/action")
|
|
async def control_robeing(robeing_id: int, action: str):
|
|
container_name = f"robeing-{robeing_id}"
|
|
container = client.containers.get(container_name)
|
|
|
|
if action == "start":
|
|
container.start()
|
|
elif action == "stop":
|
|
container.stop()
|
|
elif action == "restart":
|
|
container.restart()
|
|
elif action == "pause":
|
|
container.pause()
|
|
elif action == "unpause":
|
|
container.unpause()
|
|
|
|
return {"message": f"Action {action} applied to robeing {robeing_id}"}
|
|
```
|
|
|
|
### 스킬 기반 컨테이너 관리
|
|
```yaml
|
|
# 스킬 메타데이터 예시 (skill_metadata.yml)
|
|
skills:
|
|
- name: PDF_Parser
|
|
level_required: 5
|
|
docker_image: robeing-skills/pdf-parser:latest
|
|
resources:
|
|
memory: "512m"
|
|
cpu: "0.5"
|
|
auto_shutdown: 3600 # 1시간 후 자동 종료
|
|
|
|
- name: Advanced_LLM
|
|
level_required: 10
|
|
docker_image: robeing-skills/advanced-llm:latest
|
|
resources:
|
|
memory: "2g"
|
|
cpu: "2.0"
|
|
gpu_required: true
|
|
```
|
|
|
|
### 실시간 모니터링
|
|
```python
|
|
# monitoring.py
|
|
import asyncio
|
|
import docker
|
|
from fastapi import WebSocket
|
|
|
|
@app.websocket("/ws/monitor/{robeing_id}")
|
|
async def monitor_robeing(websocket: WebSocket, robeing_id: int):
|
|
await websocket.accept()
|
|
container_name = f"robeing-{robeing_id}"
|
|
|
|
try:
|
|
container = client.containers.get(container_name)
|
|
|
|
# 실시간 stats 스트리밍
|
|
for stats in container.stats(stream=True):
|
|
metrics = {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"cpu_percent": calculate_cpu_percent(stats),
|
|
"memory_usage": stats['memory_stats']['usage'],
|
|
"memory_percent": calculate_memory_percent(stats),
|
|
"network_rx": stats['networks']['eth0']['rx_bytes'],
|
|
"network_tx": stats['networks']['eth0']['tx_bytes']
|
|
}
|
|
|
|
await websocket.send_json(metrics)
|
|
await asyncio.sleep(1)
|
|
|
|
except docker.errors.NotFound:
|
|
await websocket.send_json({"error": "Container not found"})
|
|
except Exception as e:
|
|
await websocket.send_json({"error": str(e)})
|
|
```
|
|
|
|
---
|
|
|
|
## 결론 및 확장 방향
|
|
|
|
### 핵심 성과
|
|
1. **Docker 이미지 최적화**: 1.54GB → 예상 400-500MB로 감소
|
|
2. **동적 컨테이너 관리**: 레벨업 시 자동 자원 재할당
|
|
3. **비용 효율성**: MVP 기준 월 10만원 이하로 운영 가능
|
|
4. **확장 가능한 구조**: Docker Swarm → Kubernetes 전환 가능
|
|
|
|
### 향후 확장 계획
|
|
- **Kubernetes 전환**: StatefulSet + Custom Operator
|
|
- **로빙 간 통신**: gRPC 또는 WebSocket 기반 메시지 연동
|
|
- **고급 모니터링**: Prometheus + Grafana 연동
|
|
- **AI 기반 자원 예측**: 사용 패턴 분석으로 선제적 스케일링
|
|
|
|
### 검증된 기술 스택
|
|
- **컨테이너**: Docker + Docker Swarm
|
|
- **프록시**: Traefik (동적 라우팅)
|
|
- **백엔드**: FastAPI + PostgreSQL + Redis
|
|
- **프론트엔드**: React/Vue (대시보드)
|
|
- **모니터링**: Docker API + WebSocket
|
|
|
|
이 아키텍처를 통해 로빙의 레벨 기반 진화, 기억 지속성, 유연한 스킬 업데이트 요구사항을 모두 충족할 수 있으며, MVP에서 시작하여 단계적으로 확장 가능한 구조를 제공합니다. |