이전 포스트(Git 연동 & Webhook, 브랜치/PR 트리거 설계)에서 Jenkins와 GitHub Webhook 연동으로
develop브랜치 PR merge 시 자동 트리거 설정을 완료했습니다. 이번 글에서는 FastAPI 프로젝트를 Docker 이미지로 빌드 → tar 전송 → 원격 배포하는 파이프라인을 설명합니다.
파이프라인은 5단계로 구성되어 있습니다.
example:latest + example:v1.2.3)pipeline {
agent any
options {
timestamps()
timeout(time: 40, unit: 'MINUTES')
}
environment {
// 원격 서버 정보
REMOTE_HOST = "xx.xx.xxx"
// 원격 서버에서 Docker 컨테이너 실행 유저
REMOTE_USER = "ubuntu"
// Jenkins에 등록된 SSH Key ID
SSH_CRED_ID = "web-server-example"
DEPLOY_ROOT = "/home/ubuntu/app/jenkins/example"
// 이미지 및 파일 이름 정의
LOCAL_FASTAPI_DIR = "."
IMG_FASTAPI = "example:latest"
TAR_FASTAPI = "example-fastapi.tar"
COMPOSE_FILE = "docker-compose.deploy.yml"
}
tools {
}
stages {
// 1️⃣ 용량 확보 (Pre-cleaning)
stage('🧹 Pre-Cleanup & Build') {
steps {
script {
echo "🧹 Pre-cleaning Docker space..."
sh "docker image prune -f"
sh "docker images example:* | grep -v 'latest' | awk '{print \$3}' | xargs -r docker rmi -f || true"
sh "docker builder prune -f --filter 'until=24h'"
// 용량 확인
sh "df -h ."
// 2️⃣ Git 태그 & 빌드명 설정
def gitVer = sh(script: "git describe --tags --always", returnStdout: true).trim()
currentBuild.displayName = "#${BUILD_NUMBER}-${gitVer}"
def imgTagged = "chatbot-ai:${gitVer}"
// 3. 이미지 빌드 & tar 저장
echo "🐳 Building ${imgTagged}..."
sh "docker build -t ${imgTagged} -t ${IMG_FASTAPI} ${LOCAL_FASTAPI_DIR}"
sh "docker save -o ${TAR_FASTAPI} ${imgTagged} ${IMG_FASTAPI}"
}
}
}
// 3️⃣ 원격 서버로 tar 파일 전송
stage('Transfer to Remote') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: env.SSH_CRED_ID, keyFileVariable: 'SSH_KEY')]) {
script {
echo "🚀 Transferring artifacts to $REMOTE_HOST..."
// 1. 원격 디렉토리 생성
sh '''
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_HOST" "mkdir -p $DEPLOY_ROOT"
'''
// 2. FASTAPI Tar 파일 전송
sh '''
scp -i "$SSH_KEY" -o StrictHostKeyChecking=no "$TAR_FASTAPI" "$REMOTE_USER@$REMOTE_HOST:$DEPLOY_ROOT/"
'''
// 3. 전송 후 로컬 캐시 삭제
sh '''
rm "$TAR_FASTAPI"
'''
}
}
}
}
// 4️⃣ 원격 배포 실행 (All Services Restart)
stage('Remote Deploy') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: env.SSH_CRED_ID, keyFileVariable: 'SSH_KEY')]) {
script {
echo "🚀 Executing remote deployment on $REMOTE_HOST..."
// 원격 서버 접속 후 이미지 로드 및 컨테이너 재기동
sh '''
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_HOST" "
cd $DEPLOY_ROOT
# 1. 전체 서비스 종료
docker compose -f $COMPOSE_FILE down
# 2. 로그 디렉토리 생성 및 권한 설정 (755)
mkdir -p logs/fastapi
chmod -R 755 logs 2>/dev/null || echo 'ℹ️ Log directory exists (skipping permission change)'
# 3. 새로운 FASTAPI 이미지 로드
docker load -i $TAR_FASTAPI
# 4. 전체 서비스 실행
docker compose -f $COMPOSE_FILE up -d --no-build
# 5. 서버 용량 확보를 위해 tar 파일 삭제
rm $TAR_FASTAPI
# 6. 미사용 이미지 정리
docker image prune -f
"
'''
}
}
}
}
}
post {
success { echo "✅ FASTAPI Deployment Successful" }
failure { echo "❌ Failed" }
cleanup { sh "rm -f *.tar" }
}
}
docker image prune -f # Dangling 이미지 삭제 (~100MB)
docker images example:* | grep -v latest | awk '{print $3}' | xargs docker rmi -f # 구버전 선택 삭제
docker builder prune -f --filter 'until=24h' # 24시간 경과 빌드캐시 삭제
df -h . # 현재 디스크 상태 출력
🧹 Pre-cleaning Docker space...
Deleted Images: sha256:xxx (500MB)
Building example:v1.2.3...
Successfully tagged example:latest
SSH/SCP 동작
ssh -i $SSH_KEY ... "mkdir -p /home/ubuntu/app/jenkins/example"
scp -i $SSH_KEY example-fastapi.tar ubuntu@xx.xx.xxx:/home/ubuntu/app/jenkins/example/
rm example-fastapi # 로컬 즉시 삭제
특징: tar 파일(~500MB)만 전송, 소스코드 제외
원격 서버 실행 순서
<cd /home/ubuntu/app/jenkins/example
docker compose down # 1. 서비스 중단 (0 downtime 아님)
mkdir -p logs/fastapi # 2. 로그 디렉토리
docker load -i example-fastapi # 3. 이미지 로드 (~30초)
docker compose up -d --no-build # 4. 재시작 (FastAPI 새 이미지 사용)
rm example-fastapi # 5. tar 삭제
docker image prune -f # 6. 정리
핵심 포인트
원격 서버의 docker-compose.yml 예시:
version: '3.8'
services:
example:
image: example:latest
ports:
- "8080:8080"
volumes:
- ./logs:/app/logs
restart: unless-stopped
위치:
/home/ubuntu/app/jenkins/example/docker-compose.yml
| 변수 | 값 | 용도 |
|---|---|---|
REMOTE_HOST | xx.xx.xxx | 배포 대상 서버 |
SSH_CRED_ID | web-server-example | Jenkins SSH 자격증명 |
DEPLOY_ROOT | /home/ubuntu/app/jenkins/example | 원격 배포 경로 |
IMG_FASTAPI | example:latest | Docker 이미지명 |
TAR_FASTAPI | example-fastapi.tar | 전송용 tar 파일 |
1. GitHub: develop PR → merge (Webhook 발생)
2. Jenkins: Generic Webhook Trigger → 파이프라인 실행
3. 로컬: Docker 빌드 → tar 생성 (example:v1.2.3, latest)
4. SSH: tar 전송 → 원격 docker load
5. 원격: docker-compose 재시작 → FastAPI 배포
✅ Jenkins + Generic Webhook Trigger 플러그인 설치
✅ GitHub Webhook: /generic-webhook-trigger/invoke?token=XXX
✅ docker-compose.deploy.yml (FastAPI 정의)
✅ SSH 자격증명 등록 (web-server-example)
✅ 원격 서버: docker-compose, logs 디렉토리 권한 확인
필자는 Webhook + Docker tar 배포 조합이 보안성이 엄격한 곳이거나 소규모 팀에서 가장 실용적이라고 봅니다. Docker 레지스트리를 받을 수 없기에 SSH/SCP로 배포를 어쩔 수 없이 하는 경우와 규모가 작아 SSH/SCP를 전송할 때 용량이 크게 고려할 점이 아니라면 좋은 방식이라 생각합니다.