admin/front 분리2

This commit is contained in:
2026-07-20 17:49:39 +09:00
parent ed41ef24cb
commit 1eac5db02d
4 changed files with 154 additions and 237 deletions
+138 -170
View File
@@ -6,73 +6,110 @@ pipeline {
disableConcurrentBuilds()
}
parameters {
choice(
name: 'TARGET',
choices: ['admin', 'front', 'all'],
description: '빌드와 배포할 API 서버를 선택합니다.'
)
}
environment {
// ===== Registry =====
REGISTRY_HOST = "registry.pjt.kr"
IMAGE_REPO = "alist/api"
DOCKER_CRED = "registry-pjt"
REGISTRY_HOST = 'registry.pjt.kr'
DOCKER_CRED = 'registry-pjt'
// ===== api ssh =====
SSH_HOST = "121.160.234.222"
SSH_PORT = "2001"
SSH_USER = "bigfuntnp"
SSH_COMPOSE_DIR = "/srv/project/alist/compose"
SSH_COMPOSE_FILE = "api-compose.pjt.yml"
SSH_HOST = '121.160.234.222'
SSH_PORT = '2001'
SSH_USER = 'bigfuntnp'
SSH_COMPOSE_DIR = '/srv/project/alist/compose'
// ===== jenkins workspace path =====
WORK_COMPOSE_DIR = "deploy/pjt/compose"
WORK_COMPOSE_FILE = "api-compose.pjt.yml"
ADMIN_IMAGE_REPO = 'alist/api-admin'
ADMIN_MODULE_DIR = '02-api-admin'
ADMIN_COMPOSE_FILE = 'api-admin-compose.pjt.yml'
ADMIN_ENV_FILE = '/srv/project/alist/env/api-admin/.env'
FRONT_IMAGE_REPO = 'alist/api-front'
FRONT_MODULE_DIR = '03-api-front'
FRONT_COMPOSE_FILE = 'api-front-compose.pjt.yml'
FRONT_ENV_FILE = '/srv/project/alist/env/api-front/.env'
WORK_COMPOSE_DIR = 'deploy/pjt/compose'
}
stages {
stage('1) Git check out') {
steps {
echo "[1/1] Git check out"
checkout scm
}
}
stage('2) Build (Gradle)') {
steps {
sh '''
set -e
echo "[1/1] Gradle build"
chmod +x ./gradlew
./gradlew clean build -x test
'''
}
}
stage('3) ImageTag create') {
stage('2) ImageTag create') {
steps {
script {
echo "[1/1] ImageTag create"
env.SHORT_SHA = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
echo "SHORT_SHA = ${env.SHORT_SHA}"
env.SHORT_SHA = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
env.IMAGE_TAG = "${env.BUILD_NUMBER}-${env.SHORT_SHA}"
echo "TARGET = ${params.TARGET}"
echo "IMAGE_TAG = ${env.IMAGE_TAG}"
}
}
}
stage('4) Docker Build') {
stage('3) Build admin') {
when {
expression { params.TARGET == 'admin' || params.TARGET == 'all' }
}
steps {
sh '''
set -e
echo "[1/1] docker build"
docker build --no-cache -t ${REGISTRY_HOST}/${IMAGE_REPO}:${IMAGE_TAG} .
'''
chmod +x ./gradlew
./gradlew :02-api-admin:bootJar
'''
}
}
stage('5) Docker registry Login & Push') {
stage('4) Build front') {
when {
expression { params.TARGET == 'front' || params.TARGET == 'all' }
}
steps {
sh '''
set -e
chmod +x ./gradlew
./gradlew :03-api-front:bootJar
'''
}
}
stage('5) Docker build admin') {
when {
expression { params.TARGET == 'admin' || params.TARGET == 'all' }
}
steps {
sh '''
set -e
docker build --no-cache \\
-t ${REGISTRY_HOST}/${ADMIN_IMAGE_REPO}:${IMAGE_TAG} \\
${ADMIN_MODULE_DIR}
'''
}
}
stage('6) Docker build front') {
when {
expression { params.TARGET == 'front' || params.TARGET == 'all' }
}
steps {
sh '''
set -e
docker build --no-cache \\
-t ${REGISTRY_HOST}/${FRONT_IMAGE_REPO}:${IMAGE_TAG} \\
${FRONT_MODULE_DIR}
'''
}
}
stage('7) Docker push') {
steps {
withCredentials([usernamePassword(
credentialsId: DOCKER_CRED,
@@ -81,12 +118,15 @@ pipeline {
)]) {
sh '''
set -e
echo "[1/2] docker registry login"
echo "$DOCKER_PASS" | docker login ${REGISTRY_HOST} -u "$DOCKER_USER" --password-stdin
echo "[2/2] docker image push"
docker push ${REGISTRY_HOST}/${IMAGE_REPO}:${IMAGE_TAG}
if [ "${TARGET}" = "admin" ] || [ "${TARGET}" = "all" ]; then
docker push ${REGISTRY_HOST}/${ADMIN_IMAGE_REPO}:${IMAGE_TAG}
fi
if [ "${TARGET}" = "front" ] || [ "${TARGET}" = "all" ]; then
docker push ${REGISTRY_HOST}/${FRONT_IMAGE_REPO}:${IMAGE_TAG}
fi
docker logout ${REGISTRY_HOST}
'''
@@ -94,42 +134,10 @@ pipeline {
}
}
stage('6) Jenkins cleanup') {
steps {
sh '''
set -e
echo "[1/3] Remove pushed image from Jenkins"
docker rmi ${REGISTRY_HOST}/${IMAGE_REPO}:${IMAGE_TAG} 2>/dev/null || true
echo "[2/3] Remove dangling images"
docker image prune -f
echo "[3/3] Remove old build cache (safe-ish)"
docker builder prune -f || true
'''
stage('8) Deploy admin') {
when {
expression { params.TARGET == 'admin' || params.TARGET == 'all' }
}
}
stage('7) Copy compose') {
steps {
sshagent(['ssh-pjt']) {
sh '''
set -e
echo "[1/2] ssh login"
SRC="${WORK_COMPOSE_DIR}/${WORK_COMPOSE_FILE}"
DST="${SSH_USER}@${SSH_HOST}:${SSH_COMPOSE_DIR}/${SSH_COMPOSE_FILE}"
echo "[2/2] Copy compose $SRC -> $DST"
scp -P ${SSH_PORT} -o StrictHostKeyChecking=no "$SRC" "$DST"
'''
}
}
}
stage('8) Remote docker registry login') {
steps {
sshagent(['ssh-pjt']) {
withCredentials([usernamePassword(
@@ -140,15 +148,21 @@ pipeline {
sh '''
set -e
echo "[1/1] docker registry login"
scp -P ${SSH_PORT} -o StrictHostKeyChecking=no \\
${WORK_COMPOSE_DIR}/${ADMIN_COMPOSE_FILE} \\
${SSH_USER}@${SSH_HOST}:${SSH_COMPOSE_DIR}/${ADMIN_COMPOSE_FILE}
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${SSH_USER}@${SSH_HOST} '
set -e
REGISTRY_HOST='"${REGISTRY_HOST}"'
DOCKER_USER='"${DOCKER_USER}"'
DOCKER_PASS='"${DOCKER_PASS}"'
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \\
${SSH_USER}@${SSH_HOST} '
set -e
cd '"${SSH_COMPOSE_DIR}"'
export IMAGE_TAG='"${IMAGE_TAG}"'
echo "$DOCKER_PASS" | docker login "$REGISTRY_HOST" -u "$DOCKER_USER" --password-stdin
echo '"$DOCKER_PASS"' | docker login '"${REGISTRY_HOST}"' -u '"$DOCKER_USER"' --password-stdin
docker compose -p alist-api-admin --env-file '"${ADMIN_ENV_FILE}"' -f '"${ADMIN_COMPOSE_FILE}"' pull
docker compose -p alist-api-admin --env-file '"${ADMIN_ENV_FILE}"' -f '"${ADMIN_COMPOSE_FILE}"' up -d --remove-orphans
docker compose -p alist-api-admin --env-file '"${ADMIN_ENV_FILE}"' -f '"${ADMIN_COMPOSE_FILE}"' ps
docker logout '"${REGISTRY_HOST}"'
'
'''
}
@@ -156,105 +170,59 @@ pipeline {
}
}
stage('9) Remote docker pull') {
steps {
sshagent(['ssh-pjt']) {
sh '''
set -e
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
${SSH_USER}@${SSH_HOST} \
'set -e
cd '"${SSH_COMPOSE_DIR}"'
export IMAGE_TAG='"${IMAGE_TAG}"'
echo "[1/1] docker pull"
docker compose -p alist-api --env-file /srv/project/alist/env/api/.env -f '"${SSH_COMPOSE_FILE}"' pull
'
'''
}
stage('9) Deploy front') {
when {
expression { params.TARGET == 'front' || params.TARGET == 'all' }
}
}
stage('10) Remote docker down & up') {
steps {
sshagent(['ssh-pjt']) {
sh '''
set -e
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
${SSH_USER}@${SSH_HOST} \
'set -e
cd '"${SSH_COMPOSE_DIR}"'
export IMAGE_TAG='"${IMAGE_TAG}"'
echo "[1/3] docker down (remove orphans)"
docker compose -p alist-api --env-file /srv/project/alist/env/api/.env -f "'"${SSH_COMPOSE_FILE}"'" down --remove-orphans
echo "[2/3] docker up"
docker compose -p alist-api --env-file /srv/project/alist/env/api/.env -f '"${SSH_COMPOSE_FILE}"' up -d
echo "[3/3] docker ps"
docker compose -p alist-api --env-file /srv/project/alist/env/api/.env -f '"${SSH_COMPOSE_FILE}"' ps
'
'''
}
}
}
stage('11) Remote cleanup') {
steps {
sshagent(['ssh-pjt']) {
sh '''
set -e
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${SSH_USER}@${SSH_HOST} '
withCredentials([usernamePassword(
credentialsId: DOCKER_CRED,
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh '''
set -e
Repo='"${REGISTRY_HOST}/${IMAGE_REPO}"'
scp -P ${SSH_PORT} -o StrictHostKeyChecking=no \\
${WORK_COMPOSE_DIR}/${FRONT_COMPOSE_FILE} \\
${SSH_USER}@${SSH_HOST}:${SSH_COMPOSE_DIR}/${FRONT_COMPOSE_FILE}
echo "[1/3] Remove old image"
docker images "${Repo}" --format '{{.Repository}}:{{.Tag}}' | grep -v -F -f <(docker ps --format '{{.Image}}') | xargs -r docker rmi
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \\
${SSH_USER}@${SSH_HOST} '
set -e
cd '"${SSH_COMPOSE_DIR}"'
export IMAGE_TAG='"${IMAGE_TAG}"'
echo "[2/3] Remove dangling images"
docker image prune -f
echo "[3/3] Remove old build cache (safe-ish)"
docker builder prune -f || true
'
'''
echo '"$DOCKER_PASS"' | docker login '"${REGISTRY_HOST}"' -u '"$DOCKER_USER"' --password-stdin
docker compose -p alist-api-front --env-file '"${FRONT_ENV_FILE}"' -f '"${FRONT_COMPOSE_FILE}"' pull
docker compose -p alist-api-front --env-file '"${FRONT_ENV_FILE}"' -f '"${FRONT_COMPOSE_FILE}"' up -d --remove-orphans
docker compose -p alist-api-front --env-file '"${FRONT_ENV_FILE}"' -f '"${FRONT_COMPOSE_FILE}"' ps
docker logout '"${REGISTRY_HOST}"'
'
'''
}
}
}
}
stage('12) registry docker image cleanup') {
stage('10) Jenkins cleanup') {
steps {
withCredentials([usernamePassword(
credentialsId: DOCKER_CRED,
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh '''
set -e
sh '''
set -e
echo "[1/2] Remove registry old image tag list"
TAGS_TO_DELETE=$(curl -s -u "$DOCKER_USER:$DOCKER_PASS" https://${REGISTRY_HOST}/v2/${IMAGE_REPO}/tags/list | jq -r '.tags[]' | grep -E '^[0-9]+' | sort -t- -k1,1n | head -n -5)
if [ "${TARGET}" = "admin" ] || [ "${TARGET}" = "all" ]; then
docker rmi ${REGISTRY_HOST}/${ADMIN_IMAGE_REPO}:${IMAGE_TAG} 2>/dev/null || true
fi
echo "[2/2] Remove registry old image sha list and delete registry"
for TAG in $TAGS_TO_DELETE; do
DIGEST=$(curl -s -u "$DOCKER_USER:$DOCKER_PASS" -H "Accept: application/vnd.oci.image.index.v1+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json" -D - https://${REGISTRY_HOST}/v2/${IMAGE_REPO}/manifests/$TAG -o /dev/null | grep -i Docker-Content-Digest | awk '{print $2}' | tr -d $'\r')
if [ "${TARGET}" = "front" ] || [ "${TARGET}" = "all" ]; then
docker rmi ${REGISTRY_HOST}/${FRONT_IMAGE_REPO}:${IMAGE_TAG} 2>/dev/null || true
fi
if [ -z "$DIGEST" ]; then
echo "[SKIP] $TAG digest not found"
continue
fi
echo "[DRY] $TAG -> $DIGEST"
curl -s -u "$DOCKER_USER:$DOCKER_PASS" -X DELETE "https://${REGISTRY_HOST}/v2/${IMAGE_REPO}/manifests/${DIGEST}" -o /dev/null -w "HTTP %{http_code}\n"
done
'''
}
docker image prune -f
docker builder prune -f || true
'''
}
}
}
}