diff --git a/deploy/check-nginx.sh b/deploy/check-nginx.sh new file mode 100755 index 0000000..aacb562 --- /dev/null +++ b/deploy/check-nginx.sh @@ -0,0 +1,225 @@ +#!/bin/bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NGINX_DIR="/data/training/nginx" +PASS=0 +FAIL=0 + +# docker compose v1/v2 자동 감지 +if command -v docker-compose &>/dev/null; then + DOCKER_COMPOSE="docker-compose" +elif docker compose version &>/dev/null 2>&1; then + DOCKER_COMPOSE="docker compose" +else + DOCKER_COMPOSE="" +fi + +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' + +ok() { echo -e "${GREEN}[OK]${NC} $1"; ((PASS++)); } +fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL++)); } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +section() { echo ""; echo "=== $1 ==="; } + +# ────────────────────────────────────────── +section "디렉토리 확인" +# ────────────────────────────────────────── +for dir in \ + /data/training/request \ + /data/training/request/tmp \ + /data/training/response \ + /data/training/response/v6-cls-checkpoints \ + /data/training/tmp \ + "$NGINX_DIR" \ + "$NGINX_DIR/ssl" \ + "$NGINX_DIR/logs"; do + if [ -d "$dir" ]; then + ok "$dir" + else + fail "$dir 없음" + fi +done + +# ────────────────────────────────────────── +section "nginx 파일 확인" +# ────────────────────────────────────────── +for f in \ + "$NGINX_DIR/nginx.conf" \ + "$NGINX_DIR/docker-compose-nginx.yml" \ + "$NGINX_DIR/ssl/train-kamco.com.crt" \ + "$NGINX_DIR/ssl/train-kamco.com.key" \ + "$NGINX_DIR/ssl/openssl.cnf"; do + if [ -f "$f" ]; then + ok "$f" + else + fail "$f 없음" + fi +done + +# ────────────────────────────────────────── +section "파일 권한 확인" +# ────────────────────────────────────────── +SSL_DIR_PERM=$(stat -c "%a" "$NGINX_DIR/ssl" 2>/dev/null) +KEY_PERM=$(stat -c "%a" "$NGINX_DIR/ssl/train-kamco.com.key" 2>/dev/null) +CRT_PERM=$(stat -c "%a" "$NGINX_DIR/ssl/train-kamco.com.crt" 2>/dev/null) + +[ "$SSL_DIR_PERM" = "700" ] && ok "ssl/ 권한 700" || fail "ssl/ 권한 오류 (현재: $SSL_DIR_PERM, 기대: 700)" +[ "$KEY_PERM" = "600" ] && ok "train-kamco.com.key 권한 600" || fail "key 권한 오류 (현재: $KEY_PERM, 기대: 600)" +[ "$CRT_PERM" = "644" ] && ok "train-kamco.com.crt 권한 644" || fail "crt 권한 오류 (현재: $CRT_PERM, 기대: 644)" + +# ────────────────────────────────────────── +section "소유권 확인 (kcomu:kcomu)" +# ────────────────────────────────────────── +OWNER=$(stat -c "%U:%G" /data/training 2>/dev/null) +[ "$OWNER" = "kcomu:kcomu" ] && ok "/data/training 소유권 kcomu:kcomu" || fail "/data/training 소유권 오류 (현재: $OWNER)" + +# ────────────────────────────────────────── +section "SSL 인증서 유효성" +# ────────────────────────────────────────── +if command -v openssl &>/dev/null && [ -f "$NGINX_DIR/ssl/train-kamco.com.crt" ]; then + EXPIRY=$(openssl x509 -in "$NGINX_DIR/ssl/train-kamco.com.crt" -noout -enddate 2>/dev/null | cut -d= -f2) + EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s 2>/dev/null) + NOW_EPOCH=$(date +%s) + if [ "$EXPIRY_EPOCH" -gt "$NOW_EPOCH" ]; then + ok "인증서 유효 (만료: $EXPIRY)" + else + fail "인증서 만료됨 (만료: $EXPIRY)" + fi + + SAN=$(openssl x509 -in "$NGINX_DIR/ssl/train-kamco.com.crt" -noout -text 2>/dev/null | grep -A1 "Subject Alternative Name" | tail -1) + echo " SAN: $SAN" +else + warn "openssl 없음 또는 인증서 파일 없음 - 인증서 검증 스킵" +fi + +# ────────────────────────────────────────── +section "Docker 확인" +# ────────────────────────────────────────── +if command -v docker &>/dev/null && docker info &>/dev/null 2>&1; then + ok "Docker 실행 중" + + # Docker network + if docker network ls --format '{{.Name}}' | grep -q "^kamco-cds$"; then + ok "Docker network kamco-cds 존재" + else + fail "Docker network kamco-cds 없음 (setup.sh 재실행 필요)" + fi + + # nginx 컨테이너 상태 + CONTAINER_STATUS=$(docker inspect --format '{{.State.Status}}' kamco-train-nginx 2>/dev/null) + if [ "$CONTAINER_STATUS" = "running" ]; then + ok "kamco-train-nginx 컨테이너 실행 중" + elif [ -z "$CONTAINER_STATUS" ]; then + warn "kamco-train-nginx 컨테이너 없음 (아직 미실행)" + else + fail "kamco-train-nginx 컨테이너 상태: $CONTAINER_STATUS" + fi +else + fail "Docker 미실행 또는 설치 안 됨" +fi + +# ────────────────────────────────────────── +section "nginx 설정 문법 검사" +# ────────────────────────────────────────── +if command -v docker &>/dev/null && docker info &>/dev/null 2>&1; then + echo " docker run으로 nginx -t 실행 중..." + if docker run --rm \ + -v "$NGINX_DIR/nginx.conf:/etc/nginx/nginx.conf:ro" \ + -v "$NGINX_DIR/ssl:/etc/nginx/ssl:ro" \ + nginx:alpine nginx -t 2>&1; then + ok "nginx 설정 문법 OK" + else + fail "nginx 설정 문법 오류" + fi +else + warn "Docker 없음 - nginx 문법 검사 스킵" +fi + +# ────────────────────────────────────────── +section "/etc/hosts 확인" +# ────────────────────────────────────────── +for domain in api.train-kamco.com train-kamco.com; do + HOSTS_LINE=$(grep "$domain" /etc/hosts | grep -v "^#" | head -1) + if [ -n "$HOSTS_LINE" ]; then + ok "$domain 등록됨 → $HOSTS_LINE" + else + fail "$domain /etc/hosts 미등록" + fi +done + +# ────────────────────────────────────────── +section "도메인 해석 확인" +# ────────────────────────────────────────── +for domain in api.train-kamco.com train-kamco.com; do + RESOLVED=$(getent hosts "$domain" 2>/dev/null | awk '{print $1}' | head -1) + if [ -n "$RESOLVED" ]; then + ok "$domain → $RESOLVED" + else + fail "$domain 해석 실패 (DNS 또는 hosts 문제)" + fi +done + +# ────────────────────────────────────────── +section "포트 연결 확인 (80 / 443)" +# ────────────────────────────────────────── +for port in 80 443; do + if command -v nc &>/dev/null; then + if nc -z -w3 api.train-kamco.com "$port" 2>/dev/null; then + ok "api.train-kamco.com:$port 열림" + else + warn "api.train-kamco.com:$port 닫힘 (nginx 미실행일 수 있음)" + fi + elif command -v curl &>/dev/null; then + HTTP_CODE=$(curl -sk -o /dev/null -w "%{http_code}" --connect-timeout 3 \ + "$([ "$port" = "443" ] && echo https || echo http)://api.train-kamco.com/" 2>/dev/null) + if [ -n "$HTTP_CODE" ] && [ "$HTTP_CODE" != "000" ]; then + ok "api.train-kamco.com:$port 응답 (HTTP $HTTP_CODE)" + else + warn "api.train-kamco.com:$port 응답 없음 (nginx 미실행일 수 있음)" + fi + else + warn "nc/curl 없음 - 포트 확인 스킵" + break + fi +done + +# ────────────────────────────────────────── +section "HTTPS 헬스체크" +# ────────────────────────────────────────── +if command -v curl &>/dev/null; then + for url in \ + "https://api.train-kamco.com/monitor/health" \ + "https://train-kamco.com/monitor/health"; do + HTTP_CODE=$(curl -sk -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url" 2>/dev/null) + if [ "$HTTP_CODE" = "200" ]; then + ok "$url → HTTP $HTTP_CODE" + elif [ "$HTTP_CODE" = "000" ] || [ -z "$HTTP_CODE" ]; then + warn "$url → 응답 없음 (nginx 미실행일 수 있음)" + else + warn "$url → HTTP $HTTP_CODE" + fi + done +else + warn "curl 없음 - HTTPS 헬스체크 스킵" +fi + +# ────────────────────────────────────────── +section "결과 요약" +# ────────────────────────────────────────── +echo "" +echo -e " ${GREEN}PASS: $PASS${NC} / ${RED}FAIL: $FAIL${NC}" +echo "" +if [ $FAIL -eq 0 ]; then + echo -e "${GREEN}모든 체크 통과. nginx 실행 준비 완료.${NC}" + if [ -n "$DOCKER_COMPOSE" ]; then + echo " cd $NGINX_DIR && $DOCKER_COMPOSE -f docker-compose-nginx.yml up -d" + else + echo " [WARN] docker-compose / docker compose 를 찾을 수 없습니다." + fi +else + echo -e "${RED}$FAIL 개 항목 실패. 위 오류를 확인하세요.${NC}" + exit 1 +fi diff --git a/deploy/docker-compose-nginx.yml b/deploy/docker-compose-nginx.yml new file mode 100644 index 0000000..e7632ae --- /dev/null +++ b/deploy/docker-compose-nginx.yml @@ -0,0 +1,25 @@ +services: + nginx: + image: nginx:alpine + container_name: kamco-train-nginx + user: 1000:1000 + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./ssl:/etc/nginx/ssl:ro + - ./logs:/var/log/nginx + networks: + - kamco-cds + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "--no-check-certificate", "https://localhost/monitor/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + +networks: + kamco-cds: + external: true \ No newline at end of file diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..5d0e80a --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,195 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # 로그 설정 + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; + + sendfile on; + keepalive_timeout 65; + + # 업로드 파일 크기 / 타임아웃 (10GB, 10분) + client_max_body_size 10G; + client_body_timeout 600s; + + # Upstream 설정 + upstream api_backend { + server kamco-train-api:8080; + } + + upstream web_backend { + server kamco-train-web:3002; + } + + # HTTP → HTTPS 리다이렉트 서버 + server { + listen 80; + server_name api.train-kamco.com train-kamco.com; + + # 모든 HTTP 요청을 HTTPS로 리다이렉트 + return 301 https://$host$request_uri; + } + + # HTTPS 서버 설정 + server { + listen 443 ssl; + http2 on; + server_name api.train-kamco.com; + + # SSL 인증서 설정 (사설 인증서 - 멀티 도메인) + ssl_certificate /etc/nginx/ssl/train-kamco.com.crt; + ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key; + + # SSL 프로토콜 및 암호화 설정 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; + ssl_prefer_server_ciphers off; + + # SSL 세션 캐시 + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # HSTS (HTTP Strict Transport Security) + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # 보안 헤더 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # CORS 헤더 + add_header Access-Control-Allow-Origin "https://train-kamco.com" always; + add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS" always; + add_header Access-Control-Allow-Headers "Authorization, Content-Type, Cookie, X-Requested-With" always; + add_header Access-Control-Allow-Credentials "true" always; + + # 프록시 설정 + location / { + # OPTIONS preflight 처리 + if ($request_method = OPTIONS) { + return 204; + } + + proxy_pass http://api_backend; + proxy_http_version 1.1; + + # 프록시 헤더 설정 + 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_set_header X-Forwarded-Host $server_name; + + # 인증 헤더 및 쿠키 전달 (JWT 토큰 전달 보장) + proxy_pass_request_headers on; + proxy_set_header Cookie $http_cookie; + proxy_set_header Authorization $http_authorization; + + # 타임아웃 설정 (10분) + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + + # 대용량 업로드: 버퍼링 없이 백엔드로 스트리밍 + proxy_request_buffering off; + proxy_buffering off; + } + + # 헬스체크 엔드포인트 + location /monitor/health { + proxy_pass http://api_backend/monitor/health; + access_log off; + } + } + + # HTTPS 서버 설정 - Next.js Web Application + server { + listen 443 ssl; + http2 on; + server_name train-kamco.com; + + # SSL 인증서 설정 (사설 인증서 - 멀티 도메인) + ssl_certificate /etc/nginx/ssl/train-kamco.com.crt; + ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key; + + # SSL 프로토콜 및 암호화 설정 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; + ssl_prefer_server_ciphers off; + + # SSL 세션 캐시 + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # HSTS (HTTP Strict Transport Security) + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # 보안 헤더 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # API 프록시 설정 (Web에서 API 호출 시) + location /api/ { + proxy_pass http://api_backend/api/; + proxy_http_version 1.1; + + # 프록시 헤더 설정 + 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_set_header X-Forwarded-Host $server_name; + + # 인증 헤더 및 쿠키 전달 + proxy_pass_request_headers on; + proxy_set_header Cookie $http_cookie; + + # 타임아웃 설정 (10분) + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + + # 대용량 업로드: 버퍼링 없이 백엔드로 스트리밍 + proxy_request_buffering off; + proxy_buffering off; + } + + # 프록시 설정 + location / { + proxy_pass http://web_backend; + proxy_http_version 1.1; + + # 프록시 헤더 설정 + 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_set_header X-Forwarded-Host $server_name; + + # Next.js WebSocket 지원을 위한 Upgrade 헤더 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # 타임아웃 설정 (10분) + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + + # 버퍼 설정 + proxy_buffering on; + proxy_buffer_size 4k; + proxy_buffers 8 4k; + proxy_busy_buffers_size 8k; + } + } +} diff --git a/deploy/setup-groups.sh b/deploy/setup-groups.sh new file mode 100755 index 0000000..5fe9d4e --- /dev/null +++ b/deploy/setup-groups.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +ok() { echo -e "${GREEN}[OK]${NC} $1"; } +fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; } + +echo "=== GID 1000 그룹 설정 ===" + +# GID 1000 그룹 없으면 생성 +if ! getent group 1000 &>/dev/null; then + groupadd -g 1000 docker-users + ok "GID 1000 그룹(docker-users) 생성 완료" +else + GROUP_NAME=$(getent group 1000 | cut -d: -f1) + ok "GID 1000 그룹 이미 존재: $GROUP_NAME" +fi + +GROUP_NAME=$(getent group 1000 | cut -d: -f1) + +# kcomu, docker 를 GID 1000 그룹에 추가 +for user in kcomu docker; do + if id "$user" &>/dev/null; then + usermod -aG "$GROUP_NAME" "$user" + ok "$user → $GROUP_NAME($GROUP_NAME) 그룹 추가 완료" + else + echo " [SKIP] $user 유저 없음" + fi +done + +echo "" +echo "현재 $GROUP_NAME 그룹 멤버:" +getent group "$GROUP_NAME" +echo "" +echo "※ 그룹 변경은 재로그인 후 적용됩니다." diff --git a/deploy/setup.sh b/deploy/setup.sh new file mode 100755 index 0000000..2afee88 --- /dev/null +++ b/deploy/setup.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NGINX_DIR="/data/training/nginx" + +# docker compose v1/v2 자동 감지 +if command -v docker-compose &>/dev/null; then + DOCKER_COMPOSE="docker-compose" +elif docker compose version &>/dev/null 2>&1; then + DOCKER_COMPOSE="docker compose" +else + DOCKER_COMPOSE="" +fi + +echo "=== 디렉토리 생성 ===" +mkdir -p /data/training/request +mkdir -p /data/training/request/tmp +mkdir -p /data/training/response +mkdir -p /data/training/response/v6-cls-checkpoints +mkdir -p /data/training/tmp +mkdir -p "$NGINX_DIR/ssl" +mkdir -p "$NGINX_DIR/logs" +echo "완료" + +echo "=== nginx 파일 복사 ===" +cp "$SCRIPT_DIR/nginx.conf" "$NGINX_DIR/" +cp "$SCRIPT_DIR/docker-compose-nginx.yml" "$NGINX_DIR/" +echo "완료" + +echo "=== SSL 파일 복사 ===" +cp "$SCRIPT_DIR/ssl/openssl.cnf" "$NGINX_DIR/ssl/" +cp "$SCRIPT_DIR/ssl/train-kamco.com.crt" "$NGINX_DIR/ssl/" +cp "$SCRIPT_DIR/ssl/train-kamco.com.key" "$NGINX_DIR/ssl/" +echo "완료" + +echo "=== SSL 권한 설정 ===" +chmod 700 "$NGINX_DIR/ssl" +chmod 600 "$NGINX_DIR/ssl/train-kamco.com.key" +chmod 644 "$NGINX_DIR/ssl/train-kamco.com.crt" +echo "완료" + +echo "=== 소유권 설정 (kcomu:kcomu) ===" +chown -R kcomu:kcomu /data/training +echo "완료" + +echo "=== 그룹 설정 (setup-groups.sh) ===" +bash "$SCRIPT_DIR/setup-groups.sh" + +echo "=== docker-compose-nginx.yml 소유권 설정 (1000:1000) ===" +chown 1000:1000 "$NGINX_DIR/docker-compose-nginx.yml" +echo "완료" + +echo "=== docker-compose 래퍼 설정 ===" +if command -v docker-compose &>/dev/null; then + echo "docker-compose 이미 설치됨 (스킵)" +elif docker compose version &>/dev/null 2>&1; then + cat > /usr/local/bin/docker-compose << 'EOF' +#!/bin/bash +exec docker compose "$@" +EOF + chmod +x /usr/local/bin/docker-compose + echo "docker-compose → docker compose 래퍼 생성 완료" + DOCKER_COMPOSE="docker-compose" +else + echo "[WARN] docker compose 를 찾을 수 없습니다. Docker 설치를 확인하세요." +fi + +echo "=== Docker network 설정 ===" +if docker network ls --format '{{.Name}}' | grep -q "^kamco-cds$"; then + echo "kamco-cds 네트워크 이미 존재 (스킵)" +else + docker network create kamco-cds + echo "kamco-cds 네트워크 생성 완료" +fi + +echo "=== /etc/hosts 설정 ===" +if grep -q "train-kamco.com" /etc/hosts; then + echo "이미 설정되어 있음 (스킵)" +else + echo "127.0.0.1 api.train-kamco.com train-kamco.com" >> /etc/hosts + echo "추가 완료" +fi +echo "현재 hosts 설정:" +grep "train-kamco" /etc/hosts + +echo "" +echo "=== 설치 완료 ===" +echo "생성된 디렉토리:" +find /data -type d | sort +echo "" +echo "=== nginx 실행 방법 ===" +if [ -n "$DOCKER_COMPOSE" ]; then + echo "cd $NGINX_DIR && $DOCKER_COMPOSE -f docker-compose-nginx.yml up -d" +else + echo "[WARN] docker-compose / docker compose 를 찾을 수 없습니다. Docker 설치를 확인하세요." +fi diff --git a/deploy/ssl/openssl.cnf b/deploy/ssl/openssl.cnf new file mode 100644 index 0000000..e39a5bd --- /dev/null +++ b/deploy/ssl/openssl.cnf @@ -0,0 +1,21 @@ +[req] +default_bits = 4096 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = v3_req + +[dn] +C=KR +ST=Seoul +L=Seoul +O=KAMCO +OU=Training +CN=api.train-kamco.com + +[v3_req] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = api.train-kamco.com +DNS.2 = train-kamco.com diff --git a/docker-compose-dev-0420.yml b/docker-compose-dev-0420.yml new file mode 100644 index 0000000..34b4d84 --- /dev/null +++ b/docker-compose-dev-0420.yml @@ -0,0 +1,38 @@ +services: + kamco-changedetection-api: + build: + context: . + dockerfile: Dockerfile-dev + image: kamco-cd-training-api:${IMAGE_TAG:-latest} + container_name: kamco-cd-training-api + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + ports: + - "7200:8080" + environment: + - SPRING_PROFILES_ACTIVE=dev + - TZ=Asia/Seoul + volumes: + - /mnt/nfs_share/images:/app/original-images + - /mnt/nfs_share/model_output:/app/model-outputs + - /mnt/nfs_share/train_dataset:/app/train-dataset + - /home/kcomu/data:/home/kcomu/data + - /var/run/docker.sock:/var/run/docker.sock + networks: + - kamco-cds + restart: unless-stopped + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8080/monitor/health" ] + interval: 10s + timeout: 5s + retries: 5 + start_period: 40s + +networks: + kamco-cds: + external: true