ci(itms): CI/CD 배포 아티팩트 추가 (적용 대기·산출물 준비형)

zioinfo.co.kr 피저빌리티 게이트 결과 ITMS 실기동(Tier2)은 이 환경 불가
(런타임 DB MariaDB/Oracle 도달 불가 + JDK17 부재 + Boot2.6.2 Java21 미지원)
→ 서버 미변경, 배포 대상 환경에서 적용할 준비된 산출물만 생성.

- deploy/deploy_server_itms_block.py: deploy_server.py 에 붙일 itms 블록
  (Tier1 빌드+커밋별 아카이브; Tier2 재기동은 주석 처리, 기존 블록 불변, 자격증명 env 주입)
- deploy/build.sh: common 선행 → 5모듈 bootWar (JDK17)
- deploy/systemd/itms-{auth,api,front,batch,datatrans}.service: 4포트 WAR+데몬 유닛 템플릿
- deploy/env/itms.env.example: SPRING_PROFILES_ACTIVE 등 (실 시크릿 미포함)
- deploy/gitea-webhook.md: webhook 3대 함정(url·secret·ALLOWED_HOST_LIST) 반영 생성 가이드(미실행)
- deploy/README.md: 특수성·적용 절차·피저빌리티 근거

보안: AES 암호문·DB 접속정보·NCP 키·Gitea 비번 하드코딩 0(env 주입). 기존 단일-jar 배포 무영향.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
itms-merge-dev 2026-07-17 21:08:59 +09:00
parent 53a7891aee
commit 08e634c3a5
10 changed files with 227 additions and 0 deletions

48
deploy/README.md Normal file
View File

@ -0,0 +1,48 @@
# ITMS CI/CD 배포 아티팩트 (적용 대기)
> 생성: 2026-07-17 (itms-cicd 하네스, 산출물 준비형) | 대상 저장소: Gitea `ythong/itms`
> **상태: 적용 대기(NOT applied).** 이 디렉터리는 배포 대상 환경에서 적용할 준비된 산출물이다. 서버는 변경되지 않았다.
## 왜 "적용 대기"인가 (피저빌리티 게이트)
GUARDiA 인프라 서버(zioinfo.co.kr)에서 실측한 결과, **ITMS 앱의 실기동(Tier2)은 이 환경에서 불가**하다:
- api/batch/datatrans 런타임 DB(MariaDB `ur_itms_db`·Oracle XE)가 GUARDiA 인프라에서 **도달 불가**(고객사 전용망 DB).
- 서버 런타임 JDK가 **21뿐**(JDK17 부재). ITMS는 Boot 2.6.2로 **Java 21 미지원**.
따라서 실배포는 **고객사 배포 환경**(대상 기관 DB망 + 기관 프로파일 시크릿 + JDK17)에서 아래 아티팩트를 적용해 수행한다. Tier1(빌드 자동화)만 별도로 쓰려면 빌드 서버에 JDK17을 두고 `deploy_server_itms_block.py`의 빌드 단계만 사용한다.
## 구성 특수성
ITMS는 단일-jar가 아니라 **4포트 독립 WAR 스택 + 데몬**:
| 모듈 | 포트 | 산출물 | 런타임 의존 | systemd |
|---|---|---|---|---|
| auth | 11000 | bootWar | (RSA jwtkey) | itms-auth.service |
| api | 11010 | bootWar | MariaDB ur_itms_db | itms-api.service |
| front | 11020 | bootWar | DB 무접속(REST만) | itms-front.service |
| batch | 11030 | bootWar | MariaDB + Oracle XE + 메일/알림톡 | itms-batch.service |
| datatrans | (데몬) | bootWar | kccfnova/uac_cltr_db + NAS | itms-datatrans.service |
| common | — | 라이브러리(선행 빌드) | — | — |
| legacy/nlib | — | Maven·Java8, **CI 제외** | — | — |
## 파일
| 파일 | 용도 |
|---|---|
| `deploy_server_itms_block.py` | `scripts/deploy/deploy_server.py``_deploy()`에 붙여넣을 `elif repo == "itms":` 블록(Tier1 빌드+아카이브; 기존 블록 불변) |
| `build.sh` | 모듈별 gradle 빌드(common 선행 → 5모듈 bootWar) — JDK17 필요 |
| `systemd/itms-*.service` | 모듈별 systemd 유닛 템플릿(Tier2, 활성화 조건 충족 시) |
| `env/itms-*.env.example` | 모듈별 env 템플릿(`SPRING_PROFILES_ACTIVE`=기관) — **실 시크릿은 여기 넣지 말고 서버 로컬에서 관리** |
| `gitea-webhook.md` | Gitea `ythong/itms` push webhook 생성 가이드 + 스크립트(미실행) |
## 적용 절차 (고객사/배포 대상 환경)
1. **선결:** JDK17 설치, 런타임 DB(MariaDB/Oracle) 연결, 기관 프로파일 시크릿(keystore·yml) 배치.
2. **CI:** `deploy_server_itms_block.py`를 배포 서버 deploy_server.py에 병합 → deploy 서비스 재시작. Gitea webhook 생성(`gitea-webhook.md`).
3. **빌드:** `build.sh`(또는 deploy 블록)로 `common` 선행 → 5모듈 bootWar. 산출물 `*/build/libs/*.war`.
4. **CD(Tier2):** `env/itms-<module>.env``SPRING_PROFILES_ACTIVE=<기관>` 설정 → `systemd/itms-*.service` 배치 → `systemctl daemon-reload && enable --now`.
5. **헬스체크:** 11000/11010/11020 도달, batch/datatrans 로그 라이브니스.
## 보안 불변
- 이 디렉터리 어떤 파일에도 AES 암호문·DB 접속정보·NCP/카카오 키·Gitea 비번·SSH 계정을 **하드코딩 금지**. 자격증명은 env/서버 로컬에서만.
- force push 금지. 기존 단일-jar 솔루션 배포에 무영향(deploy_server는 itms 블록만 추가).

