본문 바로가기

AWS

nginx port 분리하여 blue/green 배포

728x90

 

dockerfile

FROM openjdk:11
ARG IDLE_PROFILE
ARG JAR_FILE=*.jar
ENV ENV_IDLE_PROFILE=$IDLE_PROFILE
COPY ${JAR_FILE} app.jar
RUN echo $ENV_IDLE_PROFILE
ENTRYPOINT ["java", "-Dspring.profiles.active=${ENV_IDLE_PROFILE}", "-jar","/app.jar"]

application.yml

spring:
  profiles:
    group:
      development:
        - common
        - development_database
        - port_8080
      production-set1:
        - common
        - production_database
        - port_9001
      production-set2:
        - common
        - production_database
        - port_9002

---

spring:
  config:
    activate:
      on-profile: common

---

spring:
  config:
    activate:
      on-profile: development_database
  datasource:
#    url: [테스트 DB]

---

spring:
  config:
    activate:
      on-profile: production_database
  datasource:
#    url: [운영 DB]

---

spring:
  config:
    activate:
      on-profile: port_8080

server:
  port: 8080

---

spring:
  config:
    activate:
      on-profile: port_9001

server:
  port: 9001

---

spring:
  config:
    activate:
      on-profile: port_9002

server:
  port: 9002

yml파일을 다음과 같이 분리하였습니다.

 

UtilController.java

package com.nick.deploy;


import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;

@RestController
@RequiredArgsConstructor
@RequestMapping("/utils")
public class UtilController {

    private final Environment env;

    @GetMapping("/profile")
    public String getProfile() {
        return Arrays.stream(env.getActiveProfiles()).findFirst().orElse("");
    }
}

해당하는 /utils/profile을 통해서 환경변수 설정되어있는 이름이 뭔지 파악하고 이 값에 따라 포트를 다르게 설정해줄 것 입니다.

 

pom.xml

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
			<version>2.6.0</version><!-- 원하는 버전으로 변경 -->
		</dependency>

actuator를 다음과 같이 설정하여 health check를 실시하여 해당하는 포트가 살아 있으면 포트를 바꿔주는 방식으로 진행하였습니다.

 

내가 하려고하는 아키텍처 구조

 

Nginx 설치
EC2에 아래 명령을 차례대로 입력하자

sudo apt update
sudo apt upgrade
sudo apt install nginx
sudo service nginx start
sudo service nginx status

nginx 를 ec2안에서 설치해줍니다.

 

/etc/nginx/conf.d/service-url.inc 해당하는 경로에 service-url.inc파일을 만들어줍니다.

service-url.inc

set $service_url http://127.0.0.1:9002;

 

다음처럼 서비스포트 환경변수를 설정해줍니다.

 

/etc/nginx/sites-available/default 

해당하는 경로에 

default에 다음소스를 추가해줍니다.

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        include /etc/nginx/conf.d/service-url.inc;
        location / {

                proxy_pass $service_url;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }
       location /utils/profile {
                proxy_pass $service_url;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                # 그 외 설정...
        }

   }

내가 여기서 겪었던 이슈는 / 프록시만 있으면 그 하위에 있는 모든 프록시 경로를 다 낚아채주는 줄 알았지만 아니였다

/utils/profile 처럼 프록시를 설정을 해주어야만 해당하는 경로로 들어 왔을때 proxy_pass로 해당하는 경로로 프록시설정을 해주는 것을 파악하였다.

 

다음은 실제 jenkins를이용해서 git clone을 통해 긁어온 소스파일들 중 dockerfile이 있는 경로에서 

docker build 를 실시하고 docker run을 통해 해당하는 컨테이너를 실행하고 만약 9091이 띄워져있으면 9092포트로 띄우고

9092 health check하여 제대로 배포되었으면 9091포트를 죽이는방식의 쉘스크립트이다 . deploy.sh 와 swithch.sh 로 두개로 

나눠서 사용하였다.

 

