DevOps/CI&CD

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์‹ค์Šต - ๋กค๋ง ์ „๋žต

ํ”„๋กœ๊ทธ๋ž˜๋จธ ์˜ค์›” 2024. 9. 6.

์•ž์„œ ์‹ค์Šต ๋ ˆํฌ์ง€ํ† ๋ฆฌ์™€ ํ™˜๊ฒฝ ์…‹ํŒ…์„ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์…‹ํŒ…์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•˜์‹  ๋ถ„์ด๋‚˜, ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

 

https://programmer-may.tistory.com/213

 

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์‹ค์Šต ๊ธฐ๋ณธ ์…‹ํŒ… (Github Actions, Nginx ํ™œ์šฉ)

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ด๋ก ์— ๋Œ€ํ•ด์„œ ํ•™์Šต์„ ํ•˜์˜€๊ณ , ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ์ด์   ์‹ค์Šตํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ก ์— ๊ด€ํ•ด์„œ๋Š” ์•„๋ž˜ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ  ํ•ด์ฃผ์„ธ์š”.https://programmer-may.tistory.com/209 ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ(Zero-downtime Deployment

programmer-may.tistory.com

 

๋กค๋ง ์ „๋žต

๋กค๋ง ์ „๋žต(Rolling Deployment)์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒˆ ๋ฒ„์ „์„ ์ ์ง„์ ์œผ๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด ์ธ์Šคํ„ด์Šค๋ฅผ ํ•˜๋‚˜์”ฉ ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ๊ต์ฒดํ•˜๋ฉฐ, ๋ฐฐํฌ ๊ณผ์ •์—์„œ ์ผ๋ถ€ ์ธ์Šคํ„ด์Šค๋Š” ์ด์ „ ๋ฒ„์ „, ๋‚˜๋จธ์ง€ ์ธ์Šคํ„ด์Šค๋Š” ์ƒˆ ๋ฒ„์ „์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ์„œ๋ฒ„๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€ ํ•˜๊ฑฐ๋‚˜ ์—ฐ๊ฒฐ์„ ๋Š์–ด์„œ ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ๊ต์ฒดํ•˜๊ณ  ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์— ๋‹ค์‹œ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ์ ์ง„์ ์œผ๋กœ ์ง„ํ–‰ํ•˜์—ฌ ์ตœ์ข…์ ์œผ๋กœ๋Š” ๋ชจ๋“  ํŠธ๋ž˜ํ”ฝ์ด ์ƒˆ ๋ฒ„์ „์— ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ณผ์ •์„ ํ†ตํ•ด ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

rolling_before ๋ธŒ๋žœ์น˜

Rolling ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ „๋žต ๋„์ž… ์ „ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. cd-rolling.yml ํŒŒ์ผ์„ ํ†ตํ•ด JAR ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๊ณ , ์ด์ „ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฃฝ์ด๊ณ  JAR ํŒŒ์ผ์„ 8081ํฌํŠธ, 8082ํฌํŠธ์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๋‹ค์šด ํƒ€์ž„์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

rolling_after ๋ธŒ๋žœ์น˜

๋กค๋ง ์ „๋žต ๋„์ž…ํ•˜์—ฌ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. cd-rolling.yml ํŒŒ์ผ์„ ํ†ตํ•ด deploy.sh ํŒŒ์ผ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

 

deploy.sh ํŒŒ์ผ์ž‘์„ฑ

#!/bin/bash

# ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํฌํŠธ ์„ค์ •
PORT1=8081
PORT2=8082
DEPLOY_PORT=0

# ํ—ฌ์Šค ์ฒดํฌ ํ•จ์ˆ˜
health_check() {
    local port=$1
    local CHECK_URL="http://localhost:$port/actuator/health"
    local RETRY_COUNT=0
    local MAX_RETRY=10

    echo "Checking health on port $port..."

    until $(curl --output /dev/null --silent --head --fail $CHECK_URL); do
        sleep 5
        RETRY_COUNT=$((RETRY_COUNT+1))
        if [ $RETRY_COUNT -eq $MAX_RETRY ]; then
            echo "Health check failed on port $port"
            return 1
        fi
    done

    echo "Health check passed on port $port"
    return 0
}

timestamp=$(date +"%Y%m%d%H%M%S")