21
deploy/build.sh Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# ITMS 모듈별 빌드 (JDK17 필요). common 선행 → 5모듈 bootWar. legacy/nlib 제외.
# 사용: JAVA_HOME=/path/to/jdk17 deploy/build.sh (repo 루트에서 실행)
set -euo pipefail
cd "$(dirname "$0")/.."
if [ -z "${JAVA_HOME:-}" ]; then
echo "WARN: JAVA_HOME 미설정 — JDK17 경로를 지정하세요 (ITMS 는 Boot 2.6.2·Java17)." >&2
fi
echo "== gradle: common build + 5모듈 bootWar =="
./gradlew --no-daemon \
:common:build \
:auth:bootWar :api:bootWar :front:bootWar :batch:bootWar :datatrans:bootWar \
-x test
echo "== 산출물 =="
for m in auth api front batch datatrans; do
ls -1 "$m"/build/libs/*.war 2>/dev/null || echo " ($m: war 없음)"
done
echo "완료. WAR 를 systemd(itms-*.service)로 기동하려면 런타임 DB·기관 프로파일 시크릿 확보 필요."

View File

@ -0,0 +1,50 @@
# =====================================================================
# ITMS CI/CD 블록 — scripts/deploy/deploy_server.py 의 _deploy(self, repo) 에
# 기존 elif 체인 말미에 그대로 붙여넣는다. (기존 블록은 변경하지 않는다.)
#
# 특수성: ITMS 는 Gradle 멀티모듈(Boot 2.6.2·Java17) 4포트 WAR 스택.
# - Tier1(빌드 자동화): common 선행 → 5모듈 bootWar → 커밋별 아카이브. DB 불필요.
# - Tier2(기동): 런타임 DB(MariaDB/Oracle)·기관 시크릿·JDK17 확보 환경에서만
# 아래 (restart ...) 단계 주석을 해제하여 systemd 재기동. 기본은 빌드+아카이브까지.
#
# 선결: 배포 서버에 JDK17 설치(예: /usr/lib/jvm/java-17-openjdk-amd64) 후 JAVA17 경로 지정.
# 자격증명: Gitea 클론 URL 의 사용자/토큰은 서버 환경변수로 주입한다(하드코딩 금지).
# 예) 서버 deploy_server 환경에 GITEA_CLONE_CRED='user:token' 를 두고 아래 f-string 에서 사용.
# =====================================================================
elif repo == "itms":
SRC = "/opt/itms/repo"
ARTIF = "/opt/itms/artifacts"
JAVA17 = "/usr/lib/jvm/java-17-openjdk-amd64" # 배포 대상 환경의 JDK17 경로로 수정
# GITEA_CLONE_CRED 는 서버 env 에서 주입(예: 'ythong:<token>'). 코드/커밋에 비번 금지.
CRED = os.environ.get("GITEA_CLONE_CRED", "") # 미설정 시 기존 리모트/캐시 사용
CLONE = (f"git clone 'http://{CRED}@127.0.0.1:9003/ythong/itms.git' {SRC}"
if CRED else f"echo 'GITEA_CLONE_CRED 미설정 — 기존 {SRC} 사용' ")
ok = run_steps(repo, [
("git pull", ["bash", "-c",
f"[ -d {SRC}/.git ] && git -C {SRC} fetch origin main && git -C {SRC} reset --hard origin/main"
f" || ( mkdir -p /opt/itms && {CLONE} )"]),
# common 선행 + 5모듈 bootWar (JDK17 지정). legacy/nlib 제외.
("gradle build", ["bash", "-c",
f"cd {SRC} && JAVA_HOME={JAVA17} ./gradlew --no-daemon "
f":common:build :auth:bootWar :api:bootWar :front:bootWar :batch:bootWar :datatrans:bootWar "
f"-x test"]),
# 커밋 해시별 산출물 아카이브(롤백 대비)
("archive war", ["bash", "-c",
f"H=$(git -C {SRC} rev-parse --short HEAD); D={ARTIF}/$H; mkdir -p $D; "
f"for m in auth api front batch datatrans; do "
f"cp {SRC}/$m/build/libs/*.war $D/itms-$m.war 2>/dev/null || true; done; "
f"ln -sfn $D {ARTIF}/current; ls -l {ARTIF}/current"]),
# --- Tier2 (기동): DB/시크릿/JDK17 확보 환경에서만 주석 해제 ---
# ("restart auth", ["bash","-c","systemctl restart itms-auth 2>/dev/null || true"]),
# ("restart api", ["bash","-c","systemctl restart itms-api 2>/dev/null || true"]),
# ("restart front", ["bash","-c","systemctl restart itms-front 2>/dev/null || true"]),
# ("restart batch", ["bash","-c","systemctl restart itms-batch 2>/dev/null || true"]),
# ("restart datatrans", ["bash","-c","systemctl restart itms-datatrans 2>/dev/null || true"]),
# ("health check", ["bash","-c","sleep 6 && for p in 11000 11010 11020; do "
# "curl -sf -o /dev/null http://127.0.0.1:$p/ && echo \"$p UP\" || echo \"$p DOWN\"; done"]),
])
if ok:
notify_itsm(True, "✅ itms 빌드/아카이브 완료 (Tier1)")
else:
notify_itsm(False, "❌ itms 빌드 실패")

7
deploy/env/itms.env.example vendored Normal file
View File

@ -0,0 +1,7 @@
# ITMS 모듈 env 템플릿 — /etc/itms/itms-<module>.env 로 복사해 사용.
# 실 시크릿(DB 접속정보·keystore 비번·NCP 키)은 여기 넣지 말고 프로파일 yml/keystore + 서버 로컬로 관리.
# 기관 프로파일 선택(파일·키 불변): 배포 대상 기관명으로 지정.
SPRING_PROFILES_ACTIVE=local
# JVM 옵션(선택)
JAVA_OPTS=-Xms256m -Xmx512m
# 필요 시 추가 환경변수(예: 외부 keystore 경로)를 여기에. 비밀번호 평문 금지.

31
deploy/gitea-webhook.md Normal file
View File

@ -0,0 +1,31 @@
# Gitea webhook — ythong/itms (적용 대기)
push 시 배포 서버 deploy_server(port 9999)로 알리는 webhook. **아직 생성하지 않음.**
## 3대 함정 (GUARDiA 기지 교훈 — 반드시 준수)
1. **url**: NAT 헤어핀 회피 위해 `http://127.0.0.1:9999/`(localhost) 로 지정.
2. **secret**: 배포 서버 deploy_server 의 검증 secret 과 **일치**시킬 것(불일치 시 403). 값은 서버 운영 시크릿 — 여기에 적지 말 것.
3. **ALLOWED_HOST_LIST**: Gitea app.ini `[webhook] ALLOWED_HOST_LIST` 에 loopback 포함(없으면 localhost 웹훅 차단).
## 생성 (배포 대상 환경에서 실행; 자격증명·secret 은 env 로 주입)
```bash
# GITEA_TOKEN, DEPLOY_SECRET 를 환경변수로 주입(하드코딩 금지)
curl -s -X POST "https://git.zioinfo.co.kr/api/v1/repos/ythong/itms/hooks" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"type": "gitea",
"active": true,
"events": ["push"],
"config": {
"url": "http://127.0.0.1:9999/",
"content_type": "json",
"secret": "'"${DEPLOY_SECRET}"'"
}
}'
```
- branch 필터가 필요하면 deploy_server 측에서 `ref == refs/heads/main` 확인(현행 deploy_server 는 repo 이름으로 분기).
- 생성 후 Gitea UI 의 "Test Delivery" 로 발화 → deploy_server 로그에서 `repo=itms` 수신 확인.
## deploy_server 반영
`deploy_server_itms_block.py` 를 배포 서버 `deploy_server.py``_deploy()` elif 체인에 병합 → 서버 사본 반영 → deploy 서비스 재시작. (교훈: 서버 사본 미반영/미재시작 시 웹훅 로그 '완료'여도 1ms no-op = 블록 부재.)

View File

@ -0,0 +1,14 @@
[Unit]
Description=ITMS Api (OAuth2 resource server, :11010)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/itms/itms-api.env
ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java -jar /opt/itms/artifacts/current/itms-api.war
Restart=on-failure
RestartSec=5
User=itms
Group=itms
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=ITMS Auth (OAuth2 authorization server, :11000)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/itms/itms-auth.env
ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java -jar /opt/itms/artifacts/current/itms-auth.war
Restart=on-failure
RestartSec=5
User=itms
Group=itms
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=ITMS Batch (Quartz clustered, :11030)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/itms/itms-batch.env
ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java -jar /opt/itms/artifacts/current/itms-batch.war
Restart=on-failure
RestartSec=8
User=itms
Group=itms
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=ITMS DataTrans (NCMS->IAMS ETL @Scheduled daemon)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/itms/itms-datatrans.env
ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java -jar /opt/itms/artifacts/current/itms-datatrans.war
Restart=on-failure
RestartSec=10
User=itms
Group=itms
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=ITMS Front (JSP/Tiles web tier, :11020)
After=network-online.target itms-auth.service itms-api.service
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/itms/itms-front.env
ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java -jar /opt/itms/artifacts/current/itms-front.war
Restart=on-failure
RestartSec=5
User=itms
Group=itms
[Install]
WantedBy=multi-user.target