[GitHub Actions, AWS๋กœ CI/CD ๊ตฌ์ถ•ํ•˜๊ธฐ] - 1. GitHub Actions ๊ตฌ์„ฑํ•˜๊ธฐ

March 3, 2023์— ์ž‘์„ฑ

Overview

์„œ๋ฒ„ ๋ฐฐํฌ๋Š” ๋‹ค์Œ ๊ณผ์ •์„ ๊ฑฐ์ณ ์ด๋ฃจ์–ด์ง„๋‹ค.

  1. ๋ ˆํฌ์ง€ํ† ๋ฆฌ์˜ main ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œํ•˜๋ฉด GitHub Action์ด ์‹คํ–‰
  2. GitHub Actions ์‹คํ–‰
    1. ์„œ๋ฒ„๋ฅผ ๋นŒ๋“œ
    2. OpenID Connect๋กœ AWS ์ธ์ฆ
    3. ๋นŒ๋“œ๋œ ํŒŒ์ผ์„ AWS S3์— ์—…๋กœ๋“œ
    4. ์—…๋กœ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด AWS CodeDeploy๋ฅผ ์‹คํ–‰
  3. AWS CodeDeploy๊ฐ€ S3์— ์—…๋กœ๋“œ๋œ zip ํŒŒ์ผ์„ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋‘” EC2 ์ธ์Šคํ„ด์Šค์— ์—…๋กœ๋“œ
  4. CodeDeploy๊ฐ€ ์†Œ์Šค์˜ ์„ค์ •ํŒŒ์ผ(appspec.yml)์— ๋”ฐ๋ผ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์„œ๋ฒ„๋ฅผ EC2 ์ธ์Šคํ„ด์Šค์—์„œ ์‹คํ–‰์‹œํ‚ด

GitHub Actions

GitHub Actions๋Š” ๊นƒ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— .github/workflow ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  yaml ํ™•์žฅ์ž ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. main ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œ๋˜๋ฉด ์ž๋™์œผ๋กœ ์„œ๋ฒ„๋ฅผ ๋นŒ๋“œํ•ด์„œ AWS EC2์— ๋ฐฐํฌํ•˜๋Š” ๋‹ค์Œ ํŒŒ์ผ์„ ๋ณด๋ฉด์„œ ๊ณผ์ •์„ ์‚ดํŽด๋ณด์ž.

yaml ํŒŒ์ผ

# [1]
name: build and deploy a spring boot server application

# [2]
on:
  push:
    branches:
      - main

# [3]
env:
  applicationfolder: backend/gfi
  AWS_REGION: # AWS ๋ฆฌ์ „. ex. ap-north-east-2
  S3_BUCKET: # ๋นŒ๋“œ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•  S3 ๋ฒ„ํ‚ท ์ด๋ฆ„
  CODE_DEPLOY_APP_NAME: # CodeDeploy์— ์„ค์ •ํ•œ ์•ฑ ์ด๋ฆ„
  CODE_DEPLOY_GROUP_NAME: # CodeDeploy์— ์„ค์ •ํ•œ ๊ทธ๋ฃน ์ด๋ฆ„

