GAE + Github Actions + Spring(Gradle) CI/CD

한얼·2023년 8월 20일
0
post-thumbnail

GAE + Github Actions + Spring(Gradle) 3개를 동시에 사용하여 CI/CD 파이프라인을 구축하는 예시를 쉽게 찾기 어려웠기 때문에 이 글을 작성하게 되었습니다.

1. CI/CD란?

CI (Continuous Integration)

빌드/테스트 자동화 과정

지속적인 통합이라는 뜻을 가지고 있습니다. 앱의 새로운 코드 변경 사항이 정기적으로 빌드테스트 되어 공유 레포지토리에 통합되는 것을 의미합니다.

소스 코드의 빌드와 배포뿐만 아니라, 소스 코드를 검증할 수 있는 방법이 필요합니다.

CI 원칙

  • 모든 소스코드가 살아있고(현재 실행되고) 어느 누구든 현재의 소스를 접근할 수 있는 단일 지점을 유지해야 합니다.
  • 빌드 프로세스를 자동화시켜, 어느 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 합니다.
  • 테스팅을 자동화시켜 단일 명령어를 통해 언제든지 시스템에 대한 적합한 테스트를 실행할 수 있도록 합니다.
  • 누구나 현재 실행 파일을 얻으면, 최선(Best)의 실행파일을 얻었다는 확신이 들도록 해야 합니다.

CI 구성 요소

  • 소스 코드 관리
    • 모든 소스 코드를 중앙 저장소(Git)에서 관리되어야 합니다.
    • 모든 개발자는 이 저장소로부터 브랜치를 생성/통합하고, 일관된 코드 베이스를 유지해야 합니다.
  • 빌드 자동화
    • 프로젝트의 빌드는 완전히 자동화되어야 합니다.
    • 일반적으로 빌드 스크립트를 사용하여 수행합니다.
  • 자동 테스트
    • 모든 코드 커밋은 자동화된 단위 테스트와 통합 테스트를 통과해야 합니다.
    • 이를 통해 새로운 코드가 기존 시스템에 부정적인 영향을 미치지 않는지 확인 가능합니다.
  • 비기능 테스트
    • 기능, 보안, 접근성 등과 같은 비기능적 특성도 고려해야 할 수 있습니다.
    • 자동화하기 어려운 부분이지만, 가능한 한 자동화가 필요합니다.
  • 정적 코드 분석
    • 정적 코드 분석 도구를 사용하여 코드 품질을 확인하고, 코드 스타일, 복잡성, 잠재적 버그등을 자동으로 검사해야 합니다.

요약 : 소스코드 관리정적 코드 분석자동 빌드자동 테스트CD

CD (Continuous Delivery, Continuous Deployment)

지속적인 서비스 제공 또는 지속적인 배포라는 뜻을 가지고 있습니다. Continuous DeliveryContinuous Deployment 모두 CD로 축약되는데, 비슷한 의미를 가지고 있지만 약간의 뉘앙스 차이가 존재합니다.

Continuous Delivery

  • 앱의 변경사항(버그 수정, 새로운 기능 추가 등)이 실제로 사용자에게 제공될 수 있는 상태로 만드는 것이 목표입니다.
  • 실제로는 프로덕션 환경에 자동으로 배포되지 않고, 개발 팀이 배포 시점을 결정하게 됩니다.
    • 배포 타이밍은 조절이 가능하며, 필요한 경우 추가적인 체크가 가능합니다.

Continuous Deployment

  • Continuous Delivery의 모든 과정을 수행한 후, 자동으로 프로덕션 환경에 배포합니다.
  • 즉, 새로운 변경사항이 모든 테스트 및 검증 과정을 통과하면 즉시 사용자에게 제공될 수 있습니다.
    • 따라서 테스트와 모니터링 전략, 효과적인 롤백 전략 등이 잘 구축되어 있어야 합니다.

CD 구성 요소

  • CI가 밑바탕이 되어야 합니다
  • 배포 자동화
    • 코드가 성공적으로 빌드되고 테스트를 통과하면, 자동화된 방식으로 프로덕션 환경 또는 스테이징 환경으로 배포되어야 합니다.
  • 모니터링 및 알림
    • 테스트 실패, 빌드 실패, 배포 실패 등의 이슈에 대해 즉시 알림을 받을 수 있어야 합니다. 이를 통해 문제를 신속하게 인지하고 해결할 수 있습니다.
  • 롤백 기능
    • 문제가 발생했을 때 이전 상태로 롤백할 수 있는 기능이 필요합니다.

