4 Commits

Author SHA1 Message Date
dabeeo
a2e5bf4e10 hoony 2026-04-20 17:15:46 +09:00
dabeeo
de2a2e2c35 hoony 2026-04-20 17:15:27 +09:00
dabeeo
f75ec77ccf hoony 2026-04-20 15:30:26 +09:00
dabeeo
9fa549285f hoony 2026-04-20 15:29:44 +09:00
11 changed files with 834 additions and 200 deletions

230
deploy/check-nginx.sh Executable file
View File

@@ -0,0 +1,230 @@
#!/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 실행 중..."
# kamco-cds 네트워크가 있으면 연결 (upstream DNS 조회 가능)
NETWORK_OPT=""
if docker network ls --format '{{.Name}}' | grep -q "^kamco-cds$"; then
NETWORK_OPT="--network kamco-cds"
fi
if docker run --rm $NETWORK_OPT \
-v "$NGINX_DIR/nginx.conf:/etc/nginx/nginx.conf:ro,Z" \
-v "$NGINX_DIR/ssl:/etc/nginx/ssl:ro,Z" \
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

View File

@@ -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,Z
- ./ssl:/etc/nginx/ssl:ro,Z
- ./logs:/var/log/nginx:Z
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

170
deploy/nginx.conf Normal file
View File

@@ -0,0 +1,170 @@
pid /var/log/nginx/nginx.pid;
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;
# user 1000:1000 실행 시 /var/cache/nginx 접근 불가 → logs 경로로 우회
client_body_temp_path /var/log/nginx/client_temp;
proxy_temp_path /var/log/nginx/proxy_temp;
fastcgi_temp_path /var/log/nginx/fastcgi_temp;
uwsgi_temp_path /var/log/nginx/uwsgi_temp;
scgi_temp_path /var/log/nginx/scgi_temp;
# 업로드 파일 크기 / 타임아웃 (10GB, 10분)
client_max_body_size 10G;
client_body_timeout 600s;
# Docker 내부 DNS - 시작 시 upstream 조회 실패 방지
resolver 127.0.0.11 valid=30s ipv6=off;
# 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_certificate /etc/nginx/ssl/train-kamco.com.crt;
ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key;
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_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
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 / {
if ($request_method = OPTIONS) {
return 204;
}
set $api http://kamco-train-api:8080;
proxy_pass $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;
proxy_set_header Authorization $http_authorization;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
proxy_request_buffering off;
proxy_buffering off;
}
location /monitor/health {
set $api http://kamco-train-api:8080;
proxy_pass $api/monitor/health;
access_log off;
}
}
# HTTPS 서버 설정 - Next.js Web Application
server {
listen 443 ssl;
http2 on;
server_name train-kamco.com;
ssl_certificate /etc/nginx/ssl/train-kamco.com.crt;
ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key;
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_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
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;
location /api/ {
set $api http://kamco-train-api:8080;
proxy_pass $api/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;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
proxy_request_buffering off;
proxy_buffering off;
}
location / {
set $web http://kamco-train-web:3002;
proxy_pass $web;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}
}

38
deploy/setup-groups.sh Executable file
View File

@@ -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 "※ 그룹 변경은 재로그인 후 적용됩니다."

97
deploy/setup.sh Executable file
View File

@@ -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

21
deploy/ssl/openssl.cnf Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -1,10 +1,7 @@
services: services:
kamco-changedetection-api: kamco-changedetection-api:
build: image: kamco-train-app:latest
context: . container_name: kamco-train-api
dockerfile: Dockerfile-dev
image: kamco-cd-training-api:${IMAGE_TAG:-latest}
container_name: kamco-cd-training-api
deploy: deploy:
resources: resources:
reservations: reservations:
@@ -12,16 +9,13 @@ services:
- driver: nvidia - driver: nvidia
count: all count: all
capabilities: [gpu] capabilities: [gpu]
ports:
- "7200:8080"
environment: environment:
- SPRING_PROFILES_ACTIVE=dev - SPRING_PROFILES_ACTIVE=dev
- TZ=Asia/Seoul - TZ=Asia/Seoul
- cors.allowed-origins=*
- cors.allowed-origins[0]=*
volumes: volumes:
- /mnt/nfs_share/images:/app/original-images - /data/training:/data/training
- /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 - /var/run/docker.sock:/var/run/docker.sock
networks: networks:
- kamco-cds - kamco-cds

View File

@@ -1,9 +1,6 @@
services: services:
kamco-train-api: kamco-changedetection-api:
build: image: kamco-train-app:latest
context: .
dockerfile: Dockerfile
image: kamco-train-api:${IMAGE_TAG:-latest}
container_name: kamco-train-api container_name: kamco-train-api
deploy: deploy:
resources: resources:
@@ -17,9 +14,10 @@ services:
environment: environment:
- SPRING_PROFILES_ACTIVE=prod - SPRING_PROFILES_ACTIVE=prod
- TZ=Asia/Seoul - TZ=Asia/Seoul
- cors.allowed-origins=*
- cors.allowed-origins[0]=*
volumes: volumes:
- ./app/model_output:/app/model-outputs - /data/training:/data/training
- ./app/train_dataset:/app/train-dataset
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
networks: networks:
- kamco-cds - kamco-cds

View File

@@ -1,179 +1,177 @@
events { events {
worker_connections 1024; worker_connections 1024;
} }
http { http {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
# 로그 설정 # 로그 설정
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" ' '$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main; access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn; error_log /var/log/nginx/error.log warn;
sendfile on; sendfile on;
keepalive_timeout 65; keepalive_timeout 65;
# 업로드 파일 크기 제한 (10GB) # 업로드 파일 크기 제한 (10GB)
client_max_body_size 10G; client_max_body_size 10G;
# Upstream 설정 # Docker 내부 DNS - 시작 시 upstream 조회 실패 방지
upstream api_backend { resolver 127.0.0.11 valid=30s ipv6=off;
server kamco-train-api:8080;
} # HTTP → HTTPS 리다이렉트 서버
server {
upstream web_backend { listen 80;
server kamco-train-web:3002; server_name api.train-kamco.com train-kamco.com;
}
# 모든 HTTP 요청을 HTTPS로 리다이렉트
# HTTP → HTTPS 리다이렉트 서버 return 301 https://$server_name$request_uri;
server { }
listen 80;
server_name api.train-kamco.com train-kamco.com; # HTTPS 서버 설정
server {
# 모든 HTTP 요청을 HTTPS로 리다이렉트 listen 443 ssl http2;
return 301 https://$server_name$request_uri; server_name api.train-kamco.com;
}
# SSL 인증서 설정 (사설 인증서 - 멀티 도메인)
# HTTPS 서버 설정 ssl_certificate /etc/nginx/ssl/train-kamco.com.crt;
server { ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key;
listen 443 ssl http2;
server_name api.train-kamco.com; # SSL 프로토콜 및 암호화 설정
ssl_protocols TLSv1.2 TLSv1.3;
# SSL 인증서 설정 (사설 인증서 - 멀티 도메인) 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_certificate /etc/nginx/ssl/train-kamco.com.crt; ssl_prefer_server_ciphers off;
ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key;
# SSL 세션 캐시
# SSL 프로토콜 및 암호화 설정 ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1.2 TLSv1.3; ssl_session_timeout 10m;
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; # HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# SSL 세션 캐시
ssl_session_cache shared:SSL:10m; # 보안 헤더
ssl_session_timeout 10m; add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# HSTS (HTTP Strict Transport Security) add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 프록시 설정
# 보안 헤더 location / {
add_header X-Frame-Options "SAMEORIGIN" always; set $api http://kamco-train-api:8080;
add_header X-Content-Type-Options "nosniff" always; proxy_pass $api;
add_header X-XSS-Protection "1; mode=block" always; proxy_http_version 1.1;
# 프록시 설정 # 프록시 헤더 설정
location / { proxy_set_header Host $host;
proxy_pass http://api_backend; proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1; 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_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; # 인증 헤더 및 쿠키 전달 (JWT 토큰 전달 보장)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_request_headers on;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Cookie $http_cookie;
proxy_set_header X-Forwarded-Host $server_name; proxy_set_header Authorization $http_authorization;
# 인증 헤더 및 쿠키 전달 (JWT 토큰 전달 보장) # 타임아웃 설정 (대용량 파일 업로드 지원)
proxy_pass_request_headers on; proxy_connect_timeout 300s;
proxy_set_header Cookie $http_cookie; proxy_send_timeout 300s;
proxy_set_header Authorization $http_authorization; proxy_read_timeout 300s;
# 타임아웃 설정 (대용량 파일 업로드 지원) # 버퍼 설정
proxy_connect_timeout 300s; proxy_buffering on;
proxy_send_timeout 300s; proxy_buffer_size 4k;
proxy_read_timeout 300s; proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# 버퍼 설정 }
proxy_buffering on;
proxy_buffer_size 4k; # 헬스체크 엔드포인트
proxy_buffers 8 4k; location /monitor/health {
proxy_busy_buffers_size 8k; set $api http://kamco-train-api:8080;
} proxy_pass $api/monitor/health;
access_log off;
# 헬스체크 엔드포인트 }
location /monitor/health { }
proxy_pass http://api_backend/monitor/health;
access_log off; # HTTPS 서버 설정 - Next.js Web Application
} server {
} listen 443 ssl http2;
server_name train-kamco.com;
# HTTPS 서버 설정 - Next.js Web Application
server { # SSL 인증서 설정 (사설 인증서 - 멀티 도메인)
listen 443 ssl http2; ssl_certificate /etc/nginx/ssl/train-kamco.com.crt;
server_name train-kamco.com; ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key;
# SSL 인증서 설정 (사설 인증서 - 멀티 도메인) # SSL 프로토콜 및 암호화 설정
ssl_certificate /etc/nginx/ssl/train-kamco.com.crt; ssl_protocols TLSv1.2 TLSv1.3;
ssl_certificate_key /etc/nginx/ssl/train-kamco.com.key; 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_protocols TLSv1.2 TLSv1.3; # SSL 세션 캐시
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_session_cache shared:SSL:10m;
ssl_prefer_server_ciphers off; ssl_session_timeout 10m;
# SSL 세션 캐시 # HSTS (HTTP Strict Transport Security)
ssl_session_cache shared:SSL:10m; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
ssl_session_timeout 10m;
# 보안 헤더
# HSTS (HTTP Strict Transport Security) add_header X-Frame-Options "SAMEORIGIN" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always; # API 프록시 설정 (Web에서 API 호출 시)
add_header X-Content-Type-Options "nosniff" always; location /api/ {
add_header X-XSS-Protection "1; mode=block" always; set $api http://kamco-train-api:8080;
proxy_pass $api/api/;
# API 프록시 설정 (Web에서 API 호출 시) proxy_http_version 1.1;
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 Host $host; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $server_name;
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;
# 인증 헤더 및 쿠키 전달
proxy_pass_request_headers on; # 타임아웃 설정
proxy_set_header Cookie $http_cookie; proxy_connect_timeout 300s;
proxy_send_timeout 300s;
# 타임아웃 설정 proxy_read_timeout 300s;
proxy_connect_timeout 300s; }
proxy_send_timeout 300s;
proxy_read_timeout 300s; # 프록시 설정
} location / {
set $web http://kamco-train-web:3002;
# 프록시 설정 proxy_pass $web;
location / { proxy_http_version 1.1;
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 Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name;
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;
# Next.js WebSocket 지원을 위한 Upgrade 헤더 proxy_set_header Connection "upgrade";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # 타임아웃 설정
proxy_connect_timeout 600s;
# 타임아웃 설정 proxy_send_timeout 600s;
proxy_connect_timeout 600s; proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s; # 버퍼 설정
proxy_buffering on;
# 버퍼 설정 proxy_buffer_size 4k;
proxy_buffering on; proxy_buffers 8 4k;
proxy_buffer_size 4k; proxy_busy_buffers_size 8k;
proxy_buffers 8 4k; }
proxy_busy_buffers_size 8k; }
} }
}
}

