Spring Boot + nginx + Github Actions로 로드 밸런싱, 무중단 배포 적용하기

soluinoon·2023년 8월 3일
3
post-thumbnail

🚨 주의!! 너무 많은 인스턴스와 서비스 사용은 엄청난 과금을 초래할 수 있습니다.

개발환경

  • MacOS(개발), Ubuntu(서버)
  • Spring Boot 2.7.10
  • Docker 20.10.17, Docker Compose
  • Github Actions
  • nginx
  • AWS ec2 t2.micro, RDS, S3

Code Depoly 사용하지 않았습니다.

로드 밸런싱과 무중단 배포 동시 적용해보기


24트만에 성공....

로드 밸런싱을 적용하면서 개발환경을 개선하고 싶어 blue/green 무중단 배포 방식도 적용해봤습니다.
적용하다 보니 Docker도 적용해서 초기 인프라 구성도 간편화됐네요.

들어가기에 앞서

검색하면 로드 밸런싱과 무중단 배포를 동시에 적용한 게시물이 없더라구요.
그만큼 헤딩을 정말 많이하고 적용시킬게 많다보니 글이 복잡할 수도 있습니다. 😅
최대한 인덱스를 확인하시면서 필요한 정보만 가져가세요.

적용하면서 많은 오류를 맞닥뜨렸지만, 모든 오류가 기억나진 않습니다 ㅠㅠ.
제가 작성한 것 외에도 구글링 하면 다 나오는 것들이니까 한번 찾아보시고
적용이 안된다면 댓글 남기시면 도와드리겠습니다.

설계

기존에는 인스턴스도 하나, WAS도 하나라서 Github Actions에서 main에 push나 merge가 된다면
기존 프로세스를 끄고 재배포를 진행했습니다.

목표인 아키텍쳐 구조입니다.

  • Docker를 사용한 WAS/웹서버 격리
    격리를 통해 일관된 환경을 사용할 수 있고, 만들어놨던 이미지를 통해 손쉽게 인프라를 구축할 수 있습니다.

  • nginx를 통한 로드 밸런싱
    요청을 8080, 8081에 띄워진 WAS로 나눠 부하를 분산시킬 수 있습니다.

  • Github Actions와 nginx를 통한 무중단 배포 (blue/green)
    blue/green 둘 중 하나만 쓰다가, 변경사항이 감지되면 멈춰있던 인스턴스에 배포를 진행하고 nginx 설정을 변경해 배포가 진행된 인스턴스로 요청을 보냅니다.
    중단(사실은 nginx를 리로드하며 잠깐의 틈이 생김)없이 서버를 운영할 수 있습니다.

평소에 쉬고있는 인스턴스가 아깝긴 하지만, 무중단 배포와 부하분산 두 가지를 모두 잡으려면 이게 BEST라고 생각했습니다.

구현

Spring

Profile 설정


spring:
  profiles: # profile이 아니라 profiles 입니다!!!
    active: local
    group:
      local: common, local-db
      prod1: common, prod-db, prod1-server
      prod2: common, prod-db, prod2-server

server:
  env: blue # 나중에 어떤 서버인지 체크하기 위해 꼭 필요, 초기 배포 때 변경

---

spring:
  config:
    activate:
      on-profile: common

servlet:
  multipart:
    max-file-size: 20MB
    max-request-size: 20MB

application:
  name: 

serverName: common
---

spring:
  config:
    activate:
      on-profile: local-db

datasource:
  url: 
  username: 
  password: 

jpa:
  hibernate:
    ddl-auto: update
  properties:
    hibernate:
      format_sql: true
      show_sql: true
dbInfo: h2

---

spring:
  config:
    activate:
      on-profile: prod-db

datasource:
  url: 
  username: 
  password: 

jpa:
  hibernate:
    ddl-auto: update
  properties:
    hibernate:
      format_sql: true
      show_sql: true
      
