github action을 이용해 빈스톡에 배포하기

최용욱·2023년 9월 18일

지금까지 배포를 할 때 그저 EC2 하나를 이용해서 Jar 파일을 실행시키는
간단한 배포만 해봤었다.
저 방식을 제외하고 딱 하나 배포때 사용해본 것이 헤로쿠이다.

허나 헤로쿠도 쓴지 오래되어 기억이 없는 상태였고
이번 CMC 핏허브 프로젝트에서 배포를 어떻게 할지 막막했다.
다행히 12기 CMC를 수료했던 친구에게서 AWS elastic beanstalk을 배웠고
프로젝트에서 적용을 해 보았다.

하나하나의 원리를 다 알고 적용한 것은 아니기에 어떻게 적용을 했는지에
초점을 맞춰서 글을 적을 것이당

깃허브 액션을 설정하기

우선 나 같은 경우, 개발서버와 실제 릴리즈 서버의 분리를 위해
develop 브랜치에 머지가 되었을 때 액션이 돌아가도록 설정했다.
릴리즈 서버는 당연히 빈스톡 하나를 새로 만들어야 하기에
yml 파일을 하나 더 만들어야 한다.

프로젝트 root 바로 밑에
.github 폴더를 만들고 그 아래에 .workflows 폴더를 만들어
dev_deploy.yml을 작성했다.

yml파일의 일부는 아래와 같다.

name: Fithub Dev CI/CD

on:
  pull_request:
    types: [closed]
  workflow_dispatch: # (2).수동 실행도 가능하도록

