Compare commits
76 Commits
feat/train
...
54991622f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 54991622f1 | |||
| 17d69486ec | |||
| 948f1061da | |||
| 42438b3cd5 | |||
| 524ae200b0 | |||
| 4f763d3c2e | |||
| 9ebf525387 | |||
| 4ab672a96e | |||
| 7d2a367e3f | |||
| 67a67749c3 | |||
| 251307b5c9 | |||
| 8423a03d31 | |||
| d6cdf6b690 | |||
| 9cfa299e58 | |||
| a137e71420 | |||
| f08f80622f | |||
| e565fd7a34 | |||
| 8c45b39dcc | |||
| b119f333ac | |||
| bbe04ee458 | |||
| fab3c83a69 | |||
| fb87a0f32f | |||
| 63794ec4ec | |||
| bf6dc9740f | |||
| a23bc8dd67 | |||
| 13023a06cc | |||
| 28b50bd949 | |||
| 78ab928459 | |||
| ae3601cff5 | |||
| 5f62f4a209 | |||
| 29bf155b4f | |||
| da03f8b749 | |||
| 6a2deff93b | |||
| b0a99afcd3 | |||
| eedf72d7aa | |||
| 25e9941464 | |||
| a0da0392cf | |||
| a3ebee12b5 | |||
| c5b14ca09d | |||
| 44b3b857b1 | |||
| 63124455fd | |||
| e75ea8d8a5 | |||
| 31ac4209c3 | |||
| df09935789 | |||
| bb15b1b0f2 | |||
| f4d491ed94 | |||
| 96cb7d2f23 | |||
| cc6305b0df | |||
| 3916b13876 | |||
| ee4a06df30 | |||
| bb5ff7c3cd | |||
| 312a96dda1 | |||
| e38231e06d | |||
| bf6e45d706 | |||
| 7fa8921a25 | |||
| 7c940351d9 | |||
| 6b834da912 | |||
| 25aaa97d65 | |||
| da9d47ae4a | |||
| 7d6a77bf2a | |||
| 26828d0968 | |||
| e2dbae15c0 | |||
| b246034632 | |||
| 687ea82d78 | |||
| 4ac0f19908 | |||
| 9e5e7595eb | |||
| 9cd9274e99 | |||
| 5d82f3ecfe | |||
| 2ce249ab33 | |||
| e34bf68de0 | |||
| 862bda0cb9 | |||
| 90f7b17d07 | |||
| 2128baa46a | |||
| 875c30f467 | |||
| 2b29cd1ac6 | |||
| 9206fff5d0 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -72,3 +72,9 @@ docker-compose.override.yml
|
||||
*.swo
|
||||
*~
|
||||
!/CLAUDE.md
|
||||
|
||||
### SSL Certificates ###
|
||||
nginx/ssl/
|
||||
*.crt
|
||||
*.key
|
||||
*.pem
|
||||
|
||||
415
DEPLOY.md
Normal file
415
DEPLOY.md
Normal file
@@ -0,0 +1,415 @@
|
||||
# KAMCO Training API 배포 가이드 (RedHat 9.6)
|
||||
|
||||
## 빠른 배포 (Quick Start)
|
||||
|
||||
이 문서는 RedHat 9.6 환경에서 HTTPS로 KAMCO Training API를 배포하는 방법을 설명합니다.
|
||||
|
||||
**접속 URL**:
|
||||
- `https://api.train-kamco.com`
|
||||
- `https://train-kamco.com`
|
||||
|
||||
## 사전 요구사항
|
||||
|
||||
- [x] Docker & Docker Compose 설치
|
||||
- [x] Git 설치
|
||||
- [x] sudo 권한
|
||||
- [x] 포트 80, 443 사용 가능
|
||||
|
||||
## 1단계: /etc/hosts 설정
|
||||
|
||||
```bash
|
||||
# root 권한으로 도메인 추가
|
||||
echo "127.0.0.1 api.train-kamco.com train-kamco.com" | sudo tee -a /etc/hosts
|
||||
|
||||
# 확인
|
||||
cat /etc/hosts | grep train-kamco
|
||||
```
|
||||
|
||||
**예상 결과**:
|
||||
```
|
||||
127.0.0.1 api.train-kamco.com train-kamco.com
|
||||
```
|
||||
|
||||
## 2단계: 방화벽 설정 (필요시)
|
||||
|
||||
```bash
|
||||
# 방화벽 상태 확인
|
||||
sudo firewall-cmd --state
|
||||
|
||||
# HTTP/HTTPS 포트 개방
|
||||
sudo firewall-cmd --permanent --add-port=80/tcp
|
||||
sudo firewall-cmd --permanent --add-port=443/tcp
|
||||
|
||||
# 방화벽 재로드
|
||||
sudo firewall-cmd --reload
|
||||
|
||||
# 확인
|
||||
sudo firewall-cmd --list-ports
|
||||
```
|
||||
|
||||
**예상 결과**: `80/tcp 443/tcp`
|
||||
|
||||
## 3단계: 프로젝트 디렉토리로 이동
|
||||
|
||||
```bash
|
||||
cd /path/to/kamco-train-api
|
||||
|
||||
# 현재 위치 확인
|
||||
pwd
|
||||
# 예상: /home/username/kamco-train-api
|
||||
```
|
||||
|
||||
## 4단계: 파일 구조 확인
|
||||
|
||||
배포 전 필수 파일이 모두 있는지 확인하세요:
|
||||
|
||||
```bash
|
||||
# SSL 인증서 확인
|
||||
ls -la nginx/ssl/
|
||||
|
||||
# 예상 결과:
|
||||
# train-kamco.com.crt (인증서)
|
||||
# train-kamco.com.key (개인 키)
|
||||
# openssl.cnf (설정 파일)
|
||||
```
|
||||
|
||||
```bash
|
||||
# Docker Compose 파일 확인
|
||||
ls -la docker-compose-prod.yml nginx/nginx.conf
|
||||
|
||||
# 예상: 두 파일 모두 존재
|
||||
```
|
||||
|
||||
## 5단계: Docker 네트워크 생성 (최초 1회)
|
||||
|
||||
```bash
|
||||
# kamco-cds 네트워크가 있는지 확인
|
||||
docker network ls | grep kamco-cds
|
||||
|
||||
# 없으면 생성
|
||||
docker network create kamco-cds
|
||||
```
|
||||
|
||||
## 6단계: Docker Compose 배포
|
||||
|
||||
```bash
|
||||
# 기존 컨테이너 중지 (있는 경우)
|
||||
docker-compose -f docker-compose-prod.yml down
|
||||
|
||||
# 새로운 이미지 빌드 및 실행
|
||||
docker-compose -f docker-compose-prod.yml up -d --build
|
||||
|
||||
# 컨테이너 상태 확인
|
||||
docker-compose -f docker-compose-prod.yml ps
|
||||
```
|
||||
|
||||
**예상 결과**:
|
||||
```
|
||||
NAME STATUS
|
||||
kamco-cd-nginx Up (healthy)
|
||||
kamco-cd-training-api Up (healthy)
|
||||
```
|
||||
|
||||
## 7단계: 배포 확인
|
||||
|
||||
### 컨테이너 로그 확인
|
||||
|
||||
```bash
|
||||
# Nginx 로그
|
||||
docker logs kamco-cd-nginx --tail 50
|
||||
|
||||
# API 로그
|
||||
docker logs kamco-cd-training-api --tail 50
|
||||
|
||||
# 실시간 로그 (Ctrl+C로 종료)
|
||||
docker-compose -f docker-compose-prod.yml logs -f
|
||||
```
|
||||
|
||||
### HTTP → HTTPS 리다이렉트 테스트
|
||||
|
||||
```bash
|
||||
# HTTP 접속 시 HTTPS로 리다이렉트되는지 확인
|
||||
curl -I http://api.train-kamco.com
|
||||
curl -I http://train-kamco.com
|
||||
|
||||
# 예상 결과: 301 Moved Permanently
|
||||
# Location: https://api.train-kamco.com/ 또는 https://train-kamco.com/
|
||||
```
|
||||
|
||||
### HTTPS 헬스체크
|
||||
|
||||
```bash
|
||||
# -k 플래그: 사설 인증서 경고 무시
|
||||
curl -k https://api.train-kamco.com/monitor/health
|
||||
curl -k https://train-kamco.com/monitor/health
|
||||
|
||||
# 예상 결과: {"status":"UP","components":{...}}
|
||||
```
|
||||
|
||||
### 브라우저 테스트
|
||||
|
||||
브라우저에서 다음 URL에 접속:
|
||||
|
||||
- `https://api.train-kamco.com/monitor/health`
|
||||
- `https://train-kamco.com/monitor/health`
|
||||
|
||||
**사설 인증서 경고**:
|
||||
- "안전하지 않음" 경고가 표시되면 **"고급"** → **"계속 진행"** 클릭
|
||||
|
||||
## 8단계: SSL 인증서 확인 (선택사항)
|
||||
|
||||
```bash
|
||||
# 인증서 정보 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -text -noout | head -30
|
||||
|
||||
# 유효 기간 확인 (100년)
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -noout -dates
|
||||
|
||||
# SAN (멀티 도메인) 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -text -noout | grep -A1 "Subject Alternative Name"
|
||||
|
||||
# 예상 결과:
|
||||
# X509v3 Subject Alternative Name:
|
||||
# DNS:api.train-kamco.com, DNS:train-kamco.com
|
||||
```
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
### 문제 1: "Connection refused"
|
||||
|
||||
**원인**: 컨테이너가 실행되지 않음
|
||||
|
||||
**해결**:
|
||||
```bash
|
||||
# 컨테이너 상태 확인
|
||||
docker ps -a | grep kamco-cd
|
||||
|
||||
# 컨테이너 재시작
|
||||
docker-compose -f docker-compose-prod.yml restart
|
||||
|
||||
# 로그 확인
|
||||
docker logs kamco-cd-nginx
|
||||
docker logs kamco-cd-training-api
|
||||
```
|
||||
|
||||
### 문제 2: "502 Bad Gateway"
|
||||
|
||||
**원인**: Nginx는 실행 중이지만 API 컨테이너가 준비되지 않음
|
||||
|
||||
**해결**:
|
||||
```bash
|
||||
# API 컨테이너 상태 확인
|
||||
docker logs kamco-cd-training-api
|
||||
|
||||
# API 헬스체크 (컨테이너 내부에서)
|
||||
docker exec kamco-cd-nginx wget -qO- http://kamco-changedetection-api:8080/monitor/health
|
||||
|
||||
# API 컨테이너 재시작
|
||||
docker-compose -f docker-compose-prod.yml restart kamco-changedetection-api
|
||||
```
|
||||
|
||||
### 문제 3: "Name or service not known"
|
||||
|
||||
**원인**: /etc/hosts에 도메인이 설정되지 않음
|
||||
|
||||
**해결**:
|
||||
```bash
|
||||
# /etc/hosts 확인
|
||||
cat /etc/hosts | grep train-kamco
|
||||
|
||||
# 없으면 추가
|
||||
echo "127.0.0.1 api.train-kamco.com train-kamco.com" | sudo tee -a /etc/hosts
|
||||
```
|
||||
|
||||
### 문제 4: 포트 80 또는 443이 이미 사용 중
|
||||
|
||||
**원인**: 다른 프로세스가 포트를 사용 중
|
||||
|
||||
**해결**:
|
||||
```bash
|
||||
# 포트 사용 확인
|
||||
sudo lsof -i :80
|
||||
sudo lsof -i :443
|
||||
|
||||
# 사용 중인 프로세스 종료 (예: httpd, nginx)
|
||||
sudo systemctl stop httpd
|
||||
sudo systemctl stop nginx
|
||||
|
||||
# Docker Compose 재시작
|
||||
docker-compose -f docker-compose-prod.yml restart
|
||||
```
|
||||
|
||||
### 문제 5: SELinux 권한 오류
|
||||
|
||||
**원인**: SELinux가 Docker 볼륨 마운트를 차단
|
||||
|
||||
**해결**:
|
||||
```bash
|
||||
# SELinux 상태 확인
|
||||
getenforce
|
||||
|
||||
# Permissive 모드로 임시 변경 (재부팅 시 초기화됨)
|
||||
sudo setenforce 0
|
||||
|
||||
# 영구 변경 (권장하지 않음)
|
||||
sudo vi /etc/selinux/config
|
||||
# SELINUX=permissive 또는 SELINUX=disabled로 변경
|
||||
```
|
||||
|
||||
## 컨테이너 관리 명령어
|
||||
|
||||
### 시작/중지/재시작
|
||||
|
||||
```bash
|
||||
# 시작
|
||||
docker-compose -f docker-compose-prod.yml up -d
|
||||
|
||||
# 중지
|
||||
docker-compose -f docker-compose-prod.yml down
|
||||
|
||||
# 재시작
|
||||
docker-compose -f docker-compose-prod.yml restart
|
||||
|
||||
# 특정 서비스만 재시작
|
||||
docker-compose -f docker-compose-prod.yml restart nginx
|
||||
docker-compose -f docker-compose-prod.yml restart kamco-changedetection-api
|
||||
```
|
||||
|
||||
### 로그 확인
|
||||
|
||||
```bash
|
||||
# 전체 로그
|
||||
docker-compose -f docker-compose-prod.yml logs
|
||||
|
||||
# 특정 서비스 로그
|
||||
docker-compose -f docker-compose-prod.yml logs nginx
|
||||
docker-compose -f docker-compose-prod.yml logs kamco-changedetection-api
|
||||
|
||||
# 실시간 로그
|
||||
docker-compose -f docker-compose-prod.yml logs -f
|
||||
|
||||
# 마지막 N줄만 보기
|
||||
docker logs kamco-cd-nginx --tail 100
|
||||
```
|
||||
|
||||
### 컨테이너 상태 확인
|
||||
|
||||
```bash
|
||||
# 실행 중인 컨테이너
|
||||
docker-compose -f docker-compose-prod.yml ps
|
||||
|
||||
# 상세 정보
|
||||
docker inspect kamco-cd-nginx
|
||||
docker inspect kamco-cd-training-api
|
||||
|
||||
# 리소스 사용량
|
||||
docker stats kamco-cd-nginx kamco-cd-training-api
|
||||
```
|
||||
|
||||
### 컨테이너 내부 접속
|
||||
|
||||
```bash
|
||||
# Nginx 컨테이너 내부 접속
|
||||
docker exec -it kamco-cd-nginx sh
|
||||
|
||||
# API 컨테이너 내부 접속
|
||||
docker exec -it kamco-cd-training-api sh
|
||||
|
||||
# 내부에서 빠져나오기
|
||||
exit
|
||||
```
|
||||
|
||||
## 업데이트 및 재배포
|
||||
|
||||
### 코드 업데이트 후 재배포
|
||||
|
||||
```bash
|
||||
# 1. Git pull (코드 업데이트)
|
||||
git pull origin develop
|
||||
|
||||
# 2. JAR 파일 빌드 (Jenkins에서 수행하는 경우 생략)
|
||||
./gradlew clean build -x test
|
||||
|
||||
# 3. 컨테이너 재빌드 및 재시작
|
||||
docker-compose -f docker-compose-prod.yml down
|
||||
docker-compose -f docker-compose-prod.yml up -d --build
|
||||
|
||||
# 4. 로그 확인
|
||||
docker-compose -f docker-compose-prod.yml logs -f
|
||||
```
|
||||
|
||||
### 설정 파일만 변경한 경우
|
||||
|
||||
```bash
|
||||
# nginx.conf 또는 docker-compose-prod.yml 변경 시
|
||||
docker-compose -f docker-compose-prod.yml down
|
||||
docker-compose -f docker-compose-prod.yml up -d
|
||||
|
||||
# 또는
|
||||
docker-compose -f docker-compose-prod.yml restart nginx
|
||||
```
|
||||
|
||||
## 모니터링
|
||||
|
||||
### 헬스체크 엔드포인트
|
||||
|
||||
```bash
|
||||
# API 헬스체크
|
||||
curl -k https://api.train-kamco.com/monitor/health
|
||||
|
||||
# 예상 결과:
|
||||
# {
|
||||
# "status": "UP",
|
||||
# "components": {
|
||||
# "db": {"status": "UP"},
|
||||
# "diskSpace": {"status": "UP"}
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
### 시스템 리소스
|
||||
|
||||
```bash
|
||||
# 디스크 사용량
|
||||
df -h
|
||||
|
||||
# 메모리 사용량
|
||||
free -h
|
||||
|
||||
# Docker 이미지 및 컨테이너 용량
|
||||
docker system df
|
||||
```
|
||||
|
||||
## 보안 권장 사항
|
||||
|
||||
1. **사설 인증서**: 현재 사설 인증서를 사용 중입니다. 프로덕션 환경에서는 **Let's Encrypt** 또는 **GlobalSign** 같은 공인 인증서 사용을 권장합니다.
|
||||
|
||||
2. **방화벽**: 필요한 포트(80, 443)만 개방하고, 불필요한 포트는 차단하세요.
|
||||
|
||||
3. **정기 업데이트**: Docker 이미지와 시스템 패키지를 정기적으로 업데이트하세요.
|
||||
|
||||
4. **로그 모니터링**: 정기적으로 로그를 확인하여 비정상적인 활동을 감지하세요.
|
||||
|
||||
5. **백업**: SSL 인증서 키 파일(`train-kamco.com.key`)과 데이터베이스를 정기적으로 백업하세요.
|
||||
|
||||
## 참고 문서
|
||||
|
||||
- **SSL 인증서 설정**: [nginx/SSL_SETUP.md](nginx/SSL_SETUP.md)
|
||||
- **프로젝트 개요**: [README.md](README.md)
|
||||
- **CLAUDE.md**: [CLAUDE.md](CLAUDE.md)
|
||||
|
||||
## 지원
|
||||
|
||||
문제가 발생하면 다음을 확인하세요:
|
||||
1. 컨테이너 로그: `docker-compose -f docker-compose-prod.yml logs`
|
||||
2. 컨테이너 상태: `docker-compose -f docker-compose-prod.yml ps`
|
||||
3. /etc/hosts 설정: `cat /etc/hosts | grep train-kamco`
|
||||
4. 방화벽 상태: `sudo firewall-cmd --list-ports`
|
||||
|
||||
---
|
||||
|
||||
**배포 완료!** 🎉
|
||||
|
||||
접속 URL:
|
||||
- `https://api.train-kamco.com`
|
||||
- `https://train-kamco.com`
|
||||
20
Dockerfile
Normal file
20
Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Stage 1: Build stage (gradle build는 Jenkins에서 이미 수행)
|
||||
FROM eclipse-temurin:21-jre-jammy
|
||||
|
||||
# docker CLI 설치 (컨테이너에서 호스트 Docker 제어용) 260212 추가
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends docker.io ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 작업 디렉토리 설정
|
||||
WORKDIR /app
|
||||
|
||||
# JAR 파일 복사 (Jenkins에서 빌드된 ROOT.jar)
|
||||
COPY build/libs/ROOT.jar app.jar
|
||||
|
||||
# 포트 노출
|
||||
EXPOSE 8080
|
||||
|
||||
# 애플리케이션 실행
|
||||
# dev 프로파일로 실행
|
||||
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "app.jar"]
|
||||
56
docker-compose-prod.yml
Normal file
56
docker-compose-prod.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: kamco-cd-nginx
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
- nginx-logs:/var/log/nginx
|
||||
depends_on:
|
||||
kamco-changedetection-api:
|
||||
condition: service_healthy
|
||||
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
|
||||
|
||||
kamco-changedetection-api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: kamco-cd-training-api:${IMAGE_TAG:-latest}
|
||||
container_name: kamco-cd-training-api
|
||||
expose:
|
||||
- "8080"
|
||||
environment:
|
||||
- SPRING_PROFILES_ACTIVE=prod
|
||||
- TZ=Asia/Seoul
|
||||
volumes:
|
||||
- ./app/model_output:/app/model-outputs
|
||||
- ./app/train_dataset:/app/train-dataset
|
||||
- /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
|
||||
|
||||
volumes:
|
||||
nginx-logs:
|
||||
driver: local
|
||||
397
nginx/SSL_SETUP.md
Normal file
397
nginx/SSL_SETUP.md
Normal file
@@ -0,0 +1,397 @@
|
||||
# SSL 사설 인증서 설정 가이드 (RedHat 9.6)
|
||||
|
||||
## 개요
|
||||
|
||||
이 문서는 RedHat 9.6 환경에서 `https://api.train-kamco.com` 및 `https://train-kamco.com` 도메인을 위한 100년 유효한 사설 SSL 인증서 설정 방법을 설명합니다.
|
||||
|
||||
## 디렉토리 구조
|
||||
|
||||
```
|
||||
nginx/
|
||||
├── nginx.conf # Nginx 설정 파일
|
||||
├── ssl/
|
||||
│ ├── openssl.cnf # OpenSSL 설정 파일 (SAN 포함)
|
||||
│ ├── train-kamco.com.crt # 사설 SSL 인증서 (멀티 도메인)
|
||||
│ └── train-kamco.com.key # 개인 키 (비공개)
|
||||
└── SSL_SETUP.md # 이 문서
|
||||
```
|
||||
|
||||
## 인증서 정보
|
||||
|
||||
- **도메인**: api.train-kamco.com, train-kamco.com (멀티 도메인)
|
||||
- **유효 기간**: 100년 (36500일)
|
||||
- **알고리즘**: RSA 4096-bit
|
||||
- **CN (Common Name)**: api.train-kamco.com
|
||||
- **SAN (Subject Alternative Names)**: api.train-kamco.com, train-kamco.com
|
||||
|
||||
## 사설 SSL 인증서 생성 (이미 생성됨)
|
||||
|
||||
인증서가 이미 생성되어 있습니다. 재생성이 필요한 경우 아래 단계를 따르세요.
|
||||
|
||||
### 1. OpenSSL 설정 파일 생성
|
||||
|
||||
```bash
|
||||
cd /path/to/kamco-train-api
|
||||
|
||||
cat > nginx/ssl/openssl.cnf << 'EOF'
|
||||
[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
|
||||
EOF
|
||||
```
|
||||
|
||||
### 2. SSL 인증서 및 개인 키 생성
|
||||
|
||||
```bash
|
||||
# nginx/ssl 디렉토리 생성 (없는 경우)
|
||||
mkdir -p nginx/ssl
|
||||
chmod 700 nginx/ssl
|
||||
|
||||
# 인증서 및 개인 키 생성 (100년 유효)
|
||||
openssl req -new -x509 -newkey rsa:4096 -sha256 -nodes \
|
||||
-keyout nginx/ssl/train-kamco.com.key \
|
||||
-out nginx/ssl/train-kamco.com.crt \
|
||||
-days 36500 \
|
||||
-config nginx/ssl/openssl.cnf \
|
||||
-extensions v3_req
|
||||
|
||||
# 파일 권한 설정
|
||||
chmod 600 nginx/ssl/train-kamco.com.key
|
||||
chmod 644 nginx/ssl/train-kamco.com.crt
|
||||
```
|
||||
|
||||
### 3. 인증서 검증
|
||||
|
||||
```bash
|
||||
# 인증서 정보 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -text -noout
|
||||
|
||||
# 유효 기간 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -text -noout | grep -A2 "Validity"
|
||||
|
||||
# SAN (멀티 도메인) 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -text -noout | grep -A1 "Subject Alternative Name"
|
||||
|
||||
# CN 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -noout -subject
|
||||
|
||||
# 개인 키 확인
|
||||
openssl rsa -in nginx/ssl/train-kamco.com.key -check
|
||||
```
|
||||
|
||||
**예상 결과**:
|
||||
```
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:api.train-kamco.com, DNS:train-kamco.com
|
||||
|
||||
Validity
|
||||
Not Before: Mar 2 23:39:XX 2026 GMT
|
||||
Not After : Feb 6 23:39:XX 2126 GMT
|
||||
```
|
||||
|
||||
## /etc/hosts 설정 (RedHat 9.6)
|
||||
|
||||
### 1. hosts 파일에 도메인 추가
|
||||
|
||||
```bash
|
||||
# root 권한으로 실행
|
||||
echo "127.0.0.1 api.train-kamco.com train-kamco.com" | sudo tee -a /etc/hosts
|
||||
|
||||
# 확인
|
||||
cat /etc/hosts | grep train-kamco
|
||||
```
|
||||
|
||||
**예상 결과**:
|
||||
```
|
||||
127.0.0.1 api.train-kamco.com train-kamco.com
|
||||
```
|
||||
|
||||
### 2. 도메인 확인
|
||||
|
||||
```bash
|
||||
# ping 테스트
|
||||
ping -c 2 api.train-kamco.com
|
||||
ping -c 2 train-kamco.com
|
||||
```
|
||||
|
||||
## Docker Compose 배포
|
||||
|
||||
### 1. 기존 컨테이너 중지 (실행 중인 경우)
|
||||
|
||||
```bash
|
||||
cd /path/to/kamco-train-api
|
||||
docker-compose -f docker-compose-prod.yml down
|
||||
```
|
||||
|
||||
### 2. Production 환경 실행
|
||||
|
||||
```bash
|
||||
# IMAGE_TAG 환경 변수 설정 (선택사항)
|
||||
export IMAGE_TAG=latest
|
||||
|
||||
# Docker Compose 실행
|
||||
docker-compose -f docker-compose-prod.yml up -d
|
||||
|
||||
# 컨테이너 상태 확인
|
||||
docker-compose -f docker-compose-prod.yml ps
|
||||
```
|
||||
|
||||
### 3. 로그 확인
|
||||
|
||||
```bash
|
||||
# Nginx 로그
|
||||
docker logs kamco-cd-nginx
|
||||
|
||||
# API 로그
|
||||
docker logs kamco-cd-training-api
|
||||
|
||||
# 실시간 로그 확인
|
||||
docker-compose -f docker-compose-prod.yml logs -f
|
||||
```
|
||||
|
||||
## HTTPS 접속 테스트
|
||||
|
||||
### 1. HTTP → HTTPS 리다이렉트 테스트
|
||||
|
||||
```bash
|
||||
# api.train-kamco.com
|
||||
curl -I http://api.train-kamco.com
|
||||
|
||||
# train-kamco.com
|
||||
curl -I http://train-kamco.com
|
||||
|
||||
# 예상 결과: 301 Moved Permanently
|
||||
# Location: https://api.train-kamco.com/ 또는 https://train-kamco.com/
|
||||
```
|
||||
|
||||
### 2. HTTPS 헬스체크 (-k: 사설 인증서 경고 무시)
|
||||
|
||||
```bash
|
||||
# api.train-kamco.com
|
||||
curl -k https://api.train-kamco.com/monitor/health
|
||||
|
||||
# train-kamco.com
|
||||
curl -k https://train-kamco.com/monitor/health
|
||||
|
||||
# 예상 결과: {"status":"UP","components":{...}}
|
||||
```
|
||||
|
||||
### 3. SSL 인증서 확인
|
||||
|
||||
```bash
|
||||
# api.train-kamco.com
|
||||
openssl s_client -connect api.train-kamco.com:443 -showcerts
|
||||
|
||||
# train-kamco.com
|
||||
openssl s_client -connect train-kamco.com:443 -showcerts
|
||||
|
||||
# CN 및 SAN 확인
|
||||
```
|
||||
|
||||
### 4. 브라우저 테스트
|
||||
|
||||
브라우저에서 다음 URL에 접속:
|
||||
|
||||
- `https://api.train-kamco.com/monitor/health`
|
||||
- `https://train-kamco.com/monitor/health`
|
||||
|
||||
**주의**: 사설 인증서이므로 "안전하지 않음" 경고가 표시됩니다.
|
||||
- **Chrome/Edge**: "고급" → "계속 진행" 클릭
|
||||
- **Firefox**: "위험 감수 및 계속" 클릭
|
||||
|
||||
## 브라우저에서 사설 인증서 신뢰 설정 (선택사항)
|
||||
|
||||
사설 인증서를 브라우저에 등록하면 경고 없이 접속 가능합니다.
|
||||
|
||||
### Chrome/Edge (RedHat Desktop)
|
||||
|
||||
1. `chrome://settings/certificates` 접속
|
||||
2. **Authorities** 탭 선택
|
||||
3. **Import** 클릭
|
||||
4. `nginx/ssl/train-kamco.com.crt` 선택
|
||||
5. **Trust this certificate for identifying websites** 체크
|
||||
6. **OK** 클릭
|
||||
|
||||
### Firefox
|
||||
|
||||
1. `about:preferences#privacy` 접속
|
||||
2. **Certificates** → **View Certificates** 클릭
|
||||
3. **Authorities** 탭 선택
|
||||
4. **Import** 클릭
|
||||
5. `nginx/ssl/train-kamco.com.crt` 선택
|
||||
6. **Trust this CA to identify websites** 체크
|
||||
7. **OK** 클릭
|
||||
|
||||
## 방화벽 설정 (RedHat 9.6)
|
||||
|
||||
### 1. 방화벽 상태 확인
|
||||
|
||||
```bash
|
||||
sudo firewall-cmd --state
|
||||
```
|
||||
|
||||
### 2. HTTP (80) 및 HTTPS (443) 포트 개방
|
||||
|
||||
```bash
|
||||
# HTTP 포트 개방
|
||||
sudo firewall-cmd --permanent --add-port=80/tcp
|
||||
|
||||
# HTTPS 포트 개방
|
||||
sudo firewall-cmd --permanent --add-port=443/tcp
|
||||
|
||||
# 방화벽 재로드
|
||||
sudo firewall-cmd --reload
|
||||
|
||||
# 확인
|
||||
sudo firewall-cmd --list-ports
|
||||
```
|
||||
|
||||
**예상 결과**:
|
||||
```
|
||||
80/tcp 443/tcp
|
||||
```
|
||||
|
||||
## 보안 체크리스트
|
||||
|
||||
- [x] `train-kamco.com.key` 파일 권한이 600으로 설정됨
|
||||
- [x] ssl 디렉토리가 버전 관리에서 제외됨 (.gitignore 확인)
|
||||
- [x] 두 도메인(api.train-kamco.com, train-kamco.com) 모두 SAN에 포함됨
|
||||
- [ ] 방화벽에서 80, 443 포트 개방 확인
|
||||
- [x] HSTS 헤더 활성화 확인
|
||||
- [x] TLS 1.2 이상만 허용 확인
|
||||
- [ ] /etc/hosts에 도메인 매핑 확인
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
### 인증서 관련 오류
|
||||
|
||||
**"certificate verify failed"**
|
||||
```bash
|
||||
# 해결: -k 플래그 사용 (사설 인증서 경고 무시)
|
||||
curl -k https://api.train-kamco.com/monitor/health
|
||||
```
|
||||
|
||||
**"NET::ERR_CERT_AUTHORITY_INVALID" (브라우저)**
|
||||
- 정상 동작: 사설 인증서이므로 브라우저 경고는 예상된 동작입니다
|
||||
- 해결: 브라우저에 인증서 등록 (위 "브라우저에서 사설 인증서 신뢰 설정" 참조)
|
||||
|
||||
### 연결 오류
|
||||
|
||||
**"Connection refused"**
|
||||
```bash
|
||||
# 컨테이너 상태 확인
|
||||
docker ps | grep kamco-cd
|
||||
|
||||
# 포트 바인딩 확인
|
||||
docker port kamco-cd-nginx
|
||||
|
||||
# 예상 결과:
|
||||
# 80/tcp -> 0.0.0.0:80
|
||||
# 443/tcp -> 0.0.0.0:443
|
||||
```
|
||||
|
||||
**"502 Bad Gateway"**
|
||||
```bash
|
||||
# API 컨테이너 상태 확인
|
||||
docker logs kamco-cd-training-api
|
||||
|
||||
# nginx → API 연결 확인
|
||||
docker exec kamco-cd-nginx wget -qO- http://kamco-changedetection-api:8080/monitor/health
|
||||
```
|
||||
|
||||
**"Name or service not known" (도메인 해석 실패)**
|
||||
```bash
|
||||
# /etc/hosts 확인
|
||||
cat /etc/hosts | grep train-kamco
|
||||
|
||||
# 없으면 추가
|
||||
echo "127.0.0.1 api.train-kamco.com train-kamco.com" | sudo tee -a /etc/hosts
|
||||
```
|
||||
|
||||
### 방화벽 관련 오류
|
||||
|
||||
**외부에서 접속 안 됨**
|
||||
```bash
|
||||
# 방화벽 확인
|
||||
sudo firewall-cmd --list-ports
|
||||
|
||||
# 포트 개방
|
||||
sudo firewall-cmd --permanent --add-port=80/tcp
|
||||
sudo firewall-cmd --permanent --add-port=443/tcp
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
## 인증서 만료 및 갱신
|
||||
|
||||
### 만료 확인
|
||||
|
||||
```bash
|
||||
# 인증서 만료일 확인
|
||||
openssl x509 -in nginx/ssl/train-kamco.com.crt -noout -enddate
|
||||
|
||||
# 예상 결과: notAfter=Feb 6 23:39:XX 2126 GMT (100년 후)
|
||||
```
|
||||
|
||||
### 갱신 방법 (필요시)
|
||||
|
||||
100년 유효한 인증서이므로 갱신이 필요하지 않지만, 재생성이 필요한 경우:
|
||||
|
||||
```bash
|
||||
# 기존 인증서 백업
|
||||
cp nginx/ssl/train-kamco.com.crt nginx/ssl/train-kamco.com.crt.bak
|
||||
cp nginx/ssl/train-kamco.com.key nginx/ssl/train-kamco.com.key.bak
|
||||
|
||||
# 위의 "사설 SSL 인증서 생성" 단계 재실행
|
||||
|
||||
# nginx 재시작
|
||||
docker-compose -f docker-compose-prod.yml restart nginx
|
||||
```
|
||||
|
||||
## 주의사항
|
||||
|
||||
1. **사설 인증서 경고**: 브라우저에서 "안전하지 않음" 경고가 표시됩니다. 프로덕션 환경에서는 **공인 인증서(Let's Encrypt, GlobalSign 등) 사용을 권장**합니다.
|
||||
|
||||
2. **포트 80/443**: Docker가 자동으로 처리하지만, 이미 사용 중인 프로세스가 있으면 충돌할 수 있습니다.
|
||||
```bash
|
||||
# 포트 사용 확인
|
||||
sudo lsof -i :80
|
||||
sudo lsof -i :443
|
||||
```
|
||||
|
||||
3. **대용량 파일 업로드**: `client_max_body_size`를 10GB로 설정했으므로, 서버 메모리 및 디스크 용량을 충분히 확보하세요.
|
||||
|
||||
4. **인증서 백업**: `train-kamco.com.key` 파일은 매우 중요합니다. 안전한 곳에 백업하세요.
|
||||
|
||||
5. **SELinux**: RedHat 9.6에서 SELinux가 활성화된 경우, Docker 볼륨 마운트 권한 문제가 발생할 수 있습니다.
|
||||
```bash
|
||||
# SELinux 상태 확인
|
||||
getenforce
|
||||
|
||||
# 필요시 permissive 모드로 변경
|
||||
sudo setenforce 0
|
||||
```
|
||||
|
||||
## 참고 자료
|
||||
|
||||
- [OpenSSL Documentation](https://www.openssl.org/docs/)
|
||||
- [Nginx SSL Configuration](https://nginx.org/en/docs/http/configuring_https_servers.html)
|
||||
- [Docker Compose Documentation](https://docs.docker.com/compose/)
|
||||
- [Let's Encrypt (공인 인증서)](https://letsencrypt.org/)
|
||||
93
nginx/nginx.conf
Normal file
93
nginx/nginx.conf
Normal file
@@ -0,0 +1,93 @@
|
||||
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)
|
||||
client_max_body_size 10G;
|
||||
|
||||
# Upstream 설정
|
||||
upstream api_backend {
|
||||
server kamco-changedetection-api:8080;
|
||||
}
|
||||
|
||||
# HTTP → HTTPS 리다이렉트 서버
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.train-kamco.com train-kamco.com;
|
||||
|
||||
# 모든 HTTP 요청을 HTTPS로 리다이렉트
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS 서버 설정
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.train-kamco.com 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;
|
||||
|
||||
# 프록시 설정
|
||||
location / {
|
||||
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;
|
||||
|
||||
# 타임아웃 설정 (대용량 파일 업로드 지원)
|
||||
proxy_connect_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
|
||||
# 버퍼 설정
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
}
|
||||
|
||||
# 헬스체크 엔드포인트
|
||||
location /monitor/health {
|
||||
proxy_pass http://api_backend/monitor/health;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package com.kamco.cd.training.common.utils;
|
||||
import static java.lang.String.CASE_INSENSITIVE_ORDER;
|
||||
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.kamco.cd.training.common.exception.CustomApiException;
|
||||
@@ -787,92 +786,6 @@ public class FIleChecker {
|
||||
return destFile;
|
||||
}
|
||||
|
||||
public static void uploadTo86(Path localFile) {
|
||||
|
||||
String host = "192.168.2.86";
|
||||
int port = 22;
|
||||
String username = "kcomu";
|
||||
String password = "Kamco2025!";
|
||||
|
||||
String remoteDir = "/home/kcomu/data/request";
|
||||
|
||||
Session session = null;
|
||||
ChannelSftp channel = null;
|
||||
|
||||
try {
|
||||
JSch jsch = new JSch();
|
||||
|
||||
session = jsch.getSession(username, host, port);
|
||||
session.setPassword(password);
|
||||
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
session.setConfig(config);
|
||||
|
||||
session.connect(10_000);
|
||||
|
||||
channel = (ChannelSftp) session.openChannel("sftp");
|
||||
channel.connect(10_000);
|
||||
|
||||
// 목적지 디렉토리 이동
|
||||
channel.cd(remoteDir);
|
||||
|
||||
// 업로드
|
||||
channel.put(localFile.toString(), localFile.getFileName().toString());
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("SFTP upload failed", e);
|
||||
} finally {
|
||||
if (channel != null) channel.disconnect();
|
||||
if (session != null) session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void unzipOn86Server(String zipPath, String targetDir) {
|
||||
|
||||
String host = "192.168.2.86";
|
||||
String user = "kcomu";
|
||||
String password = "Kamco2025!";
|
||||
|
||||
Session session = null;
|
||||
ChannelExec channel = null;
|
||||
|
||||
try {
|
||||
JSch jsch = new JSch();
|
||||
|
||||
session = jsch.getSession(user, host, 22);
|
||||
session.setPassword(password);
|
||||
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
session.setConfig(config);
|
||||
|
||||
session.connect(10_000);
|
||||
|
||||
String command = "unzip -o " + zipPath + " -d " + targetDir;
|
||||
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(command);
|
||||
channel.setErrStream(System.err);
|
||||
|
||||
InputStream in = channel.getInputStream();
|
||||
channel.connect();
|
||||
|
||||
// 출력 읽기(선택)
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
|
||||
while (br.readLine() != null) {
|
||||
// 필요하면 로그
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (channel != null) channel.disconnect();
|
||||
if (session != null) session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> execCommandAndReadLines(String command) {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
@@ -398,24 +398,6 @@ public class DatasetService {
|
||||
return datasetCoreService.getFilePathByUUIDPathType(uuid, pathType);
|
||||
}
|
||||
|
||||
private String readRemoteFileAsString(String remoteFilePath) {
|
||||
|
||||
String command = "cat " + escape(remoteFilePath);
|
||||
|
||||
List<String> lines = FIleChecker.execCommandAndReadLines(command);
|
||||
|
||||
return String.join("\n", lines);
|
||||
}
|
||||
|
||||
private JsonNode parseJson(String json) {
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.readTree(json);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("JSON 파싱 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String escape(String path) {
|
||||
// 쉘 커맨드에서 안전하게 사용할 수 있도록 문자열을 작은따옴표로 감싸면서, 내부의 작은따옴표를 이스케이프 처리
|
||||
return "'" + path.replace("'", "'\"'\"'") + "'";
|
||||
|
||||
@@ -20,9 +20,7 @@ import java.util.stream.Stream;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
@@ -268,13 +268,19 @@ public class TrainJobService {
|
||||
try {
|
||||
// 데이터셋 심볼링크 생성
|
||||
// String pathUid = tmpDatasetService.buildTmpDatasetSymlink(raw, uids);
|
||||
// train path
|
||||
// train path 모델 클래스별 조회
|
||||
List<ModelTrainLinkDto> trainList = modelTrainMngCoreService.findDatasetTrainPath(modelId);
|
||||
// validation path
|
||||
// validation path 모델 클래스별 조회
|
||||
List<ModelTrainLinkDto> valList = modelTrainMngCoreService.findDatasetValPath(modelId);
|
||||
// test path
|
||||
// test path 모델 클래스별 조회
|
||||
List<ModelTrainLinkDto> testList = modelTrainMngCoreService.findDatasetTestPath(modelId);
|
||||
|
||||
log.info(
|
||||
"createTmpFile class list trainList = {} valList = {} testList = {}",
|
||||
trainList.size(),
|
||||
valList.size(),
|
||||
testList.size());
|
||||
|
||||
// train 데이터셋 심볼링크 생성
|
||||
tmpDatasetService.buildTmpDatasetHardlink(raw, "train", trainList);
|
||||
// val 데이터셋 심볼링크 생성
|
||||
|
||||
@@ -15,7 +15,7 @@ spring:
|
||||
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
||||
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.01:15432/kamco_training_db
|
||||
url: jdbc:postgresql://kamco-cd-train-db:5432/kamco_training_db
|
||||
# url: jdbc:postgresql://localhost:15432/kamco_training_db
|
||||
username: kamco_training_user
|
||||
password: kamco_training_user_2025_!@#
|
||||
|
||||
Reference in New Issue
Block a user