# ๋กค๋ฐฑ ํ•จ์ˆ˜
rollback() {
    local port=$1
    local backup_jar="/home/ubuntu/cicd/old_build/app-${port}-${timestamp}.jar"
    local current_version_jar="/home/ubuntu/cicd/app-${port}.jar"

    echo "Rolling back on port $port..."

    # ์ƒˆ ๋ฒ„์ „ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
    sudo fuser -k -TERM $port/tcp

    # ๋ฐฑ์—…๋œ ๊ธฐ์กด ๋ฒ„์ „ ๋ณต์›
    mv $backup_jar $current_version_jar

    # ์ด์ „ ๋ฒ„์ „ ์‹คํ–‰
    sudo nohup java -jar -Dserver.port=$port $current_version_jar > /home/ubuntu/console-$port.log 2>&1 &

    # Nginx ์„ค์ • ํŒŒ์ผ ์›๋ž˜ ์ƒํƒœ๋กœ ๋ณต๊ตฌ
    sudo sed -i '/upstream backend {/,/}/ s/server 127.0.0.1:'"$port"' down;/server 127.0.0.1:'"$port"';/' /etc/nginx/sites-available/default
    sudo service nginx reload
}

# ๋ฐฐํฌ ํ•จ์ˆ˜
deploy() {
    local port=$1
    local current_version_jar="/home/ubuntu/cicd/app-${port}.jar"
    local new_version_jar=$(ls /home/ubuntu/cicd/ZeroDownTimeDeployment-*.jar | head -n 1)

    echo "Deploying new version to port $port..."

    # ๊ธฐ์กด ๋ฒ„์ „ ๋ฐฑ์—…
    mv $current_version_jar /home/ubuntu/cicd/old_build/app-${port}-${timestamp}.jar

    # ์ƒˆ ๋ฒ„์ „ ๋ฐฐํฌ
    cp $new_version_jar $current_version_jar
    sudo chmod +x $current_version_jar
    sudo nohup java -jar -Dserver.port=$port $current_version_jar > /home/ubuntu/console-$port.log 2>&1 &

    sleep 20

    # ํ—ฌ์Šค ์ฒดํฌ
    health_check $port
    if [ $? -ne 0 ]; then
        rollback $port
        return 1
    fi

    echo "Deployment successful on port $port"
    return 0
}

# 8081์— ๋Œ€ํ•œ ๋ฐฐํฌ
echo "Routing traffic away from port $PORT1..."
sudo sed -i '/upstream backend {/,/}/ s/server 127.0.0.1:'"$PORT1"';/server 127.0.0.1:'"$PORT1"' down;/' /etc/nginx/sites-available/default
sudo service nginx reload

# 8081 ํฌํŠธ์—์„œ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
echo "Stopping process on port $PORT1..."
sudo fuser -k -TERM $PORT1/tcp
if [ $? -ne 0 ]; then
    echo "Failed to stop the process on port $PORT1. Aborting..."
    exit 1
fi
echo "Process on port $PORT1 stopped."

deploy $PORT1
if [ $? -ne 0 ]; then
    echo "Deployment failed on port $PORT1. Aborting..."
    exit 1
fi

echo "Re-routing traffic to port $PORT1..."
sudo sed -i '/upstream backend {/,/}/ s/server 127.0.0.1:'"$PORT1"' down;/server 127.0.0.1:'"$PORT1"';/' /etc/nginx/sites-available/default
sudo service nginx reload

# 8082์— ๋Œ€ํ•œ ๋ฐฐํฌ
echo "Routing traffic away from port $PORT2..."
sudo sed -i '/upstream backend {/,/}/ s/server 127.0.0.1:'"$PORT2"';/server 127.0.0.1:'"$PORT2"' down;/' /etc/nginx/sites-available/default
sudo service nginx reload

# 8082 ํฌํŠธ์—์„œ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
echo "Stopping process on port $PORT2..."
sudo fuser -k -TERM $PORT2/tcp
if [ $? -ne 0 ]; then
    echo "Failed to stop the process on port $PORT2. Aborting..."
    exit 1
fi
echo "Process on port $PORT2 stopped."

deploy $PORT2
if [ $? -ne 0 ]; then
    echo "Deployment failed on port $PORT2. Aborting..."
    exit 1
fi

