You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
4.6 KiB
108 lines
4.6 KiB
// slack-notifier CI/CD |
|
// golang 컨테이너에서 빌드·테스트 → EC2(systemd)로 단일 정적 바이너리 배포. |
|
// |
|
// 필요한 Jenkins 구성 (자세한 건 deploy/README.md): |
|
// - 플러그인: Go Plugin, SSH Agent |
|
// - Global Tool Configuration → Go 에 1.26 등록(이름 = GO_TOOL 값과 일치) |
|
// - 자격증명: "SSH Username with private key" 를 만들고 ID 를 SSH_CRED_ID 와 일치시킴 (EC2 접속 키) |
|
// - 대상 EC2 1회 셋업: /opt/slack-notifier, .env.prod, systemd 서비스, Caddy (deploy/README.md) |
|
// |
|
// 호스트/유저/아키텍처는 파라미터로 조정. main 브랜치 + DEPLOY_HOST 지정 시에만 배포. |
|
|
|
pipeline { |
|
agent none |
|
|
|
options { |
|
timestamps() |
|
disableConcurrentBuilds() |
|
timeout(time: 20, unit: 'MINUTES') |
|
} |
|
|
|
parameters { |
|
// parameters 를 바꿀 때마다 이 기본값과 environment.PARAMS_VERSION 을 함께 +1 한다. |
|
// (파라미터는 1빌드 늦게 등록되므로, 아래 Guard 가 stale 빌드를 막는다) |
|
string(name: 'PARAMS_VERSION', defaultValue: '1', description: '파라미터 동기화 가드용 — 직접 바꾸지 말 것') |
|
string(name: 'DEPLOY_HOST', defaultValue: '', description: 'EC2 호스트(DNS 또는 IP). 비우면 배포 스킵(빌드/테스트만).') |
|
string(name: 'DEPLOY_USER', defaultValue: 'ubuntu', description: 'SSH 사용자 (Ubuntu=ubuntu, Amazon Linux=ec2-user)') |
|
string(name: 'APP_DIR', defaultValue: '/opt/slack-notifier', description: '원격 설치 경로') |
|
choice(name: 'ARCH', choices: ['arm64', 'amd64'], description: 'EC2 아키텍처 (Graviton/t4g=arm64, x86=amd64)') |
|
} |
|
|
|
environment { |
|
SSH_CRED_ID = 'slack-notifier-ec2' // Jenkins SSH 자격증명 ID (실제 ID로 교체 가능) |
|
BIN = 'slack-notifier' |
|
GOFLAGS = '-buildvcs=false' // .git 소유권 경고 회피 |
|
PARAMS_VERSION = '1' // ← parameters 변경 시 위 기본값과 함께 +1 |
|
} |
|
|
|
stages { |
|
// 파라미터 정의가 갱신되기 전(=stale)에 빌드/배포되는 것을 막는다. |
|
// Jenkinsfile 의 step/environment 는 즉시 반영되지만 parameters 는 1빌드 늦게 등록되므로, |
|
// "현재 코드의 PARAMS_VERSION(즉시 반영)" 과 "등록된 params.PARAMS_VERSION(지난 등록)" 이 다르면 중단. |
|
stage('Guard: params sync') { |
|
agent any |
|
steps { |
|
script { |
|
if (params.PARAMS_VERSION != env.PARAMS_VERSION) { |
|
error("파라미터가 아직 갱신되지 않음(stale): 등록=${params.PARAMS_VERSION}, 현재=${env.PARAMS_VERSION}. " + |
|
"이 빌드가 파라미터를 재등록했으니, 한 번 더 빌드하면 정상 진행됩니다.") |
|
} |
|
} |
|
} |
|
} |
|
|
|
stage('Build & Test') { |
|
agent any |
|
// 'go-1.26' = Global Tool Configuration 의 Go 도구 이름(다르면 여기와 일치시킬 것). 컨테이너 없음 — 경량. |
|
tools { go 'go-1.26.4' } |
|
steps { |
|
sh 'go version' |
|
sh 'go vet ./...' |
|
sh 'go test ./...' |
|
sh "CGO_ENABLED=0 GOOS=linux GOARCH=${params.ARCH} go build -ldflags='-s -w' -o ${BIN} ." |
|
sh "ls -lh ${BIN}" |
|
stash name: 'binary', includes: "${BIN}" |
|
} |
|
} |
|
|
|
stage('Deploy') { |
|
when { |
|
allOf { |
|
branch 'main' |
|
expression { return params.DEPLOY_HOST?.trim() } |
|
} |
|
} |
|
agent any |
|
steps { |
|
unstash 'binary' |
|
sshagent(credentials: [env.SSH_CRED_ID]) { |
|
sh ''' |
|
set -eu |
|
H="${DEPLOY_USER}@${DEPLOY_HOST}" |
|
SSH="ssh -o StrictHostKeyChecking=accept-new" |
|
|
|
# 1) 새 바이너리를 임시 위치로 전송 |
|
scp -o StrictHostKeyChecking=accept-new "${BIN}" "$H:/tmp/${BIN}.new" |
|
|
|
# 2) 원자적 교체 후 재시작. |
|
# 실행 중 바이너리 덮어쓰기는 ETXTBSY 가 나므로, 같은 파일시스템에서 rename(mv) 으로 교체한다. |
|
# (running 프로세스는 옛 inode 를 유지 → 안전) |
|
$SSH "$H" "set -e; \ |
|
sudo mkdir -p '${APP_DIR}' '${APP_DIR}/logs' '${APP_DIR}/data'; \ |
|
sudo mv '/tmp/${BIN}.new' '${APP_DIR}/${BIN}.staged'; \ |
|
sudo chmod 0755 '${APP_DIR}/${BIN}.staged'; \ |
|
sudo mv -f '${APP_DIR}/${BIN}.staged' '${APP_DIR}/${BIN}'; \ |
|
sudo systemctl restart slack-notifier; \ |
|
sleep 1; \ |
|
sudo systemctl is-active slack-notifier" |
|
''' |
|
} |
|
} |
|
} |
|
} |
|
|
|
post { |
|
success { echo "✅ ${env.BRANCH_NAME} 빌드 성공" } |
|
failure { echo "❌ 빌드 실패 — 콘솔 로그 확인" } |
|
} |
|
}
|
|
|