
실제 운영되는 서버와 테스트 하는 서버가 분리되어야 함을 알고 있었지만 다른 것들을 먼저 처리하느라 조금은 늦게 시작하게 되었다. 결론적으로 성공했지만 또 역대급 시간을 쏟은 과정이 되었다.
기존에 블루/그린 배포를 완성해 둔 상태이다.
현재 그린환경이 배포되어 있는 상태라면 블루환경에서는 새로 개발한 코드 혹은 수정한 코드에 대한 테스트를 진행하고 프론트엔드와 연동해보는 작업을 해야했다.
여태까지는 새로운 환경 (예를들어 블루 환경)을 띄우면 nginx 를 통해 블루 환경으로 연결해주고 그린환경은 끄고 컨테이너를 삭제 했다.
하지만 이제는 기존 환경을 냅두고 테스트 용을 쓰는게 목표였다.
목표를 달성하려면 동적으로 nginx 의 변수를 수정하는것이 필요하다고 생각했다. 기존 nginx.conf 파일을 nginx.conf.template 으로 바꾸고 다음과 같이 작성했다.
파일명 또한 중요하다. 나는 nginx.template.conf 라고 했었다가 한 두어시간 날렸다 헣ㅎ
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 블루와 그린 환경 정의
upstream blue_backend {
server app-blue:8080; # 블루 환경 포트
keepalive 32;
}
upstream green_backend {
server app-green:8080; # 그린 환경 포트
keepalive 32;
}
# 현재 활성화된 환경 설정 (CD에서 이 값만 변경)
map ${environment} $current_backend {
default "blue_backend"; # 기본은 blue_backend
green "green_backend"; # 환경 변수를 green으로 설정 시 green_backend로 전환
}
# 테스트 환경 설정 (활성화된 환경과 반대로 매핑)
map $environment $test_backend {
default "green_backend"; # 기본값은 green_backend
green "blue_backend"; # 활성화된 환경이 green일 때, test는 blue로 연결
}
server {
listen 80;
# 공통 URL (api.tigleisure.com)
server_name api.tigleisure.com;
location / {
# 현재 활성화된 환경에 따라 동적으로 전환
proxy_pass http://$current_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;
}
# Health Check
location /tig/health {
proxy_pass http://$current_backend/tig/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /deploy/status {
proxy_pass http://$current_backend/deploy/status; # 활성화된 환경에 대한 요청 처리
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 활성 환경 상태 확인
location = /active-environment {
proxy_pass http://$current_backend/tig/health;
proxy_intercept_errors on;
error_page 502 503 504 = @blue_backend;
}
location @blue_backend {
return 200 "blue";
}
}
server {
listen 8085; # 테스트용 포트 (사용자 트래픽과 분리)
# 테스트용 서브도메인
server_name 테스트URL; // 공개하기에는 좀 그래서..!
location / {
# 테스트 환경은 활성화된 환경과 반대의 백엔드로 연결
proxy_pass http://$test_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;
}
# 테스트용 Health Check
location /tig/health {
proxy_pass http://$test_backend/tig/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
위와 같이 현재 돌고 있는 환경이 blue 인지 green 인지에 대해서 current_backend 와 test_backend 값이 달라져야 했다.
따라서 map 을 사용했다.
environment 에 green 이라는 값이 들어오면 current_backend 는 green_backend 로 매핑된다!
green "blue_backend";반드시 이와 같이 설정해야한다. 나는 GPT가"green" blue_backend;라고 알려줘서 이걸로 한참을 헤맸다...
이제 nginx 파일에 환경변수를 주입하기만 하면 된다.
구글링을 통해 찾아보니 여러 방법이 있긴 했는데 가장 쉬운게 envsubst 명령어를 사용하는 것이었다.
크게 두가지 방법이 있는 것 같았다.
envsubst 명령어를 사용해서 주입command 를 사용해서 주입두가지 방법 모두 엄청난 삽질을 진행했고, 결국 1번을 사용하여 성공했다.
사용법은 다음과 같다.
source /home/ubuntu/.env
export environment=$(grep environment /home/ubuntu/.env | cut -d '=' -f2)
envsubst '$environment' < /home/ubuntu/nginx.conf.template > /home/ubuntu/nginx.conf
순서대로 설명해보자면, 첫번째 명령어는 EC2 환경 내에 이미 있는 .env 파일을 사용하겠다는 의미이다.
두번째 명령어는 그 중 environment 라는 환경변수를 찾아 export 한다. (반드시 export 를 해야 envsubst 를 쓸 수 있다.)
세번째 명령어는 export 했던 환경변수를 nginx.conf.template 에 주입하고 이를 통해서 template 파일을 만든 다음 nginx.conf 파일로 만든다.
위의 명령어를 사용하기 위해서는 docker-compose.yml 에서 volume 을 설정해주어야 했다.
이미지와 같이 EC2 환경 내부의 /home/ubuntu/nginx.conf 파일을 docker 컨테이너 환경 내부의 /etc/nginx/nginx.conf 로 마운트 해주는 설정을 해주어야 한다.
결론적으로 이렇게 되면 template 파일에 환경변수가 주입되고 이를 통해 EC2 환경 내부에서 nginx.conf 가 만들어진다. 동시에 docker nginx 컨테이너 내부의 nginx.conf 파일로 마운트 된다.
이제 위의 과정을 CD 에 적용해보자.
if [ -n "$IS_GREEN" ]; then
echo "Currently running GREEN environment. Switching to BLUE."
echo "ENVSUBST start"
source /home/ubuntu/.env
# .env 파일을 읽고 export
export environment=$(grep environment /home/ubuntu/.env | cut -d '=' -f2)
# 환경 변수 확인
echo $environment
envsubst '$environment' < /home/ubuntu/nginx.conf.template > /home/ubuntu/nginx.conf
# 블루 환경 준비
docker-compose pull blue
docker-compose up -d blue
# Health Check
echo "Performing health check for BLUE"
MAX_RETRIES=10
RETRY_COUNT=0
until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8081/tig/health)" == "200" ]; do
echo "Waiting for BLUE environment to be healthy..."
sleep 5
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Health check for BLUE environment failed after $MAX_RETRIES retries."
exit 1
fi
done
echo "Deployment to BLUE is successful."
# docker-compose.yml에 이미 nginx.conf가 볼륨으로 연결되어 있어야 함
echo "Applying Nginx configuration..."
docker-compose pull nginx
docker-compose up -d nginx
# Nginx 설정 검증 및 재적용
docker exec nginx nginx -t
docker exec nginx nginx -s reload
# 그린 환경 정리
echo "Cleaning up GREEN environment"
docker-compose stop green
docker-compose rm -f green
docker rmi nimikgnoej/tig-web:green || true
echo "Removing unused images..."
docker image prune -f
현재 배포되어 있는 환경이 green 인 경우의 코드이다.
원래는 docker-compose pull green nginx 로 한번에 pull 해왔지만 nginx 이미지는 따로 환경변수 설정해줄 게 있어서 분리하는게 낫겠다 싶어 분리했다.
여기서 진짜 시간을 3억년 정도 사용했다.

새벽 4시에 뭐가 틀린지 보고 있는 내모습
내가 겪었던 대표적인 에러는 다음과 같다.
environment 가 주입이 안됨 -> nginx 컨테이너가 실행되다가 environment 변수가 뭔지 모르니까 stop 함 -> nginx 설정이 적용 안됨
아무튼 위와 같이 설정을 잘 해주었다면 실행이 잘 될 것이다.
위에서 말했던 것처럼 새롭게 띄운 환경(예를들면 블루 환경)이 아닌 기존의 환경을 내렸다가 테스트 서버로 다시 올리고자 했다.
그래서 CD 파일에 다음과 같이 추가 했다.
# test-green 배포
echo "Starting test-green environment for testing..."
docker pull nimikgnoej/tig-web:green
docker run -d -p 8082:8080 --env-file ./.env --name test-green nimikgnoej/tig-web:green
docker network connect ubuntu_app-network test-green
이 부분은 쉽다! 그냥 도커 이미지를 pull 하고 해당 이미지를 토대로 환경변수 파일을 넣어주면서 실행시켜준다.
추가로 nginx 와의 연결을 위해서 동일한 network 에 연결해준다.
이제 테스트용 서브도메인으로 요청을 날리면 test-green 컨테이너에 로그가 남아야 했다!
한번에 될리가 없지
이상하게 요청을 10번정도 날리면 7-8번 정도는 새롭게 뜬 환경인 blue 컨테이너에 로그가 남았고, 2-3번 정도는 내가 원하는 대로 테스트 컨테이너에 로그가 남았다.
????????????????????

이상한 점은 내가 원하는 대로 2-3번은 됐다는 것이다. 여기서 눈치를 채버렸다. 아 로드밸런서가 지금 똑같은? 것을 쓰고 있어서 랜덤하게 보내주는구나..?

위와 같이 설정되어 있었는데 tig-target 이라는 대상 그룹이 요청을 잘 받아서 두갈래로 랜덤하게 나누어주고 있는 중이라고 판단했다.
따라서 다음과 같이 새로 대상 그룹을 만들었다. 해당 대상그룹은 8085 포트로만 연결되도록 설정했다.

또한 테스트 URL 로 접근했을때 tig-test-group 으로 연결되도록 이어주었다!
결과는,,,

결국 해내고 말았다. 운영서버와 테스트 서버의 분리. 나 어쩌면 데브옵스에 재능이 있는 것 일까.

정말 유익한 글이에요! 잘 보고 갑니다