echo "Re-routing traffic to port $PORT2..."
sudo sed -i '/upstream backend {/,/}/ s/server 127.0.0.1:'"$PORT2"' down;/server 127.0.0.1:'"$PORT2"';/' /etc/nginx/sites-available/default
sudo service nginx reload
sudo rm /home/ubuntu/cicd/no_downtime-*.jar

echo "Rolling deployment complete. Both ports are now running the new version."
  1. ํฌํŠธ ์„ค์ • ๋ฐ ํ—ฌ์Šค ์ฒดํฌ ํ•จ์ˆ˜ ์ •์˜
    • PORT1=8081๊ณผ PORT2=8082๋กœ ๋‘ ๊ฐœ์˜ ํฌํŠธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. health_check ํ•จ์ˆ˜๋Š” /actuator/health ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ ๋ฐฐํฌ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. ํƒ€์ž„์Šคํƒฌํ”„ ์ƒ์„ฑ
    • timestamp=$(date +"%Y%m%d%H%M%S")๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ์‹œ๊ฐ„์„ ํƒ€์ž„์Šคํƒฌํ”„ ํ˜•์‹์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์€ JAR ํŒŒ์ผ์„ ๋ฐฑ์—…ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  3. ๋กค๋ฐฑ ํ•จ์ˆ˜ ์ •์˜
    • rollback() ํ•จ์ˆ˜๋Š” ๋ฐฐํฌ๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ, ์ด์ „์— ๋ฐฑ์—…๋œ JAR ํŒŒ์ผ์„ ๋ณต์›ํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ์ƒˆ ๋ฒ„์ „์„ ์ข…๋ฃŒํ•˜๊ณ , ์ด์ „ ๋ฒ„์ „์„ ๋ฐฑ์—… ํด๋”์—์„œ ์›๋ž˜ ์œ„์น˜๋กœ ๋ณต์›ํ•œ ํ›„ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  4. ๋ฐฐํฌ ํ•จ์ˆ˜ ์ •์˜:
    • deploy() ํ•จ์ˆ˜๋Š” ์ƒˆ ๋ฒ„์ „์„ ํฌํŠธ์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € ๊ธฐ์กด ๋ฒ„์ „์˜ JAR ํŒŒ์ผ์„ ๋ฐฑ์—…ํ•˜๊ณ , ์ƒˆ๋กœ ๋ฐฐํฌ๋œ ๋ฒ„์ „์œผ๋กœ ๊ต์ฒดํ•œ ๋’ค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฐฐํฌ ํ›„ health_check ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ์‹คํŒจ ์‹œ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
  5. ํฌํŠธ๋ณ„ ๋ฐฐํฌ ์ ˆ์ฐจ
    • PORT1=8081, PORT2=8082๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ํฌํŠธ์— ๋Œ€ํ•ด ์‹คํ–‰ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•˜๊ณ , ์ƒˆ ๋ฒ„์ „์„ ๋ฐฐํฌํ•œ ํ›„ ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  6. Nginx ์„ค์ • ์—…๋ฐ์ดํŠธ
    • Nginx ์„ค์ •์„ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ƒˆ๋กœ์šด ํฌํŠธ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค. sed ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ proxy_pass ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ , Nginx๋ฅผ reloadํ•ฉ๋‹ˆ๋‹ค.
  7. ๋กค๋ง ๋ฐฐํฌ ์™„๋ฃŒ
    • ๋‘ ํฌํŠธ ๋ชจ๋‘ ์„ฑ๊ณต์ ์œผ๋กœ ๋ฐฐํฌ๋˜๋ฉด, Nginx ์„ค์ • ํŒŒ์ผ์„ ๋‹ค์‹œ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

 

Nginx ์„ค์ •

upstream backend {
    server 127.0.0.1:8081; 
    server 127.0.0.1:8082;
}
server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://backend;
        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;
    }
}

 

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ํ™•์ธ

rolling_before ๋ธŒ๋žœ์น˜๋ฅผ rolling ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œ ํ•˜๊ณ , rolling_after ๋ธŒ๋žœ์น˜๋ฅผ ํ‘ธ์‹œํ•˜์—ฌ ๋กค๋ง์ „๋žต ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค. 

1์ดˆ์— 1๋ฒˆ ์”ฉ simple HTTP Request ๋ฅผ ๋ณด๋‚ด์–ด ๋‹ค์šด ํƒ€์ž„์ด ์—†๋Š”์ง€ ํ™•์ธํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

Apache JMeter

๋Œ“๊ธ€