dbInfo: rds

---

spring:
  config:
    activate:
      on-profile: prod1-server

serverName: hel-gather-prod1

server:
  port: 8080

---

spring:
  config:
    activate:
      on-profile: prod2-server

serverName: hel-gather-prod2

server:
  port: 8081

---

profiles를 나눠서 설정해줍니다.

  • local : default 값으로, 초기 구동 때 프로필을 넘기지 않으면 실행됩니다.
  • prod1 : 8080 포트에 띄워질 프로필 입니다.
  • prod2 : 8081 포트에 띄워질 프로필 입니다.


정상적이라면 실행했을 때 그룹이름을 포함한 프로필이 출력되야 합니다!
local: common, local-db라면 사진처럼 local, common, local-db가 뜨는게 맞습니다.

실행 예시
java -jar ???.jar -Dspring.profiles.active=prod1 -Dserver.env=blue

주석을 달아놓은 server.env는 나중에 nginx에서 어떤 서버가 켜져있는지 확인하기 위해 만들어놓은 변수입니다.
default는 blue이고, 나중에 green 인스턴스 위에서 띄울 때는 green으로 입력을 넣어 변경할 것 입니다.

주의사항 1. 꼭 통합 테스트를 진행해주세요!!!

제가 직전에 작성한 게시글에서 알 수 있듯이, application.yml이 잘못되면 통합테스트에서 인식을 못합니다.
꼭 통합 테스트를 실행해주세요.

Health Check를 위한 Controller 추가

ServerController.java

@RestController
public class ServerController {
    @Value("${serverName}") // application.yml의 값들을 매핑시킴
    private String serverName;
    @Value("${dbInfo}")
    private String dbInfo;
    @Value("${server.env}")
    private String env;
    private Integer visitedCount = 0;

    @GetMapping("/getServerInfo")
    public ResponseEntity<Map<String, String>> getServerInfo() {
        visitedCount++;

        Map<String, String> serverInfo = new HashMap<>();
        serverInfo.put("ServerName:", serverName);
        serverInfo.put("visitedCount:", visitedCount.toString());
        serverInfo.put("dbInfo", dbInfo);
        serverInfo.put("env:", env);

        return ResponseEntity.ok(serverInfo);
    }

    @GetMapping("/env")
    public String getEnv() {
        return env;
    }

정상적으로 작동된다면, 요청을 보내면 다음과 같이 나옵니다.


이 API를 통해서 로드 밸런싱이 잘 작동하고 있는지, blue/green 중에 어떤 환경에서 배포 중인지 알 수 있습니다.

Dockerfile 작성

FROM amazoncorretto:11-alpine-jdk # 어떤 이미지 위에서 구동할 것인지

# ARG로 프로필 변수 정의
ARG JAR_FILE=./build/libs/~~~-0.0.1-SNAPSHOT.jar # 프로젝트 빌드 JAR 파일
ARG PROFILES # 이건 나중에 docker-compose에서 초기화 해줍니다.
ARG ENV

# JAR 파일 메인 디렉토리에 복사
COPY ${JAR_FILE} app.jar

# 시스템 진입점 정의
ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILES}", "-Dserver.env=${ENV}", "-jar", "/app.jar"]

생각해볼 점 1. 개발환경과 똑같은 이미지 vs 경량화 이미지

저는 amazoncorretto:11-alpine-jdk를 선택했고, 경량화와 클라우드에서 띄우는데 최적화된 이미지라 생각해 선택했습니다.
하지만 도커에 대해 공부하면서 어떤 이미지를 선택해야 하는지 의견이 많이 갈리더라구요.
어떤 분은 '개발 환경과 완전히 똑같은 이미지를 사용해야 사전에 버그를 예방할 수 있다' 라고 주장하시고,
또 어떤 분은 '경량화 이미지를 사용해도 큰 문제가 없고, 똑같은 이미지를 사용한다면 크기가 엄청 커져 불편하다'라고 주장하셨습니다.
선택은 여러분의 몫 입니다.

