[2주차] GitHub Action 환경에서의 AWS ELB, EC2 배포 설정 및 배포 자동화 (Next SSR) - CLI
이 문서는 [월간-CS][24년 4월] React, Next 배포와 배포 자동화 A부터 Z를 위해서 작성된 문서입니다. 이 문서에는 그 어떤 저작권이 없으며, 편하게 참고 및 사용하셔도 됩니다.
!주의! !주의! !주의!
이번 실습은 시간당 과금되는 비용이 존재합니다. 따라서 몇일에 걸쳐서 실습을 진행할 경우, 아래와 같은 이유로 많은 비용이 과금될 수 있습니다.
리소스를 생성하고 삭제를 까먹은 경우
리소스를 생성하고 너무 오랜 시간 동안 실습을 진행한 경우
리소스를 생성해놓고 다른 PC에서 이어서 실습을 진행하는 경우
따라서 실습을 중단할 것이라면, 반드시 이 문서의 마지막에 있는 리소스 제거하기 파트를 진행해주세요.
사전 지식
지난 주차에는 아래의 2단계의 실습을 통해, AWS CloudFront, S3를 통해서 정적 파일을 제공하는 법을 배웠습니다. 따라서, 해당 내용을 반드시 숙지하고 진행해주세요.
사전 준비
이번에는
이번 실습에서는 [2주차] AWS ELB, EC2, CodeDeploy에 대한 이해에서 설계한 아키텍처를 실제로 배포하고 배포 자동화를 구현해볼 것입니다.
필요 프로그램 설치
이번 실습에서는 코드 파일을 통해, AWS AMI를 생성하는데 사용 가능한 Packer를 설치할 것입니다.
Packer란?
Packer는 HashiCorp 사에서 만든 인프라 자동화 도구입니다.
다양한 환경에서 다양한 가상 머신 이미지(VMI, Virtual Machine Image)를 생성하는데 사용됩니다.
Packer 사용 예시
아래와 같은 요구사항에서 packer는 코드 파일(*.pkr.hcl)을 기반으로 탬플핏화된 가상 머신 이미지(VMI)를 생성할 수 있습니다.
요구 사항
서비스 기업 A에서 운영 중인 모든 서버는 Node 20 및 CodeDeploy Agent라는 툴을 설치가 되어야 합니다.
해결 방법
서버가 실행될 때, 선택하는 이미지를 원하는 형태로 미리 만들어 놓으면 된다.
AWS AMI란?
AWS EC2(서버)가 시작되는데 필요한 가상 머신 이미지입니다.
서버가 가동되기 위한 운영체제(Guest OS)를 포함한 일련의 툴(Bins/Libs)들이 설치가 되어 있습니다.코드 → 주요 코드 생략
# VMI/AMI에서 사용할 블록 스토리지(EBS) source "amazon-ebs" "custom_ami_ebs" { } # VMI/AMI 빌드 선언 build { provisioner "shell" { inline = [ # [STEP 1] CodeDeploy에서 필요한 필수 프로그래 설치 # [STEP 2] Node.js 20 버전 설치 # [STEP 3] CodeDeploy Agent 설치 ] } }
Packer 설치하기
인프라 소개하기
후술…
네트워크 부분
후술…
컴퓨팅 부분
후술…
배포 자동화 부분
후술…
작업 환경 준비
실습용 저장소 접속하기
https://github.com/monthly-cs/2024-04-cicd-week-2-template
실습용 저장소를 개인 저장소로 포크 뜨기
해당 저장소를 클론 받기
git clone https://github.com/<본인 GitHub UserName>/2024-04-cicd-week-2-template 예) git clone https://github.com/unchaptered/2024-04-cicd-week-1-template.git 2024-04-cicd-week-2-template-practice
실습 진행
이 실습은 [1주차] CloudFront, S3 쉽게 배포하기 - DNS 리소스 배포하기 + 가비아의 도메인 NS 변경하기를 진행한 후에 진행됩니다.
수동으로 배포하기
AMI 배포하기
해당 리소스는 N분 내로 배포가 완료되었습니다.
# 프로젝트 루트 경로에서 cd ./infra/ami packer init . packer build .
Server 배포하기
해당 리소스는 NN분 내로 배포가 완료되었습니다.
# 프로젝트 루트 경로에서 cd ./infra/server terraform init terraform apply -var="profile=monthly-cs" -var="domain_name=<가비아에서 구매한 도메인>" -var="ami_id=<AMI ID>" 예) terraform apply -var="profile=monthly-cs" -var="domain_name=unchaptered.shop" -var=ami_id=ami-0e22b048b09df457f"
AWS Console 로그인하기
AWS Console에서 CodeDeploy 검색하기
배포된 CodeDeploy Application 클릭하기
리전을 us-east-1으로 선택하셔야 합니다!
CodeDeploy Deployment 클릭하기
CodeDeploy Deployment에서 아래 빨간 체크박스에 맞게 수정하기
로컬에서 배포 준비하기
프로젝트 루트 디렉토리로 이동하기
cd ../../
npm install
를 이용해서 모듈 다운받기npm run build
를 이용해서 빌드하기Next 빌드 파일은 .next 폴더 안에 들어가있습니다.
MacOS에서는 [커맨드] + [쉬프트] + [온점(.)]으로 볼 수 있습니다.npm start
를 이용해서 서버 시작해보기Next 서버를 가동한 이후, 별도의 터미널(cmd)에서 curl 요청을 보내서 이를 확인해볼 수 있습니다
빌드 파일을 zip 형태로 압축하기
Windows
zip -r next-server.zip ^ ./.next ^ ./component ^ ./public ^ ./scripts ^ ./src ^ ./appspec.yml ^ ./ecosystem.config.json ^ ./next-env.d.ts ^ ./next.config.mjs ^ ./package-lock.json ^ ./package.json ^ ./README.md ^ ./tsconfig.json ^ ./yarn.lock
MacOS, Linux
zip -r next-server.zip \ ./.next \ ./component \ ./public \ ./scripts \ ./src \ ./appspec.yml \ ./ecosystem.config.json \ ./next-env.d.ts \ ./next.config.mjs \ ./package-lock.json \ ./package.json \ ./README.md \ ./tsconfig.json \ ./yarn.lock
빌드 파일을 s3에 업로드하기
cd ./infra/server # 아래 명령어로 빌드 파일을 업로드할 s3 이름을 확인 terraform output s3_name
<terraform output s3_name의 결과>에는 쌍따옴표(“)를 제거하고 적어주세요.
즉, 출력값이 “*-monthly-cs-s3-bucket“ 이라면 *-monthly-cs-s3-bucket 라고만 적어주세요.Windows
aws s3 cp ^ --region us-east-1 ^ ../../next-server.zip ^ s3://<terraform output s3_name의 결과>/next-server.zip ^ --profile monthly-cs 예) aws s3 cp ^ --region us-east-1 ^ ../../next-server.zip ^ s3://*-monthly-cs-s3-bucket/next-server.zip ^ --profile monthly-cs
MacOS, Linux
aws s3 cp \ --region us-east-1 \ ../../next-server.zip \ s3://<terraform output s3_name의 결과>/next-server.zip \ --profile monthly-cs
빌드 파일로 CodeDeploy 배포 실행하기
cd ./infra/server # 만약 이미 server 폴더라면, 제외 terraform output s3_name terraform output codedeploy_app_name terraform output codedeploy_deployment_name
Windows
aws deploy create-deployment ^ --application-name <출력값 codedeploy_app_name> ^ --deployment-group-name <출력값 codedeploy_deployment_name> ^ --s3-location bucket=<출력값 s3_name>,bundleType=zip,key=next-server.zip ^ --profile monthly-cs
MacOS,Linux
aws deploy create-deployment \ --application-name <출력값 codedeploy_app_name> \ --deployment-group-name <출력값 codedeploy_deployment_name> \ --s3-location bucket=<출력값 s3_name>,bundleType=zip,key=next-server.zip ^ --profile monthly-cs
결론
위 과정을 모두 완료하고 week2.<가비아에서 구매한 도메인>
에 접속하면, 사이트가 나옵니다.
배포 자동화 구축
appspec.yml 파일
프로젝트 루트 경로에 아래 파일을 생성해주세요
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/project
overwrite: yes
file_exists_behavior: OVERWRITE
permissions:
- object: /home/ubuntu
pattern: '**'
owner: ubuntu
group: ubuntu
hooks:
AfterInstall:
- location: scripts/after-install.sh
timeout: 300
runas: ubuntu
scripts/after-install
프로젝트 루트 경로에 scripts 폴더를 만들고 after-install.sh
파일을 만들어주세요.
DIRECTORY=/home/ubuntu/project
cd $DIRECTORY
sudo npm i pm2 -g
sudo npm i
sudo pm2 start npm --name "next-server" -- start
로그 확인하기
모든 방법이 실패했을 때
ssh "PEM 키 주소" ubuntu@IP 주소
PEM 키 권한 400으로 변경하기
Windows 현장설명
MacOS
# 프로젝트 루트 경로에서 chmod 400 <프로젝트 루트 경로>/infra/server/monthly-cs.pem 혹은 chmod 400 ./infra/server/monthly-cs.pem
SSH 설정하기
MacOS는 ssh의 위치가 다릅니다.
이 경우where ssh
혹은which ssh
로 위치를 찾고 아래에 C:/ 경로를 바꿉시다.Host Monthly-Bastion-Host HostName <Public IP> IdentityFile "<프로젝트 루트 경로>\infra\server\monthly-cs.pem" User ubuntu Host Monthly-Next-Server HostName <Private IP> IdentityFile "<프로젝트 루트 경로>\infra\server\monthly-cs.pem" User ubuntu # Windows ProxyCommand C:/Windows/System32/OpenSSH/ssh.exe -q -W %h:%p Monthly-Bastion-Host # MacOS ProxyCommand ssh -q -W %h:%p Monthly-Bastion-HostSSH
로그 모니터링하기
sudo su - pm2 log
포켓몬 잡기를 시도해서 로그가 찍히는 것을 확인하기
리소스 정리
아래의 순서에 맞춰서 리소스를 정리해주세요.
모든 리소스를 제거해야 비용이 나오지 않습니다.
반드시 마지막 리소스까지 제거해주세요!
AWS S3 버킷 비우기
S3 버킷 이름 조회하기
cd ./infra/server
terraform output s3_name
S3 버킷 이름 지우기
aws s3 rm s3://<출력값 s3_name> --recursive --profile monthly-cs
Next Server 배포 지우기
# 프로젝트 루트 경로에서
cd ./infra/server
terraform init
terraform destroy -var="profile=monthly-cs" -var="domain_name=<가비아에서 구매한 도메인>" -var "ami_id=<AMI_ID>"
예)
terraform destroy -var="profile=monthly-cs" -var="domain_name=unchaptered.shop" -var=ami_id=ami-0e22b048b09df457f"
AWS AMI 이미지 조회 및 삭제하기
이미지 조회하기
이미지 삭제하기
한 줄 구문(Windows)
한 줄 구문(MacOS/Linux)
이미지 조회하기
AWS AMI 리스트 조회하기
aws ec2 describe-images --owners self --profile monthly-cs
Monthly-CS에서 Packer로 생성한 이미지 조회하기
aws ec2 describe-images --owners self --filters "Name=tag:monthly-cs,Values=monthly-cs" --profile monthly-cs
Monthly-CS에서 Packer로 생성한 이미지의 ID(ami_id) 조회하기
배열
aws ec2 describe-images --owners self --filters "Name=tag:monthly-cs,Values=monthly-cs" --query "Images[*].ImageId" --profile monthly-cs
배열의 첫번째 인덱스만
aws ec2 describe-images --owners self --filters "Name=tag:monthly-cs,Values=monthly-cs" --query "Images[0].ImageId" --profile monthly-cs
이미지 삭제하기
특정한 이미지 삭제하기
aws ec2 deregister-image --image-id <조회한 AWS AMI ID>
한 줄 구문(Windows)
for /f %i in ('aws ec2 describe-images ^
--owners self ^
--filters "Name=tag:monthly-cs,Values=monthly-cs" ^
--query "Images[0].ImageId" ^
--profile monthly-cs') do aws ec2 deregister-image --image-id %i --profile monthly-cs
한 줄 구문(MacOS/Linux)
aws ec2 deregister-image $(aws ec2 describe-images \
--owners self \
--filters "Name=tag:monthly-cs,Values=monthly-cs" \
--query "Images[0].ImageId" \
--profile monthly-cs) \
--profile monthly-cs
AWS Snapshot 조회 및 삭제하기
이미지 조회하기
이미지 삭제하기
한 줄 구문(Windows)
한 줄 구문(MacOS/Linux)
이미지 조회하기
AWS Snapshot 리스트 조회하기
aws ec2 describe-snapshots ^ --profile monthly-cs aws ec2 describe-snapshots \ --profile monthly-cs
내 계정의 AWS Snapshot 리스트 조회하기
aws ec2 describe-snapshots ^ --owner-ids self ^ --profile monthly-cs aws ec2 describe-snapshots \ --owner-ids self \ --profile monthly-cs
내 계정의 Monthly-CS에서 AWS Snapshot 리스트 조회하기
aws ec2 describe-snapshots ^ --filters "Name=tag:monthly-cs,Values=monthly-cs" ^ --profile monthly-cs
내 계정의 Monthly-CS에서 AWS Snapshot 리스트 ID(ami_id) 조회하기
배열
aws ec2 describe-snapshots ^ --filters "Name=tag:monthly-cs,Values=monthly-cs" ^ --query "Snapshots[*].SnapshotId" ^ --profile monthly-cs
배열의 첫번째 인덱스만
aws ec2 describe-snapshots ^ --filters "Name=tag:monthly-cs,Values=monthly-cs" ^ --query "Snapshots[0].SnapshotId" ^ --profile monthly-cs
이미지 삭제하기
특정한 이미지 삭제하기
aws ec2 delete-snapshot ^ --snapshot-id <스냅샷 ID> ^ --profile monthly-cs
한 줄 구문(Windows)
for /f %i in ('aws ec2 describe-snapshots ^
--filters "Name=tag:monthly-cs,Values=monthly-cs" ^
--query "Snapshots[0].SnapshotId" ^
--profile monthly-cs') do aws ec2 delete-snapshot --snapshot-id %i --profile monthly-cs
한 줄 구문(MacOS/Linux)
aws ec2 deregister-image $(aws ec2 describe-images \
--owners self \
--filters "Name=tag:monthly-cs,Values=monthly-cs" \
--query "Images[0].ImageId" \
--profile monthly-cs) \
--profile monthly-cs