DevOps/CI&CD

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์‹ค์Šต - ์นด๋‚˜๋ฆฌ ์ „๋žต

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

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

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

 

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

 

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

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

programmer-may.tistory.com

 

์นด๋‚˜๋ฆฌ ์ „๋žต

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

์นด๋‚˜๋ฆฌ ๋ฐฐํฌ์˜ ํ•ต์‹ฌ์€ ์†Œ์ˆ˜์˜ ํŠธ๋ž˜ํ”ฝ์œผ๋กœ ์ƒˆ๋กœ์šด ๋ฒ„์ „์— ๋Œ€ํ•œ ์˜ค๋ฅ˜๋ฅผ ์กฐ๊ธฐ์— ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํŠน์ง• ๋•๋ถ„์— A/B ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ์—๋„ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

 

canary_before ๋ธŒ๋žœ์น˜

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

 

canary_after ๋ธŒ๋žœ์น˜

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

 

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

#!/bin/bash

# ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํฌํŠธ ์‹๋ณ„
CURRENT_PORT=$(sudo ss -tulnp | grep java | awk '{print $5}' | grep -o '[0-9]*$')
echo "Current port is: $CURRENT_PORT"

# ๊ธฐ๋ณธ ํฌํŠธ ์„ค์ •
PORT1=8081
PORT2=8082
NEW_PORT=0

# ํ˜„์žฌ ํฌํŠธ์— ๋”ฐ๋ผ ์ƒˆ ํฌํŠธ ๊ฒฐ์ •
if [ "$CURRENT_PORT" -eq "$PORT1" ]; then
    NEW_PORT=$PORT2
else
    NEW_PORT=$PORT1
fi

echo "Deploying new application on port: $NEW_PORT"

# ๊ธฐ์กด์˜ app-$NEW_PORT.jar ํŒŒ์ผ์„ old_build ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
if [ -f /home/ubuntu/cicd/app-$NEW_PORT.jar ]; then
    mv /home/ubuntu/cicd/app-$NEW_PORT.jar /home/ubuntu/cicd/old_build/app-$NEW_PORT-$TIMESTAMP.jar
fi

# ์ƒˆ๋กœ ๋ฐฐํฌ๋œ .jar ํŒŒ์ผ์„ app-$NEW_PORT.jar๋กœ ์ด๋ฆ„ ๋ณ€๊ฒฝ
NEW_JAR=$(ls /home/ubuntu/cicd/ZeroDownTimeDeployment-*.jar | head -n 1)
mv $NEW_JAR /home/ubuntu/cicd/app-$NEW_PORT.jar

# ์ƒˆ๋กœ ๋ฐฐํฌ๋œ app-$NEW_PORT.jar ํŒŒ์ผ ์‹คํ–‰
sudo chmod +x /home/ubuntu/cicd/app-$NEW_PORT.jar
sudo nohup java -jar -Dserver.port=$NEW_PORT /home/ubuntu/cicd/app-$NEW_PORT.jar > /home/ubuntu/console-$NEW_PORT.log 2>&1 &

sleep 20

# ํ—ฌ์Šค ์ฒดํฌ ํ•จ์ˆ˜
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
}

# ํ—ฌ์Šค ์ฒดํฌ
health_check $NEW_PORT
if [ $? -ne 0 ]; then
    sudo fuser -k $NEW_PORT/tcp
    sudo sed -i "/upstream backend {/,/}/ s|server 127.0.0.1:$NEW_PORT.*;|server 127.0.0.1:$CURRENT_PORT;|" $NGINX_CONFIG
    sudo service nginx reload
    echo "Deployment failed. Rolling back."
    exit 1
fi

# Nginx ์„ค์ • ํŒŒ์ผ์—์„œ proxy_pass ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ
NGINX_CONFIG="/etc/nginx/sites-available/default"

# Nginx ์„ค์ •์—์„œ canary ๋ฐฐํฌ ์„ค์ • ์—…๋ฐ์ดํŠธ
echo "Configuring Nginx for canary deployment with 30% traffic to new version..."
sudo sed -i "/upstream backend {/,/}/ s|server 127.0.0.1:$CURRENT_PORT;|server 127.0.0.1:$CURRENT_PORT weight=70; server 127.0.0.1:$NEW_PORT weight=30;|" $NGINX_CONFIG
sudo service nginx reload
# Nginx ์žฌ๋กœ๋“œ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ํ™•์ธ
if [ $? -ne 0 ]; then
		echo "Error: Failed to reload Nginx."
		exit 1
