16 Commits

Author SHA1 Message Date
b85f920f40 학습 소요시간 로직 수정 2026-05-19 14:55:49 +09:00
e4851b1153 test 실행 도커 명령어 수정 2026-05-19 14:33:36 +09:00
b73aef5cf8 class 명칭 가져오는 부분 영어버전 추가 2026-05-15 14:59:39 +09:00
72c8f6a047 제작 업로드 시 dataset_path 안 맞는 현상 수정 2026-05-15 10:45:44 +09:00
c7a49ea4ea headerUtil 확인 2026-05-15 10:36:05 +09:00
fbef92af55 모델관리 API 영문버전 로직 수정 2026-05-12 14:51:18 +09:00
154db0ac27 영어버전도 나오게 추가 2026-05-12 14:27:09 +09:00
9835170cd7 납품데이터 등록 시 totalSize 추가 2026-05-08 10:40:06 +09:00
fd51f21ba6 납품 데이터 저장 시 uid 수정, 파일 업로드 경로 수정 2026-05-08 09:58:52 +09:00
776622e0a2 트랜잭션 5분 걸려있던 내용 주석처리 2026-05-07 15:29:24 +09:00
dabeeo
4d2d7a9ad1 check 2026-04-20 17:45:33 +09:00
dabeeo
532fbdbee4 hello 2026-04-20 17:35:26 +09:00
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
26 changed files with 1007 additions and 235 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

@@ -18,10 +18,7 @@ services:
- 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
- /backup/data/training:/backup/data/training
- /var/run/docker.sock:/var/run/docker.sock
networks:
- kamco-cds

View File

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

View File