#[4]
jobs:
  # [5]
  build:
    name: Build and Package
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ${{ env.applicationfolder }}

    # OIDC์— ์“ฐ์ด๋Š” ID ํ† ํฐ ๋ฐœ๊ธ‰์„ ์œ„ํ•ด ์ •์˜
    permissions:
      id-token: write
      contents: read

    steps:
	  # [5-1]
      - uses: actions/checkout@v3

	  # [5-2]
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

	  # [5-3]
      - name: Grant execute permission to gradlew
        run: chmod +x ./gradlew
        shell: bash

	  # [5-4]
      - name: Execute Gradle build
        run: ./gradlew build

	  # [5-5] OIDC
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
          aws-region: ${{ env.AWS_REGION }}

	  # [5-6]
      - name: Create a .env file
        run: |
          echo "PORT=${{ vars.SERVER_PORT }}" >> .env

	  # [5-7]
      - name: Make zip file
        run: zip -r $GITHUB_SHA.zip ./
        shell: bash

      - name: Upload Artifact to s3
        run: aws s3 cp $GITHUB_SHA.zip s3://${{ env.S3_BUCKET }}/

  # [6]
  deploy:
    needs: build
    runs-on: ubuntu-latest

    # OIDC์— ์“ฐ์ด๋Š” ID ํ† ํฐ ๋ฐœ๊ธ‰์„ ์œ„ํ•ด ์ •์˜
    permissions:
      id-token: write
      contents: read

    steps:
      - uses: actions/checkout@v2

      - uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
          aws-region: ${{ env.AWS_REGION }}
      - run: |
          aws deploy create-deployment \
            --application-name ${{ env.CODE_DEPLOY_APP_NAME }} \
            --deployment-group-name ${{ env.CODE_DEPLOY_GROUP_NAME }} \
            --s3-location bucket=${{ env.S3_BUCKET }},bundleType=zip,key=$GITHUB_SHA.zip

[1] name

์‹คํ–‰๋  GitHub Actions์˜ ์ด๋ฆ„์„ ์ •์˜ํ•œ๋‹ค.

[2] on

main ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œ๋œ ๊ฒฝ์šฐ์—๋งŒ GitHub Actions๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ•œ๋‹ค.

[3] env