fi
sleep 10
# ์ถ”๊ฐ€ ๊ฐ€์ค‘์น˜ ๋ณ€๊ฒฝ
for weight in 50 70; do
    echo "Updating Nginx for canary deployment with $weight% traffic to new version..."
    sudo sed -i "/upstream backend {/,/}/ s|server 127.0.0.1:$CURRENT_PORT weight=[0-9]*; server 127.0.0.1:$NEW_PORT weight=[0-9]*;|server 127.0.0.1:$CURRENT_PORT weight=$((100-weight)); server 127.0.0.1:$NEW_PORT weight=$weight;|" $NGINX_CONFIG
    sudo service nginx reload
    # Nginx ์žฌ๋กœ๋“œ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ํ™•์ธ
		if [ $? -ne 0 ]; then
		    echo "Error: Failed to reload Nginx."
		    exit 1
		fi
		sleep 5
    health_check $NEW_PORT
    if [ $? -ne 0 ]; then
        sudo fuser -k $NEW_PORT/tcp
        sudo sed -i "/upstream backend {/,/}/ s|server 127.0.0.1:$CURRENT_PORT weight=[0-9]*; server 127.0.0.1:$NEW_PORT weight=[0-9]*;|server 127.0.0.1:$CURRENT_PORT;|" $NGINX_CONFIG
        sudo service nginx reload
        echo "Deployment failed. Rolling back."
        exit 1
    fi
done


# ์ตœ์ข… Nginx ํŠธ๋ž˜ํ”ฝ ์‹ ๋ฒ„์ „ ํฌํŠธ๋กœ 100% ๋ผ์šฐํŒ…
echo "Finalizing Nginx configuration with 100% traffic to new version..."
sudo sed -i "/upstream backend {/,/}/ s|server 127.0.0.1:$CURRENT_PORT weight=[0-9]*; server 127.0.0.1:$NEW_PORT weight=[0-9]*;|server 127.0.0.1:$NEW_PORT;|" $NGINX_CONFIG
sudo service nginx reload

# Nginx ์žฌ๋กœ๋“œ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ํ™•์ธ
if [ $? -ne 0 ]; then
    echo "Error: Failed to reload Nginx."
    exit 1
fi
sleep 5
health_check $NEW_PORT
if [ $? -ne 0 ]; then
    sudo fuser -k $NEW_PORT/tcp
    sudo sed -i "/upstream backend {/,/}/ s|server 127.0.0.1:$CURRENT_PORT weight=[0-9]*; server 127.0.0.1:$NEW_PORT weight=[0-9]*;|server 127.0.0.1:$CURRENT_PORT;|" $NGINX_CONFIG
    sudo service nginx reload
    echo "Deployment failed. Rolling back."
    exit 1
fi

# ๊ตฌ๋ฒ„์ „ JAR ํŒŒ์ผ์„ old_build ํด๋”๋กœ ์ด๋™ํ•˜๊ณ , ๊ตฌ๋ฒ„์ „ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
if [ "$CURRENT_PORT" -ne "$NEW_PORT" ]; then
    TIMESTAMP=$(date +"%Y%m%d%H%M%S")

    # ๊ตฌ๋ฒ„์ „ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
    OLD_PID=$(lsof -t -i:$CURRENT_PORT)
    if [ -n "$OLD_PID" ]; then
        sudo kill -15 $OLD_PID
        sleep 7
        # ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—ฌ์ „ํžˆ ์ข…๋ฃŒ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๊ฐ•์ œ ์ข…๋ฃŒ ์‹œ๋„
        if sudo kill -0 $OLD_PID 2>/dev/null; then
            echo "Process $OLD_PID did not terminate, killing with -9"
            sudo kill -9 $OLD_PID
        fi
    fi

    # ๊ตฌ๋ฒ„์ „ JAR ํŒŒ์ผ์„ old_build ํด๋”๋กœ ์ด๋™
    sudo mv /home/ubuntu/cicd/app-${CURRENT_PORT}.jar /home/ubuntu/cicd/old_build/app-${CURRENT_PORT}-${TIMESTAMP}.jar
    echo "Old version moved to old_build: app-${CURRENT_PORT}-${TIMESTAMP}.jar"
fi