@@ -20,14 +20,8 @@ http {
# 업로드 파일 크기 제한 (10GB)
client_max_body_size 10G;
# Upstream 설정
upstream api_backend {
server kamco-train-api:8080;
}
upstream web_backend {
server kamco-train-web:3002;
}
# Docker 내부 DNS - 시작 시 upstream 조회 실패 방지
resolver 127.0.0.11 valid=30s ipv6=off;
# HTTP → HTTPS 리다이렉트 서버
server {
@@ -66,7 +60,8 @@ http {
# 프록시 설정
location / {
proxy_pass http://api_backend;
set $api http://kamco-train-api:8080;
proxy_pass $api;
proxy_http_version 1.1;
# 프록시 헤더 설정
@@ -95,7 +90,8 @@ http {
# 헬스체크 엔드포인트
location /monitor/health {
proxy_pass http://api_backend/monitor/health;
set $api http://kamco-train-api:8080;
proxy_pass $api/monitor/health;
access_log off;
}
}
@@ -128,7 +124,8 @@ http {
# API 프록시 설정 (Web에서 API 호출 시)
location /api/ {
proxy_pass http://api_backend/api/;
set $api http://kamco-train-api:8080;
proxy_pass $api/api/;
proxy_http_version 1.1;
# 프록시 헤더 설정
@@ -150,7 +147,8 @@ http {
# 프록시 설정
location / {
proxy_pass http://web_backend;
set $web http://kamco-train-web:3002;
proxy_pass $web;
proxy_http_version 1.1;
# 프록시 헤더 설정

View File

@@ -37,7 +37,8 @@ public class GpuDmonReader {
int avg =
deque.isEmpty()
? 0
: (int) Math.round(deque.stream().mapToInt(Integer::intValue).average().orElse(0));
: (int)
Math.round(deque.stream().mapToInt(Integer::intValue).average().orElse(0));
result.put(gpu, avg);
}
});
@@ -98,8 +99,7 @@ public class GpuDmonReader {
Process process = pb.start();
// 프로세스 실행 후 stdout 읽기
try (BufferedReader br =
new BufferedReader(new InputStreamReader(process.getInputStream()))) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;

View File

@@ -1,7 +1,12 @@
package com.kamco.cd.training.common.utils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Slf4j
public final class HeaderUtil {
private HeaderUtil() {}
@@ -20,4 +25,20 @@ public final class HeaderUtil {
public static String getRequired(HttpServletRequest request, String headerName) {
return get(request, headerName);
}
public static boolean isEnglishRequest() {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (!(attrs instanceof ServletRequestAttributes servletAttrs)) {
return false;
}
String acceptLanguage = servletAttrs.getRequest().getHeader("Accept-Language");
if (acceptLanguage == null || acceptLanguage.isBlank()) {
return false;
}
return acceptLanguage.toLowerCase().startsWith("en");
}
}

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.training.common.utils.enums;
import com.kamco.cd.training.common.utils.HeaderUtil;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -31,10 +32,11 @@ public class Enums {
// enum -> CodeDto list
public static List<CodeDto> toList(Class<? extends Enum<?>> enumClass) {
Object[] enums = enumClass.getEnumConstants();
boolean english = HeaderUtil.isEnglishRequest();
return Arrays.stream(enums)
.map(e -> (EnumType) e)
.map(e -> new CodeDto(e.getId(), e.getText()))
.map(e -> new CodeDto(e.getId(), english ? e.getId() : e.getText()))
.toList();
}

View File

@@ -293,6 +293,8 @@ public class DatasetApiController {
DatasetService.validateTrainValTestDirs(req.getFilePath());
// 파일 개수 검증
DatasetService.validateDirFileCount(req.getFilePath());
// 폴더명(uid)으로 등록한 건이 있는지 체크
datasetService.validateExistsUidChk(req.getFilePath());
datasetAsyncService.insertDeliveriesDatasetAsync(req);
return ApiResponseDto.createOK("ok");

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kamco.cd.training.common.enums.LearnDataRegister;
import com.kamco.cd.training.common.enums.LearnDataType;
import com.kamco.cd.training.common.enums.ModelType;
import com.kamco.cd.training.common.utils.HeaderUtil;
import com.kamco.cd.training.common.utils.enums.Enums;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -90,7 +91,8 @@ public class DatasetDto {
public String getStatus(String status) {
LearnDataRegister type = Enums.fromId(LearnDataRegister.class, status);
return type == null ? null : type.getText();
boolean english = HeaderUtil.isEnglishRequest();
return type == null ? null : (english ? type.getId() : type.getText());
}
public String getYear() {
@@ -99,7 +101,8 @@ public class DatasetDto {
public String getDataTypeName() {
LearnDataType type = Enums.fromId(LearnDataType.class, this.dataType);
return type == null ? null : type.getText();
boolean english = HeaderUtil.isEnglishRequest();
return type == null ? null : (english ? type.getId() : type.getText());
}
}
@@ -315,7 +318,7 @@ public class DatasetDto {
public String getDataTypeName(String groupTitleCd) {
LearnDataType type = Enums.fromId(LearnDataType.class, groupTitleCd);
return type == null ? null : type.getText();
return type == null ? null : (HeaderUtil.isEnglishRequest() ? type.getId() : type.getText());
}
public String getYear() {

View File

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.training.common.enums.DetectionClassification;
import com.kamco.cd.training.common.utils.HeaderUtil;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.ZonedDateTime;
@@ -131,7 +132,10 @@ public class DatasetObjDto {
private String classCd;
public String getClassName() {
return DetectionClassification.fromString(classCd).getDesc();
return HeaderUtil.isEnglishRequest()
? DetectionClassification.fromString(classCd).getId()
: DetectionClassification.fromString(classCd).getDesc();
// fromString 메서드를 사용하여 안전하게 변환 (미정의 값은 ETC로 처리)
// return DetectionClassification.valueOf(classCd.toUpperCase()).getDesc();
}

View File

@@ -4,7 +4,11 @@ import com.kamco.cd.training.common.enums.LearnDataRegister;
import com.kamco.cd.training.dataset.dto.DatasetDto.AddDeliveriesReq;
import com.kamco.cd.training.dataset.dto.DatasetDto.DatasetMngRegDto;
import com.kamco.cd.training.postgres.core.DatasetCoreService;
import java.util.UUID;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.scheduling.annotation.Async;
@@ -45,7 +49,11 @@ public class DatasetAsyncService {
try {
// ===== 1. UID 생성 =====
String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
// String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
// 26-05-08: UID를 생성하지 않고 folder name 을 uid 로 저장하기 -> req.getFilePath()
Path selectedPath = Paths.get(req.getFilePath());
String uid = selectedPath.getFileName().toString();
log.info("{} 생성된 UID: {}", LOG_PREFIX, uid);
// ===== 2. 마스터 데이터 생성 =====
@@ -71,6 +79,7 @@ public class DatasetAsyncService {
datasetMngRegDto.setTitle(title);
datasetMngRegDto.setMemo(req.getMemo());
datasetMngRegDto.setDatasetPath(req.getFilePath());
datasetMngRegDto.setTotalSize(getDirectorySize(req.getFilePath())); // 선택한 폴더 안의 모든 파일 용량 합계
// 마스터 저장
datasetUid = datasetCoreService.insertDatasetMngData(datasetMngRegDto);
@@ -121,4 +130,24 @@ public class DatasetAsyncService {
}
}
}
private Long getDirectorySize(String filePath) {
Path selectedPath = Paths.get(filePath);
try (Stream<Path> paths = Files.walk(selectedPath)) {
return paths
.filter(Files::isRegularFile)
.mapToLong(
path -> {
try {
return Files.size(path);
} catch (IOException e) {
return 0L;
}
})
.sum();
} catch (IOException e) {
return 0L;
}
}
}

View File

@@ -286,7 +286,7 @@ public class DatasetService {
.memo(addReq.getMemo())
.roundNo(stage)
.totalSize(addReq.getFileSize())
.datasetPath(addReq.getFilePath())
.datasetPath(addReq.getFilePath() + uid)
.build();
datasetUid = datasetCoreService.insertDatasetMngData(mngRegDto); // tb_dataset 에 insert
@@ -676,4 +676,18 @@ public class DatasetService {
total,
System.currentTimeMillis() - start);
}
public void validateExistsUidChk(String filePath) {
Path selectedPath = Paths.get(filePath);
String uid = selectedPath.getFileName().toString();
// 같은 uid 로 등록한 파일이 있는지 확인
Long existsCnt = datasetCoreService.findDatasetByUidExistsCnt(uid);
if (existsCnt > 0) {
throw new CustomApiException(
ApiResponseCode.DUPLICATE_DATA.getId(),
HttpStatus.CONFLICT,
"이미 등록된 회차 데이터 파일입니다. 확인 부탁드립니다.");
}
}
}

View File

@@ -277,9 +277,7 @@ public class FileManagerDto {
@Schema(description = "디스크 사용률 (%)", example = "50.5")
private Double usagePercentage;
@JsonFormatDttm
private ZonedDateTime lastModifiedDate;
@JsonFormatDttm private ZonedDateTime lastModifiedDate;
public ZonedDateTime getLastModifiedDate() {
return ZonedDateTime.now();

View File

@@ -627,8 +627,7 @@ public class FileManagerService {
duPb.redirectErrorStream(true);
Process duProcess = duPb.start();
try (java.io.BufferedReader br =
new java.io.BufferedReader(
new java.io.InputStreamReader(duProcess.getInputStream()))) {
new java.io.BufferedReader(new java.io.InputStreamReader(duProcess.getInputStream()))) {
String line = br.readLine();
if (line != null) {
// du -sb 출력 형식: "12345678\t/path/to/dir"

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.training.common.enums.LearnDataType;
import com.kamco.cd.training.common.enums.TrainStatusType;
import com.kamco.cd.training.common.enums.TrainType;
import com.kamco.cd.training.common.utils.HeaderUtil;
import com.kamco.cd.training.common.utils.enums.Enums;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import com.kamco.cd.training.dataset.dto.DatasetDto.SelectTransferDataSet;
@@ -32,6 +33,8 @@ public class ModelTrainDetailDto {
private String modelNo;
private String modelVer;
@JsonFormatDttm private ZonedDateTime step1StrtDttm;
@JsonFormatDttm private ZonedDateTime step1EndDttm;
@JsonFormatDttm private ZonedDateTime step2StrtDttm;
@JsonFormatDttm private ZonedDateTime step2EndDttm;
private String statusCd;
private String trainType;
@@ -40,7 +43,9 @@ public class ModelTrainDetailDto {
public String getStatusName() {
if (this.statusCd == null || this.statusCd.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
return HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.statusCd).getId()
: TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.statusCd; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -49,18 +54,16 @@ public class ModelTrainDetailDto {
public String getTrainTypeName() {
if (this.trainType == null || this.trainType.isBlank()) return null;
try {
return TrainType.valueOf(this.trainType).getText(); // 또는 getName()
return HeaderUtil.isEnglishRequest()
? TrainType.valueOf(this.trainType).getId()
: TrainType.valueOf(this.trainType).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.trainType; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
}
private String formatDuration(ZonedDateTime start, ZonedDateTime end) {
if (end == null) {
end = ZonedDateTime.now();
}
if (start == null) {
if (start == null || end == null) {
return null;
}
@@ -70,11 +73,42 @@ public class ModelTrainDetailDto {
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;
if (HeaderUtil.isEnglishRequest()) {
return String.format("%dh %dm %ds", hours, minutes, seconds);
} else {
return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
}
}
public String getStepAllDuration() {
return formatDuration(this.step1StrtDttm, this.step2EndDttm);
if (this.step2EndDttm != null) {
// step1 + step2 실제 소요시간 합산
long step1Seconds = 0;
long step2Seconds = 0;
if (this.step1StrtDttm != null && this.step1EndDttm != null) {
step1Seconds =
Math.abs(Duration.between(this.step1StrtDttm, this.step1EndDttm).getSeconds());
}
if (this.step2StrtDttm != null && this.step2EndDttm != null) {
step2Seconds =
Math.abs(Duration.between(this.step2StrtDttm, this.step2EndDttm).getSeconds());
}
long totalSeconds = step1Seconds + step2Seconds;
long hours = totalSeconds / 3600;
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;
if (HeaderUtil.isEnglishRequest()) {
return String.format("%dh %dm %ds", hours, minutes, seconds);
} else {
return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
}
} else {
// step2 없으면 step1만
return formatDuration(this.step1StrtDttm, this.step1EndDttm);
}
}
}
@@ -174,7 +208,7 @@ public class ModelTrainDetailDto {
public String getDataTypeName(String groupTitleCd) {
LearnDataType type = Enums.fromId(LearnDataType.class, groupTitleCd);
return type == null ? null : type.getText();
return type == null ? null : (HeaderUtil.isEnglishRequest() ? type.getId() : type.getText());
}
}

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.training.model.dto;
import com.kamco.cd.training.common.dto.HyperParam;
import com.kamco.cd.training.common.enums.TrainStatusType;
import com.kamco.cd.training.common.enums.TrainType;
import com.kamco.cd.training.common.utils.HeaderUtil;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@@ -44,8 +45,8 @@ public class ModelTrainMngDto {
private String requestPath;
private String packingState;
private ZonedDateTime packingStrtDttm;
private ZonedDateTime packingEndDttm;
@JsonFormatDttm private ZonedDateTime packingStrtDttm;
@JsonFormatDttm private ZonedDateTime packingEndDttm;
private Long beforeModelId;
private Integer bestEpoch;
@@ -53,7 +54,9 @@ public class ModelTrainMngDto {
public String getStatusName() {
if (this.statusCd == null || this.statusCd.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
return (HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.statusCd).getId()
: TrainStatusType.valueOf(this.statusCd).getText()); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.statusCd; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -62,7 +65,9 @@ public class ModelTrainMngDto {
public String getStep1StatusName() {
if (this.step1Status == null || this.step1Status.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.step1Status).getText(); // 또는 getName()
return (HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.step1Status).getId()
: TrainStatusType.valueOf(this.step1Status).getText()); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.step1Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -71,7 +76,9 @@ public class ModelTrainMngDto {
public String getStep2StatusName() {
if (this.step2Status == null || this.step2Status.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.step2Status).getText(); // 또는 getName()
return (HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.step2Status).getId()
: TrainStatusType.valueOf(this.step2Status).getText()); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.step2Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -80,7 +87,9 @@ public class ModelTrainMngDto {
public String getTrainTypeName() {
if (this.trainType == null || this.trainType.isBlank()) return null;
try {
return TrainType.valueOf(this.trainType).getText(); // 또는 getName()
return (HeaderUtil.isEnglishRequest()
? TrainType.valueOf(this.trainType).getId()
: TrainType.valueOf(this.trainType).getText()); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.trainType; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -97,8 +106,12 @@ public class ModelTrainMngDto {
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;
if (HeaderUtil.isEnglishRequest()) {
return String.format("%dh %dm %ds", hours, minutes, seconds);
} else {
return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
}
}
public String getStep1Duration() {
return formatDuration(this.step1StrtDttm, this.step1EndDttm);
@@ -260,7 +273,9 @@ public class ModelTrainMngDto {
public String getStatusName() {
if (this.statusCd == null || this.statusCd.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
return HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.statusCd).getId()
: TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.statusCd; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -269,7 +284,9 @@ public class ModelTrainMngDto {
public String getStep1StatusName() {
if (this.step1Status == null || this.step1Status.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.step1Status).getText(); // 또는 getName()
return HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.step1Status).getId()
: TrainStatusType.valueOf(this.step1Status).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.step1Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -278,7 +295,9 @@ public class ModelTrainMngDto {
public String getStep2StatusName() {
if (this.step2Status == null || this.step2Status.isBlank()) return null;
try {
return TrainStatusType.valueOf(this.step2Status).getText(); // 또는 getName()
return HeaderUtil.isEnglishRequest()
? TrainStatusType.valueOf(this.step2Status).getId()
: TrainStatusType.valueOf(this.step2Status).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.step2Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -287,7 +306,9 @@ public class ModelTrainMngDto {
public String getTrainTypeName() {
if (this.trainType == null || this.trainType.isBlank()) return null;
try {
return TrainType.valueOf(this.trainType).getText(); // 또는 getName()
return HeaderUtil.isEnglishRequest()
? TrainType.valueOf(this.trainType).getId()
: TrainType.valueOf(this.trainType).getText(); // 또는 getName()
} catch (IllegalArgumentException e) {
return this.trainType; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
}
@@ -304,8 +325,12 @@ public class ModelTrainMngDto {
long minutes = (totalSeconds % 3600) / 60;
long seconds = totalSeconds % 60;
if (HeaderUtil.isEnglishRequest()) {
return String.format("%dh %dm %ds", hours, minutes, seconds);
} else {
return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
}
}
public String getStep1Duration() {
return formatDuration(this.step1StrtDttm, this.step1EndDttm);

View File

@@ -80,6 +80,8 @@ public class ModelDetailRepositoryImpl implements ModelDetailRepositoryCustom {
modelMasterEntity.modelNo,
modelMasterEntity.modelVer,
modelMasterEntity.step1StrtDttm,
modelMasterEntity.step1EndDttm,
modelMasterEntity.step2StrtDttm,
modelMasterEntity.step2EndDttm,
modelMasterEntity.statusCd,
modelMasterEntity.trainType,

View File

@@ -515,7 +515,8 @@ public class DockerTrainService {
}
c.add("-v");
c.add(responseDir + ":/checkpoints");
// c.add(responseDir + ":/checkpoints");
c.add(responseDir + "/" + req.getOutputFolder() + ":/checkpoints");
c.add("kamco-cd-train:latest");
@@ -523,7 +524,7 @@ public class DockerTrainService {
c.add("/workspace/change-detection-code/run_evaluation_pipeline.py");
addArg(c, "--dataset-folder", req.getDatasetFolder());
addArg(c, "--output-folder", req.getOutputFolder());
// addArg(c, "--output-folder", req.getOutputFolder());
c.add("--epoch");
c.add(modelFile);

View File

@@ -30,20 +30,46 @@ token:
swagger:
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:
dataset-dir: /home/kcomu/data/request/
base_path: /backup/data/training
dataset-dir: ${file.base_path}/request/
dataset-tmp-dir: ${file.dataset-dir}tmp/
pt-path: /home/kcomu/data/response/v6-cls-checkpoints/
pt-path: ${file.base_path}/response/v6-cls-checkpoints/
pt-FileName: yolov8_6th-6m.pt
train:
docker:
image: kamco-cd-train:latest
base_path: /home/kcomu/data
base_path: /backup/data/training
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
hyper:
parameter:
gpus: 1
gpu-ids: 0
batch-size: 10

View File

@@ -37,8 +37,8 @@ spring:
max-file-size: 10GB
max-request-size: 10GB
transaction:
default-timeout: 300 # 5분 트랜잭션 타임아웃
#transaction:
# default-timeout: 300 # 5분 트랜잭션 타임아웃
logging:
level: