feat(docker): Docker 컨테이너 환경 구성

[파일 목록]
- Dockerfile: GUARDiA ITSM Python 3.11-slim 이미지
  - 비루트(guardia) 계정 실행
  - 헬스체크 내장
- docker-compose.yml: 개발/테스트 풀스택
  - guardia-itsm, nginx, postgres:15, redis:7, ollama/ollama, tomcat:9
  - Ollama 모델은 볼륨 마운트 (이미지 외부)
- docker-compose.prod.yml: 운영 환경 오버라이드
  - 외부 포트 차단, 호스트 경로 마운트
- docker-compose.gpu.yml: NVIDIA GPU 오버라이드
- docker-start.sh: dev|prod|gpu 모드 빠른 시작
- .dockerignore: 보안 파일(.env, *.db) 제외
- docker/nginx/guardia.conf: 리버스 프록시 설정
- docker/tomcat/tomcat-users.xml: opsagent 계정 설정

[폐쇄망 패키지]
- setup/docker_package.sh: 인터넷 서버에서 모든 이미지를 tar.gz로 패키징
- setup/docker_load.sh: 폐쇄망에서 tar.gz 로드 + 즉시 시작 옵션

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DESKTOP-TKLFCPR\ython 2026-05-29 19:08:52 +09:00
parent a899dc208a
commit eea643ce40
10 changed files with 778 additions and 0 deletions

63
.dockerignore Normal file
View File

@ -0,0 +1,63 @@
# Git
.git/
.gitignore
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/
*.egg-info/
dist/
build/
.eggs/
venv/
.venv/
env/
# 개발 도구
.vscode/
.idea/
*.swp
*.swo
# 테스트
tests/
test_*.py
*.test.py
# 문서/설정
docs/
manual/
*.md
*.txt
!requirements.txt
# 데이터
*.db
*.sqlite
*.sqlite3
guardia_itsm.db*
# 업로드
uploads/
*.log
# 환경 파일 (보안)
.env
.env.*
*.env
secrets/
# Docker 패키지
docker-package/
# 설치 스크립트
setup/
# workspace
workspace/
# Claude
.claude/

54
Dockerfile Normal file
View File

