우테코 레벨3의 프론트엔드 요구사항에 따라 'AWS에서 프론트엔드 리소스 빌드 및 배포 환경을 구축'해야했다.
당시 프론트는 정적 파일을 사용한 CSR 방식으로 프로젝트를 진행하고 있었고, CDN과 CDN캐시를 사용한 빠른 로드도 구현해야하는 상황이었다.
AWS의 여러 배포 방식 중, 현재 팀 프로젝트에 맞는 방식은 S3를 버킷으로 사용하고 CloudFront를 통한 배포라고 생각했다.
S3는 스토리지 서비스로서 장점이지만, DDos 공격에서는 취약하다는 단점이 있다. 그래서 S3를 정적 리소스를 담는 저장소로 사용하고, SSL/TLS 암호화와 AWS Shield를 통한 DDoS 공격으로부터 보호되며 CDN을 사용할 수 있는 CloudFront로 배포하는 것이 좋다고 생각했다.
방법 | 설명 | 특징 | 비용 |
---|---|---|---|
S3 + CloudFront + CodePipeline | S3에 정적 파일을 저장하고, CloudFront로 CDN 제공, CodePipeline으로 CI/CD 자동화 | 정적 파일 배포에 적합, 비용 효율적, 서버 관리 필요 없음 | 저렴 |
AWS Amplify | Git 연동하여 빌드 및 배포 자동화 지원, CI/CD 파이프라인을 자동 생성 | 설정이 간편하고 빠름, 서버리스 환경에서 완전 자동화된 관리형 서비스 | 저렴 (트래픽 증가 시 비용 상승) |
EC2 + NGINX + CodeDeploy | EC2 인스턴스에 NGINX 설정 후, CodeDeploy로 배포 자동화 | 서버에 대한 완전한 제어, SSR이나 백엔드 로직이 필요한 경우 적합 | 중간~높음 |
Elastic Beanstalk | 풀스택 애플리케이션 배포에 적합한 PaaS | 서버 관리가 자동화되며, Node.js 등 백엔드 로직이 포함된 애플리케이션 배포 가능 | 중간 |
Lightsail | 소규모 프로젝트용 저비용 서버 제공 | 설정이 간단하고 저렴, 소규모 애플리케이션 배포에 적합 | 저렴 |
AWS 계정의 Access Key와 SSH 비대칭 키를 미제공하기 때문에, 터미널 명령어로 CD를 구축할 수 없었다. CI/CD 파이프라인을 자동으로 생성해주는 Amplify를 사용할 수 없었다. (아마 CI,CD를 직접 구현해보라는 의도가 아닐까 싶다)
1. CloudFront 캐시 무효화 자동화
S3에 변경된 정적 파일이 올라갔을 때, CloudFront의 캐시 무효화를 진행해야했다.
Codepipeline에 Lambda(람다)를 통해 캐시 무효화 과정을 자동화할 수 있다.
2.일관된 배포 프로세스
CodePipeline은 소스-빌드-배포라는 일관된 단계를 거친다. 문제가 발생하거나 테스트가 실패시, 해당 단계를 버튼으로 다시 실행할 수 있다. 또한 전체 프로세스를 다시 실행할 수도 있다.
3. 빌드 및 테스트 통합
CodeBuild를 사용해 빌드할 수 있고 테스트를 실행해, 빌드 단계에서 문제가 발생하면 배포되지 않도록 할 수 있다
4.빌드 기록 저장
빌드 시, CloudWatch를 사용해 로그들을 S3 버킷에 담아 로그들을 트랙킹할 수 있다.
5. 이미 사용하고 있는 S3,CloudFront와 연동
이미 S3,CloudFront로 배포하고 있었기 때문에 CodePipeline은 이들과 연동해 CD를 구축할 수 있다.
1. 소스
연결된 Github 저장소에 지정한 브랜치에 변동 사항이 있을 때, 파이프라인이 시작된다.
2.빌드
CodeBuild를 통해 설정한 yml을 실행해, 테스트 및 빌드를 진행한다.
3.배포
성공적으로 빌드된 파일들을 S3에 업로드한다. 이때 S3에 트리거된 람다가 S3 변경을 감지해 CloudFront 캐시를 무효화한다. 캐시가 무효화되면서, 변경사항이 반영된 콘텐츠가 제공된다.
해당 단계에서는 배포 파일들이 있는 Github 저장소와 브랜치를 설정해주면 된다.
refs/heads/develop로 설정해, develop 브랜치에 변경된 코드를 업로드하면 CodeBuild를 가동시켰다.
어떤 운영체제 상에서 빌드할 것 인지를 정해야한다. Ubuntu를 운영체제로 지정했다.
빌드를 어떻게 할 것인지에 대한 설정이다. Github action을 통한 테스트 및 빌드 시에 사용하는 yml 파일을 그대로 사용했고 빌드한 파일들이 담기는 S3 버킷 주소를 추가했다.
//추가된 설정
artifacts:
files:
- '**/*'
discard-paths: yes
base-directory: frontend/dist
name: techcourse-project-2024/review-me/frontend-deploy
yml에서 사용하는 환경변수는 CodePipeline 설정 시,추가한 변수를 의미한다. 즉, 환경변수는 CodePipeline에서 설정해야 빌드 시 해당 환경 변수를 사용할 수 있다.
빌드에 대한 로그를 어디에 저장할 것인지를 설정하는 단계이다.만들고 있는 CodeBuild에 대한 로그를 담을 S3를 설정해줬다.
배치 구성,아티팩트는 따로 설정하지 않았다.
서비스 역할 권한은 우테코의 인프라 공지를 적힌 CodeBuild 서비스 역할을 사용했다.
파이프라인,버킷(=CD 구현 결과를 저장한 버킷) 설정을 진행하고 CodePipeline 단계에 맞는 설정을 해주면 된다.
'소스 스테이지 추가'에서는 정적 파일을 불러오는 Github 저장소와 브랜치를 연결한다. CodePipeline을 통한 변경 감지를 선택한다.
빌드 공급자에서 'Other build providers'를 선택해 위에서 만든 CodeBuild를 빌드 공급자로 선택한다.
빌드 시 사용되는 환경 변수가 있다면 빌드 설정 시 '환경 변수 추가'를 통해 추가하고, 어느 지역 내의 CodeBuild 인프라에서 빌드를 실행할 지 '리전(Region)'에 정하면된다.
이때 리전이 사용자와 가까울수록 네트워크 지연 시간이 줄어들기 때문에, 대부분의 사용자가 있는 지역을 선택하는 것이 좋다
작업 공급자가 S3로 설정되면, 어떤 S3버킷의 정적 파일로 배포할 것인지 S3 버킷과 배포 경로를 설정하면 된다. review-me라는 S3 버킷에 dev와 prodution 빌드 사항을 폴더로 나누어서 관리하고 있기때문에, dev 배포 페이이지를 위한 S3 경로를 'review-me/dev'로 설정했다.
빌드를 통해 S3에 변경된 리소스가 올라가면, CloudFront를 무효화하기 위해 Lambda를 생성해야한다.
람다로 CloudFront 캐시 무효화하는 코드를 추가한다.
Node.js로 람다를 실행하려했지만, 안되어서 검색과 GPT를 통해 생성한 python코드를 사용했다.
import json
import boto3
cloud_front = boto3.client("cloudfront")
def lambda_handler(event, context):
# Extract information from S3 event
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
# Specify the CloudFront distribution ID
distribution_id = "{id}"
# Specify the object path to be invalidated (e.g., '/*' to invalidate all)
object_paths = ["/*"]
try:
# Create CloudFront invalidation
cloud_front.create_invalidation(
DistributionId=distribution_id,
InvalidationBatch={
'Paths': {
'Quantity': len(object_paths),
'Items': object_paths,
},
'CallerReference': str(hash(key)), # You can use a different reference based on your needs
}
)
except Exception as e:
print(f"Error: {e}")
raise e
람다가 변경 사항을 감지한 S3 버킷을 연결하면 된다. 만약 특정 S3 버킷의 세부 경로를 사용한다면 review-me/dev/index.html
와 같은 접두사를 사용하면 된다.
AWS와 CD도 처음이라 막막했지만, 미리 CodePipeline을 구현한 다른 크루가 있어서 해당 크루의 글이 큰 도움이 되었다. 그러나 CodePipeline 설정 과정에서 각 설정이 무엇을 의미하는 지 하나하나 찾아보아야 했다. 더욱이 시간적 여유가 부족해 반나절만에 CD를 구축해야해서 힘들었었다.
그럼에도 불구하고 CD를 성공적으로 구축하고 나니, 막연히 두려웠던 AWS와 CD를 스스로 구현했다는 점에서 자존감이 올라갔고, 한층 더 성장했다는 것을 느꼈다. 이후에 release용 CD 구축 시 보다 빠르게 작업할 수 있었고 CodePipeline을 사용하려는 다른 크루들을 도와주었다. 또한 자동으로 빌드되니까 정말 편했다.😄👍
엇 제 글을 링크로 달아주시다니 영광이에요 😄
저보다 더 깔끔하게 잘 정리해준거 같네요 ㅎㅎ