본문 바로가기

CodeLap 프로젝트

[CodeLap] Github Action, AWS(EC2, S3, RDS, CodeDeploy)를 활용한 자바 + 스프링부트 백앤드 서버 배포 - 1편

우선, CI/CD의 중요성에 대해서 먼저 설파하자면, 전 회사에서 프로젝트를 할 때 테스트서버를 구축했었는데,

 

놀고있는 노트북 한대를 서버컴퓨터로 정한 후 FileZila 등을 사용해 프로젝트를 Jar파일로 압축해 SSH로 전송하고, SSH 서버에 접속해서 프로젝트 서버 실행.. 이런식으로 했었는데,

 

날을 잡아서 이 과정을 반복하는게 매우 고됐고 시간낭비라는 생각을 버릴 수 없었다.

 

직접 겪어보니 CI/CD를 왜 해야하는지, 정말 뼈저리게 느꼈다.

 

  • Github Action같은 CI/CD 툴을 사용하면, 파이프라인을 어떻게 짜느냐에 따라 다르겠지만 대개 배포과정을 자동화 해준다.
  • AWS EC2 인스턴스를 사용하면, 해당 프로젝트에 맞는 스펙의 클라우드 컴퓨터를 대여해 서버를 열 수 있고, 사용시간에 따른 비용만 지불하면 된다. 물론 이조차도 간단한 프로젝트의 경우에는 프리티어를 사용해 서버를 오픈할 수 있다.
  • RDS를 사용하면 로컬환경의 DB를 서버에 올릴 수 있고, 프로젝트에서 DB서버에 접근해서 서비스를 제공할 수 있다.
  • S3와 CodeDeploy를 사용하면 귀찮은 배포과정이 스킵되고 EC2에 자동으로 배포, 실행 해준다.

 

 

 

현재 진행중인 프로젝트에서, 프론트앤드 팀의 퍼블리싱 과정이 끝나면 백앤드 API 요청을 통해서 개발이 가능해야한다.

 

슬슬 퍼블리싱도 끝나갈 것 같고해서 EC2 인스턴스에 Jar 파일을 정상적으로 배포했고, 이에 대한 과정을 써보려고 한다.

 

우선 우리 백엔드 프로젝트에서는 Pull Request가 들어오면 먼저 멀티모듈로 구성되어있는 프로젝트에서 Common 모듈 테스트, API 모듈 테스트를 진행한다.

 

PR에서 정상적으로 테스트에서 통과 했을 때 비로소 merge를 하게 되는데, 메인 브랜치에 merge가 되면 S3 버킷에 코드를 업로드하고 CodeDeploy가 코드를 가져와 EC2 인스턴스에서 에이전트로 코드를 배포하는 전략을 세웠다.

 

* 테스트는 병렬이 아니라 순차적이다.

 

 

인프라 구조를 그림으로 설명하자면 이렇게 되겠다. 굉장히 조잡하고 이해하기 힘들 수 있겠지만 양해 바랍니다. 죄송합니다!!

 

실제 깃허브 액션에서 어떤식으로 작동하는지 알아보자면,

 

각 액션마다 PR, Merge가 붙어있는걸 확인할 수 있는데, 먼저 PR이 들어오면 전체적인 테스트를 진행한다.

 

후에 테스트가 정상적으로 완료될 시에 Merge를 해서 서버로 배포하는 과정이다.

 

PR의 CI 과정

 

Merge시 CI CD 과정

 

 

env:
  API_RESOURCE_PATH: ./api/src/test/resources/application.yml
  COMMON_RESOURCE_PATH: ./common/src/test/resources/application.yml


name: PR

on:
  pull_request:
    branches: [ main ]
    types: [ opened, synchronize, edited ]

permissions:
  contents: read

jobs:
  Test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up JDK 17
        uses: actions/setup-java@v1
        with:
          java-version: 17

      - name: "API Test application.yml Set"
        uses: microsoft/variable-substitution@v1
        with:
          files: ${{ env.API_RESOURCE_PATH }}
        env:
          spring.datasource.url: ${{ secrets.RDB_TEST_URL }}
          spring.datasource.username: ${{ secrets.RDB_USER_NAME }}
          spring.datasource.password: ${{ secrets.RDB_PASSWORD }}

      - name: "COMMON Test application.yml Set"
        uses: microsoft/variable-substitution@v1
        with:
          files: ${{ env.COMMON_RESOURCE_PATH }}
        env:
          spring.datasource.url: ${{ secrets.RDB_TEST_URL }}
          spring.datasource.username: ${{ secrets.RDB_USER_NAME }}
          spring.datasource.password: ${{ secrets.RDB_PASSWORD }}

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash

      - name: Common Test
        run: ./gradlew common:test
        shell: bash

      - name: API Test
        run: ./gradlew api:test
        shell: bash

PR시의 CI.yml 파일이다.

 

깃허브 액션은 work flow를 yml 파일로 작성해서 순차적으로 실행해주는데, 해당 코드에서는 먼저 환경변수를 설정한 후,

 

리눅스 버전에서 자바 설치, 환경변수, 시크릿 변수를 문자열로 치환해준 후 권한을 부여해서 Gradle 테스트를 돌려준다.

 

자 여기서, 깃허브 액션은 우리의 로컬 DB에 접근할 수 없기 때문에 테스트를 돌릴수가 없다!

 

실패의 흔적들

실제로 RDS에 올라간 DB에 제대로 접근을 하지 못해 테스트에 실패한 목록이다.

files 입력값을 명시해주지 않았다는데, 접근 경로나 시크릿 변수를 제대로 설정해주지 않으면 발생할 수 있는 오류이다.

정상적으로 작동할 시에 이런식으로 테스트에 통과한다.

 

어쨌든 RDS 서버에 DB를 올려줘야하는데, 

 

EC2 인스턴스에서 실행되는 애플리케이션에서 RDS DB에 접근해줄 수 있어야 하기 때문에 보안그룹을 묶어 EC2와 RDS를 연동해준다.

 

연동이 끝나면, 시크릿 변수에 RDB_URL : ENDPOINT/DB스키마 형식으로 만들어준다.

 

RDB USER명과 PASSWORD 또한 마찬가지.

 

이렇게되면 정상적으로 CI가 작동하는 것을 볼 수 있다.

 

해당 포스팅에서는 CI/CD의 이유, 전반적인 흐름에 대해서 설명하는 포스팅이기 때문에 본 포스팅을 참조해서 CI/CD를 마무리하기에는 무리가 있다.

 

전반적인 흐름을 한 번 파악해본 뒤, 다른 블로그나 책, 강의 등을 참고하면서 해보는것이 좋을 것 같다.