์์ ์ค์ต ๋ ํฌ์งํ ๋ฆฌ์ ํ๊ฒฝ ์ ํ ์ ์งํํ์ต๋๋ค.
๊ธฐ๋ณธ ์ ํ ์ ๋ํด ๊ถ๊ธํ์ ๋ถ์ด๋, ์์ธํ ๋ด์ฉ์ ์๋ ํฌ์คํ ์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
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."
- ํ์ฌ ์คํ ์ค์ธ Spring Boot ์ ํ๋ฆฌ์ผ์ด์
์ ํฌํธ ํ์ธ
- ํ์ฌ ์คํ ์ค์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ด 8081 ํฌํธ์์ ๋์ ์ค์ธ์ง 8082 ํฌํธ์์ ๋์ ์ค์ธ์ง ํ์ธํ์ฌ CURRENT_PORT ๋ณ์์ ์ ์ฅํฉ๋๋ค.
- ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌํ ํฌํธ(NEW_PORT)๋ฅผ ๊ฒฐ์ ํฉ๋๋ค. ํ์ฌ ์คํ ์ค์ธ ํฌํธ๊ฐ 8081์ด๋ผ๋ฉด, ์ ์ ํ๋ฆฌ์ผ์ด์ ์ 8082์ ๋ฐฐํฌ๋๊ณ ๊ทธ ๋ฐ๋๋ผ๋ฉด 8081ํฌํธ์ ๋ฐฐํฌ๋ฉ๋๋ค.
- ๊ธฐ์กด JAR ํ์ผ ๋ฐฑ์
- ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฐฐํฌ๋ ํฌํธ์ ๊ธฐ์กด JAR ํ์ผ์ old_build ๋๋ ํ ๋ฆฌ๋ก ์ด๋์์ผ ๋ฐฑ์ ํฉ๋๋ค. ๋ฐฑ์ ํ์ผ๋ช ์๋ ํ์์คํฌํ๊ฐ ์ถ๊ฐ๋์ด ๊ตฌ๋ถ๋ฉ๋๋ค.
- ์ ์ ํ๋ฆฌ์ผ์ด์
๋ฐฐํฌ
- ๋น๋๋ JAR ํ์ผ์ app-$NEW_PORT.jar๋ก ์ด๋ฆ์ ๋ณ๊ฒฝํ๊ณ ์ ํฌํธ์์ ์คํ์ํต๋๋ค.
- ํฌ์ค ์ฒดํฌ
- ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ ๋๋ก ์คํ๋๋์ง ํ์ธํ๊ธฐ ์ํด /actuator/health ์๋ํฌ์ธํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ต๋ 10ํ๋์ ํฌ์ค ์ฒดํฌ๋ฅผ ๋ฐ๋ณตํฉ๋๋ค.
- ํฌ์ค ์ฒดํฌ์ ์คํจํ๋ฉด ์ ์ ํ๋ฆฌ์ผ์ด์ ํ๋ก์ธ์ค๋ฅผ ์ข ๋ฃํ๊ณ , Nginx๋ฅผ ํตํด ๊ตฌ๋ฒ์ ์ผ๋ก ๋กค๋ฐฑํฉ๋๋ค. ์ด๋ ์๋ก์ด JAR ํ์ผ๋ ๋ฐฑ์ ๋๊ณ , ์ด์ JAR ํ์ผ๋ก ๋ณต์๋ฉ๋๋ค.
- Nginx ์ค์ ์
๋ฐ์ดํธ - ํธ๋ํฝ 30%๋ก ๋ผ์ฐํ
- Nginx ์ค์ ์ ์ ๋ฐ์ดํธํ์ฌ ๊ตฌ๋ฒ์ ์ 70%, ์๋ฒ์ ์ 30% ํธ๋ํฝ์ ๋ผ์ฐํ ํฉ๋๋ค. ์ค์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ ์ฉ๋๋ฉด Nginx๋ฅผ reloadํฉ๋๋ค.
- ์ ์ง์ ํธ๋ํฝ ์ฆ๊ฐ - 50%, 70%๋ก ์
๋ฐ์ดํธ
- ์ผ์ ์๊ฐ์ด ๊ฒฝ๊ณผํ ๋๋ง๋ค ์ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๊ฐ๋ ํธ๋ํฝ ๋น์จ์ 50%, 70%๋ก ์ ์ง์ ์ผ๋ก ์ฆ๊ฐ์ํต๋๋ค. ๊ฐ ๋จ๊ณ๋ง๋ค ํฌ์ค ์ฒดํฌ๋ฅผ ์ํํ์ฌ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ ์ ์ผ๋ก ๋์ํ๋์ง ํ์ธํฉ๋๋ค.
- ๋ง์ฝ ํฌ์ค ์ฒดํฌ๊ฐ ์คํจํ๋ฉด Nginx ์ค์ ์ ์๋๋๋ก ๋๋๋ฆฌ๊ณ , ์ ์ ํ๋ฆฌ์ผ์ด์ ํ๋ก์ธ์ค๋ฅผ ์ข ๋ฃํ์ฌ ๋กค๋ฐฑํฉ๋๋ค.
- ์ต์ข
ํธ๋ํฝ 100%๋ก ์ ํ
- ๋ชจ๋ ํฌ์ค ์ฒดํฌ๊ฐ ์ฑ๊ณตํ๋ฉด Nginx ์ค์ ์ ์ ๋ฐ์ดํธํ์ฌ ํธ๋ํฝ์ 100% ์ ๋ฒ์ ์ผ๋ก ์ ํํฉ๋๋ค.
- ๊ตฌ๋ฒ์ ํ๋ก์ธ์ค ์ข
๋ฃ ๋ฐ JAR ํ์ผ ๋ฐฑ์
- ๊ตฌ๋ฒ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋์ ์ค์ธ ํฌํธ๋ฅผ ํ์ธํ ํ, ์์ ํ๊ฒ ์ข ๋ฃํฉ๋๋ค.
- ๊ตฌ๋ฒ์ JAR ํ์ผ์ old_build ํด๋๋ก ์ด๋์์ผ ํ์์คํฌํ์ ํจ๊ป ๋ฐฑ์ ํฉ๋๋ค.
- ์นด๋๋ฆฌ ๋ฐฐํฌ ์๋ฃ
- ๋ฐฐํฌ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋์์ผ๋ฉฐ, ์ด์ ์๋ก์ด ๋ฒ์ ์ด 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 ๋ธ๋์น๋ฅผ ํธ์ํ์ฌ ๋กค๋ง์ ๋ต ๋ฌด์ค๋จ ๋ฐฐํฌ๋ฅผ ๋์ ํฉ๋๋ค.
๊ตฌ ๋ฒ์ ๊ณผ ์ ๋ฒ์ ์ด ์ค์ ํด๋ ๊ฐ์ค์น์ ๋ฐ๋ผ ๋ฒ๊ฐ์ ๋ํ๋๋ค๊ฐ ๋ง์ง๋ง์ ์ ๋ฒ์ ๋ง ๋จ๋ ๊ฒ์ ํ์ธํ ์ ์์์ต๋๋ค.
'DevOps > CI&CD' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฌด์ค๋จ ๋ฐฐํฌ ์ค์ต - ๋กค๋ง ์ ๋ต (0) | 2024.09.06 |
---|---|
๋ฌด์ค๋จ ๋ฐฐํฌ ์ค์ต - ๋ธ๋ฃจ๊ทธ๋ฆฐ ์ ๋ต (0) | 2024.08.18 |
๋ฌด์ค๋จ ๋ฐฐํฌ ์ค์ต ๊ธฐ๋ณธ ์ ํ (Github Actions, Nginx ํ์ฉ) (0) | 2024.08.13 |
๋ฌด์ค๋จ ๋ฐฐํฌ(Zero-downtime Deployment) ์ ๋ต (0) | 2024.07.23 |
CI/CD ๋ ๋ฌด์์ธ๊ฐ? (0) | 2024.07.17 |
๋๊ธ