ํ•ด๋‹น ์•ก์…˜์—์„œ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ์ •์˜ํ•œ ๋ณ€์ˆ˜๋Š” ์•ก์…˜ ๋‚ด๋ถ€์—์„œ ${{ env.๋ณ€์ˆ˜๋ช… }} ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งŒ์•ฝ ์—ฌ๋Ÿฌ ์•ก์…˜์—์„œ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ฅผ ๊ณตํ†ต์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, GitHub ๋ ˆํฌ์˜ ์„ค์ •์— ๋“ค์–ด๊ฐ€์„œ Repository variable์„ ์„ค์ •ํ•˜๋ฉด ${{ vars.๋ณ€์ˆ˜๋ช… }} ์œผ๋กœ ์•ก์…˜ ๋‚ด๋ถ€์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค (๋‹ค๋งŒ ์ด ๋ณ€์ˆ˜๋Š” ์™ธ๋ถ€์— ๊ณต๊ฐœ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์‹œํฌ๋ฆฟ ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” Secret variable๋กœ ์„ค์ •ํ•ด์„œ ${{ secrets.๋ณ€์ˆ˜๋ช… }} ์œผ๋กœ ์‚ฌ์šฉํ•˜์ž.

[4] job

์•ก์…˜์ด ์‹คํ–‰๋  ๋•Œ ์ˆ˜ํ–‰ํ•  ์ž‘์—…(job)๋“ค์„ ์ •์˜ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ์„œ๋ฒ„ ํŒŒ์ผ์„ ๋นŒ๋“œํ•˜๋Š” ์ž‘์—…์ธ build ์™€, ํ•ด๋‹น ๋นŒ๋“œ ํŒŒ์ผ์„ ๋ฐฐํฌํ•˜๋Š” deploy ๋‘ ๊ฐœ์˜ ์žก์ด ์ •์˜๋˜์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์žก๋“ค์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” build ์ž‘์—…์ด ๋๋‚œ ํ›„์—๋งŒ deploy ์ž‘์—…์„ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋ฏ€๋กœ needs ์˜ต์…˜์„ ์„ค์ •ํ•ด์„œ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰๋˜๊ฒŒ ํ•˜์˜€๋‹ค.

job ๋‚ด๋ถ€์˜ ์ˆœ์„œ๋“ค์€ step ์„ ํ†ตํ•ด ์ •์˜ํ•œ๋‹ค.

[5] job: build

build ์žก์€ ubuntu ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰(runs-on)ํ•˜๊ณ , ๋ณ€์ˆ˜์— ์„ค์ •ํ•ด๋‘” ํด๋”๋ฅผ(working-directory) ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์„ค์ •ํ•œ๋‹ค.

5-1

๊ฐ€์ƒ๋จธ์‹ ์— ํ•ด๋‹น ๋ ˆํฌ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

5-2

Spring ์„œ๋ฒ„ ๋นŒ๋“œ๋ฅผ ์œ„ํ•ด JDK๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

5-3, 5-4

Gradlew๋กœ ๋นŒ๋“œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์‹คํ–‰๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๊ณ , ์„œ๋ฒ„๋ฅผ ๋นŒ๋“œํ•œ๋‹ค.

5-5

๋นŒ๋“œ๋œ ์„œ๋ฒ„๋ฅผ AWS์— ์—…๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์—…๋กœ๋“œํ•  AWS ๊ณ„์ •์— ๋Œ€ํ•œ ์ธ์ฆ์ด ํ•„์š”ํ•˜๋‹ค.

์ด์ „๊นŒ์ง€๋Š” ๊นƒํ—™ ์•ก์…˜์„ ์ด์šฉํ•˜๋Š” IAM ์œ ์ €๋ฅผ ์ƒ์„ฑํ•œ ํ›„์— AWS ์•ก์„ธ์Šค ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ํ•ด์„œ ์ธ์ฆํ•˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ์‹์ด ๋งŽ์•˜์ง€๋งŒ,

uses: aws-actions/configure-aws-credentials@v1
with:
  aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
  aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  aws-region: ${{ secrets.AWS_REGION }}

https://github.com/aws-actions/configure-aws-credentials ์˜ ๋ฆฌ๋“œ๋ฏธ๋ฅผ ๋ณด๋ฉด, GitHub's OIDC provider ๋ฅผ ์ด์šฉํ•ด ์ธ์ฆํ•˜๋Š” ๋ฐฉ์‹์„ ์ถ”์ฒœํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋Š” OpenID Connect๋ฅผ ํ†ตํ•ด ์ธ์ฆํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด 1. ํ‚ค๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์žฌ๋ฐœ๊ธ‰ํ•˜๊ฑฐ๋‚˜ 2. ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ํ‚ค๋ฅผ ์ƒˆ๋กœ ๋ฐœ๊ธ‰ํ•˜๋Š” ๊ท€์ฐฎ์€ ๊ณผ์ •์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

OIDC ์…‹์—…ํ•˜๊ธฐ

1) ์ž๊ฒฉ ์ฆ๋ช… ๊ณต๊ธ‰์ž ์ถ”๊ฐ€

์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด AWS์˜ IAM ์œ ์ € ์ฝ˜์†”์— ๋“ค์–ด๊ฐ€์„œ, ๊ณต๊ธ‰์ž URL์€ https://token.actions.githubusercontent.com, ๋Œ€์ƒ์€ http://sts.amazonaws.com ์œผ๋กœ ์„ค์ •ํ•œ ํ›„ โ€˜๊ณต๊ธ‰์ž ์ถ”๊ฐ€'๋ฅผ ๋ˆ„๋ฅธ๋‹ค.

2) ์—ญํ•  ๋ฐ ์‹ ๋ขฐ ๊ด€๊ณ„ ์„ค์ •

GitHub Actions์—์„œ AWS์˜ S3 ๋ฒ„ํ‚ท๊ณผ CodeDeploy์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ๊ถŒํ•œ์— AmazonS3FullAccess ์™€ AWSCodeDeployFullAccess ๊ถŒํ•œ์„ ์ฃผ๊ณ , ์‹ ๋ขฐ ๊ด€๊ณ„๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ์„ค์ •ํ•˜๋ฉด OIDC ๋ฅผ ์œ„ํ•œ ์—ญํ•  ์ƒ์„ฑ์ด ์™„๋ฃŒ๋œ๋‹ค.