echo "Canary deployment complete. New version is now serving 100% of traffic."
  1. ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํฌํŠธ ํ™•์ธ
    • ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด 8081 ํฌํŠธ์—์„œ ๋™์ž‘ ์ค‘์ธ์ง€ 8082 ํฌํŠธ์—์„œ ๋™์ž‘ ์ค‘์ธ์ง€ ํ™•์ธํ•˜์—ฌ CURRENT_PORT ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
    • ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•  ํฌํŠธ(NEW_PORT)๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ํฌํŠธ๊ฐ€ 8081์ด๋ผ๋ฉด, ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ 8082์— ๋ฐฐํฌ๋˜๊ณ  ๊ทธ ๋ฐ˜๋Œ€๋ผ๋ฉด 8081ํฌํŠธ์— ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.
  2. ๊ธฐ์กด JAR ํŒŒ์ผ ๋ฐฑ์—…
    • ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐฐํฌ๋  ํฌํŠธ์˜ ๊ธฐ์กด JAR ํŒŒ์ผ์„ old_build ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™์‹œ์ผœ ๋ฐฑ์—…ํ•ฉ๋‹ˆ๋‹ค. ๋ฐฑ์—… ํŒŒ์ผ๋ช…์—๋Š” ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค.
  3. ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ
    • ๋นŒ๋“œ๋œ JAR ํŒŒ์ผ์„ app-$NEW_PORT.jar๋กœ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์ƒˆ ํฌํŠธ์—์„œ ์‹คํ–‰์‹œํ‚ต๋‹ˆ๋‹ค.
  4. ํ—ฌ์Šค ์ฒดํฌ
    • ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ œ๋Œ€๋กœ ์‹คํ–‰๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด /actuator/health ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ๋Œ€ 10ํšŒ๋™์•ˆ ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.
    • ํ—ฌ์Šค ์ฒดํฌ์— ์‹คํŒจํ•˜๋ฉด ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•˜๊ณ , Nginx๋ฅผ ํ†ตํ•ด ๊ตฌ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์ƒˆ๋กœ์šด JAR ํŒŒ์ผ๋„ ๋ฐฑ์—…๋˜๊ณ , ์ด์ „ JAR ํŒŒ์ผ๋กœ ๋ณต์›๋ฉ๋‹ˆ๋‹ค.
  5. Nginx ์„ค์ • ์—…๋ฐ์ดํŠธ - ํŠธ๋ž˜ํ”ฝ 30%๋กœ ๋ผ์šฐํŒ…
    • Nginx ์„ค์ •์„ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๊ตฌ๋ฒ„์ „์— 70%, ์ƒˆ๋ฒ„์ „์— 30% ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค. ์„ค์ •์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ ์šฉ๋˜๋ฉด Nginx๋ฅผ reloadํ•ฉ๋‹ˆ๋‹ค.
  6. ์ ์ง„์  ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€ - 50%, 70%๋กœ ์—…๋ฐ์ดํŠธ
    • ์ผ์ • ์‹œ๊ฐ„์ด ๊ฒฝ๊ณผํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๊ฐ€๋Š” ํŠธ๋ž˜ํ”ฝ ๋น„์œจ์„ 50%, 70%๋กœ ์ ์ง„์ ์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค. ๊ฐ ๋‹จ๊ณ„๋งˆ๋‹ค ํ—ฌ์Šค ์ฒดํฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    • ๋งŒ์•ฝ ํ—ฌ์Šค ์ฒดํฌ๊ฐ€ ์‹คํŒจํ•˜๋ฉด Nginx ์„ค์ •์„ ์›๋ž˜๋Œ€๋กœ ๋˜๋Œ๋ฆฌ๊ณ , ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•˜์—ฌ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
  7. ์ตœ์ข… ํŠธ๋ž˜ํ”ฝ 100%๋กœ ์ „ํ™˜
    • ๋ชจ๋“  ํ—ฌ์Šค ์ฒดํฌ๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด Nginx ์„ค์ •์„ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ์„ 100% ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  8. ๊ตฌ๋ฒ„์ „ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ๋ฐ JAR ํŒŒ์ผ ๋ฐฑ์—…
    • ๊ตฌ๋ฒ„์ „ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋™์ž‘ ์ค‘์ธ ํฌํŠธ๋ฅผ ํ™•์ธํ•œ ํ›„, ์•ˆ์ „ํ•˜๊ฒŒ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
    • ๊ตฌ๋ฒ„์ „ JAR ํŒŒ์ผ์„ old_build ํด๋”๋กœ ์ด๋™์‹œ์ผœ ํƒ€์ž„์Šคํƒฌํ”„์™€ ํ•จ๊ป˜ ๋ฐฑ์—…ํ•ฉ๋‹ˆ๋‹ค.
  9. ์นด๋‚˜๋ฆฌ ๋ฐฐํฌ ์™„๋ฃŒ
    • ๋ฐฐํฌ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์œผ๋ฉฐ, ์ด์ œ ์ƒˆ๋กœ์šด ๋ฒ„์ „์ด 100%์˜ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

Nginx ์„ค์ •

sudo nano /etc/nginx/sites-available/default

 

upstream backend {
    server 127.0.0.1:8081; 
}
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;
    }
}

 

 

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

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

๊ตฌ ๋ฒ„์ „๊ณผ ์‹  ๋ฒ„์ „์ด ์„ค์ •ํ•ด๋‘” ๊ฐ€์ค‘์น˜์— ๋”ฐ๋ผ ๋ฒˆ๊ฐˆ์•„ ๋‚˜ํƒ€๋‚˜๋‹ค๊ฐ€ ๋งˆ์ง€๋ง‰์—” ์‹  ๋ฒ„์ „๋งŒ ๋œจ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๋Œ“๊ธ€