생각해볼 점 2. ./gradlew는?

제가 작성한 Dockerfile은 ./gradlew를 자동으로 진행하지 않습니다.
그 이유는 Github Actions에서 따로 프로젝트를 build해서 jar파일을 최신으로 만들어주기 때문입니다.

인프라 세팅

인스턴스


위에 구조처럼 인스턴스를 3개 만들어줍니다.

생각해볼 점 1. 인바운드 룰 구성

요청의 흐름은 다음과 같습니다.

  • 요청 -> nginx의 80포트
  • nginx의 80포트 -> blue/green의 8080포트
  • nginx의 80포트 -> blue/green의 8081포트

그렇다면, 인바운드 룰은 어떻게 세팅해야 할까요?
아직 개발단계라 가정하면, 필수는 다음과 같습니다.

  • nginx
    요청을 보내는 본인 IP의 80(HTTP)포트 (실서비스 중이라면 모두에게 오픈되어 있겠죠?)
  • blue/green
    nginx 인스턴스 IP의 8080, 8081 포트

저는 시큐리티 그룹을 통합해서 하나로 쓰지만, 세세하게 설정하신 분들은 정확하게 설정해주세요.
이 밖에도 접속을 위한 SSH가 있겠죠?

blue/green

blue/green 인스턴스에서는 간단합니다. Docker Image를 받아와서 띄워주기만 하면 되니까요.
초기세팅에서는 다음 과정이 필요합니다.

  1. 도커 설치
  2. 도커 컴포즈 설치
  3. 도커 허브 로그인
  4. 도커 컴포즈 작성 및 실행 -> WAS 구동

각자 환경에 맞는 도커와 컴포즈를 설치해주세요. 저는 우분투라 이쪽 게시물을 참고했습니다.
공식 문서를 참고하셔도 좋습니다.

도커허브 계정이 없으시다면 도커허브 홈페이지에서 회원가입 해주세요.

마지막으로 도커 컴포즈 파일을 작성해보겠습니다.

docker-compose.yml (green)

version: '3.8'

services:
  prod1:
    image: 도커계정/올렸던 이미지:태그
    container_name: prod1
    ports:
      - "8080:8080" # 호스트의 8080 포트와 컨테이너의 8080 포트를 연결
    environment:
      - PROFILES=prod1 # app1의 프로파일을 지정
      - ENV=green 

  prod2:
    image: 도커계정/올렸던 이미지:태그
    container_name: prod2
    ports:
      - "8081:8081" # 호스트의 8081 포트와 컨테이너의 8081 포트를 연결
    environment:
      - PROFILES=prod2 # app2의 프로파일을 지정
      - ENV=green

제가 docker-compose를 세세하게 아는건 아니라서, 가장 기본적인 사항만 작성했습니다.
PROFILES, ENV는 이전 Dockerfile를 작성할 때 선언했던 변수들을 초기화 시켜줍니다.
blue 인스턴스에는 ENV만 blue로 바꿔서 작성하면 되겠죠?

docker-compose.yml (blue)

version: '3.8'

services:
  prod1:
    image: 도커계정/올렸던 이미지:태그
    container_name: prod1
    ports:
      - "8080:8080" # 호스트의 8080 포트와 컨테이너의 8080 포트를 연결
    environment:
      - PROFILES=prod1 # app1의 프로파일을 지정
      - ENV=blue

  prod2:
    image: 도커계정/올렸던 이미지:태그
    container_name: prod2
    ports:
      - "8081:8081" # 호스트의 8081 포트와 컨테이너의 8081 포트를 연결
    environment:
      - PROFILES=prod2 # app2의 프로파일을 지정
      - ENV=blue

명령어는 다음과 같습니다.

docker-compose up -d