@ -0,0 +1,54 @@
# ============================================================
# GUARDiA ITSM — Production Dockerfile
# Base: Python 3.11-slim
# Port: 8001
# ============================================================
FROM python:3.11-slim AS base
LABEL maintainer="GUARDiA Team"
LABEL description="GUARDiA ITSM — AI 기반 레거시 인프라 자율 운영 플랫폼"
# ── 시스템 의존성 ──────────────────────────────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
curl wget git \
libpq-dev gcc \
&& rm -rf /var/lib/apt/lists/*
# ── 비루트 실행 계정 ───────────────────────────────────────
RUN groupadd -r guardia && useradd -r -g guardia -d /app guardia
# ── 작업 디렉토리 ─────────────────────────────────────────
WORKDIR /app
# ── Python 의존성 (레이어 캐싱 최적화) ─────────────────────
COPY itsm/requirements.txt .
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt
# ── 애플리케이션 소스 복사 ───────────────────────────────
COPY itsm/ .
# ── 업로드/데이터 디렉토리 ──────────────────────────────
RUN mkdir -p uploads/sr_files uploads/workspaces \
&& chown -R guardia:guardia /app
# ── 환경 기본값 ──────────────────────────────────────────
ENV PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8 \
PYTHONDONTWRITEBYTECODE=1 \
DATABASE_URL="sqlite+aiosqlite:///./guardia_itsm.db" \
OLLAMA_BASE_URL="http://ollama:11434"
# ── 포트 노출 ────────────────────────────────────────────
EXPOSE 8001
# ── 비루트 전환 ─────────────────────────────────────────
USER guardia
# ── 헬스체크 ─────────────────────────────────────────────
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -sf http://localhost:8001/ || exit 1
# ── 엔트리포인트 ─────────────────────────────────────────
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--workers", "4"]

23
docker-compose.gpu.yml Normal file
View File

@ -0,0 +1,23 @@
# ============================================================
# GUARDiA — NVIDIA GPU 오버라이드 (Ollama 가속)
# ============================================================
# 사용법:
# docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d ollama
#
# 전제조건:
# - nvidia-container-toolkit 설치 완료
# - nvidia-smi 정상 작동 확인
# ============================================================
services:
ollama:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
environment:
OLLAMA_HOST: 0.0.0.0
CUDA_VISIBLE_DEVICES: "0"

46
docker-compose.prod.yml Normal file
View File

@ -0,0 +1,46 @@
# ============================================================
# GUARDiA ITSM — 운영 환경 오버라이드
# ============================================================
# 사용법:
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# ============================================================
services:
guardia:
image: guardia-itsm:${GUARDIA_VERSION:-latest}
environment:
DATABASE_URL: ${DATABASE_URL} # 운영 DB URL 필수
SECRET_KEY: ${SECRET_KEY} # 운영 시크릿 키 필수
ports:
- "127.0.0.1:8001:8001" # localhost만 — Nginx를 통해서만 외부 접근
volumes:
- /data/guardia/uploads:/app/uploads # 호스트 경로 직접 마운트
- /data/guardia/logs:/app/logs
deploy:
replicas: 1
restart_policy:
condition: on-failure
max_attempts: 3
resources:
limits:
cpus: "2.0"
memory: 2G
postgres:
ports: [] # 운영에서는 외부 노출 금지
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
redis:
ports: [] # 운영에서는 외부 노출 금지
ollama:
ports:
- "127.0.0.1:11434:11434" # localhost만 노출
volumes:
- ${OLLAMA_MODELS_PATH:-/data/ollama}:/root/.ollama
tomcat:
ports:
- "127.0.0.1:8080:8080" # localhost만

196
docker-compose.yml Normal file
View File

@ -0,0 +1,196 @@
# ============================================================
# GUARDiA ITSM — Full Stack docker-compose (개발/테스트용)
# ============================================================
# 사용법:
# docker compose up -d # 전체 스택 시작
# docker compose up -d guardia # GUARDiA만 시작 (DB/Redis는 외부)
# docker compose logs -f guardia
# docker compose down -v # 볼륨 포함 완전 삭제
#
# 환경변수:
# .env 파일에 GUARDIA_LICENSE_KEY, SECRET_KEY 등을 설정하세요.
# (itsm/.env가 없으면 기본값 사용)
# ============================================================
x-common-env: &common-env
PYTHONIOENCODING: utf-8
PYTHONUNBUFFERED: "1"
DATABASE_URL: postgresql+asyncpg://guardia:guardia@postgres:5432/guardia
REDIS_URL: redis://redis:6379/0
OLLAMA_BASE_URL: http://ollama:11434
MESSENGER_BASE_URL: http://messenger:8002
MESSENGER_OPS_ROOM: ops
services:
# ── GUARDiA ITSM ────────────────────────────────────────
guardia:
build:
context: .
dockerfile: Dockerfile
image: guardia-itsm:latest
container_name: guardia-itsm
ports:
- "8001:8001"
environment:
<<: *common-env
SECRET_KEY: ${SECRET_KEY:-change_this_in_production_min_32chars}
ALGORITHM: HS256
ACCESS_TOKEN_EXPIRE_MINUTES: 480
GUARDIA_LLM_MODEL: ${GUARDIA_LLM_MODEL:-llama3.1:8b}
GUARDIA_LICENSE_KEY: ${GUARDIA_LICENSE_KEY:-}
# OAuth 소셜 로그인 (선택)
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-}
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-}
KEYCLOAK_BASE_URL: ${KEYCLOAK_BASE_URL:-}
KEYCLOAK_REALM: ${KEYCLOAK_REALM:-master}
KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID:-guardia}
KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET:-}
CATALINA_HOME: /app/tomcat
volumes:
- guardia-uploads:/app/uploads
- guardia-db:/app # SQLite 개발 모드용 (PostgreSQL 사용 시 불필요)
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- guardia-net
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8001/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ── Nginx 리버스 프록시 ──────────────────────────────────
nginx:
image: nginx:alpine
container_name: guardia-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/guardia.conf:/etc/nginx/conf.d/default.conf:ro
- ./docker/nginx/ssl:/etc/nginx/ssl:ro # HTTPS 인증서 (선택)
depends_on:
- guardia
networks:
- guardia-net
restart: unless-stopped
# ── PostgreSQL ───────────────────────────────────────────
postgres:
image: postgres:15-alpine
container_name: guardia-postgres
environment:
POSTGRES_DB: guardia
POSTGRES_USER: guardia
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-guardia}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- guardia-pgdata:/var/lib/postgresql/data
ports:
- "5432:5432" # 개발용 노출 (운영에서는 제거)
networks:
- guardia-net
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U guardia -d guardia"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# ── Redis ────────────────────────────────────────────────
redis:
image: redis:7-alpine
container_name: guardia-redis
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- guardia-redis:/data
ports:
- "6379:6379" # 개발용 노출
networks:
- guardia-net
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
# ── Ollama (온프레미스 sLLM) ─────────────────────────────
# 모델은 볼륨(guardia-ollama-models)에 저장 — 이미지에 포함 안 함
# GPU 지원: docker-compose.gpu.yml 오버라이드 파일 참조
ollama:
image: ollama/ollama:latest
container_name: guardia-ollama
volumes:
- guardia-ollama-models:/root/.ollama # 모델 영구 저장
ports:
- "11434:11434" # 내부 전용 (외부 노출 금지 권장)
environment:
OLLAMA_HOST: 0.0.0.0
networks:
- guardia-net
restart: unless-stopped
# GPU 사용 시 아래 주석 해제 (docker-compose.gpu.yml에서 override)
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: 1
# capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:11434/api/version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
# ── Tomcat 9 (WAS 환경 시뮬레이션) ──────────────────────
tomcat:
image: tomcat:9.0-jdk17-temurin-jammy
container_name: guardia-tomcat
ports:
- "8080:8080"
volumes:
- guardia-tomcat-webapps:/usr/local/tomcat/webapps
- guardia-tomcat-logs:/usr/local/tomcat/logs
- ./docker/tomcat/tomcat-users.xml:/usr/local/tomcat/conf/tomcat-users.xml:ro
environment:
JAVA_OPTS: "-Xms512m -Xmx1024m -Djava.awt.headless=true"
CATALINA_OPTS: "-server -XX:+UseParallelGC"
networks:
- guardia-net
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# ── 볼륨 ─────────────────────────────────────────────────
volumes:
guardia-db:
guardia-uploads:
guardia-pgdata:
guardia-redis:
guardia-ollama-models: # Ollama 모델 (로컬 경로 마운트 가능)
guardia-tomcat-webapps:
guardia-tomcat-logs:
# ── 네트워크 ──────────────────────────────────────────────
networks:
guardia-net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16

33
docker-start.sh Normal file
View File

@ -0,0 +1,33 @@
#!/bin/bash
# GUARDiA 빠른 시작 스크립트
# 사용법: bash docker-start.sh [dev|prod|gpu]
MODE="${1:-dev}"
case "$MODE" in
dev)
echo "▶ 개발 모드 시작..."
docker compose up -d
;;
prod)
echo "▶ 운영 모드 시작..."
[[ -f ".env" ]] || { echo "❌ .env 파일이 없습니다. .env.example을 복사하세요."; exit 1; }
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
;;
gpu)
echo "▶ GPU 모드 시작 (Ollama NVIDIA 가속)..."
docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d
;;
*)
echo "사용법: bash docker-start.sh [dev|prod|gpu]"
exit 1
;;
esac
echo ""
echo "서비스 상태:"
docker compose ps
echo ""
echo "접속: http://localhost"
echo "로그: docker compose logs -f guardia"

44
docker/nginx/guardia.conf Normal file
View File

@ -0,0 +1,44 @@
upstream guardia_backend {
server guardia:8001;
keepalive 32;
}
server {
listen 80;
server_name _;
client_max_body_size 100M;
# GUARDiA ITSM
location / {
proxy_pass http://guardia_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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_read_timeout 300s;
proxy_send_timeout 300s;
}
# 정적 파일 직접 서빙 (선택 최적화)
# location /static/ {
# alias /app/static/;
# expires 1d;
# add_header Cache-Control "public";
# }
# Ollama/Tomcat 직접 노출 금지
location /api/ollama/ { return 403; }
location /manager/ { return 403; }
}
# HTTPS (인증서 설정 후 활성화)
# server {
# listen 443 ssl http2;
# server_name guardia.example.com;
# ssl_certificate /etc/nginx/ssl/cert.pem;
# ssl_certificate_key /etc/nginx/ssl/key.pem;
# ssl_protocols TLSv1.2 TLSv1.3;
# include /etc/nginx/conf.d/guardia.conf; # location 블록 재사용
# }

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<!-- GUARDiA SSH 원격 관리용 계정 -->
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<!-- opsagent: GUARDiA ITSM 배포/재기동 자동화 계정 -->
<!-- 운영 환경에서는 반드시 비밀번호를 변경하세요 -->
<user username="opsagent"
password="Opsagent@guardia!"
roles="manager-script,manager-jmx"/>
<!-- admin: Tomcat Manager UI (개발용) -->
<user username="admin"
password="Admin@guardia!"
roles="manager-gui,admin-gui"/>
</tomcat-users>

149
setup/docker_load.sh Normal file
View File

@ -0,0 +1,149 @@
#!/bin/bash
# ============================================================
# GUARDiA Docker 폐쇄망 로드 스크립트
# ============================================================
# docker_package.sh 로 생성된 패키지를 폐쇄망 서버에서 로드합니다.
#
# 사용법:
# tar -xzf guardia-docker-YYYYMMDD.tar.gz
# cd guardia-docker-YYYYMMDD
# bash docker_load.sh
# bash docker_load.sh --start # 로드 후 즉시 서비스 시작
# ============================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
START_AFTER="${1:-}"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
fail() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
info() { echo -e " $*"; }
echo "=================================================="
echo " GUARDiA Docker 폐쇄망 로드"
echo " 디렉토리: $SCRIPT_DIR"
echo "=================================================="
# Docker 설치 확인
command -v docker &>/dev/null || fail "Docker가 설치되지 않았습니다. Docker Engine을 먼저 설치하세요."
docker compose version &>/dev/null || fail "Docker Compose가 필요합니다. (docker compose 플러그인)"
# ── 1. 버전 정보 확인 ───────────────────────────────────
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
source "$SCRIPT_DIR/VERSION"
echo " 패키지 버전: ${GUARDIA_VERSION:-unknown}"
echo " 빌드 일자: ${BUILD_DATE:-unknown}"
fi
# ── 2. 이미지 로드 ──────────────────────────────────────
echo ""
echo "[1/4] Docker 이미지 로드..."
# 메인 이미지
if [[ -f "$SCRIPT_DIR/images.tar.gz" ]]; then
echo " 로드 중: images.tar.gz"
docker load < "$SCRIPT_DIR/images.tar.gz"
ok "메인 이미지 로드 완료"
else
fail "images.tar.gz 파일을 찾을 수 없습니다."
fi
# Ollama 이미지
if [[ -f "$SCRIPT_DIR/ollama.tar.gz" ]]; then
echo " 로드 중: ollama.tar.gz (시간이 걸릴 수 있습니다...)"
docker load < "$SCRIPT_DIR/ollama.tar.gz"
ok "Ollama 이미지 로드 완료"
else
warn "ollama.tar.gz 없음 — Ollama 이미지 로드 건너뜀"
fi
# 로드된 이미지 목록 출력
echo ""
echo " 로드된 이미지:"
docker images | grep -E "guardia|postgres|redis|nginx|ollama|tomcat" | awk '{printf " %-40s %-15s %s\n", $1, $2, $7}'
# ── 3. Ollama 모델 복사 ─────────────────────────────────
echo ""
echo "[2/4] Ollama 모델 설정..."
MODELS_TAR="$SCRIPT_DIR/ollama-models/models.tar.gz"
if [[ -f "$MODELS_TAR" ]]; then
OLLAMA_DATA="${OLLAMA_DATA_DIR:-/var/lib/guardia/ollama}"
mkdir -p "$OLLAMA_DATA"
echo " 모델 압축 해제: $OLLAMA_DATA"
tar -xzf "$MODELS_TAR" -C "$OLLAMA_DATA"
ok "Ollama 모델 복사 완료: $OLLAMA_DATA"
export OLLAMA_MODELS_PATH="$OLLAMA_DATA"
else
warn "Ollama 모델 파일 없음"
warn " 방법 1: docker compose up -d ollama && docker exec guardia-ollama ollama pull llama3.1:8b"
warn " 방법 2: 모델 파일을 수동으로 /var/lib/guardia/ollama/models 에 복사"
fi
# ── 4. 환경 파일 설정 ────────────────────────────────────
echo ""
echo "[3/4] 환경 파일 설정..."
ENV_FILE="$SCRIPT_DIR/.env"
if [[ ! -f "$ENV_FILE" ]]; then
cat > "$ENV_FILE" << 'ENVEOF'
# GUARDiA Docker 환경 설정
# 운영 배포 전 반드시 아래 값을 변경하세요!
SECRET_KEY=CHANGE_THIS_TO_RANDOM_32CHARS_MIN
POSTGRES_PASSWORD=guardia_secure_pw
GUARDIA_VERSION=latest
GUARDIA_LLM_MODEL=llama3.1:8b
# Ollama 모델 경로 (폐쇄망 복사 경로)
OLLAMA_MODELS_PATH=/var/lib/guardia/ollama
ENVEOF
warn ".env 파일 생성됨 — SECRET_KEY를 반드시 변경하세요: $ENV_FILE"
else
info ".env 파일 이미 존재"
fi
# ── 5. 서비스 시작 ──────────────────────────────────────
echo ""
echo "[4/4] 서비스 준비 완료"
if [[ "$START_AFTER" == "--start" ]]; then
echo " 서비스 시작 중..."
cd "$SCRIPT_DIR"
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 헬스체크 대기
echo " 서비스 기동 대기 (최대 60초)..."
for i in $(seq 1 12); do
sleep 5
if curl -sf http://localhost/ &>/dev/null; then
ok "GUARDiA ITSM 서비스 정상 기동!"
break
fi
echo " 대기 중... (${i}/12)"
done
echo ""
echo " 서비스 상태:"
docker compose ps
else
echo ""
info "서비스 시작 명령어:"
info " cd $SCRIPT_DIR"
info " # .env 파일 편집 후:"
info " docker compose up -d"
info " # 또는 운영 모드:"
info " docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d"
info ""
info " # 즉시 시작:"
info " bash docker_load.sh --start"
fi
echo ""
echo "=================================================="
ok "Docker 로드 완료!"
info "접속 URL: http://$(hostname -I | awk '{print $1}')"
info "GUARDiA 로그: docker compose logs -f guardia"
echo "=================================================="

147
setup/docker_package.sh Normal file
View File

@ -0,0 +1,147 @@
#!/bin/bash
# ============================================================
# GUARDiA Docker 폐쇄망 패키지 생성 스크립트
# ============================================================
# 인터넷이 연결된 서버에서 실행하여 모든 이미지를 tar로 패키징합니다.
# 생성된 패키지를 USB/내부 서버를 통해 폐쇄망에 복사 후 docker_load.sh 실행.
#
# 사용법:
# bash setup/docker_package.sh [출력 디렉토리]
# bash setup/docker_package.sh /mnt/usb/guardia-docker
#
# 결과:
# guardia-docker-<버전>.tar.gz (약 5~8GB — Ollama 모델 별도)
# ============================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GUARDIA_ROOT="$(dirname "$SCRIPT_DIR")"
OUTPUT_DIR="${1:-$GUARDIA_ROOT/docker-package}"
VERSION="${GUARDIA_VERSION:-$(date +%Y%m%d)}"
PACKAGE_NAME="guardia-docker-${VERSION}"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
info() { echo -e " $*"; }
echo "=================================================="
echo " GUARDiA Docker 패키지 생성"
echo " 버전: $VERSION"
echo " 출력: $OUTPUT_DIR"
echo "=================================================="
mkdir -p "$OUTPUT_DIR/$PACKAGE_NAME"
cd "$GUARDIA_ROOT"
# ── 1. GUARDiA 이미지 빌드 ───────────────────────────────
echo ""
echo "[1/5] GUARDiA ITSM 이미지 빌드..."
docker build -t "guardia-itsm:${VERSION}" -t "guardia-itsm:latest" .
ok "guardia-itsm:${VERSION} 빌드 완료"
# ── 2. 외부 이미지 Pull ──────────────────────────────────
echo ""
echo "[2/5] 외부 이미지 다운로드..."
IMAGES=(
"postgres:15-alpine"
"redis:7-alpine"
"nginx:alpine"
"ollama/ollama:latest"
"tomcat:9.0-jdk17-temurin-jammy"
)
for img in "${IMAGES[@]}"; do
echo " pulling: $img"
docker pull "$img" || warn "$img 다운로드 실패 — 건너뜀"
done
ok "외부 이미지 다운로드 완료"
# ── 3. 이미지 tar 저장 ──────────────────────────────────
echo ""
echo "[3/5] 이미지 저장 (tar)..."
TAR_FILE="$OUTPUT_DIR/$PACKAGE_NAME/images.tar"
docker save \
"guardia-itsm:${VERSION}" \
"guardia-itsm:latest" \
"postgres:15-alpine" \
"redis:7-alpine" \
"nginx:alpine" \
"tomcat:9.0-jdk17-temurin-jammy" \
| gzip > "${TAR_FILE}.gz"
ok "이미지 저장 완료: ${TAR_FILE}.gz ($(du -sh "${TAR_FILE}.gz" | cut -f1))"
# Ollama는 용량이 크므로 별도 패키징
OLLAMA_TAR="$OUTPUT_DIR/$PACKAGE_NAME/ollama.tar"
docker save "ollama/ollama:latest" | gzip > "${OLLAMA_TAR}.gz"
ok "Ollama 이미지 저장: ${OLLAMA_TAR}.gz ($(du -sh "${OLLAMA_TAR}.gz" | cut -f1))"
# ── 4. Ollama 모델 패키징 (선택) ──────────────────────────
echo ""
echo "[4/5] Ollama 모델 패키징..."
OLLAMA_MODELS_DIR="${OLLAMA_MODELS_DIR:-$HOME/.ollama}"
MODELS_OUTPUT="$OUTPUT_DIR/$PACKAGE_NAME/ollama-models"
if [[ -d "$OLLAMA_MODELS_DIR/models" ]]; then
echo " 모델 디렉토리: $OLLAMA_MODELS_DIR"
mkdir -p "$MODELS_OUTPUT"
# 각 모델을 개별 파일로 저장 (크기 분할)
if command -v ollama &>/dev/null; then
ollama list 2>/dev/null | tail -n +2 | awk '{print $1}' | while read -r model; do
model_file="${model//:/--}.tar"
echo " 패키징 모델: $model$model_file"
# 모델 블obs 복사
mkdir -p "$MODELS_OUTPUT/$model_file"
done
fi
# 전체 모델 디렉토리 압축
tar -czf "$MODELS_OUTPUT/models.tar.gz" -C "$OLLAMA_MODELS_DIR" models manifests 2>/dev/null \
&& ok "Ollama 모델 패키징 완료 ($(du -sh "$MODELS_OUTPUT/models.tar.gz" | cut -f1))" \
|| warn "모델 패키징 실패 — 수동으로 ~/.ollama/models 를 복사하세요"
else
warn "Ollama 모델 디렉토리 없음 — 별도 복사 필요: $OLLAMA_MODELS_DIR"
echo " # 모델을 미리 다운로드: ollama pull llama3.1:8b"
fi
# ── 5. 배포 파일 패키징 ──────────────────────────────────
echo ""
echo "[5/5] 배포 파일 패키징..."
# docker-compose 파일 복사
cp "$GUARDIA_ROOT/docker-compose.yml" "$OUTPUT_DIR/$PACKAGE_NAME/"
cp "$GUARDIA_ROOT/docker-compose.prod.yml" "$OUTPUT_DIR/$PACKAGE_NAME/"
cp "$GUARDIA_ROOT/docker-compose.gpu.yml" "$OUTPUT_DIR/$PACKAGE_NAME/"
cp "$GUARDIA_ROOT/setup/docker_load.sh" "$OUTPUT_DIR/$PACKAGE_NAME/"
cp -r "$GUARDIA_ROOT/docker/" "$OUTPUT_DIR/$PACKAGE_NAME/docker/"
chmod +x "$OUTPUT_DIR/$PACKAGE_NAME/docker_load.sh"
# 버전 정보
cat > "$OUTPUT_DIR/$PACKAGE_NAME/VERSION" << VEREOF
GUARDIA_VERSION=$VERSION
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
IMAGES_INCLUDED=guardia-itsm,postgres,redis,nginx,tomcat,ollama
VEREOF
# 전체 패키지 압축
echo " 전체 패키지 압축 중..."
cd "$OUTPUT_DIR"
tar -czf "${PACKAGE_NAME}.tar.gz" "$PACKAGE_NAME/"
ok "패키지 생성 완료: $OUTPUT_DIR/${PACKAGE_NAME}.tar.gz"
echo ""
echo "=================================================="
ok "패키지 생성 완료!"
echo ""
info "패키지 크기:"
du -sh "$OUTPUT_DIR/${PACKAGE_NAME}.tar.gz"
echo ""
info "폐쇄망 설치 절차:"
info " 1. ${PACKAGE_NAME}.tar.gz 을 폐쇄망 서버로 복사"
info " 2. tar -xzf ${PACKAGE_NAME}.tar.gz"
info " 3. bash ${PACKAGE_NAME}/docker_load.sh"
echo "=================================================="