AWS S3, Cloudfront, Route53과 github Actions으로 정적 웹페이지 배포, 호스팅 자동화하기

AWS S3, Cloudfront, Route53과 github Actions으로 정적 웹페이지 배포, 호스팅 자동화하기

들어가기 전에

예전에 잠깐 창업에 발을 살짝 들였을 때, 팀 사이트를 오늘 소개한 방법으로 배포한 적이 있다. 새로운 블로그를 최근에 만들었는데, 이 페이지를 배포할 방법으로 netlify 같은 서비스도 고민했지만, 그냥 깔끔하게 내가 다 만들어버리고 블로그 포스팅도 하자는 생각으로 다시 시도하면서 정리하게 되었다.

이 포스트에서는 vue, react 의 Client side application 뿐만 아니라 jekyll 과 같은 static web page 를 aws S3와 Cloudfront라는 CDN을 통해 배포하는 과정, 그리고 github Actions 을 통해 모든 것을 자동화하는 과정을 설명한다.

이 자동화 배포를 완성하기 위해서는 직접 초기 작업을 수행해야 한다. 물론 AWS CLI 를 사용한다거나, 여러가지옵션이 있겠지만 aws 를 제일 쉽게 사용할 수 있는 웹 콘솔 기준으로 설명한다.

IAM 만들기

github actions 상에서 사용할 aws credentials를 위하여 IAM 계정을 만들어야 한다. 절대로 AdministratorAccess 과 같은 권한을 주면 안된다. 이 때, 이 IAM에 쥐어주는 권하는 최대한 줄여 필요한 권한만 갖고 있도록 한다. 이 프로젝트에서 github actions 에서 사용하는 aws cli 명령은 aws s3 sync 와 aws cloudfront create-invalidation 이다. 이 명령어만 겨우 수행할 정도의 IAM 권한을 주는걸 추천한다. 이 분의 경험을 직접 겪고싶지 않다면 말이다. (https://youtu.be/r6TFnNQsQLY?t=1814) 요즘 회사에서 작업하고 있는 부분이 IAM 의 관리와 Security 에 대한 이야기인데, 나중에 더 이야기할 수 있었으면 좋겠다.

기존 정책을 갖다 쓸 수도 있겠지만, [정책 생성]을 눌러 손쉽게 사용할 정책json을 만들 수 있다.

IAM 을 만들어 aws credentials 를 받았다면 github repository 에 secrets 설정을 하러 가보자.

Github repository 생성 및 Secrets 설정

github repository 를 생성한다. 본인은 private repository 를 만들었다. 만들고 웹 소스코드를 push하도록 하자. 그 이후에 github actions 에서 aws cli 를 사용하여 당신의 aws 리소스에 접근시키도록 하려면, aws credentials 를 github actions 컨테이너 안에서 불러올 수 있어야 한다. 이를 위해 아래의 이미지처럼 settings > Secrets 탭에서 aws credentials를 등록하자. 이제 github actions 에서 aws 리소스들을 사용할 수 있다.

참고 문서 : https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions


Amazon S3 생성 및 구성

aws 웹 콘솔을 들어가서 S3 버킷을 생성하고, 퍼블릭 엑세스 차단을 비활성한다. 추가로 [속성] 탭에 진입하여 맨 밑에 있는 “정적 웹사이트 호스팅”을 활성화시킨다.

github actions 에서도 접근을 해야하기 때문에, 버킷 정책을 아래와 같이 설정한다.

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "GithubAction",
            "Effect": "Allow",
            "Principal": {
                "AWS": "<방금 만든 IAM의 arn>"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::<생성한 버킷 이름>",
                "arn:aws:s3:::<생성한 버킷 이름>/*"
            ]
        },
        {
            "Sid": "Access",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::<생성한 버킷 이름>",
                "arn:aws:s3:::<생성한 버킷 이름>/*"
            ]
        }
    ]
}

외부 도메인의 Route 53 DNS 설정