View File

@@ -30,20 +30,45 @@ token:
swagger: swagger:
local-port: 8080 local-port: 8080
# file:
# dataset-dir: /home/kcomu/data/request/
# dataset-tmp-dir: ${file.dataset-dir}tmp/
# pt-path: /home/kcomu/data/response/v6-cls-checkpoints/
# pt-FileName: yolov8_6th-6m.pt
# train:
# docker:
# image: kamco-cd-train:latest
# base_path: /home/kcomu/data
# request_dir: ${train.docker.base_path}/request
# response_dir: ${train.docker.base_path}/response
# symbolic_link_dir: ${train.docker.base_path}/tmp
# container_prefix: kamco-cd-train
# shm_size: 16g
# ipc_host: true
file: file:
dataset-dir: /home/kcomu/data/request/ dataset-dir: /data/training/request/
dataset-tmp-dir: ${file.dataset-dir}tmp/ dataset-tmp-dir: ${file.dataset-dir}tmp/
pt-path: /home/kcomu/data/response/v6-cls-checkpoints/ pt-path: /data/training/response/v6-cls-checkpoints/
pt-FileName: yolov8_6th-6m.pt pt-FileName: yolov8_6th-6m.pt
train: train:
docker: docker:
image: kamco-cd-train:latest image: kamco-cd-train:latest
base_path: /home/kcomu/data base_path: /data/training
request_dir: ${train.docker.base_path}/request request_dir: ${train.docker.base_path}/request
response_dir: ${train.docker.base_path}/response response_dir: ${train.docker.base_path}/response
symbolic_link_dir: ${train.docker.base_path}/tmp symbolic_link_dir: ${train.docker.base_path}/tmp
container_prefix: kamco-cd-train container_prefix: kamco-cd-train
shm_size: 16g shm_size: 16g
ipc_host: true ipc_host: true
hyper:
parameter:
gpus: 1
gpu-ids: 0
batch-size: 10