FastAPI 파이프라인 스크립트

DevBadger·2026년 1월 25일
post-thumbnail

이전 포스트(Git 연동 & Webhook, 브랜치/PR 트리거 설계)에서 Jenkins와 GitHub Webhook 연동으로 develop 브랜치 PR merge 시 자동 트리거 설정을 완료했습니다. 이번 글에서는 FastAPI 프로젝트를 Docker 이미지로 빌드 → tar 전송 → 원격 배포하는 파이프라인을 설명합니다.

파이프라인 전체 구조

파이프라인은 5단계로 구성되어 있습니다.

  1. Pre-cleaning: Docker 디스크 정리
  2. 자동 버전 태깅: Git 태그 추출 및 빌드명 설정
  3. SSH tar 전송: 이미지 tar 원격 전송
  4. 원격 배포: docker-compose 재실행

주요 특징

  • Docker 용량 자동 정리 (Pre-cleaning)
  • Git 태그 듀얼 태깅 (example:latest + example:v1.2.3)
  • 대용량 tar 전송 (이미지 빌드 생략)
  • docker-compose 통합 배포

전체 Jenkinsfile 스크립트

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" }
  }
}

단계별 상세 설명

1. 디스크 용량 최적화 (Pre-cleaning)

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 .  # 현재 디스크 상태 출력

2. 자동 버전 태깅

  • git describe --tags --always → v1.2.3 또는 HEAD 추출
  • 듀얼 태깅: chatbot-ai:v1.2.3 + chatbot-ai:latest
  • docker save로 tar 압축 (~500MB)
🧹 Pre-cleaning Docker space...
Deleted Images: sha256:xxx (500MB)
Building example:v1.2.3...
Successfully tagged example:latest

3. SSH로 산출물(tar) 전송

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)만 전송, 소스코드 제외

4. 원격 배포 자동화

원격 서버 실행 순서

<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. 정리

핵심 포인트

  • --no-build: 로컬 이미지 사용으로 빌드 생략
  • logs 디렉토리 755 권한: 컨테이너 appuser 쓰기 보장

배포 서버 필수 설정

1. docker-compose.yml

원격 서버의 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

2. 환경 변수 정리

변수용도
REMOTE_HOSTxx.xx.xxx배포 대상 서버
SSH_CRED_IDweb-server-exampleJenkins SSH 자격증명
DEPLOY_ROOT/home/ubuntu/app/jenkins/example원격 배포 경로
IMG_FASTAPIexample:latestDocker 이미지명
TAR_FASTAPIexample-fastapi.tar전송용 tar 파일

3. 배포 시나리오

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 배포

장단점

장점:

  • 완전 자동화: PR merge → 배포까지 1클릭 불필요
  • 안정성: Pre-cleaning으로 용량 문제 해결
  • 추적성: Git 태그로 이미지 버전 관리

단점:

  • tar 전송 시간: 이미지 크기에 비례 (최적화 권장)
  • SSH 의존: 키 관리 및 네트워크 안정성 필요
  • 단일 실패점: Jenkins 노드 장애 시 배포 중단

도입 체크리스트

✅ 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를 전송할 때 용량이 크게 고려할 점이 아니라면 좋은 방식이라 생각합니다.

profile
개발 오소리

0개의 댓글