개요
이번 포스트에서는 KAG 프로젝트의 Docker 컨테이너 오케스트레이션을 심층 분석합니다. KAG는 마이크로서비스 아키텍처를 기반으로 웹 애플리케이션, Elasticsearch, Neo4j를 독립적인 컨테이너로 구성하여 확장성과 유지보수성을 극대화했습니다.
1. Docker Compose 아키텍처 개요
1.1 전체 서비스 구성
# KAG 마이크로서비스 구성
services:
app: # KAG 메인 애플리케이션
elasticsearch: # 검색 엔진 서비스
neo4j: # 그래프 데이터베이스 서비스
networks:
kag-network: # 전용 브리지 네트워크
volumes:
neo4j_data: # Neo4j 데이터 영속성
neo4j_logs: # Neo4j 로그 영속성
1.2 서비스 간 의존성 그래프
graph TD
subgraph "외부 접근"
A[사용자 브라우저]
B[API 클라이언트]
end
subgraph "KAG Docker Network"
C[KAG App :8000]
D[Elasticsearch :9200]
E[Neo4j :7474/:7687]
end
subgraph "데이터 영속성"
F[(neo4j_data)]
G[(neo4j_logs)]
end
A -->|HTTP/HTTPS| C
B -->|REST API| C
C -->|검색 쿼리| D
C -->|그래프 쿼리| E
E --> F
E --> G
C -.->|depends_on| D
C -.->|depends_on| E
2. KAG 메인 애플리케이션 컨테이너
2.1 애플리케이션 서비스 구성
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/app # 개발 시 코드 동기화
ports:
- "8000:8000" # HTTP 서비스 포트
depends_on:
- elasticsearch # ES 서비스 의존성
- neo4j # Neo4j 서비스 의존성
environment:
- ELASTICSEARCH_HOST=elasticsearch
- NEO4J_HOST=neo4j
- NEO4J_USER=neo4j
- NEO4J_PASSWORD=password
networks:
- kag-network # 전용 네트워크 사용
2.2 환경 변수 기반 서비스 디스커버리
내부 DNS 해석:
# 컨테이너 내부에서의 서비스 접근
http://elasticsearch:9200 # Elasticsearch 내부 접근
bolt://neo4j:7687 # Neo4j Bolt 프로토콜
http://neo4j:7474 # Neo4j HTTP 인터페이스
환경 변수 주입 방식:
import os
# KAG 애플리케이션에서의 설정
ELASTICSEARCH_HOST = os.getenv('ELASTICSEARCH_HOST', 'localhost')
NEO4J_HOST = os.getenv('NEO4J_HOST', 'localhost')
NEO4J_USER = os.getenv('NEO4J_USER', 'neo4j')
NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD', 'neo4j')
# 연결 URL 구성
elasticsearch_url = f"http://{ELASTICSEARCH_HOST}:9200"
neo4j_url = f"bolt://{NEO4J_HOST}:7687"
2.3 볼륨 마운팅 전략
volumes:
- .:/app # 호스트 코드 → 컨테이너 /app 디렉토리
개발 워크플로우 최적화:
- 코드 변경 시 즉시 반영: 컨테이너 재빌드 불필요
- Hot Reload 지원: Python 개발 서버의 자동 재시작
- 디버깅 편의성: 호스트에서 직접 코드 수정 가능
3. Elasticsearch 검색 엔진 컨테이너
3.1 Elasticsearch 서비스 구성
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0
environment:
- discovery.type=single-node # 단일 노드 클러스터
- xpack.security.enabled=false # 보안 기능 비활성화
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" # JVM 메모리 설정
ports:
- "9200:9200" # HTTP API 포트
networks:
- kag-network
3.2 Elasticsearch 구성 분석
3.2.1 클러스터 설정
# 단일 노드 모드 설정
discovery.type=single-node
단일 노드 모드의 특징:
- 개발/테스트 최적화: 프로덕션 복잡성 제거
- 리소스 효율성: 클러스터 오버헤드 최소화
- 빠른 시작: 노드 디스커버리 과정 생략
- 제한사항: 고가용성 및 샤딩 기능 제한
3.2.2 보안 설정
# X-Pack 보안 비활성화
xpack.security.enabled=false
보안 비활성화 이유:
- 개발 환경 단순화: 인증/인가 복잡성 제거
- 빠른 프로토타이핑: 보안 설정 없이 즉시 사용
- 내부 네트워크: Docker 네트워크 내부에서만 접근
3.2.3 JVM 메모리 최적화
"ES_JAVA_OPTS=-Xms512m -Xmx512m"
메모리 설정 전략:
- 힙 크기 고정: 512MB로 최소/최대 동일 설정
- 컨테이너 최적화: 제한된 리소스 환경 고려
- GC 최적화: 고정 힙으로 가비지 컬렉션 안정화
3.3 KAG에서의 Elasticsearch 활용
# KAG에서의 Elasticsearch 클라이언트 구성 예시
from elasticsearch import Elasticsearch
class KAGSearchEngine:
def __init__(self):
self.es = Elasticsearch([{
'host': os.getenv('ELASTICSEARCH_HOST', 'localhost'),
'port': 9200,
'scheme': 'http'
}])
async def index_knowledge(self, documents):
"""지식 문서 인덱싱"""
for doc in documents:
await self.es.index(
index="kag_knowledge",
id=doc['id'],
body={
'content': doc['content'],
'embeddings': doc['embeddings'],
'metadata': doc['metadata']
}
)
async def semantic_search(self, query, size=10):
"""의미적 검색 수행"""
search_body = {
"query": {
"multi_match": {
"query": query,
"fields": ["content^2", "metadata.title"]
}
},
"size": size
}
return await self.es.search(index="kag_knowledge", body=search_body)
4. Neo4j 그래프 데이터베이스 컨테이너
4.1 Neo4j 서비스 구성
neo4j:
image: neo4j:5.13.0
environment:
- NEO4J_AUTH=neo4j/password # 인증 정보
ports:
- "7474:7474" # HTTP 브라우저 인터페이스
- "7687:7687" # Bolt 프로토콜
volumes:
- neo4j_data:/data # 데이터 영속성
- neo4j_logs:/logs # 로그 영속성
networks:
- kag-network
4.2 Neo4j 구성 세부 분석
4.2.1 인증 및 보안
NEO4J_AUTH=neo4j/password
인증 설정:
- 사용자명:
neo4j(기본 관리자) - 비밀번호:
password(개발용 단순 비밀번호) - 보안 고려사항: 프로덕션 환경에서는 강력한 비밀번호 필요
4.2.2 포트 구성
ports:
- "7474:7474" # Neo4j Browser (웹 인터페이스)
- "7687:7687" # Bolt 프로토콜 (애플리케이션 연결)
포트별 용도:
- 7474 (HTTP): 웹 기반 Neo4j Browser 인터페이스
- 7687 (Bolt): 고성능 바이너리 프로토콜
4.2.3 데이터 영속성 전략
volumes:
- neo4j_data:/data # 그래프 데이터베이스 파일
- neo4j_logs:/logs # 트랜잭션 로그 및 시스템 로그
볼륨 마운팅 이점:
- 데이터 보존: 컨테이너 재시작 시 데이터 유지
- 백업 용이성: 호스트 파일시스템에서 백업 가능
- 성능 최적화: 영속 볼륨을 통한 I/O 성능 향상
4.3 KAG에서의 Neo4j 활용
# KAG에서의 Neo4j 연동 예시
from neo4j import GraphDatabase
class KAGKnowledgeGraph:
def __init__(self):
self.driver = GraphDatabase.driver(
f"bolt://{os.getenv('NEO4J_HOST', 'localhost')}:7687",
auth=(
os.getenv('NEO4J_USER', 'neo4j'),
os.getenv('NEO4J_PASSWORD', 'neo4j')
)
)
async def create_knowledge_node(self, entity_data):
"""지식 엔티티 노드 생성"""
with self.driver.session() as session:
query = """
CREATE (e:Entity {
name: $name,
type: $type,
properties: $properties
})
RETURN e
"""
return session.run(query, **entity_data)
async def create_relationship(self, from_entity, to_entity, relation):
"""엔티티 간 관계 생성"""
with self.driver.session() as session:
query = """
MATCH (a:Entity {name: $from_name})
MATCH (b:Entity {name: $to_name})
CREATE (a)-[r:RELATES {type: $relation_type}]->(b)
RETURN r
"""
return session.run(query,
from_name=from_entity,
to_name=to_entity,
relation_type=relation
)
async def knowledge_reasoning(self, start_entity, max_depth=3):
"""지식 그래프 추론 쿼리"""
with self.driver.session() as session:
query = f"""
MATCH path = (start:Entity )
-[*1..{max_depth}]-(connected:Entity)
RETURN path, connected
ORDER BY length(path)
"""
return session.run(query, entity_name=start_entity)
5. 네트워크 아키텍처
5.1 브리지 네트워크 구성
networks:
kag-network:
driver: bridge
브리지 네트워크 특징:
- 격리성: 외부 네트워크와 분리된 내부 통신
- DNS 해석: 컨테이너 이름으로 서비스 디스커버리
- 보안성: 내부 트래픽만 허용
5.2 서비스 디스커버리 메커니즘
graph TB
subgraph "kag-network (172.18.0.0/16)"
A[app: 172.18.0.2]
B[elasticsearch: 172.18.0.3]
C[neo4j: 172.18.0.4]
end
subgraph "내부 DNS 해석"
D[app → elasticsearch:9200]
E[app → neo4j:7687]
end
subgraph "외부 접근"
F[localhost:8000 → app]
G[localhost:9200 → elasticsearch]
H[localhost:7474 → neo4j]
end
내부 통신 플로우:
- 서비스 이름 해석: Docker의 내장 DNS 서버 사용
- IP 주소 할당: 동적 IP 주소 자동 할당
- 포트 매핑: 컨테이너 간 직접 통신
6. 데이터 플로우 및 상호작용
6.1 전체 데이터 플로우
sequenceDiagram
participant User as 사용자
participant App as KAG App
participant ES as Elasticsearch
participant Neo4j as Neo4j
User->>App: 질의 요청
App->>App: 질의 분석
par 병렬 검색
App->>ES: 의미적 검색
ES-->>App: 관련 문서 반환
and
App->>Neo4j: 그래프 추론
Neo4j-->>App: 관련 엔티티 반환
end
App->>App: 결과 융합 및 추론
App-->>User: 증강된 응답 반환
6.2 서비스별 역할 분담
KAG 애플리케이션 (포트 8000)
역할:
- 사용자 인터페이스 제공
- 질의 분석 및 처리
- 검색 결과 융합
- 응답 생성 및 반환
주요 기능:
- REST API 서버
- 웹 UI 제공
- 비즈니스 로직 처리
- 외부 서비스 조정
Elasticsearch (포트 9200)
역할:
- 문서 인덱싱
- 의미적 검색
- 벡터 검색 지원
- 검색 결과 랭킹
주요 기능:
- 전문 검색 (Full-text Search)
- 벡터 유사도 검색
- 집계 및 분석
- 실시간 인덱싱
Neo4j (포트 7474/7687)
역할:
- 지식 그래프 저장
- 그래프 쿼리 처리
- 관계 추론
- 경로 탐색
주요 기능:
- Cypher 쿼리 언어
- 그래프 알고리즘
- 관계 데이터 모델링
- 트랜잭션 처리
7. 개발 및 배포 워크플로우
7.1 개발 환경 설정
# 1. 프로젝트 클론
git clone <kag-repository>
cd kag-docker
# 2. 컨테이너 빌드 및 실행
docker-compose up --build
# 3. 서비스 확인
curl http://localhost:8000/health
curl http://localhost:9200/_cluster/health
curl http://localhost:7474/
7.2 개발 중 디버깅
# 개별 서비스 로그 확인
docker-compose logs -f app
docker-compose logs -f elasticsearch
docker-compose logs -f neo4j
# 컨테이너 내부 접근
docker-compose exec app bash
docker-compose exec elasticsearch bash
docker-compose exec neo4j cypher-shell
7.3 데이터 관리
# 데이터 볼륨 백업
docker run --rm -v kag-docker_neo4j_data:/data \
-v $(pwd):/backup alpine \
tar czf /backup/neo4j_backup.tar.gz -C /data .
# 데이터 볼륨 복원
docker run --rm -v kag-docker_neo4j_data:/data \
-v $(pwd):/backup alpine \
tar xzf /backup/neo4j_backup.tar.gz -C /data
8. 프로덕션 배포 고려사항
8.1 보안 강화
# 프로덕션용 환경 변수
environment:
- NEO4J_AUTH=admin/${STRONG_PASSWORD}
- ELASTICSEARCH_USERNAME=${ES_USERNAME}
- ELASTICSEARCH_PASSWORD=${ES_PASSWORD}
- SSL_ENABLED=true
8.2 성능 튜닝
# Elasticsearch 메모리 증설
"ES_JAVA_OPTS=-Xms2g -Xmx2g"
# Neo4j 성능 설정
NEO4J_dbms_memory_heap_initial__size=1g
NEO4J_dbms_memory_heap_max__size=2g
NEO4J_dbms_memory_pagecache_size=1g
8.3 모니터링 및 로깅
# 로깅 드라이버 설정
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
# 헬스체크 추가
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
9. 확장성 및 고가용성
9.1 수평 확장 전략
graph TB
subgraph "로드 밸런서"
LB[Nginx/HAProxy]
end
subgraph "KAG 애플리케이션 클러스터"
A1[KAG App 1]
A2[KAG App 2]
A3[KAG App 3]
end
subgraph "Elasticsearch 클러스터"
E1[ES Master]
E2[ES Data 1]
E3[ES Data 2]
end
subgraph "Neo4j 클러스터"
N1[Neo4j Core 1]
N2[Neo4j Core 2]
N3[Neo4j Read Replica]
end
LB --> A1
LB --> A2
LB --> A3
A1 --> E1
A2 --> E2
A3 --> E3
A1 --> N1
A2 --> N2
A3 --> N3
9.2 Docker Swarm/Kubernetes 마이그레이션
# Docker Swarm용 설정 예시
version: '3.8'
services:
app:
image: kag:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
결론
KAG의 Docker 컨테이너 오케스트레이션은 마이크로서비스 아키텍처의 모범 사례를 보여줍니다. 각 서비스가 독립적으로 확장 가능하면서도 유기적으로 연결된 구조를 통해 높은 가용성과 성능을 제공합니다.
핵심 아키텍처 장점:
- 서비스 분리: 각 컴포넌트의 독립적 관리 및 확장
- 개발 효율성: Docker Compose를 통한 간편한 환경 구성
- 데이터 영속성: 볼륨을 통한 안정적인 데이터 보존
- 네트워크 격리: 보안성과 성능을 고려한 네트워크 설계
다음 포스트에서는 KAG Builder 모듈의 상세한 아키텍처와 지식 추출 프로세스를 분석하겠습니다.
연관 포스트:
참고 자료: