Overview
์๋ฒ ๋ฐฐํฌ๋ ๋ค์ ๊ณผ์ ์ ๊ฑฐ์ณ ์ด๋ฃจ์ด์ง๋ค.
- ๋ ํฌ์งํ ๋ฆฌ์
main
๋ธ๋์น์ ํธ์ํ๋ฉด GitHub Action์ด ์คํ - GitHub Actions ์คํ
- ์๋ฒ๋ฅผ ๋น๋
- OpenID Connect๋ก AWS ์ธ์ฆ
- ๋น๋๋ ํ์ผ์ AWS S3์ ์ ๋ก๋
- ์ ๋ก๋๊ฐ ์๋ฃ๋๋ฉด AWS CodeDeploy๋ฅผ ์คํ
- AWS CodeDeploy๊ฐ S3์ ์ ๋ก๋๋ zip ํ์ผ์ ๋ฏธ๋ฆฌ ์ ์ํด๋ EC2 ์ธ์คํด์ค์ ์ ๋ก๋
- 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์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋์ด์ผ ํ๋ค.