요약 : CI배포 자동화모니터링 & 알림

2. CI/CD 구축

이번 프로젝트에서 CI/CD를 구축할 때 git actions를 활용하여 단순한 CI/CD를 구축했습니다.

Platform : GCP App Engine
CI tool : Github Actions
Application : Spring boot(Gradle)

GCP App Engine(GAE) 설정

GCP 계정 및 프로젝트는 이미 생성되어 있다고 가정합니다.

GAE 자체 설정

  • 표준 환경 / 가변 환경 중 표준 환경 선택
  • 위치 : 서울(asia-northeast3) 선택
  • Google Cloud SDK 설치하여 CLI로 추가 작업하기
    • gcloud components update
    • gcloud components install app-engine-java
  • API 사용 설정
    • App Engine Admin api 사용

app.yaml

우선 GAE에 스프링부트 프로젝트가 배포될 수 있도록 app.yaml 및 build.gradle 파일을 구성해야 합니다. src/main/appengine/app.yaml 경로에 다음과 같은 app.yaml 파일을 추가합니다.

# app.yaml

runtime: java11
entrypoint: 'java -jar ./build/libs/yeon-0.0.1-SNAPSHOT.jar'
handlers:
  - url: /.*
    script: this field is required, but ignored

manual_scaling:
  instances: 1
  • runtime : java 11 버전인 경우 java11
  • entrypoint : 진입점 경로, jar 파일 위치. 옵션 항목이지만 이번 ci/cd 구축 시 필수 항목입니다.(생략할 경우 빌드된 jar 파일을 찾지 못합니다.)
  • env : 앱 엔진 표준 환경 또는 가변 환경, 옵션 항목이며, 사용하지 않을 경우 표준 환경입니다.
  • runtime_config : 런타임 세부 설정. 옵션 항목이며, 작성하지 않았습니다.
  • handlers : 필수 항목입니다. 없으면 에러가 발생하는데, 이유는 찾지 못했습니다.
  • manual_scaling : 오케스트레이션 관련 설정입니다. 인스턴스를 1개로 유지하도록 하였습니다.

build.gradle에는 다음과 같은 코드를 추가합니다.

// build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.2.0'
    }
}

...

apply plugin: 'com.google.cloud.tools.appengine'
appengine {  // App Engine tasks configuration
    deploy {   // deploy configuration
        projectId = '[PROJECT_ID]' // gcloud config set project
        version = '1'   // gcloud to generate a version
    }
}

...

다음의 글을 참고하여 build.gradle을 수정했습니다.

Deploying a Spring Boot Gradle app to App Engine Standard Java 11

  • [PROJECT_ID] : GCP 프로젝트 ID
  • 버전은 앱 엔진 배포 버전을 의미하나, 해당 버전과 상관없이 자동으로 버전이 증가 되는 것을 확인하였습니다.

gradle-wrapper.jar

Gradle을 CI 서버에 깔지 않고, 프로젝트에 함께 포함시켜 배포할 수 있도록 gradle-wrapper.jar를 사용합니다.

  • IntellJ 환경
    • Gradle 도구 → Tasks → build setup → wrapper 더블 클릭 시 새로운 gradle-wrapper.jar 생성
  • terminal 환경
    • gradle wrapper 또는 gradlew wrapper 명령어 입력 시 새로운 gradle-wrapper.jar 생성

gradle-wrapper.jar 위치 : root/gradle/wrapper

Github Actions 설정

GAE 설정이 완료되었다면, develop 브랜치에 코드가 push되었을 때 CI가 동작하도록 github actions과 관련된 부분을 설정해야합니다.

Github Actions 시작하기 & main.yaml

  1. 우선 github에서, Actions 탭을 들어가, set up a workflow yourself → 를 클릭해줍니다.

  1. 그럼 .github/workflows/main.yaml이 생성됩니다. main.yaml 파일은 아래와 같이 구성했습니다.
# main.yaml

name: deploy GAE