나는 gabia라는 곳에서 도메인을 구매하였다. 비용으로 Route53 안에서 구매하는 것도 좋은 방법이었지만, [co.kr](<http://co.kr>) 과 같은 한국 도메인은 구매할 수 없었다. 만약 .com 으로 끝나는 도메인이 비어있었다면 Route53 안에서 구매하였을 것이다.

gabia에서 도메인을 구매하여, gabia의 네임서버를 사용하지 않고 Route53 의 네임서버를 사용해야 한다. Route 53 의 대시보드에서 “호스팅 영역” 에 진입, [호스팅 영역 생성] 버튼을 클릭한다. 도메인 이름을 입력하고 호스팅 영역을 생성하면, 아래와 같은 테이블을 확인할 수 있을 것이다.

aws 에서 제공되는 네임서버이며, 이 정보를 gabia 의 네임서버 세팅에 1차~4차 네임서버로 입력하면 된다. 순서는 상관없다.

ACM 설정

HTTPS 프로토콜로 구매한 도메인을 통해 호스팅하기 위해서는 인증서 발급이 필요하다. ACM (AWS Certificate Manager)를 통해 인증서를 발급받을 수 있다. 여기서 중요한 점은 ACM 대시보드에 진입하고 나서 Region을 꼭 “us-east-1”으로 세팅한다. 그래야 Cloudfront 에서 인증서를 불러올 수 있다.

Route53에 DNS 설정을 마무리하였으므로, 인증서를 발급하는 건 쉽다. [인증서 요청]을 통하여 해당 도메인의 인증서를 발급받도록 하자. 약 30분정도 걸린다고 하는데, 10분쯤 걸린 것 같다.

Cloudfront 초기 세팅

[Create Distribution] 을 눌러 디스트리뷰션을 만든다. Origin Domain Name 에서는 웹 콘솔의 폼에 나오는 url 을 선택하지 않고 S3 웹 콘솔에 있는 아래의 주소를 입력하도록 한다.

그 외의 Distribution Settings 은 아래와 같이 세팅한다.

Github actions 설정

이제 코드 개발 이후 github 레포지토리에 push만 하면 trigger 되는 빌드/배포 작업을 세팅해야 한다. 이 작업을 마무리하게 되면, 앞으로 git push 명령어만으로 AWS CDN에 새로 빌드된 정적 웹페이지가 수 분 내로 배포되게 된다.

로컬에서 하던 대로 github actions 스크립트를 만들면 된다. 레포지토리에서 .github/workflow/<FILE_NAME>.yml 로 정의하면 알아서 github actions 이 수행된다.

name: OWLSNESTBLOG
on: 
  push:
    branches:
      - master
jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout source code.
        uses: actions/checkout@master
      - name: Cache node modules
        uses: actions/cache@v1
        with:
          path: node_modules
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}-
      - name: Install dependencies
        run: npm install
      - name: Set up Ruby 2.6
        uses: actions/setup-ruby@v1
        with:
          ruby-version: 2.6
      - name: Install bundler
        run: 
          gem install bundler
          
      - name: Run bundle install
        run: 
          bundle install
      - name: Build scss
        run: gulp styles && gulp scripts
      - name: Build _site
        run: bundle exec jekyll build
      - name: SHOW AWS CLI VERSION
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_EC2_METADATA_DISABLED: true
        run: |
          aws --version
      - name: Sync Bucket
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_EC2_METADATA_DISABLED: true
        run: |
          aws s3 sync \
            --region ap-northeast-2 \
            _site s3://owlsnest-kr-blog \
            --delete
      - name: Invalidate Cache
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_EC2_METADATA_DISABLED: true
        run: |
          aws cloudfront create-invalidation \
          --distribution-id {{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} \
          --paths "/*"

⚠️ UBUNTU 20.04 이미지를 사용할 때

github actions 의 컨테이너 이미지 OS 설정 시 Ubuntu 20.04 를 사용할 경우, aws cli v2를 사용할 수 있고 Ubuntu 18.04 이하의 디스트로에서는 aws cli v1을 사용한다. 그 이유는 공식 레포의 스크립트를 참고하면 확인할 수 있다. (https://github.com/actions/virtual-environments/blob/main/images/linux/scripts/installers/aws.sh) 당연히 18.04에서 aws cli v2를 쓸 줄 알고 썼다가 시행 착오를 몇 번 했다.

CLI 2 로 넘어오면서 이런 에러를 확인할 수 있다. 이에 대한 해결법으로는 환경 변수에AWS_EC2_METADATA_DISABLED: true를 추가해주면 해결 가능하다.

빌드와 배포에 성공한 모습이다.