6722d774a7
- 삭제정책 추가
323 lines
9.0 KiB
Plaintext
323 lines
9.0 KiB
Plaintext
pipeline {
|
|
agent any
|
|
|
|
options {
|
|
timestamps()
|
|
disableConcurrentBuilds()
|
|
}
|
|
|
|
environment {
|
|
// ===== Registry =====
|
|
REGISTRY_HOST = "registry.pjt.kr"
|
|
IMAGE_REPO = "alist/api"
|
|
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"
|
|
|
|
// ===== jenkins workspace path =====
|
|
WORK_COMPOSE_DIR = "deploy/pjt/compose"
|
|
WORK_COMPOSE_FILE = "api-compose.pjt.yml"
|
|
}
|
|
|
|
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') {
|
|
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.IMAGE_TAG = "${env.BUILD_NUMBER}-${env.SHORT_SHA}"
|
|
echo "IMAGE_TAG = ${env.IMAGE_TAG}"
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('4) Docker Build') {
|
|
steps {
|
|
sh '''
|
|
set -e
|
|
|
|
echo "[1/1] docker build"
|
|
|
|
docker build -t ${REGISTRY_HOST}/${IMAGE_REPO}:${IMAGE_TAG} .
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('5) Docker registry Login & Push') {
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: DOCKER_CRED,
|
|
usernameVariable: 'DOCKER_USER',
|
|
passwordVariable: 'DOCKER_PASS'
|
|
)]) {
|
|
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}
|
|
|
|
docker logout ${REGISTRY_HOST}
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
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('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(
|
|
credentialsId: DOCKER_CRED,
|
|
usernameVariable: 'DOCKER_USER',
|
|
passwordVariable: 'DOCKER_PASS'
|
|
)]) {
|
|
sh '''
|
|
set -e
|
|
|
|
echo "[1/1] docker registry login"
|
|
|
|
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}"'
|
|
|
|
echo "$DOCKER_PASS" | docker login "$REGISTRY_HOST" -u "$DOCKER_USER" --password-stdin
|
|
'
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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('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} \
|
|
'set -e
|
|
|
|
echo "[0] Collect images used by running containers"
|
|
RUNNING_IMAGES=$(docker ps -q | xargs -r docker inspect --format="{{.Image}}" | sort -u)
|
|
|
|
echo "[1/3] Remove old registry images (except running ones)"
|
|
docker images '"${REGISTRY_HOST}/${IMAGE_REPO}"' --format "{{.ID}} {{.CreatedAt}}" | \
|
|
sort -k2 -r | \
|
|
awk "{print \$1}" | \
|
|
while read IMAGE_ID; do
|
|
echo "$RUNNING_IMAGES" | grep -q "$IMAGE_ID" && continue
|
|
docker rmi -f "$IMAGE_ID" 2>/dev/null || true
|
|
done
|
|
|
|
echo "[2/3] Remove dangling images (safe)"
|
|
docker image prune -f
|
|
|
|
echo "[3/3] Remove unused volumes (safe)"
|
|
docker volume prune -f
|
|
'
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('12) Registry cleanup (keep last 5 + protect running)') {
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: DOCKER_CRED,
|
|
usernameVariable: 'DOCKER_USER',
|
|
passwordVariable: 'DOCKER_PASS'
|
|
)]) {
|
|
sshagent(['ssh-pjt']) {
|
|
sh '''#!/usr/bin/env bash
|
|
set -e
|
|
|
|
ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
|
${SSH_USER}@${SSH_HOST} \
|
|
"REGISTRY_HOST='${REGISTRY_HOST}' IMAGE_REPO='${IMAGE_REPO}' DOCKER_USER='${DOCKER_USER}' DOCKER_PASS='${DOCKER_PASS}' bash -se" <<'REMOTE_EOF'
|
|
set -e
|
|
|
|
REGISTRY_URL="https://${REGISTRY_HOST}"
|
|
REPO="${IMAGE_REPO}"
|
|
KEEP=5
|
|
AUTH="${DOCKER_USER}:${DOCKER_PASS}"
|
|
|
|
command -v jq >/dev/null 2>&1 || (sudo apt-get update -y && sudo apt-get install -y jq)
|
|
|
|
echo "[1/6] Debug URL"
|
|
echo "TAGS_URL=${REGISTRY_URL}/v2/${REPO}/tags/list"
|
|
|
|
echo "[2/6] Collect running tags to protect"
|
|
RUNNING_TAGS=$(docker ps --format '{{.Image}}' | \
|
|
grep -E "^${REGISTRY_HOST}/${REPO}:" | \
|
|
awk -F: '{print $2}' | sort -u || true)
|
|
|
|
echo "[3/6] Fetch tags from registry"
|
|
TAGS=$(curl -fsS -u "${AUTH}" "${REGISTRY_URL}/v2/${REPO}/tags/list" | jq -r '.tags[]?' || true)
|
|
|
|
if [ -z "$TAGS" ]; then
|
|
echo "No tags found in registry. Skip."
|
|
exit 0
|
|
fi
|
|
|
|
echo "[4/6] Sort tags desc"
|
|
SORTED=$(echo "$TAGS" | sort -r)
|
|
KEEP_TAGS=$(echo "$SORTED" | head -n $KEEP)
|
|
DEL_CANDIDATES=$(echo "$SORTED" | tail -n +$((KEEP+1)))
|
|
|
|
echo "Keep tags:"
|
|
echo "$KEEP_TAGS" | sed 's/^/ - /'
|
|
|
|
echo "[5/6] Delete old tags except running tags"
|
|
for TAG in $DEL_CANDIDATES; do
|
|
if [ -n "$RUNNING_TAGS" ] && echo "$RUNNING_TAGS" | grep -qx "$TAG"; then
|
|
echo "Skip running tag: ${REPO}:${TAG}"
|
|
continue
|
|
fi
|
|
|
|
echo "Deleting ${REPO}:${TAG}"
|
|
|
|
DIGEST=$(curl -fsSI -u "${AUTH}" \
|
|
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
|
"${REGISTRY_URL}/v2/${REPO}/manifests/${TAG}" \
|
|
| awk -F': ' '/Docker-Content-Digest/ {print $2}' | tr -d '\r')
|
|
|
|
if [ -z "$DIGEST" ]; then
|
|
echo " digest not found. skip ${TAG}"
|
|
continue
|
|
fi
|
|
|
|
curl -fsS -u "${AUTH}" -X DELETE "${REGISTRY_URL}/v2/${REPO}/manifests/${DIGEST}" || true
|
|
done
|
|
|
|
echo "[6/6] Garbage collect (reclaim disk)"
|
|
docker exec registry registry garbage-collect /etc/docker/registry/config.yml
|
|
|
|
echo "Registry cleanup done."
|
|
REMOTE_EOF
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|