{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Principal": {
              "Federated": // ์•„๊นŒ ๋ฐœ๊ธ‰ํ•œ ์ž๊ฒฉ ์ฆ๋ช… ๊ณต๊ธ‰์ž์˜ ARN์„ ์ž…๋ ฅํ•œ๋‹ค. ex. "arn:aws:iam::0123456789:oidc-provider/token.actions.githubusercontent.com"
          },
          "Action": "sts:AssumeRoleWithWebIdentity",
          "Condition": {
              "StringEquals": {
                  "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
              },
              "StringLike": {
                  "token.actions.githubusercontent.com:sub": // ์ ‘๊ทผํ•  ์œ ์ €, ๋ ˆํฌ, ๋ธŒ๋žœ์น˜ ๋“ฑ์„ ์ •์˜ํ•œ๋‹ค. ex. "repo:ORG_OR_USER_NAME/REPOSITORY"
              }
          }
      }
  ]
}

3) ๋ ˆํฌ์— ํ•ด๋‹น ์—ญํ• ์„ ์‹œํฌ๋ฆฟ ๋ณ€์ˆ˜๋กœ ์ •์˜

์ƒ์„ฑํ•œ ์—ญํ• (Role)์˜ ARN์„ ํ•ด๋‹น ๋ ˆํฌ์— AWS_ROLE_TO_ASSUME ๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ์‹œํฌ๋ฆฟ ํ‚ค๋กœ ์„ค์ •ํ•œ๋‹ค.

5-6

AWS ์ธ์ฆ์ด ์™„๋ฃŒ๋˜๋ฉด ์„œ๋ฒ„๋ฅผ ๋„์šธ ๋•Œ ์ •์˜ํ•  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ๋‹ด๊ธด ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค. ๊นƒ์— ์˜ฌ๋ผ๊ฐ€๋ฉด ์•ˆ๋˜๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด๋‚˜ ๊นƒ์— ์˜ฌ๋ผ๊ฐˆ ํ•„์š”๊ฐ€ ์—†๋Š” ๋ณ€์ˆ˜๋“ค์„ ์„œ๋ฒ„์— ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค. .env ํŒŒ์ผ์€ KEY=VALUE ๊ฐ€ ์—ฌ๋Ÿฌ ์ค„์ธ ํ˜•์‹์œผ๋กœ ์ •์˜๋˜๋Š”๋ฐ, ์ด ํ˜•์‹์€ linux์—์„œ cat .env | xargs ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋ฐ”๋กœ ์‹œ์Šคํ…œ ๋ณ€์ˆ˜๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋ฉฐ Spring ์—์„œ๋Š” ์„ค์ •ํŒŒ์ผ์ธ application.yaml ์—์„œ ์‹œ์Šคํ…œ ๋ณ€์ˆ˜๋ฅผ ๋ฐ”๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

5-7

์ด์ œ ์„œ๋ฒ„์— ๋„์šธ ํŒŒ์ผ๋“ค์„ zip ์œผ๋กœ ์••์ถ•ํ•˜๊ณ  aws cli ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด s3 ๋ฒ„ํ‚ท์— ์˜ฌ๋ฆฐ๋‹ค. zip ํŒŒ์ผ์˜ ์ด๋ฆ„์€ ์‰ฌ์šด ๊ตฌ๋ถ„์„ ์œ„ํ•ด ๊นƒํ—™ ์ปค๋ฐ‹ ID($GITHUB_SHA)๋กœ ์ •์˜ํ•˜์˜€๋‹ค.

[6] job: deploy

์•ž์„  build job์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ์ด ์ž‘์—…์ด ์‹คํ–‰๋œ๋‹ค.

aws cli ๋ช…๋ น์–ด์ธ aws deploy code-deployment ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด CodeDeploy๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ์š”์ฒญํ•œ๋‹ค.

์ด ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” CodeDeploy์— ์„œ๋ฒ„ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ CodeDeploy Group๊ณผ CodeDeploy App์„ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋‘์–ด์•ผ ํ•œ๋‹ค.