deploy.sh

#!/bin/bash
echo "> 현재 구동중인 profile 확인"
CURRENT_PROFILE=$(curl -s http://localhost/utils/profile)
echo "> $CURRENT_PROFILE"

if [ "$CURRENT_PROFILE" == "production-set1" ]
then
  IDLE_PROFILE="production-set2"
  IDLE_PORT=9002
elif [ "$CURRENT_PROFILE" == "production-set2" ]
then
  IDLE_PROFILE="production-set1"
  IDLE_PORT=9001
else
  echo "> 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE"
  echo "> set1을 할당합니다. IDLE_PROFILE: set1"
  IDLE_PROFILE="production-set1"
  IDLE_PORT=9001
fi

IMAGE_NAME="app_server"
TAG_ID=$(docker images | sort -r -k2 -h | grep "${IMAGE_NAME}" | awk 'BEGIN{tag = 1} NR==1{tag += $2} END{print tag}')

echo "> 도커 build 실행 : docker build --build-arg IDLE_PROFILE=${IDLE_PROFILE} -t ${IMAGE_NAME}:${TAG_ID} ."
docker build --build-arg IDLE_PROFILE=${IDLE_PROFILE} -t ${IMAGE_NAME}:${TAG_ID} /home/ubuntu/app/step1/realdeploy-test

echo "> $IDLE_PROFILE 배포"
echo "> 도커 run 실행 :  sudo docker run --name $IDLE_PROFILE -d --rm -p $IDLE_PORT:${IDLE_PORT} ${IMAGE_NAME}:${TAG_ID}"
docker run --name $IDLE_PROFILE -d --rm -p $IDLE_PORT:${IDLE_PORT} ${IMAGE_NAME}:${TAG_ID}

echo "> $IDLE_PROFILE 10초 후 Health check 시작"
echo "> curl -s http://localhost:$IDLE_PORT/actuator/health "
sleep 10

for retry_count in {1..10}
do
  response=$(curl -s http://localhost:$IDLE_PORT/actuator/health)
  up_count=$(echo "$response" | grep 'UP' | wc -l)

  if [ $up_count -ge 1 ]
  then
    echo "> Health check 성공"
    break
  else
    echo "> Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다."
    echo "> Health check: ${response}"
  fi

  if [ $retry_count -eq 10 ]
  then
    echo "> Health check 실패. "
    echo "> Nginx에 연결하지 않고 배포를 종료합니다."
    exit 1
  fi

  echo "> Health check 연결 실패. 재시도..."
  sleep 10
done

echo "> 스위칭을 시도합니다..."
sleep 5

/home/ubuntu/app/step1/realdeploy-test/switch.sh

switch.sh

#!/bin/bash
echo "> 현재 구동중인 Port 확인"
CURRENT_PROFILE=$(curl -s http://localhost/utils/profile)

if [ "$CURRENT_PROFILE" == "production-set1" ]
then
  CURRENT_PORT=9001
  IDLE_PORT=9002
elif [ "$CURRENT_PROFILE" == "production-set2" ]
then
  CURRENT_PORT=9002
  IDLE_PORT=9001
else
  echo "> 일치하는 Profile이 없습니다. Profile:$CURRENT_PROFILE"
  echo "> 9001을 할당합니다."
  IDLE_PORT=9001
fi

echo "> 현재 구동중인 Port: $CURRENT_PORT"
echo "> 전환할 Port : $IDLE_PORT"
echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc

echo "> ${CURRENT_PROFILE} 컨테이너 삭제"
sudo docker stop $CURRENT_PROFILE
sudo docker rm $CURRENT_PROFILE

echo "> Nginx Reload"

sudo service nginx reload

nginx reload 를 사용하게되면 프록시 설정이 0.1초만에 일어나기 때문에 

무중당배포라고 볼 수 있을 것 같다. 끊김없이 바로바로 진행되기때문에 문제가 없다.

 

728x90