on:
  # Triggers the workflow on push or pull request events but only for the master branch
  push:
    branches: [ develop ]
  #pull_request:
  #  branches: [ develop ]
    
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
  
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    # Steps represent a sequence of tasks that will be executed as part of the job
    
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v3
      
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
      
      - name: Build with Gradle
        run: ./gradlew build
      
      - name: Auth to GCP
        uses: 'google-github-actions/auth@v1'
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}
          
      - name: Deploy to App Engine
        uses: 'google-github-actions/deploy-appengine@v1'
        with:
          deliverables: build.gradle
          project_id: ${{ secrets.PROJECT_ID }}
  • name : CI 이름 설정
  • on : CI trigger 설정
    • push : 어떤 branch에서 push 이벤트를 받았을 때 CI를 실행할지 적습니다.
    • pull_request : 어떤 branch에서 PR 이벤트를 받았을 때 CI를 실행할지 적습니다.
      • 두 개를 함께 쓰는 경우 develop에 PR 시, 2번의 CI가 발생 하는 것을 확인하였습니다.
      • 따라서 push 이벤트만 받도록 pull_request 이벤트는 주석 처리 하였습니다.
    • workflow_dispatch : 수동 CI 실행 관련인 듯 하나, 정확히는 확인하지 못했습니다.
  • jobs : CI/CD 파이프라인 설정, 하위 build는 job의 이름입니다.
    • runs-on : 실행 될 환경, ubuntu-latest 고정입니다. ubuntu-18.04는 지원 하지 않습니다.
  • steps
    • - 단위로 pipeline 단계 구성 할 수 있습니다.
    • uses : 사용할 액션, git repo 형식. Marketplace에서 여러 액션을 찾을 수 있습니다.
      • GCP에 인증하는 용도로 google-github-actions/auth@v1를 사용했으며,
      • GAE에 배포하는 용도로 google-github-actions/deploy-appengine@v1을 사용했습니다.
    • name : 파이프라인 단계의 이름입니다.
    • with : 액션의 파라미터
    • run : 특정 명령어를 실행할 수 있습니다.

즉, 현재 파이프라인 구성은 다음과 같습니다.

repo checkoutgradle 권한 수정gradle로 spring 프로젝트 빌드(gradle-wrapper 이용)GCP에 로그인(Auth)GAE에 빌드 된 jar 파일 배포

SECRETS

다음으로는 main.yaml에 써있는 secrets.GCP_SA_KEYsecrets.PROJECT_ID를 등록해주어야 합니다.

Settings → Secrets and variables → Actions 에서 New repository secret을 눌러 추가합니다.

  1. PROJECT_ID

    PROJECT_ID는 GCP의 프로젝트 ID를 말합니다. 다음과 같이 ID를 찾을 수 있습니다.

  2. GCP_SA_KEY

    1. 먼저 IAM 및 관리자 → 서비스 계정에 들어가, 새로운 서비스 계정을 만들어줍니다.

    2. 그 후 서비스 계정 세부 정보를 입력하고, 역할을 선택해줍니다. 추가해야 할 역할은 다음과 같습니다.

      • 서비스 계정 사용자
      • 환경 및 Storage 객체 관리자
      • App Engine 관리자
      • App Engine 배포자
      • App Engine 서비스 관리자
      • Cloud 빌드 서비스 계정
      • Cloud Build 통합 편집자

      불필요한 권한이 있을 수 있습니다. 자세한 권한은 다음 링크를 확인해주세요.
      https://cloud.google.com/iam/docs/understanding-roles?hl=ko#cloud-storage-roles

    3. 그 다음, 생성된 서비스 계정에 들어가 키 → 키 추가를 눌러 새로운 키를 발급 받습니다.
      키 유형은 JSON으로 발급 받습니다. 발급 받은 키는 유출되지 않도록 유의해주세요.

    4. 마지막으로 발급 받은 키가 있는 위치에서 쉘을 실행시킨 후, 다음과 같이 입력합니다.

      $ cat [본인의 키].json | base64

      여기서 나온 base64가 GCP_SA_KEY입니다.

CI/CD 구축 완료

여기까지 완료했다면, develop 브랜치에 코드가 push 되었을 때 자동으로 app engine에 배포되는 단순한 CI/CD 파이프라인을 확인할 수 있습니다.

0개의 댓글