feat : war cd

master
Kurt92 5 months ago
parent ca2e042a96
commit 16553744c9

@ -3,7 +3,6 @@
### 연계 디비폴링, 프로그램 업데이트를 위한 워커
기존 델파이 데몬을 스프링 스케쥴러로 대체한다.
localhost:XXXX을 통한 로그 확인

@ -62,6 +62,9 @@ dependencies {
// === QueryDsl end ===
// sftp
implementation 'com.jcraft:jsch:0.1.55'
}

@ -1,4 +1,4 @@
package com.worker.scheduler.smg.sechdule;
package com.worker.scheduler.smg.schedule;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

@ -0,0 +1,155 @@
package com.worker.scheduler.update.schedule;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.file.*;
import java.util.Properties;
@Slf4j
@Component
public class WarSyncScheduler {
// OS 확인
private final String os = System.getProperty("os.name").toLowerCase();
private final boolean isWindows = os.contains("win");
// SFTP 설정
private final String SFTP_HOST = "211.119.124.103";
private final int SFTP_PORT = 1922;
private final String SFTP_USER = "cc-war";
private final String SFTP_PASS = "xit5811807!";
// private final String REMOTE_CC_WAR_PATH = "D:\\prod-cc\\deploy\\clean-parking-boot.war";
private final String REMOTE_CC_WAR_PATH = "/deploy/clean-parking-boot.war";
// 경로 설정
private final Path LOCAL_WAR_PATH = isWindows
? Paths.get("D://prod-cc/deploy/clean-parking-boot.war")
: Paths.get("/opt/prod-cc/deploy/clean-parking-boot.war");
private final Path LOCAL_TMP_PATH = isWindows
? Paths.get("D://prod-cc/tmp/clean-parking-boot.war")
: Paths.get("/opt/prod-cc/tmp/clean-parking-boot.war");
private final Path LoCAL_BACKUP_PATH = isWindows
? Paths.get("D://prod-cc/backup/clean-parking-boot.war")
: Paths.get("/opt/prod-cc/backup/clean-parking-boot.war");
private final Path META_FILE_PATH = isWindows
? Paths.get("D://prod-cc/meta/clean-parking.war.meta")
: Paths.get("/opt/prod-cc/meta/clean-parking.war.meta");
private final Path PID_FILE_PATH = isWindows
? Paths.get("D://prod-cc/deploy/clean-parking.pid")
: Paths.get("/opt/prod-cc/deploy/clean-parking.pid");
private final File LOG_FILE = isWindows
? new File("D://prod-cc/logs/clean-parking.log")
: new File("/opt/prod-cc/logs/clean-parking.log");
@Scheduled(fixedRate = 5 * 60 * 1000) // 5분
// @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시
public void checkAndDeploy() {
log.info("[배포] SFTP에서 .war 변경 여부 확인 시작");
Session session = null;
ChannelSftp sftp = null;
try {
// SFTP 연결
JSch jsch = new JSch();
session = jsch.getSession(SFTP_USER, SFTP_HOST, SFTP_PORT);
session.setPassword(SFTP_PASS);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
// 리모트 파일 속성
SftpATTRS attrs = sftp.lstat(REMOTE_CC_WAR_PATH);
long remoteSize = attrs.getSize();
int remoteMtime = attrs.getMTime();
// 메타파일 비교
boolean isUpdated = isWarUpdated(remoteSize, remoteMtime);
if (!isUpdated) {
log.info("[배포] 변경 없음. 스킵");
return;
}
// 다운로드
try (InputStream in = sftp.get(REMOTE_CC_WAR_PATH)) {
Files.copy(in, LOCAL_TMP_PATH, StandardCopyOption.REPLACE_EXISTING);
log.info("[배포] SFTP 다운로드 완료");
}
// 실행 중이면 종료
killPreviousWar();
// 교체
Files.move(LOCAL_TMP_PATH, LOCAL_WAR_PATH, StandardCopyOption.REPLACE_EXISTING);
log.info("[배포] WAR 파일 교체 완료");
// 실행
runWar();
log.info("[배포] WAR 실행 완료");
// 메타 저장
String meta = remoteSize + "," + remoteMtime;
Files.writeString(META_FILE_PATH, meta);
log.info("[배포] 메타 저장 완료");
} catch (Exception e) {
log.error("[배포] 실패", e);
} finally {
if (sftp != null) sftp.disconnect();
if (session != null) session.disconnect();
}
}
private boolean isWarUpdated(long remoteSize, int remoteMtime) throws IOException {
if (!Files.exists(META_FILE_PATH)) return true;
String[] parts = Files.readString(META_FILE_PATH).trim().split(",");
long prevSize = Long.parseLong(parts[0]);
int prevMtime = Integer.parseInt(parts[1]);
return (remoteSize != prevSize || remoteMtime != prevMtime);
}
private void killPreviousWar() {
try {
if (!Files.exists(PID_FILE_PATH)) return;
long pid = Long.parseLong(Files.readString(PID_FILE_PATH).trim());
String[] cmd = isWindows
? new String[]{"taskkill", "/F", "/PID", String.valueOf(pid)}
: new String[]{"kill", "-9", String.valueOf(pid)};
new ProcessBuilder(cmd).start();
log.info("[배포] 기존 프로세스 종료 완료");
} catch (Exception e) {
log.warn("[배포] 기존 종료 실패: " + e.getMessage());
}
}
private void runWar() throws IOException {
ProcessBuilder pb = new ProcessBuilder("java", "-jar", LOCAL_WAR_PATH.toString());
pb.directory(LOCAL_WAR_PATH.getParent().toFile());
pb.redirectOutput(LOG_FILE);
pb.redirectErrorStream(true);
Process process = pb.start();
Files.writeString(PID_FILE_PATH, String.valueOf(process.pid()));
log.info("[배포] 새 PID 저장: " + process.pid());
}
}
Loading…
Cancel
Save