feat(devops): Phase E CI/CD 파이프라인 — 개발서버 배포 구성
- deploy/systemd: kintex.service(백엔드 8021) + kintex-nanobanana.service(워커, GEMINI env 워커 한정) - deploy/nginx: kintex.zioinfo.kr vhost(/ static, /api·/ws → 8021) - deploy/deploy_kintex.sh: 검증→원자교체→헬스체크→롤백(WISE 패턴) - deploy/setup_kintex_service.py: 멱등 프로비저닝(유닛·venv·nginx) - deploy/deploy_server_kintex_block.py: 서버 webhook 블록 정본 사본 - Jenkinsfile: push→build(vite+bootJar)→deploy→health - .gitattributes: 배포 산출물 LF 강제 포트 8021 배정(8003~8020 기존 GUARDiA 예약). 시크릿은 서버 env only(미커밋). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eccbeb1337
commit
21fecc37da
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# 배포 산출물은 Linux 서버에서 실행/파싱되므로 LF 강제(CRLF 시 systemd 유닛·bash 파싱 오류).
|
||||||
|
deploy/**/*.sh text eol=lf
|
||||||
|
deploy/**/*.service text eol=lf
|
||||||
|
deploy/**/*.conf text eol=lf
|
||||||
|
deploy/**/*.py text eol=lf
|
||||||
|
Jenkinsfile text eol=lf
|
||||||
72
Jenkinsfile
vendored
Normal file
72
Jenkinsfile
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// KINTEX 자동전시시스템 — CI/CD 파이프라인 (git push → Gitea webhook → 빌드 → 배포 → 헬스체크)
|
||||||
|
// 실행 노드: 서버(101.79.17.164) Jenkins. 프론트(vite)→/var/www/kintex, 백엔드(bootJar)→/opt/kintex/app/app.jar,
|
||||||
|
// kintex.service + kintex-nanobanana.service 재기동. 배포 단계는 sudo(deploy_kintex.sh) 위임.
|
||||||
|
// ※ 상시 자동배포는 deploy_server.py(webhook, 포트 9999)의 kintex 블록이 1차 경로. 본 파이프라인은 Jenkins 병행 트랙.
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
options {
|
||||||
|
timeout(time: 40, unit: 'MINUTES')
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
WEB_ROOT = '/var/www/kintex'
|
||||||
|
APP_JAR = '/opt/kintex/app/app.jar'
|
||||||
|
BACKEND_PORT = '8021'
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps { checkout scm }
|
||||||
|
}
|
||||||
|
stage('Frontend Build') {
|
||||||
|
steps {
|
||||||
|
dir('src/frontend') {
|
||||||
|
sh 'npm ci --silent || npm install --silent'
|
||||||
|
sh 'NODE_OPTIONS=--max-old-space-size=4096 npm run build'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Backend Build') {
|
||||||
|
steps {
|
||||||
|
dir('src/backend') {
|
||||||
|
sh 'chmod +x gradlew'
|
||||||
|
sh './gradlew clean bootJar -x test -q --no-daemon'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Verify Jar') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
JAR=$(ls -1 src/backend/build/libs/kintex-backend-*.jar | grep -v plain | head -1)
|
||||||
|
[ -n "$JAR" ] || { echo "jar not found"; exit 1; }
|
||||||
|
unzip -l "$JAR" | grep -q 'BOOT-INF/classes/com/zioinfo/kintex/KintexApplication.class' \
|
||||||
|
|| { echo "invalid jar (no main class)"; exit 1; }
|
||||||
|
echo "jar OK: $JAR"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy') {
|
||||||
|
steps {
|
||||||
|
// 권한 필요한 배포는 deploy_kintex.sh 로 위임(sudo NOPASSWD 권장).
|
||||||
|
sh 'sudo /opt/kintex/deploy_kintex.sh "$WORKSPACE"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Health Check') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
for i in $(seq 1 15); do
|
||||||
|
code=$(curl -s -m 6 -o /dev/null -w '%{http_code}' http://127.0.0.1:'"$BACKEND_PORT"'/health || true)
|
||||||
|
echo "health try $i: $code"
|
||||||
|
if [ "$code" = "200" ]; then exit 0; fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
echo "health check failed"; systemctl is-active kintex.service || true; exit 1
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
success { echo 'KINTEX 배포 성공 (kintex.zioinfo.kr, 백엔드 8021)' }
|
||||||
|
failure { echo 'KINTEX 빌드/배포 실패 — 롤백은 deploy_kintex.sh 가 수행' }
|
||||||
|
}
|
||||||
|
}
|
||||||
77
deploy/deploy_kintex.sh
Normal file
77
deploy/deploy_kintex.sh
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# KINTEX 배포 스크립트 — deploy_server.py(webhook) 또는 Jenkins가 실행.
|
||||||
|
# 인자: $1 = 소스 체크아웃 루트(빌드 산출물 위치, 기본 /opt/kintex/src)
|
||||||
|
# 동작(fail-safe): 프론트 dist 검증→원자적 교체(/var/www/kintex)→백엔드 jar 교체→서비스 재기동→헬스체크.
|
||||||
|
# 깨진 빌드가 라이브를 무너뜨리지 않도록 "검증 후 교체" + 실패 시 롤백(WISE deploy_uiws.sh 패턴).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SRC="${1:-/opt/kintex/src}"
|
||||||
|
WEB_ROOT=/var/www/kintex
|
||||||
|
APP_JAR=/opt/kintex/app/app.jar
|
||||||
|
BACKEND_PORT=8021
|
||||||
|
|
||||||
|
log(){ echo "[deploy-kintex] $*"; }
|
||||||
|
die(){ echo "[deploy-kintex] ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# ── 1. 프론트: 빌드 완전성 검증(라이브 건드리기 전) → 스테이징 → 원자적 rename ──
|
||||||
|
DIST="$SRC/src/frontend/dist"
|
||||||
|
if [ -d "$DIST" ]; then
|
||||||
|
[ -f "$DIST/index.html" ] || die "dist/index.html 없음 — 불완전 빌드, 배포 중단(라이브 무변경)"
|
||||||
|
[ -d "$DIST/assets" ] || die "dist/assets 없음 — 불완전 빌드, 배포 중단"
|
||||||
|
mapfile -t REFS < <(grep -oE '/assets/[A-Za-z0-9._-]+\.(js|css)' "$DIST/index.html" | sort -u)
|
||||||
|
[ "${#REFS[@]}" -gt 0 ] || die "index.html 참조 assets 없음 — 비정상 빌드, 중단"
|
||||||
|
for ref in "${REFS[@]}"; do
|
||||||
|
[ -f "${DIST}${ref}" ] || die "참조 자산 누락: ${ref} — 불완전 빌드, 중단"
|
||||||
|
done
|
||||||
|
log "프론트 빌드 검증 통과(${#REFS[@]} assets)"
|
||||||
|
|
||||||
|
STAGE="${WEB_ROOT}.new.$$"
|
||||||
|
rm -rf "$STAGE"; mkdir -p "$STAGE"
|
||||||
|
cp -r "$DIST/." "$STAGE/"
|
||||||
|
if [ -d "$WEB_ROOT" ] && [ -n "$(ls -A "$WEB_ROOT" 2>/dev/null)" ]; then
|
||||||
|
tar czf "/var/www/kintex.bak_$(date +%Y%m%d_%H%M%S).tgz" -C "$WEB_ROOT" . || log "WARN: 프론트 백업 실패(계속)"
|
||||||
|
fi
|
||||||
|
OLD="${WEB_ROOT}.old.$$"
|
||||||
|
[ -d "$WEB_ROOT" ] && mv "$WEB_ROOT" "$OLD" || true
|
||||||
|
mv "$STAGE" "$WEB_ROOT"
|
||||||
|
rm -rf "$OLD"
|
||||||
|
log "프론트 배포 완료 → $WEB_ROOT"
|
||||||
|
else
|
||||||
|
log "WARN: $DIST 없음 — 프론트 배포 건너뜀(백엔드만)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. 백엔드 jar: 검증(BOOT-INF 메인 클래스) 후 교체 ──
|
||||||
|
JAR=$(ls -1 "$SRC"/src/backend/build/libs/kintex-backend-*.jar 2>/dev/null | grep -v plain | head -1 || true)
|
||||||
|
if [ -n "$JAR" ]; then
|
||||||
|
unzip -l "$JAR" | grep -q 'BOOT-INF/classes/com/zioinfo/kintex/KintexApplication.class' \
|
||||||
|
|| die "유효하지 않은 jar(메인 클래스 없음) — 배포 중단"
|
||||||
|
[ -f "$APP_JAR" ] && cp "$APP_JAR" "${APP_JAR}.bak" || true
|
||||||
|
cp "$JAR" "$APP_JAR"
|
||||||
|
log "백엔드 jar 교체 → $APP_JAR ($(basename "$JAR"))"
|
||||||
|
else
|
||||||
|
log "WARN: bootJar 없음 — 백엔드 교체 건너뜀"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3. 워커 의존성 갱신(venv) — requirements 변경 대비(멱등) ──
|
||||||
|
if [ -x /opt/kintex/worker/venv/bin/pip ]; then
|
||||||
|
/opt/kintex/worker/venv/bin/pip install -q redis Pillow google-genai 2>/dev/null || log "WARN: 워커 의존성 갱신 스킵"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. 서비스 재기동 ──
|
||||||
|
systemctl restart kintex.service
|
||||||
|
systemctl restart kintex-nanobanana.service 2>/dev/null || log "WARN: 워커 재기동 스킵"
|
||||||
|
|
||||||
|
# ── 5. 헬스체크(백엔드 /health 200 = success:true) — 실패 시 jar 롤백 ──
|
||||||
|
ok=0
|
||||||
|
for i in $(seq 1 20); do
|
||||||
|
sleep 3
|
||||||
|
code=$(curl -s -m 5 -o /dev/null -w '%{http_code}' "http://127.0.0.1:${BACKEND_PORT}/health" || true)
|
||||||
|
if [ "$code" = "200" ]; then log "헬스체크 OK ($code)"; ok=1; break; fi
|
||||||
|
done
|
||||||
|
if [ "$ok" != "1" ]; then
|
||||||
|
log "헬스체크 실패 — jar 롤백 시도"
|
||||||
|
if [ -f "${APP_JAR}.bak" ]; then cp "${APP_JAR}.bak" "$APP_JAR"; systemctl restart kintex.service; fi
|
||||||
|
systemctl is-active kintex.service || true
|
||||||
|
die "배포 헬스체크 실패(롤백 수행)"
|
||||||
|
fi
|
||||||
|
log "배포 완료 — 백엔드 8021 UP, 워커 재기동"
|
||||||
46
deploy/deploy_server_kintex_block.py
Normal file
46
deploy/deploy_server_kintex_block.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# KINTEX 블록 — 서버 /opt/zioinfo/deploy_server.py 의 _deploy() 체인에 삽입되는 정본 사본.
|
||||||
|
# (deploy_server.py 는 루트 인프라 파일이므로 kintex 리포엔 이 참조 사본만 둔다. 서버 사본이 실행 권위.)
|
||||||
|
# 삽입 위치: 마지막 `elif repo == "uiws":` 블록 다음, `logging.info(f"=== {repo} 배포 완료 ===")` 앞.
|
||||||
|
# Gitea owner=zio, 기본 브랜치=main, 백엔드 포트 8021.
|
||||||
|
|
||||||
|
'''
|
||||||
|
elif repo == "kintex":
|
||||||
|
# 분리형 배포: 프론트 dist(Vite) → /var/www/kintex, 백엔드 bootJar(Gradle) → /opt/kintex/app/app.jar,
|
||||||
|
# kintex.service + kintex-nanobanana.service(나노바나나 워커, GEMINI 키는 워커 env 전용) 재기동.
|
||||||
|
# Flyway baseline-on-migrate=true → 기동 시 스키마 자동 적용. deploy_kintex.sh 가 검증→원자교체→헬스체크→롤백.
|
||||||
|
SRC = "/opt/kintex/src"
|
||||||
|
ok = run_steps(repo, [
|
||||||
|
("git pull", ["bash", "-c",
|
||||||
|
f"if [ -d {SRC}/.git ]; then "
|
||||||
|
f"git -C {SRC} fetch origin main && git -C {SRC} reset --hard origin/main; "
|
||||||
|
f"else "
|
||||||
|
f"[ -e {SRC} ] && mv {SRC} {SRC}.bak_$(date +%Y%m%d%H%M%S); "
|
||||||
|
f"git clone 'http://zio:1q2w3e%21Q@127.0.0.1:9003/zio/kintex.git' {SRC}; "
|
||||||
|
f"fi"]),
|
||||||
|
("npm build", ["bash", "-c",
|
||||||
|
f"cd {SRC}/src/frontend && (npm ci 2>/dev/null || npm install) && "
|
||||||
|
f"NODE_OPTIONS=--max-old-space-size=4096 npm run build"]),
|
||||||
|
("gradle build", ["bash", "-c",
|
||||||
|
f"cd {SRC}/src/backend && chmod +x gradlew 2>/dev/null; sed -i 's/\\r$//' gradlew 2>/dev/null; "
|
||||||
|
f"./gradlew clean bootJar -x test -q --no-daemon > /tmp/kintex_gradle.log 2>&1; "
|
||||||
|
f"rc=$?; tail -8 /tmp/kintex_gradle.log; exit $rc"]),
|
||||||
|
("verify jar", ["bash", "-c",
|
||||||
|
f"JAR=$(ls -1 {SRC}/src/backend/build/libs/kintex-backend-*.jar 2>/dev/null | grep -v plain | head -1); "
|
||||||
|
f"[ -n \"$JAR\" ] || {{ echo 'jar not found'; exit 1; }}; "
|
||||||
|
f"unzip -l \"$JAR\" | grep -q 'BOOT-INF/classes/com/zioinfo/kintex/KintexApplication.class' "
|
||||||
|
f"|| {{ echo 'invalid jar'; exit 1; }}; echo \"jar OK: $JAR\""]),
|
||||||
|
("deploy (deploy_kintex.sh)", ["bash", "-c",
|
||||||
|
f"sed -i 's/\\r$//' {SRC}/deploy/*.sh 2>/dev/null; "
|
||||||
|
f"bash {SRC}/deploy/deploy_kintex.sh {SRC}"]),
|
||||||
|
("health check", ["bash", "-c",
|
||||||
|
"for i in $(seq 1 20); do sleep 3; "
|
||||||
|
"code=$(curl -s -m 5 -o /dev/null -w '%{http_code}' http://127.0.0.1:8021/health); "
|
||||||
|
"if [ \"$code\" = '200' ]; then echo \"health OK ($code)\"; exit 0; fi; "
|
||||||
|
"done; echo 'health FAIL'; systemctl is-active kintex.service 2>/dev/null; exit 1"]),
|
||||||
|
])
|
||||||
|
if ok:
|
||||||
|
notify_itsm(True, "✅ kintex 배포 완료 (kintex.zioinfo.kr, 포트 8021)")
|
||||||
|
trigger_jenkins("kintex")
|
||||||
|
else:
|
||||||
|
notify_itsm(False, "❌ kintex 빌드/배포 실패")
|
||||||
|
'''
|
||||||
50
deploy/nginx/kintex.zioinfo.kr.conf
Normal file
50
deploy/nginx/kintex.zioinfo.kr.conf
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# KINTEX 자동전시시스템 — nginx vhost (개발: kintex.zioinfo.kr → 101.79.17.164)
|
||||||
|
# / → /var/www/kintex (React/Vite SPA, try_files 폴백)
|
||||||
|
# /api/, /health → 127.0.0.1:8021 (Spring Boot 백엔드)
|
||||||
|
# /ws → 127.0.0.1:8021 (STOMP/WebSocket 업그레이드)
|
||||||
|
# TLS(443)는 certbot --nginx -d kintex.zioinfo.kr 로 후속 주입(80→443 리다이렉트 자동).
|
||||||
|
# 운영 도메인 kintex.wise.ai.kr 은 server_name 추가 또는 별도 vhost 로 후속 구성.
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name kintex.zioinfo.kr;
|
||||||
|
|
||||||
|
root /var/www/kintex;
|
||||||
|
index index.html;
|
||||||
|
client_max_body_size 50m;
|
||||||
|
|
||||||
|
access_log /var/log/nginx/kintex.access.log;
|
||||||
|
error_log /var/log/nginx/kintex.error.log;
|
||||||
|
|
||||||
|
# 백엔드 API
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8021;
|
||||||
|
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_read_timeout 120s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 헬스체크(공개)
|
||||||
|
location = /health {
|
||||||
|
proxy_pass http://127.0.0.1:8021/health;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebSocket(STOMP) — RenderJob 완료·승인 이벤트 실시간
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://127.0.0.1:8021;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 프론트 SPA — 정적 서빙 + 클라이언트 라우팅 폴백
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
85
deploy/setup_kintex_service.py
Normal file
85
deploy/setup_kintex_service.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""KINTEX 서버 프로비저닝(멱등) — systemd 유닛·디렉터리·워커 venv·nginx vhost 설치.
|
||||||
|
|
||||||
|
DB(kintex_db+PostGIS)·시크릿 env 는 provision.sh(별도, 시크릿 미출력)가 담당한다.
|
||||||
|
이 스크립트는 "코드로 유지되는" 서비스 구성만 다룬다(시크릿 없음 → 리포 커밋 가능).
|
||||||
|
|
||||||
|
전제: /opt/kintex/kintex.env·/opt/kintex/nanobanana.env 가 이미 존재(provision.sh 산출, root 600).
|
||||||
|
사용:
|
||||||
|
sudo python3 deploy/setup_kintex_service.py # 유닛/디렉터리/venv/nginx 설치
|
||||||
|
sudo python3 deploy/setup_kintex_service.py --no-nginx # nginx 제외
|
||||||
|
|
||||||
|
배정 사실(2026-07-11 서버 survey):
|
||||||
|
- 백엔드 포트 8021 (8003~8020 = 기존 GUARDiA 솔루션 예약, 8020=guardia-rag)
|
||||||
|
- 개발 도메인 kintex.zioinfo.kr → 101.79.17.164
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
KINTEX_ROOT = "/opt/kintex"
|
||||||
|
SRC = f"{KINTEX_ROOT}/src"
|
||||||
|
WEB_ROOT = "/var/www/kintex"
|
||||||
|
VENV = f"{KINTEX_ROOT}/worker/venv"
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def sh(cmd, check=True):
|
||||||
|
print(f"[setup] $ {cmd}")
|
||||||
|
r = subprocess.run(cmd, shell=True, text=True)
|
||||||
|
if check and r.returncode != 0:
|
||||||
|
sys.exit(f"[setup] 실패(rc={r.returncode}): {cmd}")
|
||||||
|
return r.returncode
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--no-nginx", action="store_true")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
sys.exit("[setup] root 로 실행하세요(sudo).")
|
||||||
|
|
||||||
|
# 1. 디렉터리
|
||||||
|
for d in (f"{KINTEX_ROOT}/app", SRC, f"{KINTEX_ROOT}/worker",
|
||||||
|
f"{KINTEX_ROOT}/output/visualizations", WEB_ROOT, "/var/log/kintex"):
|
||||||
|
os.makedirs(d, exist_ok=True)
|
||||||
|
|
||||||
|
# 2. deploy_kintex.sh 설치(서버 표준 위치)
|
||||||
|
sh(f"install -m 755 {HERE}/deploy_kintex.sh {KINTEX_ROOT}/deploy_kintex.sh")
|
||||||
|
|
||||||
|
# 3. 워커 venv + 의존성(google-genai·Pillow·redis)
|
||||||
|
if not os.path.exists(f"{VENV}/bin/python"):
|
||||||
|
sh(f"python3 -m venv {VENV}")
|
||||||
|
sh(f"{VENV}/bin/pip install -q --upgrade pip")
|
||||||
|
sh(f"{VENV}/bin/pip install -q redis Pillow google-genai")
|
||||||
|
|
||||||
|
# 4. systemd 유닛 설치
|
||||||
|
for unit in ("kintex.service", "kintex-nanobanana.service"):
|
||||||
|
src = f"{HERE}/systemd/{unit}"
|
||||||
|
shutil.copyfile(src, f"/etc/systemd/system/{unit}")
|
||||||
|
os.chmod(f"/etc/systemd/system/{unit}", 0o644)
|
||||||
|
print(f"[setup] 유닛 설치: {unit}")
|
||||||
|
sh("systemctl daemon-reload")
|
||||||
|
sh("systemctl enable kintex.service kintex-nanobanana.service", check=False)
|
||||||
|
|
||||||
|
# 5. nginx vhost
|
||||||
|
if not args.no_nginx:
|
||||||
|
conf = "kintex.zioinfo.kr.conf"
|
||||||
|
shutil.copyfile(f"{HERE}/nginx/{conf}", f"/etc/nginx/sites-available/{conf}")
|
||||||
|
link = f"/etc/nginx/sites-enabled/{conf}"
|
||||||
|
if not os.path.islink(link) and not os.path.exists(link):
|
||||||
|
os.symlink(f"/etc/nginx/sites-available/{conf}", link)
|
||||||
|
if sh("nginx -t", check=False) == 0:
|
||||||
|
sh("systemctl reload nginx")
|
||||||
|
else:
|
||||||
|
print("[setup] WARN: nginx -t 실패 — vhost 반영 보류(타 사이트 무영향)")
|
||||||
|
|
||||||
|
print("[setup] 완료. env(시크릿)는 provision.sh 산출물 사용. "
|
||||||
|
"서비스 기동은 배포(deploy_kintex.sh)에서 jar 배치 후 수행.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
22
deploy/systemd/kintex-nanobanana.service
Normal file
22
deploy/systemd/kintex-nanobanana.service
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
# KINTEX 나노바나나 렌더 워커 — Redis 큐(kintex:renderjob:queue) 소비 → Gemini 이미지 생성.
|
||||||
|
# GEMINI_API_KEY 는 이 유닛의 EnvironmentFile(/opt/kintex/nanobanana.env, root 600)에서만 로드.
|
||||||
|
# 백엔드·타 서비스에 미노출. G1 승인(2026-07-11)으로 NANOBANANA_LIVE=1(라이브 생성).
|
||||||
|
Description=KINTEX Nanobanana Render Worker (Gemini)
|
||||||
|
After=network.target redis-server.service kintex.service
|
||||||
|
Wants=redis-server.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
# `python -m tools.nanobanana.worker` 는 소스 루트(/opt/kintex/src)를 CWD·sys.path 로 요구.
|
||||||
|
WorkingDirectory=/opt/kintex/src
|
||||||
|
EnvironmentFile=/opt/kintex/nanobanana.env
|
||||||
|
ExecStart=/opt/kintex/worker/venv/bin/python -m tools.nanobanana.worker
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=append:/var/log/kintex/worker.log
|
||||||
|
StandardError=append:/var/log/kintex/worker.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
22
deploy/systemd/kintex.service
Normal file
22
deploy/systemd/kintex.service
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
# KINTEX 자동전시시스템 — 백엔드(Spring Boot 3.2.5, 포트 8021)
|
||||||
|
# 시크릿은 EnvironmentFile(/opt/kintex/kintex.env, root 600)로만 주입. GEMINI 키는 여기 없음(워커 전용).
|
||||||
|
Description=KINTEX AI Exhibition Backend (Spring Boot)
|
||||||
|
After=network.target postgresql.service redis-server.service
|
||||||
|
Wants=redis-server.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/opt/kintex/app
|
||||||
|
EnvironmentFile=/opt/kintex/kintex.env
|
||||||
|
# JDK21로 Java17 타깃 bootJar 실행(Spring Boot 3.2.5 호환). 힙은 공유 서버 보호로 상한.
|
||||||
|
ExecStart=/usr/bin/java -Xms128m -Xmx512m -jar /opt/kintex/app/app.jar
|
||||||
|
SuccessExitStatus=143
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=append:/var/log/kintex/backend.log
|
||||||
|
StandardError=append:/var/log/kintex/backend.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Loading…
Reference in New Issue
Block a user