Home Terraform으로 AWS 무중단 배포 인프라 구성하기 - 5. 운영환경 (프론트)
Post
Cancel

Terraform으로 AWS 무중단 배포 인프라 구성하기 - 5. 운영환경 (프론트)

Terraform으로 AWS 무중단 배포 인프라 구성하기

  1. 개요
  2. 기초
  3. 네트워크
  4. 테스트 환경
  5. 운영환경 (프론트)
  6. 운영환경 (백엔드)
  7. 마무리

Terraform으로 AWS ECS 무중단 배포 인프라 구성하기 - 5. 운영환경 (프론트)

프론트 운영환경을 구축한다.

리소스

1. S3 버킷

먼저 S3 버킷을 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
resource "aws_s3_bucket" "app-prod-react" {
  bucket = "app-prod-react"

  tags = {
    Name = "app-prod-react"
  }
}

resource "aws_s3_bucket_ownership_controls" "app-prod-react-ownership" {
  bucket = aws_s3_bucket.app-prod-react.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_acl" "app-prod-react-acl" {
  depends_on = [aws_s3_bucket_ownership_controls.app-prod-react-ownership]

  bucket = aws_s3_bucket.app-prod-react.id
  acl    = "private"
}

resource "aws_s3_object" "app-prod-react-admin" {
  bucket = aws_s3_bucket.app-prod-react.id
  content_type = "application/x-directory"
  key = "admin/"
}

resource "aws_s3_object" "app-prod-react-user" {
  bucket = aws_s3_bucket.app-prod-react.id
  content_type = "application/x-directory"
  key = "user/"
}

버킷과 엑세스 제어 목록, 관리자와 유저 어플리케이션이 배포될 폴더를 정의했다.

관리자와 유저 어플리케이션을 업로드해 둘 것이다. 이 또한 terraform으로 진행이 가능하지만, 어짜피 앱 배포는 추후 github action을 통해 빌드하고 s3에 업로드할 것이므로 지금은 콘솔 환경에서 직접 업로드 한다.

s3-upload

이 상태에서 버킷을 퍼블릭 액세스로 열어버리고 주소로 접근하면 웹 페이지가 뜬다. 그러나 앞단에 CloudFront를 두어 그곳에서 s3로 라우팅할 것이기 때문에 넘어간다.

2. CloudFront

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
resource "aws_cloudfront_origin_access_control" "admin-front" {
  name                              = "admin-front"
  description                       = "admin front"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_cloudfront_distribution" "admin-distribution" {
  origin {
    domain_name = aws_s3_bucket.app-prod-react.bucket_regional_domain_name
    origin_id   = aws_s3_bucket.app-prod-react.id
    origin_access_control_id = aws_cloudfront_origin_access_control.admin-front.id
    origin_path = "/admin"
  }

  enabled = true
  default_root_object = "index.html"
  comment = "admin distribution"

  aliases = ["app-admin.keencho.com"]

  default_cache_behavior {
    allowed_methods        = ["GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    target_origin_id       = aws_s3_bucket.app-prod-react.id

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl = 0
    default_ttl = 3600
    max_ttl = 86400
  }

  price_class = "PriceClass_100"

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["KR"]
    }
  }

  viewer_certificate {
    acm_certificate_arn = aws_acm_certificate.ssl-certificate-virginia.arn
    ssl_support_method = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  custom_error_response {
    error_code = 403
    error_caching_min_ttl = 10
    response_page_path = "/index.html"
    response_code = 200
  }
}

관리자 배포를 생성했다. 대체 도메인을 지정할 경우 SSL 인증서는 필수인데, 앞서 말했던 것처럼 여기엔 us-east-1 리전에 존재하는 인증서만 지정할 수 있다.

현 시점엔 라우팅되는 모든 트래픽이 s3 버킷의 /admin 폴더로 전송된다.

1
2
3
4
5
6
7
8
9
10
11
resource "aws_route53_record" "app-admin" {
  zone_id = aws_route53_zone.keencho.id
  name    = "app-admin.keencho.com"
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.admin-distribution.domain_name
    zone_id                = aws_cloudfront_distribution.admin-distribution.hosted_zone_id
    evaluate_target_health = true
  }
}

Route 53 레코드를 생성하여 app-admin.keencho.com 으로 들어오는 요청이 CloudFront로 라우팅 되도록 하였다. 사용자(user) 배포도 이름만 바꾸어 생성한다.

S3 버킷 정책 변경

현재 S3 버킷 정책은 아래 이미지와 같이 모든 퍼블릭 엑세스가 차단되어 있을 것이다.

s3 bucket block access

CloudFront 에서 온 요청은 허용하는 정책을 적용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
resource "aws_s3_bucket_policy" "allow-from-cloudfront-policy" {
  bucket = aws_s3_bucket.app-prod-react.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid      = "AllowCloudFrontServicePrincipal",
        Action   = "s3:GetObject",
        Effect   = "Allow",
        Resource = "${aws_s3_bucket.app-prod-react.arn}/*",
        Principal = {
          "Service": "cloudfront.amazonaws.com"
        },
        Condition = {
          StringEquals = {
            "AWS:SourceArn": [
              aws_cloudfront_distribution.admin-distribution.arn,
              aws_cloudfront_distribution.user-distribution.arn,
            ]
          }
        }
      }
    ]
  })
}

도메인으로 접속했을 때 의도한대로 페이지가 동작하는지 확인하자.

prod front admin

prod front user

3. Github Actions 배포 스크립트 작성

react 프로젝트 구조이다. root 폴더에서 npm install 명령어를 수행한 후 각 admin, user 폴더에서 빌드를 수행해야 한다.

react-tree

다음은 Github Actions 배포 스크립트이다. 앞서 설명한대로 installbuild를 수행한 후 S3에 업로드한다. 이때 기존 파일은 모두 삭제한다. 물론 실제 운영환경에선 따로 백업해두는게 안전하다.

그 후 CloudFront Invalidate 를 통해 캐싱된 파일을 무효화 하여 사용자가 배포된 파일을 확인할 수 있게 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
name: Deploy Admin and User to S3

on:
  workflow_dispatch:

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest

    steps:
      - name: Check out the repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install dependencies for Root Project
        working-directory: react
        run: npm install

      - name: Build Admin
        working-directory: react/app/admin
        run: npm run build:production

      - name: Build User
        working-directory: react/app/user
        run: npm run build:production

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

      - name: Remove Exist & Upload New to S3
        working-directory: react
        run: |
          aws s3 rm s3://${{ secrets.S3_BUCKET_NAME }}/admin --recursive
          aws s3 rm s3://${{ secrets.S3_BUCKET_NAME }}/user --recursive
          aws s3 cp app/admin/dist s3://${{ secrets.S3_BUCKET_NAME }}/admin --recursive
          aws s3 cp app/user/dist s3://${{ secrets.S3_BUCKET_NAME }}/user --recursive

      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID_ADMIN }} --paths "/*"
          aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID_USER }} --paths "/*"
This post is licensed under CC BY 4.0 by the author.

Athena로 ALB Access Log 분석하기

Terraform으로 AWS 무중단 배포 인프라 구성하기 - 6. 운영환경 (백엔드)