๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Server

๋‹จ์ผ ์„œ๋ฒ„์—์„œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌํ˜„ํ•˜๊ธฐ — Jenkins + Docker + Nginx Blue-Green ์ „๋žต

๐Ÿ™‹ ๋“ค์–ด๊ฐ€๋ฉฐ

์•ˆ๋…•ํ•˜์„ธ์š”! ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์„œ๋น„์Šค์˜ ๋‹จ์ผ Oracle Cloud ์ธ์Šคํ„ด์Šค์—์„œ ๋‹ค์šดํƒ€์ž„ ์—†์ด ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ผ์šฐ๋“œ ๋น„์šฉ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ๋ฐฐํฌํ•  ๋•Œ๋งˆ๋‹ค ์„œ๋น„์Šค๊ฐ€ ์ž ์‹œ ์ค‘๋‹จ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด 8080 / 8081 ํฌํŠธ๋ฅผ ๋ฒˆ๊ฐˆ์•„ ์‚ฌ์šฉํ•˜๋Š” Blue-Green ์ „๋žต๊ณผ Nginx ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์ „ํ™˜ ๋ฐฉ์‹์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ ๋‹ค๋ฃฐ ๋‚ด์šฉ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋‹จ์ผ ์„œ๋ฒ„ Blue-Green ๋ฐฐํฌ์˜ ์›๋ฆฌ
  • Jenkins Multibranch Pipeline ์„ค์ • ๋ฐ ๋ธŒ๋žœ์น˜๋ณ„ ์ž๋™ ๋ฐฐํฌ
  • Jenkins Pipeline ์ „์ฒด ํ๋ฆ„
  • Nginx ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์ „ํ™˜ ๋ฐฉ๋ฒ•
  • ํ—ฌ์Šค์ฒดํฌ ๊ธฐ๋ฐ˜ ์ž๋™ ๋กค๋ฐฑ

๐Ÿ—๏ธ 1. ์ „์ฒด ๋ฐฐํฌ ํ๋ฆ„

GitHub Push (develop / main ๋ธŒ๋žœ์น˜)
  → GitHub Webhook
    → Jenkins Multibranch Pipeline ์ž๋™ ํŠธ๋ฆฌ๊ฑฐ
      → Gradle bootJar ๋นŒ๋“œ
        → JAR ํŒŒ์ผ ์›๊ฒฉ ์„œ๋ฒ„(SSH)๋กœ ์ „์†ก
          → ์›๊ฒฉ ์„œ๋ฒ„์—์„œ Docker Image ๋นŒ๋“œ
            → ์ƒˆ ํฌํŠธ(8080 or 8081)์— ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰
              → ํ—ฌ์Šค์ฒดํฌ ํ†ต๊ณผ
                → Nginx ์„ค์ • ์ „ํ™˜ (์‹ฌ๋ณผ๋ฆญ ๋งํฌ)
                  → ์ด์ „ ์ปจํ…Œ์ด๋„ˆ ์ข…๋ฃŒ

์ „์ฒด ๋ฐฐํฌ๊ฐ€ ์ž๋™ํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ ๋กค๋ฐฑ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ”ต๐ŸŸข 2. Blue-Green ๋ฐฐํฌ ์›๋ฆฌ

์„œ๋ฒ„ ๋‚ด๋ถ€์—๋Š” ํ•ญ์ƒ ๋‘ ๊ฐœ์˜ ํฌํŠธ๊ฐ€ ์ค€๋น„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • Blue (์˜ˆ: 8080) : ํ˜„์žฌ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›๊ณ  ์žˆ๋Š” ์ปจํ…Œ์ด๋„ˆ
  • Green (์˜ˆ: 8081) : ์ƒˆ ๋ฒ„์ „์„ ๋ฐฐํฌํ•  ๋Œ€๊ธฐ ํฌํŠธ

๋ฐฐํฌ ์‹œ Green์— ์ƒˆ ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๊ณ , ํ—ฌ์Šค์ฒดํฌ๊ฐ€ ํ†ต๊ณผํ•˜๋ฉด Nginx๊ฐ€ Green์œผ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ Blue ์ปจํ…Œ์ด๋„ˆ๋Š” ๊ทธ ํ›„์— ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

[์‚ฌ์šฉ์ž ์š”์ฒญ]
     ↓
  Nginx (80)
     ↓ (์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋กœ ํ˜„์žฌ ํ™œ์„ฑ ํฌํŠธ ๊ฒฐ์ •)
  app_8080 ๋˜๋Š” app_8081 (Docker ์ปจํ…Œ์ด๋„ˆ)
     ↓
  Redis (๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ)