blue/green 중 하나의 인스턴스에서만 실행해주세요. 그래야 무중단 배포가 가능합니다.

nginx

엔진엑스 인스턴스에서는 엔진엑스 도커 컨테이너 하나만 실행해줍니다.
마찬가지로 도커를 설치해주세요.
이번엔 compose를 작성하지 않고 직접 이미지를 받아와서 설치했습니다.

sudo su # docker는 관리자 모드로 해야합니다. 사용자 추가는 아래서 다루겠습니다.

docker pull nginx # nginx 이미지 받아오기

# nginx를 webserver로 이름짓고 실행, -d는 데몬(백그라운드)로, -p는 포트 할당입니다.
docker container run --name webserver -d -p 80:80 nginx

# bash를 통해 nginx를 들어가봅니다.
docker exec -it webserver bash

여기까지 하시면 nginx 컨테이너 안으로 들어옵니다.
하지만! 편하게 설정을 변경하시려면 몇가지 추가 옵션을 설치해주셔야 합니다.

생각해볼 점 1. 기본 nginx 이미지엔 Vim이 없다.

💡 Vim이란?
Vim은 커맨드 라인 인터페이스에서 사용하는 텍스트 에디터 툴 입니다.
nano도 있지만, 하이라이트 기능이 없어서 보기 어렵습니다.

기본 엔진엑스 이미지엔 Vim을 비롯한 몇가지 기능들이 빠져있습니다.
따라서 수동으로 설치해야 합니다.

sudo apt-get install vim

추가적으로 포트 상태를 확인할 수 있는 netstat을 위한 net-tools도 설치하면 좋습니다.

sudo apt-get install net-tools

이 밖에도 필요하신게 있다면 설치 후 자신만의 커스텀 이미지를 만들어주시면 되겠습니다.

설정

먼저, etc/nginx/conf.d/에서 default.conf를 변경하시면 됩니다. 저는 이름을 변경하겠습니다.

cp default.conf xxx.conf
rm default.conf

이름을 변경해도 동작하는 이유는 etc/nginx/nginx.conf에서 다음과 같이 와일드 카드로 conf.d에 있는 모든 conf를 포함하기 때문입니다.

