인프라

쉽고 간단한 백엔드 인프라, 배포부터 보안까지 (4) - Github Actions(CI/CD)

ppaekkom 2023. 9. 17. 20:45

https://github.com/fineman999/simple_spring_mvc

 

GitHub - fineman999/simple_spring_mvc: Amazon Elastic Beanstalk + Amazon Route 53 + ACM + 가비아 + Github Actions(CI/CD) + Am

Amazon Elastic Beanstalk + Amazon Route 53 + ACM + 가비아 + Github Actions(CI/CD) + Amazon RDS(MySQL) - GitHub - fineman999/simple_spring_mvc: Amazon Elastic Beanstalk + Amazon Route 53 + ACM + 가비아 + ...

github.com


목차

    서론

    지난 Step에서 AWS Elastic Beanstalk와 Amazon RDS를 연결하는 작업을 진행하였다. 버전을 업데이트하는 과정에서 직접 새로운 파일을 업로드하는 방식으로 진행하였다.

    이번 Step에서는 Github Actiions을 이용하여 Github에 Push나 Pull Request 등 어떠한 이벤트가 발생하면 자동으로 빌드 후 업로드 하는 방식으로 진행해 보자.

     

    아키텍처

    GitHub Actions 추가

     

    GitHub Actions

    GitHub Actions

    GitHub Actions를 사용하여 리포지토리에서 바로 소프트웨어 개발 워크플로를 자동화, 사용자 지정 및 실행할 수 있다.

    기능이 잘 기억나지 않는다면 https://ppaekkomlog.tistory.com/17 다시 한번 보자.

     

    IAM 인증키

    GitHub Actions를 사용하여 AWS 클라우드 리소스를 배포하거나 관리할 때, AWS 서비스에 액세스해야 한다. 이를 통해 애플리케이션을 배포하고 관리하는 데 필요한 AWS 리소스를 프로그래밍 방식으로 제어할 수 있다.

    AWS 액세스 키와 비밀 키는 AWS 계정 및 IAM(Identity and Access Management) 사용자에 대한 인증 및 권한 부여를 위해 사용된다. 이러한 인증 정보를 통해 GitHub Actions는 AWS 계정 내의 리소스에 대한 액세스 권한을 얻는다.

    이를 통해 보안을 강화하고 권한이 없는 사용자나 서비스가 AWS 리소스에 접근하는 것을 방지한다.

     

    액세스 키와 비밀 키를 만들기 전에 해당 github actions용 유저를 만들어야 한다.

     

    IAM Dashboard

    IAM Dashboard

    AWS 검색 창에 IAM을 입력하자.

    Access Management의 Users를 클릭한 후 Create user를 클릭하자.

     

    User details

    User details

    유저 이름을 입력한다. 유저 이름과 비밀번호를 통해 AWS 접근할 필요가 없으므로 Next를 클릭한다.

     

    Set permissions

    Set permissions

    ElasitcBeanstalk의 사용자의 권한을 얻기 위해 AdministratorAccess-AWSElasticBeanstalk을 선택하자.

    그리고 Next를 누르자.

     

    Review and create

    Review and create

    문제가 없으면 Create User를 클릭하자.

     

    Users

    github-actions-for-elastic-beanstalk

    이제 액세스키와 비밀키를 생성해 보자.

    유저 이름을 클릭해 보자.

     

    Summary

    Security credetials를 클릭하자.

     

    Create access key

     

    Access key best practices & alternatives

    Access key best practices & alternatives

    github actions가 이용하기 때문에 Third-party service를 클릭 후 Next를 누르자.

     

    Retrieve access keys

    Retrieve access keys

    Download .csv file를 하여 안전하게 보관하자.

     

    이제 github repository에 액세스 키와 비밀 키를 저장하자.

     

    GitHub Repository

    GitHub Repository

    적용시킬 GitHub Repository의 Settings을 클릭하자.

     

    Actions secrets and variables

    Actions secrets and variables

    왼쪽 하단의 Secrets and variables를 클릭 후 New repository secret를 클릭하자.

     

    New secret

    총 2개의 새로운 secret를 생성해야 된다.

    AWS_ACCESS_KEY_ID: 액세스 키

    AWS_SECRET_ACCESS_KEY: 비밀 키

    후 Add secret를 하자.

     

    2개의 secrets 생성

    두 개의 secrets가 생성되었다.

     

    또한 이전 Spring Boot에서 데이터베이스를 테스트하기 위해서 환경변수를 설정해야 한다.

     

    Databse 환경변수

     

    Elastic Beanstalk 환경 세팅하기

    EB Setting

    .ebextensions

    EC2 서버의 timezone 설정을 위해서는 구성파일(.exbextensions)을 추가하여 환경을 구성하고 환경에 있는 AWS 리소스를 사용자 지정할 수 있다.

    EC2 서버에서 직접 설정할 필요 없다. 만일 EC2 서버에 직접 timezone 설정을 하면 문제가 발생한다. 배포를 할 때마다 매번 새로운 EC2 인스턴스를 제공해 주기 때문에 일일이 수작업이 필요해진다. 그러므로 애플리케이션 소스 번들 루트에 정의하면 된다.

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

    대한민국 서울 시간으로 정하였다.

     

    Procfile

    Java platform 에서 jar로 배포할 경우 추가적인 실행 옵션이 필요하다. 기본적으로 Tomcat platform + war 배포일 경우 deploy시 application이 실행되지만 Java platform + jar 배포일 경우에는 JVM command line 옵션은 콘솔을 통해서 설정할 수 없다. 그러므로 이 상태에서 배포를 하게 되면 "java -jar {application}.jar"로 실행옵션이 적용되지 않는다. 그러므로 Elastic Beanstalk에 실행할 JAR를 알려 주는 Procfile 파일을 포함시켜야 한다.

     

    web: java -jar simple-spring-mvc.jar

    애플리케이션의 기본 JAR를 실행하는 명령 이름은 web(필수)으로 지정해야 하며, Procfile에 나열된 첫 번째 명령이어야 한다.

    Elastic Beanstalk는 Procfile의 모든 항목이 항상 실행되어야 함을 가정하며, 종료되는 Procfile에 정의된 모든 애플리케이션을 자동으로 다시 시작한다.

     

    GitHub Actions 워크플로 작성하기

    Workflow 작성하기

    name: main-to-eb
    
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
      cancel-in-progress: true
    
    env:
      DB_URL: ${{ secrets.DB_URL }}
      DB_DATABASE: ${{ secrets.DB_DATABASE }}
      DB_USERNAME: ${{ secrets.DB_USERNAME }}
      DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
    
    on:
      push:
        branches: [ main ]
    
    jobs:
    
      build:
        name: Build
        runs-on: ubuntu-latest
    
        steps:
          - name: Checkout source code
            uses: actions/checkout@v4.1.0
    
          - name: Set up JDK 17
            uses: actions/setup-java@v3
            with:
              distribution: 'corretto'
              java-version: 17
    
          - name: Setup Gradle
            uses: gradle/gradle-build-action@v2
    
          - name: Grant execute permission for gradlew
            run: chmod +x ./gradlew
    
          - name: Build with Gradle
            run: ./gradlew clean build
    
          - name: Upload artifact
            uses: actions/upload-artifact@v3
            with:
              name: simple-spring-mvc
              path: build/libs/simple_spring_mvc-0.0.1-SNAPSHOT.jar
    
      deploy:
        name: Deploy
        needs: build
        runs-on: ubuntu-latest
    
        steps:
          - name: Checkout source code
            uses: actions/checkout@v4.1.0
    
          - name: Get current time
            uses: josStorer/get-current-time@v2.1.1
            id: current-time
            with:
              format: YYYYMMDDTHHmm
              utcOffset: "+09:00"
    
          - name: Download artifact
            uses: actions/download-artifact@v2
            with:
              name: simple-spring-mvc
              path: simple-spring-mvc/build/libs
    
          - name: Generate deployment package
            run: |
              mkdir -p deploy
              cp simple-spring-mvc/build/libs/simple_spring_mvc-0.0.1-SNAPSHOT.jar deploy/simple-spring-mvc.jar
              cp ./eb/Procfile deploy/Procfile
              cp -r ./eb/.ebextensions deploy/.ebextensions
              cd deploy && zip -r simple-spring-mvc-label-${{steps.current-time.outputs.formattedTime}}-${{github.sha}} .
    
          - name: Deploy Consumer to EB
            uses: einaregilsson/beanstalk-deploy@v21
            with:
              aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
              aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
              application_name: simple-spring-mvc
              environment_name: Simple-spring-mvc-env
              version_label: simple-spring-mvc-label-${{steps.current-time.outputs.formattedTime}}-${{github.sha}}
              region: ap-northeast-2
              deployment_package: deploy/simple-spring-mvc-label-${{steps.current-time.outputs.formattedTime}}-${{github.sha}}.zip
              wait_for_environment_recovery: 300

     

    1. 워크플로 이름 설정(name: main-to-eb)
      • 워크플로의 이름은 사용자 정의 가능하며, 워크플로를 구분하기 위해 사용된다.
    2. concurrency
      • group: group 속성은 워크플로우 작업을 그룹화하는 역할을 한다.
        • github.workflow 및 github.ref 값을 사용하여 그룹을 생성한다.
        • 이것은 특정 워크플로와 브랜치/레퍼런스에 대한 고유 그룹을 만든다.
        • 즉, 동일한 워크플로와 브랜츠에서 동시에 여러 작업이 실행되지 않도록 한다.
      • cancel-in-progress: 현재 진행 중인 작업을 중단할지 여부를 설정한다.
        • true로 설정하면 새로운 작업이 시작되면 이전에 실행 중인 작업을 중단하게 된다.
    3. env 설정
      • 테스트 시에 데이터베이스를 접근하여 해당 데이터베이스가 유효한지 체크한다.
      • 그러므로 데이터베이스 정보를 접근하기 위해 환경 변수를 입력해야 된다.
      • actions secrets에 접근하기 위해서는 secrets.~~로 시작하면 된다.
    4. event 설정(on: push)
      • 이 워크플로는 GitHub Main Branch로 푸시될 때 이벤트가 발생한다.
      • 즉, 이 워크플로가 실행될 수 있는 트리거 역할을 한다.
      • Push 뿐만 아니라 Commit, Pull Request 다양한 이벤트를 트리거할 수 있게 설정할 수 있다.
    5. 작업 설정(jobs)
      • build와 deploy로 두 개의 작업으로 나누어져 있다.
    6. build: 작업 환경 설정(runs-on: ubuntu-latest)
      • 이 작업은 GitHub Actions가 실행될 환경을 지정한다.
      • 여기서는 ubuntu-latest를 사용하여 Ubuntu 환경에서 작업을 실행한다.
    7. build: 작업 단계 설정(steps)
      1. Checkout Source Code
        • actions/checkout 액션을 이용하여 GitHub Repository의 소스 코드를 현재 작업 디렉터리로 가져올 수 있다. 이전에 변경된 코드를 사용하기 위해 소스코드를 체크아웃하는 작업이다.
      2. Set up JDK 17
        • actions/setup-java 액션을 이용하여 GitHub Actions 환경에 JDK 17을 설정하고 활성화한다.
        • distribution으로 AWS 오픈 소스인 corretto를 이용하였다.
        • 현재 애플리케이션 버전이 JDK 17이 필요하므로 이 단계에서 설치 및 설정한다.
      3. Setup Grade
        • actions/gradle-build-action을 이용하여 Gradle 빌드 도구를 이용하여 캐시, 결과 정보를 확인할 수 있다.
      4. Grant execute permission for gradlew
        • 이 단계에서는 Gradle Wrapper 스크립트(gradlew)에 실행 권한을 부여한다.
        • Gradle Wrapper는 프로젝트의 빌드를 관리하기 위해 사용되며 실행 권한이 필요
      5. Build with Gradle
        • 이 단계에서는 Gradle을 사용하여 Spring Boot 애플리케이션을 빌드한다.
        • clean build 명령을 실행하여 프로젝트를 정리하고 JAR 파일을 빌드한다.
      6. Upload artifact:
        • 일반적으로 job은 독립적이고 서로 간에 의존을 하지 않는다.
        • 하지만 아티팩트를 통해 워크플로의 잡들 사이에서 데이터를 공유하게 할 수 있다.(90일 동안 아티팩트를 저장함)
        • actions/upload-artifact를 이용하여 이름은 simple-spring-mvc로 해당 path에 담긴 jar파일을 아티팩트로 업로드한다.
    8. deploy: 작업  needs
      •  needs가 없을 경우 동시에 여러 개의 jobs를 실행시킬 수 있지만 needs가 있을 경우 해당 job이 끝난 후 실행하도록 설정할 수 있다.
    9. depoy: 작업 steps 
      1. Checkout soruce code 
        • Procfile과. ebextensions 파일을 읽어와야 하기 때문에 필요
      2. Get current time
        • josStorer/get-current-time 액션을 이용하여 현재 시간을 가져온다.
        • format과 utfOffset 옵션을 사용하여 시간 형식 및 시간대 오프셋을 설정할 수 있다.
        • 또한 배포 패키지 구분을 위해 파일명에 추가하였다.
      3. Download artifact
        1. actions/download-artifact를 이용하여 이전 빌드 단계에서 업로드한 아티팩트를 다운로드할 수 있다.
      4. Generate deployement package
        1. 이 단계는 배포 패키지를 생성하고 AWS Elastic Beanstlak 환경으로 전송하기 위한 단계이다.
        2. mkdir -p deploy
          • "deploy"라는 디렉터리를 생성한다. 이 디렉터리는 배포 패키지를 저장하기 위한 곳이다.
        3. cp. simplep-spring-mvc/build/libs/simple_spring_mvc-0.0.1-SNAPSHOT.jar deploy/simple-spring-mvc.jar
          • 아티팩트로 다운로드된 Spring Boot 애플리케이션 JAR 파일을 "deploy" 디렉터리로 복사한다.
          • 이때 이름을 변경한다.
        4. cp ./eb/Procfile deploy/Procfile
          • Procfile을 "deploy" 디렉터리로 복사한다.
        5. cp -r ./eb/.ebextensions deploy/.ebextensions
          • .exbextensions 디렉터리를 재귀적으로 "deploy" 디렉터리로 복사한다.
        6. cd deploy && zip -r simple-spring-mvc-label-${{steps.current-time.outputs.formattedTime}}-${{github.sha}} .
          • "deploy" 디렉터리로 이동한 후, 모든 파일 및 디렉터리를 포함하는 ZIP 아카이브를 생성한다.
          • ZIP 파일의 이름은 현재 시간과 GitHub 커밋의 SHA 값에 기반하여 생성된다.
          • 이 ZIP 파일은 Elastic Beanstalk 환경으로 전송하여 애플리케이션을 배포하는 데 사용된다.
      5. Deploy Consumer to EB
        • einaregilsson/beanstlak-deploy 액션을 이용하여 Elastic Beanstalk 환경으로 애플리케이션을 배포할 수 있다.
        • with: 액션에 전달되는 입력 매개변수들을 정의할 수 있다.
          • aws_access_key: AWS 액세스 키 ID를 지정할 수 있다. 이전에 GitHub Secrets에 저장하였다.
          • aws_secret_key: AWS 시크릿 액세스 키를 지정할 수 있다. 이전에 GitHub Secrets에 저장하였다.
          • application_name: EB 환경에서 배포할 애플리케이션의 이름을 지정한다.
          • environment_name: 배포할 Elastic Beanstalk 환경의 이름을 지정한다.
          • version_label: 배포할 애플리케이션의 버전 레이블을 설정한다. 이때 버전을 구분하기 위해 고유한 버전 레이블 생성한다.
          • region: AWS 리전을 지정한다.
          • deployment_package: 배포할 패키지의 경로를 지정한다.
          • wait_for_environment_recovery: EB 환경이 복구될 때까지 대기하는 시간을 설정한다. 프리티어를 사용하고 있으므로 충분한 시간으로 설정하자.

     

    Actions

    Actions

    GitHub Repository에 들어가면 상단에 Actions가 있다. 클릭을 하면 다양한 Actions가 있는 것을 확인할 수 있다. 현재 사용하고 있는 프로젝트 토대로 Actions를 제안한다.

     

    set up a workflow yourself를 누르자.

     

    workflow

    main-to-eb.yml

     

    위의 코드를 복사하자.

    Workflows setting

    Search Marketplace for Actions를 통해 현재 외부 액션들의 버전과 사용법을 쉽게 확인할 수 있고 최신 버전으로 업데이트할 수 있다.

    완료가 되었으면 Commit changes를 누르자.

     

    결과

    GitHub

    Summary

    총 2개의 잡을 확인할 수 있다.

     

    Artifacts & Gradle Builds

    다운로드된 아티팩트와 빌드 결과 정보도 같이 확인할 수 있다.

     

    Elastic Beanstalk

    versions

    Amazon S3 새로운 버전이 저장되어 있는 것과 현재 배포 버전도 같이 확인할 수 있다.

     

     

    Github Actions - concurrency: cancel-in-progress

    Email

     

    새로운 작업이 시작되면 깃허브에서 이메일로 이전에 실행 중인 작업을 중단하게 되었다는 걸 알린다.

    어떤 잡이 실행되었고 취소되었는지 알려준다.

     


    참고

    https://docs.aws.amazon.com/ko_kr/powershell/latest/userguide/pstools-appendix-sign-up.html

     

    AWS 계정 및 액세스 키 - AWS Tools for PowerShell

    이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

    docs.aws.amazon.com

    https://github.com/marketplace/actions/beanstalk-deploy

     

    Beanstalk Deploy - GitHub Marketplace

    Deploy a zip file to AWS Elastic Beanstalk

    github.com

    https://earth-95.tistory.com/111

     

    [AWS, Github Action] Elastic Beanstalk에 SpringBoot 배포하기(2) - IAM 인증키 Github Action에서 사용하여 배포

    들어가기 전에 해당 글은 Elastic Beanstalk의 기본 세팅이 되어 있다는 가정하에 진행합니다. 만약, Elastic Beanstalk의 어플리케이션 및 환경 구성이 되어 있지 않다면 하기 글을 클릭하여 먼저 구성하

    earth-95.tistory.com

    https://techblog.woowahan.com/2539/

     

    Elastic Beanstalk Configuration files(.ebextensions) | 우아한형제들 기술블로그

    {{item.name}} Elastic Beanstalk 사내 서버 인프라가 거의 대부분 AWS 환경으로 넘어가면서 신규로 구축되는 많은 시스템들이 배포 및 확장, 관리등의 용이성 때문에 Elastic Beanstalk 으로 구축되고 있습니

    techblog.woowahan.com

    https://earth-95.tistory.com/110

     

    [AWS] Github Action을 통해 Elastic Beanstalk으로 배포된 EC2 timezone 설정하기(.ebextensions)

    들어가기 전에 이 포스팅은 SpringBoot 어플리케이션을 Github Action을 통해 Beanstalk에 배포하여 생성된 EC2의 timezone 설정에 대한 글입니다. 따라서, EC2 서버에 직접 접근하여 timezone 변경하는 것이 아

    earth-95.tistory.com

    https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/java-se-procfile.html

     

    Procfile을 사용한 애플리케이션 프로세스 구성 - AWS Elastic Beanstalk

    Procfile을 사용한 애플리케이션 프로세스 구성 애플리케이션 소스 번들의 루트에 JAR 파일이 두 개 이상 있는 경우, Elastic Beanstalk에 실행할 JAR를 알려 주는 Procfile 파일을 포함시켜야 합니다. 단일

    docs.aws.amazon.com

    https://docs.github.com/ko/actions/using-workflows/storing-workflow-data-as-artifacts

     

    워크플로 데이터를 아티팩트로 저장 - GitHub Docs

    Artifacts allow you to persist data after a job has completed, and share that data with another job in the same workflow. An artifact is a file or collection of files produced during a workflow run. For example, you can use artifacts to save your build and

    docs.github.com

    https://www.daleseo.com/github-actions-artifacts/

     

    GitHub Actions의 아티팩트(Artifact)로 파일 올리거나 내려받기

    Engineering Blog by Dale Seo

    www.daleseo.com

    https://docs.github.com/en/actions/using-jobs/using-concurrency

     

    Using concurrency - GitHub Docs

    Run a single job at a time.

    docs.github.com

     

    반응형