Nginx ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์ „ํ™˜

app_8080.conf์™€ app_8081.conf ๋‘ ๊ฐœ์˜ ์„ค์ • ํŒŒ์ผ์„ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋‘๊ณ , ํ™œ์„ฑํ™”ํ•  ํŒŒ์ผ์„ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

# /etc/nginx/sites-available/app_8080.conf
server {
    listen 80;
    server_name _;

    # API ์š”์ฒญ
    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

app_8081.conf๋Š” ํฌํŠธ ๋ฒˆํ˜ธ๋งŒ 8081๋กœ ๋ฐ”๊ฟ” ๋™์ผํ•˜๊ฒŒ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐํฌ ์‹œ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ๊ต์ฒดํ•˜๊ณ  Nginx๋ฅผ reloadํ•˜๋ฉด ํŠธ๋ž˜ํ”ฝ์ด ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค.

sudo ln -sf /etc/nginx/sites-available/app_8081.conf /etc/nginx/sites-enabled/app.conf
sudo nginx -t && sudo systemctl reload nginx

systemctl reload nginx๋Š” ๊ธฐ์กด ์ปค๋„ฅ์…˜์„ ๋Š์ง€ ์•Š๊ณ  ์ƒˆ ์„ค์ •์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ง„ํ–‰ ์ค‘์ธ ์š”์ฒญ์€ ์ด์ „ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ฒ˜๋ฆฌ๋ฅผ ์™„๋ฃŒํ•˜๊ณ , ์ƒˆ ์š”์ฒญ๋ถ€ํ„ฐ ์ƒˆ ์ปจํ…Œ์ด๋„ˆ๋กœ ํ–ฅํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ› ๏ธ 3. Jenkins ์„ค์ •

Multibranch Pipeline ์ƒ์„ฑ

Jenkins์—์„œ New Item → Multibranch Pipeline์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜ Pipeline๊ณผ ๋‹ฌ๋ฆฌ Multibranch Pipeline์€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์Šค์บ”ํ•˜์—ฌ ๋ธŒ๋žœ์น˜๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

  • develop ๋ธŒ๋žœ์น˜ Push/Merge → dev ์„œ๋ฒ„ ์ž๋™ ๋ฐฐํฌ
  • main ๋ธŒ๋žœ์น˜ Push/Merge → prod ์„œ๋ฒ„ ์ž๋™ ๋ฐฐํฌ

Branch Sources์—์„œ GitHub ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ , Scan Multibranch Pipeline Triggers์—์„œ GitHub hook trigger๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด Push/Merge ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ํŒŒ์ดํ”„๋ผ์ธ์ด ์ž๋™์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

Credentials ๋“ฑ๋ก

Jenkins > Manage Jenkins > Credentials์—์„œ ์•„๋ž˜ ํ•ญ๋ชฉ๋“ค์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

Credential ID ํƒ€์ž… ์šฉ๋„
goody_server_key SSH Username with private key ๋ฐฐํฌ ์„œ๋ฒ„ SSH ์ ‘์† ํ‚ค
jenkins_github_key SSH Username with private key GitHub ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํด๋ก  ํ‚ค
fcm-private-key Secret text FCM ์„œ๋น„์Šค ๊ณ„์ • ํ‚ค
SENTRY_AUTH_TOKEN Secret text Sentry ๋ฐฐํฌ ์•Œ๋ฆผ ํ† ํฐ

โš™๏ธ 4. Jenkins Pipeline ์ „์ฒด ํ๋ฆ„

ํŒŒ์ดํ”„๋ผ์ธ์€ ์ด 4๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„ ์„ค๋ช…
Git Clone ๋ธŒ๋žœ์น˜ ๊ธฐ์ค€์œผ๋กœ ์ฝ”๋“œ ํด๋ก 
Jar Build Gradle๋กœ bootJar ์ƒ์„ฑ
Find Jar File ๋นŒ๋“œ๋œ JAR ํŒŒ์ผ ๊ฒฝ๋กœ ํ™•์ธ
Remote Deploy ์›๊ฒฉ ์„œ๋ฒ„์— JAR ์ „์†ก → ํ™œ์„ฑ ํฌํŠธ ๊ฐ์ง€ ํ›„ ๋‹ค๋ฅธ ํฌํŠธ์—์„œ Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ → ํ—ฌ์Šค์ฒดํฌ → Nginx ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ๋ณ€๊ฒฝ → ์ด์ „ ์ปจํ…Œ์ด๋„ˆ ์ •๋ฆฌ
def jarFile

pipeline {
    agent any

    environment {
        IMAGE_NAME = "goody-api"
        SERVER = env.BRANCH_NAME == 'main' ? '<prod_server_ip>' : '<dev_server_ip>'
        SSH_USER = "ubuntu"
        SSH_KEY = credentials('goody_server_key')
        GIT_CREDENTIALS_ID = 'jenkins_github_key'
        SENTRY_AUTH_TOKEN = credentials('SENTRY_AUTH_TOKEN')
    }

    stages {
        stage('Git Clone') {
            steps {
                checkout([$class: 'GitSCM',
                    branches: [[name: env.BRANCH_NAME]],
                    userRemoteConfigs: [[
                        url: 'git@github.com:<org>/<repo>.git',
                        credentialsId: "${GIT_CREDENTIALS_ID}"
                    ]]
                ])
            }
        }

        stage('Jar Build') {
            steps {
                sh 'chmod +x ./gradlew'
                sh './gradlew --no-daemon clean :goody-server-api:bootJar'
            }
        }

        stage('Find Jar File') {
            steps {
                script {
                    def jarPath = "./goody-server-api/build/libs/"
                    jarFile = sh(script: "find ${jarPath} -name '*.jar' -print -quit", returnStdout: true).trim()
                    if (jarFile.isEmpty()) {
                        error "Jar file not found in ${jarPath}"
                    }
                    echo "Found Jar: ${jarFile}"
                }
            }
        }

        stage('Remote Deploy') {
            steps {
                script {
                    withCredentials([string(credentialsId: 'fcm-private-key', variable: 'FCM_KEY_JSON')]) {

                        // JAR ํŒŒ์ผ์„ ์›๊ฒฉ ์„œ๋ฒ„๋กœ ์ „์†ก
                        sh "scp -i $SSH_KEY ${jarFile} $SSH_USER@$SERVER:/home/$SSH_USER/app.jar"

                        // ์›๊ฒฉ ์„œ๋ฒ„์—์„œ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰
                        sh """
                        ssh -i $SSH_KEY $SSH_USER@$SERVER /bin/bash -s << 'EOF'
                            # ํ˜„์žฌ ํ™œ์„ฑ ํฌํŠธ ๊ฐ์ง€
                            activePort=\\$(docker ps --format "{{.Names}}" | grep -oE "app_(8080|8081)" | cut -d'_' -f2)

                            if [ "\\$activePort" = "8080" ]; then
                                nextPort="8081"
                                prevContainer="app_8080"
                            else
                                nextPort="8080"
                                prevContainer="app_8081"
                            fi
                            nextContainer="app_\\${nextPort}"

                            # ์›๊ฒฉ ์„œ๋ฒ„์—์„œ Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ
                            docker build -t ${IMAGE_NAME} -f /home/${SSH_USER}/Dockerfile /home/${SSH_USER}/

                            # ๊ธฐ์กด ๋Œ€๊ธฐ ์ปจํ…Œ์ด๋„ˆ ์ •๋ฆฌ ํ›„ ์ƒˆ ๋ฒ„์ „ ์‹คํ–‰
                            docker stop \\$nextContainer || true
                            docker rm \\$nextContainer || true

                            docker run -d --name \\$nextContainer \\\\
                                --env-file /home/${SSH_USER}/.env \\\\
                                --network ubuntu_goody-net \\\\
                                -p \\$nextPort:8080 \\\\
                                ${IMAGE_NAME}

                            # ํ—ฌ์Šค์ฒดํฌ — 90์ดˆ(18ํšŒ × 5์ดˆ) ์‹œ๋„
                            for i in {1..18}; do
                                status_code=\\$(curl -s -o /dev/null -w "%{http_code}" <http://localhost>:\\${nextPort}/actuator/health)

                                if [ "\$status_code" -eq 200 ]; then
                                    echo "ํ—ฌ์Šค์ฒดํฌ ์„ฑ๊ณต — Nginx ์ „ํ™˜"

                                    sudo ln -sf /etc/nginx/sites-available/app_\${nextPort}.conf \\
                                        /etc/nginx/sites-enabled/app.conf
                                    sudo nginx -t && sudo systemctl reload nginx

                                    docker stop \${prevContainer} || true
                                    docker rm \${prevContainer} || true
                                    exit 0
                                fi

                                echo "ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ (\\${i}/18) — 5์ดˆ ํ›„ ์žฌ์‹œ๋„"
                                sleep 5
                            done

                            # 90์ดˆ ํ›„์—๋„ ์‹คํŒจ → ๋กค๋ฐฑ
                            echo "๋ฐฐํฌ ์‹คํŒจ — ๋กค๋ฐฑ"
                            docker stop \\${nextContainer} || true
                            docker rm \\${nextContainer} || true
                            exit 1
                        'EOF'
                        """
                    }
                }
            }
        }
    }
}

4-1. Remote Deploy ๋‹จ๊ณ„ ํ๋ฆ„

Remote Deploy ์Šคํ…Œ์ด์ง€๋Š” ์›๊ฒฉ ์„œ๋ฒ„์—์„œ ์•„๋ž˜ ์ˆœ์„œ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

โ‘  ํ™œ์„ฑ ํฌํŠธ ๊ฐ์ง€

docker ps๋กœ ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„์„ ํ™•์ธํ•ด ํ™œ์„ฑ ํฌํŠธ(8080 ๋˜๋Š” 8081)๋ฅผ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ง€๋œ ํฌํŠธ์˜ ๋ฐ˜๋Œ€ ํฌํŠธ๊ฐ€ ๋‹ค์Œ ๋ฐฐํฌ ๋Œ€์ƒ์ด ๋ฉ๋‹ˆ๋‹ค.

activePort=$(docker ps --format "{{.Names}}" | grep -oE "app_(8080|8081)" | cut -d'_' -f2)
# activePort=8080 ์ด๋ฉด → nextPort=8081
# activePort=์—†์Œ(์ดˆ๊ธฐ ๋ฐฐํฌ) ์ด๋ฉด → nextPort=8080

โ‘ก Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰

Jenkins์—์„œ ์ „์†ก๋ฐ›์€ JAR ํŒŒ์ผ๋กœ ์›๊ฒฉ ์„œ๋ฒ„์˜ Dockerfile์„ ์ด์šฉํ•ด ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ , ๋น„ํ™œ์„ฑ ํฌํŠธ์— ์ƒˆ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—๋Š” ์•„์ง Nginx๊ฐ€ ์ด์ „ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ์–ด ์„œ๋น„์Šค๋Š” ์ •์ƒ ์šด์˜ ์ค‘์ž…๋‹ˆ๋‹ค.

โ‘ข ํ—ฌ์Šค์ฒดํฌ ๋ฐ ์ž๋™ ๋กค๋ฐฑ (์ตœ๋Œ€ 90์ดˆ)

์ƒˆ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋œฐ ๋•Œ๊นŒ์ง€ 5์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์ตœ๋Œ€ 18ํšŒ /actuator/health ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ ๋™์ž‘

HTTP 200 ์‘๋‹ต Nginx ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์ „ํ™˜ → ์ด์ „ ์ปจํ…Œ์ด๋„ˆ ์ข…๋ฃŒ (์ •์ƒ ๋ฐฐํฌ)
90์ดˆ ์ดˆ๊ณผ ์‹คํŒจ ์ƒˆ ์ปจํ…Œ์ด๋„ˆ ์ฆ‰์‹œ ์ œ๊ฑฐ → ์ด์ „ ์ปจํ…Œ์ด๋„ˆ ์œ ์ง€ (์ž๋™ ๋กค๋ฐฑ)
์ด์ „ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฏธ๋ฆฌ ์ข…๋ฃŒํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ๋กค๋ฐฑ ์‹œ์—๋„ ์„œ๋น„์Šค ์ค‘๋‹จ์ด ์—†์Šต๋‹ˆ๋‹ค.

Docker ๋นŒ๋“œ๋ฅผ ์›๊ฒฉ ์„œ๋ฒ„์—์„œ ํ•˜๋Š” ์ด์œ 

์ดˆ๊ธฐ์—๋Š” Jenkins ์„œ๋ฒ„์—์„œ Docker ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•ด .tar๋กœ ์ €์žฅํ•œ ๋’ค ์›๊ฒฉ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

[์ดˆ๊ธฐ ๋ฐฉ์‹]
Jenkins: Gradle ๋นŒ๋“œ → Docker ๋นŒ๋“œ → docker save(.tar) → scp → docker load

๊ทธ๋Ÿฐ๋ฐ dev ์„œ๋ฒ„์™€ prod ์„œ๋ฒ„๋Š” Dockerfile ๋‚ด์šฉ์ด ์„œ๋กœ ๋‹ฌ๋ผ, Jenkins ์„œ๋ฒ„ ํ•œ ๊ณณ์—์„œ Dockerfile์„ ๊ด€๋ฆฌํ•˜๋ฉด ํ™˜๊ฒฝ๋ณ„ ์ฐจ์ด๋ฅผ ์ œ์–ดํ•˜๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ ์„œ๋ฒ„๊ฐ€ ์ž์‹ ์˜ Dockerfile์„ ์ง์ ‘ ์œ ์ง€ํ•˜๊ณ  ๋นŒ๋“œํ•˜๋„๋ก ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

[๊ฐœ์„  ๋ฐฉ์‹]
Jenkins: Gradle ๋นŒ๋“œ → scp(JAR๋งŒ ์ „์†ก) → ์›๊ฒฉ ์„œ๋ฒ„์—์„œ Docker ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ (์„œ๋ฒ„๋ณ„ Dockerfile ์‚ฌ์šฉ)

๐Ÿ“Š 5. ์„œ๋ฒ„ ๋‚ด๋ถ€ ๊ตฌ์กฐ ์ •๋ฆฌ

goody-server-001
โ”œโ”€โ”€ Nginx (Host:80)
โ”‚     โ””โ”€โ”€ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ → app_8080 ๋˜๋Š” app_8081
โ”‚
โ””โ”€โ”€ Docker ๋„คํŠธ์›Œํฌ (ubuntu_goody-net)
      โ”œโ”€โ”€ app_8080 (Spring Boot)
      โ”œโ”€โ”€ app_8081 (Spring Boot)  ← ๋ฐฐํฌ ์‹œ ์ƒˆ ๋ฒ„์ „
      โ””โ”€โ”€ redis    (๋‚ด๋ถ€ ํ†ต์‹  ์ „์šฉ, ์™ธ๋ถ€ ๋ฏธ๋…ธ์ถœ)

 


 

โš ๏ธ 6. ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

activePort ๊ฐ์ง€ ์˜ค๋ฅ˜

์ฒ˜์Œ์— ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ–ˆ๋‹ค๊ฐ€ app_8081์ด ์‹คํ–‰ ์ค‘์ผ ๋•Œ app_8080๋„ ๋งค์นญ๋˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

# ์ž˜๋ชป๋œ ์ฝ”๋“œ — app_8081 ์‹คํ–‰ ์ค‘์—๋„ app_8080์ด ๋งค์นญ๋  ์ˆ˜ ์žˆ์Œ
activePort=$(docker ps --format "{{.Names}}" | grep app_8080 || true | cut -d'_' -f2)

# ์ˆ˜์ •๋œ ์ฝ”๋“œ — ์ •ํ™•ํ•œ ํŒจํ„ด ๋งค์นญ
activePort=$(docker ps --format "{{.Names}}" | grep -oE "app_(8080|8081)" | cut -d'_' -f2)

๐Ÿ”Ž 7. ๋งˆ๋ฌด๋ฆฌ

๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋ผ๋„ ๋‘ ํฌํŠธ + Nginx ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์กฐํ•ฉ์œผ๋กœ ์ถฉ๋ถ„ํžˆ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ•ต์‹ฌ์€ ํ—ฌ์Šค์ฒดํฌ ๊ธฐ๋ฐ˜ ์ž๋™ ๋กค๋ฐฑ์œผ๋กœ ๋ฐฐํฌ ์‹คํŒจ ์‹œ ์•ˆ์ „ํ•˜๊ฒŒ ์ด์ „ ๋ฒ„์ „์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐฐํฌ ๊ฒฐ๊ณผ — ๋‹ค์šดํƒ€์ž„ ๋น„๊ต

๋ฐฐํฌ ๋ฐฉ์‹ ๋‹ค์šดํƒ€์ž„
๊ธฐ์กด (ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ → ์žฌ๊ธฐ๋™) ์•ฝ 32~45์ดˆ
๊ฐœ์„  (Blue-Green + Nginx ์ „ํ™˜) ์•ฝ 1์ดˆ

 

→ ๋‹จ์ˆœํ•œ ํฌํŠธ ์ „ํ™˜๋งŒ์œผ๋กœ ๋‹ค์šดํƒ€์ž„ ์•ฝ 97% ๋‹จ์ถ•์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.