nginx.conf
...
include /etc/nginx/conf.d/*.conf;
...

이제 저희가 이름을 변경한 설정파일을 바꿔보겠습니다.

xxx.conf

# upstream : 상류라는 의미, 상류(엔진엑스)에서 하위(blue/green)로 흘려보낸다고 생각하면 됨.
# 기본전략은 Round Robin으로, 한번씩 번갈아가면서 요청을 보낸다.
# 추가적으로 설정하면 Least-connected, ip hash 등으로 설정할 수 있다.
upstream blue {
   server 111.111.111.111:8080; # prod1 << health check target
   server 111.111.111.111:8081; # prod2
}

upstream green {
   server 222.222.222.222:8080; # prod1 << health check target
   server 222.222.222.222:8081; # prod2
}

server {
	# 80포트를 사용
    listen       80; # IPv4 access
    listen  [::]:80; # IPv6 access
    server_name  xxx-webserver;

    access_log  /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

	# 아래에서 작성할 변수 다루는 파일
    include /etc/nginx/conf.d/service-env.inc;

    location / {
    	# 여기서 수정됨. -가 아닌_인 것에 주의
        proxy_pass http://$service_env/env
        
        # 아래는 어떤 IP가 들어왔는지 확인할 수 있는 설정
        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;
        
        ....

이렇게 설정하고 include /etc/nginx/conf.d/service-env.inc;에서 사용되는 service-env.inc를 작성해봅시다.

service-env.inc

set $service_url blue;

저렇게 한줄만 써주시면 됩니다. 초기설정은 blue이므로 nginx에서 blue 인스턴스의 prod1, prod2로 Round Robin 전략으로 로드 밸런싱을 해줄 것 입니다.

이제 blue/green을 바꾸고 싶다면 service-env.inc를 변경해서 green으로 바꿔주면 되겠죠?

마지막으로 엔진엑스 설정을 바꿨으니 리로딩을 해줍니다.

nginx -s reload

점검

여기까지 오셨다면 다음과 같은 상태여야 합니다.

  • blue
    docker-compose.yml가 있고 실행시켜서 2개의 WAS 컨테이너가 실행 중이다.
  • green
    docker-compose.yml이 있지만, 실행시키지 않아서 아무것도 실행 중이지 않다.
  • nginx
    nginx 컨테이너가 구동 중이다.

여기서 nginx ip로 /env 요청을 보내보면 blue가 나타나는게 정상입니다.
http://{nginx ip}/env

또, getServerInfo를 요청하면 prod1, prod2가 번갈아가며 나타나는게 정상입니다.
http://{nginx ip}/getServerInfo

{
    "dbInfo": "rds",
    "env:": "blue",
    "ServerName:": "hel-gather-prod1",
    "visitedCount:": "1"
}
{
    "dbInfo": "rds",
    "env:": "blue",
    "ServerName:": "hel-gather-prod2",
    "visitedCount:": "2"
}
{
    "dbInfo": "rds",
    "env:": "blue",
    "ServerName:": "hel-gather-prod1",
    "visitedCount:": "3"
}

이제 blue/green 배포를 위한 Github Actions를 작성해보겠습니다.

Github Actions

name: hel-gather prod deploy

# 메인에 푸쉬나 피알되면 이벤트 발생
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read


jobs:
  # jobs는 build, deploy 2개가 있습니다.
  # jobs는 다른 러너(환경)에서 구동됩니다.
  # step는 같은 러너에서 구동됩니다.
  build:
    runs-on: ubuntu-latest
	
    steps:
    # 러너에 JDK 설치
    - uses: actions/checkout@v3
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin'
        
    # 러너에서 gradle을 사용해 빌드 진행
    - name: Build with Gradle
      run : ./gradlew clean build #우 리가 도커에서 생략했던 부분
      
    # 도커허브에 로그인
    - name: Login to DockerHub
      uses: docker/login-action@v1
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }} # 요건 Github에서 관리하는 시크릿, 아래서 다루겠습니다.
        password: ${{ secrets.DOCKERHUB_TOKEN }}
        
    # 도커 빌드
    - name: Build Docker
      # macOS는 arm64이기 때문에 옵션 없이 빌드해서 보내면
      # amd64인 리눅스에선 오류가 발생합니다.
      run : docker build --platform linux/amd64 -t ${{ secrets.DOCKERHUB_USERNAME }}/hel-gather .
      
	# 빌드한 이미지를 푸쉬해줍니다.
    - name: Docker push
      run : docker push ${{ secrets.DOCKERHUB_USERNAME }}/hel-gather:latest
      
  # 이제 다음 job으로 옵니다. 다른 환경 입니다.
  deploy:
    needs: build # 빌드가 성공해야만 진행됩니다.
    runs-on: ubuntu-latest
    steps:
      # 깃허브 액션 러너의 아이피를 얻어온다.
      - name: Get Github action IP
        id: ip
        uses: haythem/public-ip@v1.2
      
      # 환경변수 설정.
      - name: Setting environment variables
        run: |
          echo "AWS_DEFAULT_REGION=ap-northeast-2" >> $GITHUB_ENV
      
      # AWS 설정
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          # 아이엠 키 설정, 인바운드 룰 수정을 위해 설정합니다.
          # IAM 설정하실 때, AWSEC2FullAccess 권한을 부여해주세요.
          # 더 자세한 사항은 제 블로그 이전 Github Actions 게시물을 참고해주세요.
           aws-access-key-id: ${{ secrets.AWS_IAM_ACCESS_KEY_ID }} 
           aws-secret-access-key: ${{ secrets.AWS_IAM_SECRET_KEY }} 
           aws-region: ${{ env.AWS_DEFAULT_REGION }}
      
      # 깃허브 액션의 아이피를 인바운드 룰에 임시 등록
      # 22, 80, 8080 포트를 추가합니다.
      - name: Add Github Actions IP to Security group
        run: |
          aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32    
          aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 80 --cidr ${{ steps.ip.outputs.ipv4 }}/32 
          aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 8080 --cidr ${{ steps.ip.outputs.ipv4 }}/32 
          
      # 블루/그린 헬스체크로 변수 초기화
      - name: Blue/Green health check
        run: |
          echo "INSTANCE_ENV=$(curl -s "http://${{ secrets.NGINX_IP }}/env")" >> $GITHUB_ENV
          
      # 헬스 체크를 통한 인스턴스 체크, 어떤 환경이 운영중인가?
      - name: Set target ip
      	# prod1, prod2 둘 다 검사해야 하지만, 현재는 prod1만 검사
      	# CURRENT_UPSTREAM : env로 요청 보내서 blue 인지, green인지 알아옴. 이걸 curl로 해서 변수로 만든다. 즉 blue,green,오류 중에 하나
        # CURRENT_IP : 만약 환경이 blue라면 blue 인스턴스의 prod1의 ip(111.111.111.111:8080)가 들어감
        # STOPPED_IP : 만약 환경이 blue라면 멈춰있는 green의 prod1 ip가 들어감.
        # TARGET_UPSTREAM : 바꿀 env
        run: |
          CURRENT_UPSTREAM=$(curl -s "http://${{ secrets.NGINX_IP }}/env")
          echo $CURRENT_UPSTREAM
          if [ $CURRENT_UPSTREAM = "blue" ]; then
            echo "CURRENT_IP=${{ secrets.BLUE_IP }}" >> $GITHUB_ENV
            echo "STOPPED_IP=${{ secrets.GREEN_IP }}" >> $GITHUB_ENV
            echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV
            echo "blue"
          elif [ $CURRENT_UPSTREAM = "green" ]; then
            echo "CURRENT_IP=${{ secrets.GREEN_IP }}" >> $GITHUB_ENV
            echo "STOPPED_IP=${{ secrets.BLUE_IP }}" >> $GITHUB_ENV
            echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV
            echo "green"
          else
            echo "error"
            exit 1
          fi
          
      # 멈춰있는 서버에 있는 도커 컴포즈 실행, 혹시 실행되고 있는 인스턴스가 있을 수 있으므로, 추후에 중단하는 작업도 추가해야함.
      - name: Execute Server Docker compose
        uses: appleboy/ssh-action@master
        with:
          username: ${{ secrets.SSH_USER }}
          host: ${{ env.STOPPED_IP }} # 위에서 지정한 환경변수
          key: ${{ secrets.SSH_KEY }}
          script_stop: true
          script: |
            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/hel-gather
            docker-compose up -d
      
      # 새로운 인스턴스에 헬스체크
      - name: Check the deployed service URL
        uses: jtalk/url-health-check-action@v3
        with:
          # 원래라면 8081도 하는게 맞음. 하고 싶다면 인바운드 룰에 8081도 추가해야 함.
          url: http://${{ env.STOPPED_IP }}:8080/env
          # 총 5번 하는데, 15초의 간격을 두고함. 이때까지 응답이 정상이 아니라면 배포 실패
          max-attempts: 5 # Optional, defaults to 1
          retry-delay: 15s # Optional, only applicable to max-attempts > 1
        
      # 엔진엑스의 프록시 변경
      - name: Change nginx upstream
        uses: appleboy/ssh-action@master
        with:
          username: ${{ secrets.SSH_USER }}
          host: ${{ secrets.NGINX_IP }}
          key: ${{ secrets.SSH_KEY }}
          script_stop: true
          # 도커로 들어가서 service_env를 바꿔주고 reload
          # 여기서 -i가 아닌 -it로 진행하면 오류가 발생하고, -c가 없으면 도커가 아닌 호스트에서 경로를 찾는다. 주의
          script: |
            docker exec -i webserver bash -c 'echo "set \$service_env ${{ env.TARGET_UPSTREAM }};" > /etc/nginx/conf.d/service-env.inc && service nginx reload'
        
      # 기존 인스턴스 중단
      - name: Terminate prev instance
        uses: appleboy/ssh-action@master
        with:
          username: ${{ secrets.SSH_USER }}
          host: ${{ env.CURRENT_IP }}
          key: ${{ secrets.SSH_KEY }}
          script_stop: true
          script: | 
            docker stop prod1
            docker stop prod2
            docker rm prod1
            docker rm prod2
            
      # 깃허브 러너 아이피를 인바운드 룰에서 제거
      - name: Remove Github Actions IP from security group
      	# if: always()를 해놓으면 무조건 실행됨. 따라서 위에서 deploy가 실패해도 인바운드 룰로 열어놨던 ip를 모두 닫음.
        if: always()
        run: |
          aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
          aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 80 --cidr ${{ steps.ip.outputs.ipv4 }}/32
          aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 8080 --cidr ${{ steps.ip.outputs.ipv4 }}/32

Github Secrets

Secret 설정 방법은 다음 게시물을 참고 부탁드립니다.

Secrets들을 한번 정리해드리겠습니다.

  • DOCKERHUB_USERNAME
    도커 허브 아이디 입니다.

  • DOCKERHUB_TOKEN
    도커 허브 패스워드 입니다.

  • AWS_IAM_ACCESS_KEY_ID
    AWS IAM의 엑세스 키 입니다. 이전 Github Actions 게시물을 참고해주세요.

  • AWS_IAM_SECRET_KEY
    AWS IAM의 시크릿 키 입니다.

  • NGINX_IP
    nginx 인스턴스의 퍼블릭 아이피 입니다. (ipv4)

  • BLUE_IP
    블루 인스턴스의 퍼블릭 아이피 입니다. (ipv4)

  • GREEN_IP
    그린 인스턴스의 퍼블릭 아이피 입니다. (ipv4)

  • SSH_USER
    여러분이 ec2를 ssh로 접속하실 때 사용하는 유저 id 입니다.
    예를들어,

    ssh -i "사용할 키" {{id}}@ec2-227.ap-northeast-2.compute.amazonaws.com

    에서 {{id}}에 오는 부분을 설정합니다.

  • SSH_KEY
    위의 명령어에서 사용할 키를 텍스트 편집기로 여신 다음에, 나오는 전체를 복사해서 넣어주시면 됩니다. RSA키 기준으로 다음 포맷이 나타납니다.

    -----BEGIN RSA PRIVATE KEY-----
    쌀라쌀라
    -----END RSA PRIVATE KEY-----

    주의할 점으로 ---BEGIN 부분과 ---END 부분 모두 포함해야 합니다!!

마치며

아직 부족한 부분이 많습니다. 예를 들어 헬스 체크를 prod1에만 진행하는 점, 번잡한 스크립트 등등...
3일을 쏟아부어 완성하며 힘들었지만, 다운타임이 원래 대략 16~18초 정도 나타났는데 1초도 안되게 줄어드니 뿌듯합니다.
긴 글 읽어주셔서 감사합니다. 🙇🏻

profile
수박개 입니다.

3개의 댓글

comment-user-thumbnail
2024년 2월 23일

혹시 docker-compose 파일의 위치는 각 서버의 어느 디렉토리에 놓던 상관없나요?

1개의 답글
comment-user-thumbnail
2024년 5월 23일

저는 jenkins를 사용하고 있지만 nginx 로드밸런싱 , blue/green 무중단 배포 관련해서 큰 도움이 되었습니다!

답글 달기