jobs:
  build:
    runs-on: ubuntu-latest # (3).OS환경
    if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop'

    steps:
      - name: Checkout
        uses: actions/checkout@v2 # (4).코드 check out

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: 11 # (5).자바 설치
          distribution: 'adopt'

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash # (6).권한 부여

      - name: Build with Gradle
        run: ./gradlew clean build -x test
        shell: bash # (7).build 시작

      - name: Get current time
        uses: 1466587594/get-current-time@v2
        id: current-time
        with:
          format: YYYY-MM-DDTHH-mm-ss
          utcOffset: "+09:00" # (8).build 시점의 시간확보

      - name: Show Current Time
        run: echo "CurrentTime=$"
        shell: bash # (9).확보한 시간 보여주기

      - name: Generate deployment package
        run: |
          mkdir -p deploy
          cp build/libs/*.jar deploy/application.jar
          cp Procfile deploy/Procfile
          cp -r .ebextensions deploy/.ebextensions
          cp -r .platform deploy/.platform
          cd deploy && zip -r deploy.zip .
         

코드를 보면 pull request가 닫혔을 때, 그 중 develop 브랜치로의 pr일 때
액션이 실행이 되도록 설정했다.

나는 Spring boot 2.7을 사용했기에 java 버전은 11로 설정했다.

이후 스탭들은 배포를 위한 패키지를 만드는 과정들이다.

추가적인 설정

deploy.yml 말고도 여러 설정이 필요하다.

첫 번째로 .ebextensions이다.

.ebextensions는 폴더로 빈스톡에 여러 설정 파일들을 배치할 수 있으며
CI/CD 과정에서 해당 폴더의 파일들을 읽어들여 빈스톡의 설정을 하게 된다.
.ebextensions의 파일들은 이름에 00 01 02 이렇게 번호가 붙는데, 번호 순서대로 읽어들여
설정을 한다고 한다.

.ebextensions는 root 바로 밑 경로에 만들어줘야 한다.

나는 아래의 2개의 설정을 해 주었다.

00-makeFiles.config

files:
    "/sbin/appstart" :
        mode: "000755"
        owner: webapp
        group: webapp
        content: |
            #!/usr/bin/env bash
            JAR_PATH=/var/app/current/application.jar

            # run app
            killall java
            java -Dfile.encoding=UTF-8 -jar $JAR_PATH

01-set-timezone.config

commands:
  set_time_zone:
    command: ln -f -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime

위 처럼 00 01 이렇게 번호를 부여했다.

다음으로 나는 nginx와 함께 Spring boot를 사용했기에 nginx 설정도 추가했다.

root 바로 밑에 .platforms 폴더를 만들고 nginx.conf 파일을 추가한다.

nginx.conf 파일

user                    nginx;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
worker_processes        auto;
worker_rlimit_nofile    33282;

events {
    use epoll;
    worker_connections  1024;
    multi_accept on;
}

http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;


  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

  include       conf.d/*.conf;

  map $http_upgrade $connection_upgrade {
      default     "upgrade";
  }

  upstream springboot {
    server 127.0.0.1:8080;
    keepalive 1024;
  }

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

      location / {
          proxy_pass          http://springboot;
                  # CORS 관련 헤더 추가
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
          proxy_http_version  1.1;
          proxy_set_header    Connection          $connection_upgrade;
          proxy_set_header    Upgrade             $http_upgrade;

          proxy_set_header    Host                $host;
          proxy_set_header    X-Real-IP           $remote_addr;
          proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
      }

      access_log    /var/log/nginx/access.log main;

      client_header_timeout 60;
      client_body_timeout   60;
      keepalive_timeout     60;
      gzip                  off;
      gzip_comp_level       4;

      # Include the Elastic Beanstalk generated locations
      include conf.d/elasticbeanstalk/healthd.conf;
  }
}

이제 Procfile을 만들어주면 된다.

Procfile 이 이름 그대로 만들어야 하며 루트 바로 아래 경로에 추가하면 된다.

Procfile의 내용

web: appstart

마지막으로 bulid.gradle에서 아래의 명령어를 추가한다.


jar {
    enabled = false
}

위의 명령어가 있어야 깃허브 액션이 돌면서 빌드가 된다.

빈스톡 만들기

elastic beanstalk 만드는 과정도 복잡하다.

나 같은 경우는 VPC의 public 서브넷에 ec2를 배치할 생각이었고
RDS의 경우는 빈스톡과 무관하게 관리를 하려고 했다.

빈스톡의 생성은 아래와 같다.

1. IAM에서 역할 만들기
빈스톡을 생성할 때 2개의 역할이 필요하다.
하나는 빈스톡 내부 ec2와 관련된 역할, 하나는 빈스톡 자체의 역할
아래는 빈스톡 자체에 부여할 역할이며 정책은 아래 사진처럼 연결하면 된다.

주의해야 하는 것은 신뢰관계에 빈스톡을 넣어야 한다는 것이다.

다음으로 빈스톡 내부의 ec2에 대한 역할이다.
ec2에는 아래와 같이 3개의 정책을 연결하면 된다.

역시 주의할 점은 신뢰관계에 ec2가 들어가야 한다는 것이다.

2. 빈스톡 설정하기
이제 빈스톡을 설정하면 된다.
어플리케이션 생성으로 이동해 아래 사진처럼 진행한다.
편의 상 이름은 temp-dev라고 했다.

나는 java 기반 Springboot를 사용할 것이기에 아래 사진처럼 설정했다.
사용자 지정 구성은 추후 뒤 설정에서 무중단 CI/CD를 구성을 위해서이다.

다음 설정에서 IAM에서 역할을 만들어 두었다면 아래처럼 자동으로 기입이 될 것이고
키 페어는 ec2에 원격 접속 때 사용이 되므로 적절히 설정하면 된다.

다음 설정에서 나는 VPC를 미리 만들어 둔 VPC로 설정을 했고
인스턴스 설정에서 퍼블릭 아이피 할당을 선택했다.
❗여기서 의문이 있는데, 찾아보니 빈스톡이 생성될 때 ec2를 만드는데, 해당 ec2가 빈스톡에게
나 잘 만들어졌어!!!! 하고 알려주지 않으면 생성이 되지 않는다고 한다. 따라서 나는 서브넷도 퍼블릭 서브넷으로 설정하고, 퍼블릭 IP 역시 할당을 해줬다.
그런데, 여기서 퍼블릭 IP를 주지 않으면 빈스톡 생성이 되지 않는다....
그런데 왜 퍼블릭 IP 설정을 디폴트가 선택 해제인지 아직 잘 모르겠다..

이후 데이터베이스 관련 설정은 하나도 하지 않았다. 나는 RDS는 따로 관리를 하려고 했다.
이후 루트 볼륨의 설정은 디폴트 값을 그대로 두고 다 넘겼다.
다음으로 보안그룹은 내가 전에 VPC 생성시 설정한 보안그룹을 선택했는데,

TCP 22, 3306, 80, 443, 8080 인바운드 규칙에 대해 어느 곳에서나 허용하도록 해 두었다.

다음으로 아래 설정에서 최소를 1개, 최대를 2개(밸런싱된 로드)로 설정을 했다.
나는 ec2 하나에 대해서만 무중단 배포를 할 것이기에 새로 배포가 되는 순간에는
2개가 되었다가 1개로 줄어들게 된다.
나머지 설정은 전부 기본값을 그대로 두고 진행했다.


다음으로 리스너와 프로세스 설정이다.
사실 리스너는 추후 https 적용 시 건드려야 하지만 이번 포스팅은 빈스톡을 이용한
무중단 ci/cd 파이프라인 구축에 집중하므로 리스너는 건드리지 않는다.
프로세스는 상태확인 경로를 /health로 변경해서 진행을 했다.

아래처럼 프로세스의 상태확인 url을 /health로 변경했다.

그러면 아래 사진처럼 변경이 된다.

상태확인 프로세스란 빈스톡에 새 ec2가 생성이 되고 해당 경로로 get 요청을 보내
응답이 오는지 체크한다. 여기서 응답이 오지 않는다면, 제대로 배포가 된 것으로 판단하지 않게 되고
깃허브 액션이 끝이 나지 않은 채 계속 대기상태에 빠지게 된다.
이를 위해 실제 스프링 boot에서도 /health 요청에 대해 응답을 주도록 해야한다.
아래 사진처럼 복잡하게 할 것 없이 간단하게 만들었다.

상태보고는 그저 강화됨 하나만 설정을 우선 했다. 기본으로 하게 되면 상태가 심각함 상태가 되어도
왜 저런지 전혀 알수가 없다...

나머지 설정은 그대로 두고 진행하다가 아래의 롤링 업데이트 및 배포에서
✔️추가 배치를 사용한 롤링✔️으로 설정을 한다.
추가배치를 위해 기존의 것을 그대로 두고 새로 만든 뒤 기존 것을 삭제한다는 것이다.
위에서 최소 1개, 최대 2개를 설정한 것도 이를 위해서 이다.

마지막으로 환경변수이다. 이제 추후 포스팅에서도 등장을 하겠지만
어플리케이션의 환경변수들을 아래에 설정이 가능하며 언제든 수정이 가능하다.
여기서 PORT를 8080으로 설정했다.
이유는 기본 포트가 5000이기에
/health로 요청을 보낼 때 5000 포트로 요청을 보내게 되고
이렇게 되면 8080에서 실행중인 Spring boot에 닿지 못한다.

이제 설정은 다 끝났고 환경을 생성하면 된다.
이 때, 7분 이상 걸린다면 망한 것이다. 무언가 설정이 잘못된 것으로
빈스톡 환경은 빠르게 삭제가 안되기에 새로 하나를 파거나 해야한다....

IAM 사용자 만들기

S3도 그렇듯이, AWS 외부 어플리케이션에서 AWS 서비스를 사용하려면
사용자의 액세스 키가 필요하다.
깃허브 액션을 통한 빈스톡 배포도 깃허브라는 외부 어플리케이션이 빈스톡을 사용하는 것이기에
IAM에서 사용자를 만들어 액세스 키를 줘야 한다.

아래처럼 사용자를 하나 만들고 권한은
단, 하나만 주면 된다.

이후
보안자격 증명으로 가서 액세스 키를 만들어야 한다.

액세스 키 생성을 클릭 후 아래 처럼 AWS 외부 애플리케이션을 선택한다.

그러면 아래 사진처럼 액세스 키와 시크릿 키가 발급이 되는데
여기서 새로고침이나 뒤로가기나 다른 행동을 하지 않고
아래 키를 CI/CD를 하려는 깃허브 리포지토리에서 사용이 가능하도록 한다.

아래 처럼 세팅에서 좌측 사이드 바를 살펴보면 Secrets and variables가 보이고
세부 메뉴에 Actions가 보인다.

이제 아래 사진에서 New repository secret으로 들어가서

아래의 키들을 설정을 해야한다.

진짜 마지막, deploy.yml 완성하기

이제 완성이 덜 된 deploy.yml을 완성하면 된다.

      - name: Beanstalk Deploy
        uses: einaregilsson/beanstalk-deploy@v20
        with:
          aws_access_key: ${{ secrets.AWS_ACTION_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_ACTION_SECRET_ACCESS_KEY }}
          application_name: fithub-prod
          environment_name: Fithub-prod-env
          version_label: github-action-${{ steps.current-time.outputs.formattedTime }}
          region: ap-northeast-2
          deployment_package: deploy/deploy.zip
          wait_for_environment_recovery: 60

기존 yml 파일에 위의 스탭을 추가하면 된다.
위의 스탭은 빈스톡에 배포를 하는 과정이다.
여기서

      aws_access_key: ${{ secrets.AWS_ACTION_ACCESS_KEY_ID }}
      aws_secret_key: ${{ secrets.AWS_ACTION_SECRET_ACCESS_KEY }}
      application_name: fithub-prod
      environment_name: Fithub-prod-env
      

이 4개는 매 프로젝트마다 다르게 설정을 해야하는데,
위의 2개는 아까 깃허브에서 IAM 액세스 키를 저장할 때
그 이름을 넣으면 되는 것이고(AWS_ACTION_ACCESS_KEY_ID 이거)

아래 2개는 AWS에서 빈스톡을 만들때
환경 이름과 어플리케이션 이름을 넣으면 된다!

설정 완료, 이제 develop으로 머지를 하자!

이제 develop으로 머지를 할 경우 action이 돌면서
빈스톡에 배포가 된다.

필자는 프로젝트 시작과 동시에
이슈1 번을 만들어서 deploy.yml 등의 설정을 모두 하고
develop으로 머지를 했다.

아래 사진처럼 액션이 성공하면 파이프라인 구축이 된 것이다!

profile
안녕하세요 :)

0개의 댓글