[TIG] 운영서버와 테스트서버 분리하기

JEONG KI MIN·2024년 11월 26일

TIG

목록 보기
10/12

실제 운영되는 서버와 테스트 하는 서버가 분리되어야 함을 알고 있었지만 다른 것들을 먼저 처리하느라 조금은 늦게 시작하게 되었다. 결론적으로 성공했지만 또 역대급 시간을 쏟은 과정이 되었다.


블루/그린 배포

기존에 블루/그린 배포를 완성해 둔 상태이다.
현재 그린환경이 배포되어 있는 상태라면 블루환경에서는 새로 개발한 코드 혹은 수정한 코드에 대한 테스트를 진행하고 프론트엔드와 연동해보는 작업을 해야했다.

여태까지는 새로운 환경 (예를들어 블루 환경)을 띄우면 nginx 를 통해 블루 환경으로 연결해주고 그린환경은 끄고 컨테이너를 삭제 했다.

하지만 이제는 기존 환경을 냅두고 테스트 용을 쓰는게 목표였다.

동적으로 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_backendtest_backend 값이 달라져야 했다.

따라서 map 을 사용했다.

environment 에 green 이라는 값이 들어오면 current_backendgreen_backend 로 매핑된다!

green "blue_backend"; 반드시 이와 같이 설정해야한다. 나는 GPT가 "green" blue_backend; 라고 알려줘서 이걸로 한참을 헤맸다...

nginx에 환경변수 주입하기

이제 nginx 파일에 환경변수를 주입하기만 하면 된다.
구글링을 통해 찾아보니 여러 방법이 있긴 했는데 가장 쉬운게 envsubst 명령어를 사용하는 것이었다.

크게 두가지 방법이 있는 것 같았다.

  1. CD 파일에서 직접 envsubst 명령어를 사용해서 주입
  2. docker-compose.yml 파일에서 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에 적용

이제 위의 과정을 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 으로 연결되도록 이어주었다!

결과는,,,

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

profile
열심히 해볼게요

2개의 댓글

comment-user-thumbnail
2024년 11월 26일

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

1개의 답글