[Tistory] 월 1000원 미만으로 웹 배포 하기(AWS S3 + CloudFront + Route53 + Docker + Jenkins)

원글 페이지 : 바로가기

아래와 같이 S3 정적 웹 사이트 호스팅을 통한 웹 배포 프로세스를 작성해보려 합니다. 배포 Flow 및 아키텍처는 아래와 같습니다. 1. Jenkins에서 Git push (main branch) 를 감지 2. Jenkins에서 Git clone, build, deploy 진행. 3. deploy는 AWS CLI를 통해서 AWS S3에 정적 웹 사이트 호스팅 파일 업로드를 완료 후 CloudFront 캐시 무효화를 진행(Jenkins 스크립트 작성) 4. 웹 배포 완료! 우선 EC2 를 사용 할 것입니다. EC2는 가상 컴퓨팅 환경으로 컴퓨터를 하나 빌리는 것과 동일 합니다. 서버컴퓨터를 따로 만들지 않더라도 클라우드 환경에서 1분이면 컴퓨터를 빌려 올 수 있습니다. EC2 안에 Docker 컨테이너를 띄울 것이고 그 안에는 Jenkins를 설치 해 줄 것입니다. Jenkin를 사용하기 위한 환경설정이 이미 되어있는 Docker image를 이용할 것 이기 때문에 별도의 환경 설정 없이 Jenkins를 이용 하기 위함입니다. 그리고 S3를 통해 정적 웹 사이트 파일을 호스팅해주고 CloudFront를 통해서 배포를 해줄 계획입니다. 또한 Route 53으로 도메인 연결하고 AWS Certificate Manager를 통해 HTTPS 통신까지 이뤄낼 것입니다. AWS 프리티어를 이용할 것 이기때문에 월 1000원 미만의 가격으로 웹 배포가 가능합니다!! 저는 실제로 첫 달 723원? 인가 나왔습니다. 작업 순서는 아래와 같습니다. 1. AWS 인스턴스 생성 2. EC2에 Docker 설치 3. Docker 컨테이너 안에 젠킨스 설치 4. S3 버킷 생성 및 정적 웹 호스팅 진행 5. 가비아 네임서버 등록 및 route53 이용해서 도메인 입혀주기 6. CICD를 위한 github와 jenkins 설정(aws cli설치, 메모리 스왑 포함) 현 게시물이 모든 과정을 설명하진 못합니다. 따라서 제가 참고했던 블로그나 유튜브를 기재할 것이니 부족한 부분은 해당 참고 블로그 또는 유튜브를 확인해 주세요. https://hudi.blog/install-jenkins-with-docker-on-ec2/ (EC2 환경에서 도커를 활용한 젠킨스 설치하기) https://whyeskang.com/411 (AWS S3로 React 배포하기) https://velog.io/@kimsehwan96/Jenkins-Github%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%95%B1-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC-with-aws-S3 (Jenkins + Github을 이용한 리액트 앱 자동 배포 (with aws S3)) https://dundung.tistory.com/284 (메모리 스왑) https://velog.io/@xeropise1/Jenkins-Container%EC%97%90-Aws-Cli-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0 (젠킨스 컨테이너(Jenkins Container)에 Aws Cli 설치하기) https://velog.io/@suhongkim98/jenkins-%EC%9B%B9%ED%9B%85-%EC%84%A4%EC%A0%95%ED%95%B4%EC%84%9C-CI-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0 ([jenkins] webhook 설정해서 CI 구축하기) https://www.youtube.com/watch?v=RbJ4-GWckeM (AWS기반으로 월 10원으로 내 포트폴리오 웹사이트 운영하기) 우선 EC2, Docker, Jenkins를 작업 합시다. 1. AWS 가입 하시고 EC2 인스턴스를 생성 ✔️ 이름 : 원하는 것 ✔️ 운영체제 : Ubuntu 22.04 LTS ✔️ 보안그룹 : 보안 그룹 생성 – 보안그룹이름(아무거나) – SSH , HTTP 인바운드 허용 ✔️ 인스턴스 유형 : t2.micro (프리티어 사용하기 위함) ✔️ 키페어 : 없다면 생성(RSA – .pem) ❗ EBS 용량 프리티어가 30GB까지 가능하다고합니다. 그러나 저는 30GB까지 필요없고 넉넉하게 20GB로 설정했습니다. ( 처음에 저용량으로 설정 했다가 Jenkins가 계속 먹통되길래 확인해보니 용량부족이 원인 이었습니다. ) 이후에 설명 하겠지만 t2.micro가 Ram이 1GB라서 Jenkins build시 먹통 될 수가 있습니다. 그래서 메모리 스왑이라는 것을 이용할 것인데 이 때 여유있는 저장공간이 필요합니다. ✔️ 사진은 없지만 EIP ( 탄력적 IP )를 사용하시길 권장합니다. 방법은 구글링으로 쉽게 찾을 수 있습니다. ) 2. OpenSSH로 인스턴스 접근 2가지 방법이 있습니다. AWS에서 직접 접근하는 것과 프로그램을 이용하는 방법입니다. 저는 SSH 접속 Tool인 MobaXterm을 이용하였습니다. 어떤 방식을 선택하든 상관없습니다. 환경설정을 하기 위함입니다. AWS에서 직접 접근하는 방법은 아래와 같습니다. ✔️ EC2 인스턴스에서 연결을 클릭 ✔️ 아래 “인스턴스에 연결” 화면에서 작성 후 우측 하단 주황박스 연결 클릭 MobaXterm을 이용한 방법은 아래와 같습니다. ✔️ MobaXterm 설치 후 실행 및 세션 추가 ✔️ Remote host : EC2 퍼블릭 IP 입력 ✔️ Specify username : Ubuntu ✔️ Port : 22 ✔️ Advanced SSH settings – Use private key : EC2 인스턴스 생성 때 사용했던 키페어 불러오기 위 설명과 같이 설정하고 OK 눌리고 Accept를 누르면 우측 화면처럼 원격으로 SSH 접속이 가능합니다. 이제 Docker를 설치 해줍시다. 3. Docker 설치 및 젠킨스 컨테이너 실행 레포지토리 셋업 아래 명령을 통해서 우분투의 apt의 패키지 인덱스를 최신화하고, apt가 HTTPS를 통해 패키지를 설치 할 수 있도록 설정합니다. 아래 명령어들을 따라 와 주세요. $ sudo apt-get update
$ sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release 도커의 공식 GPG 키 추가 GPG 키는 소프트웨어를 다운로드 할 때 무결성과 정품인지 확인하는 키이고 따라서 docker를 공식 저장소에서 받기 이전에 먼저 GPG 키를 추가하는 것이다. 키를 통해 해당 소프트웨어를 검증함으로써 보안을 강화하는 용도로 사용 $ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg –dearmor -o /etc/apt/keyrings/docker.gpg 레포지토리 셋업 $ echo \
“deb [arch=$(dpkg –print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable” | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 도커 엔진 설치 $ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin 도커설치 확인 $ sudo docker run hello-world 설치가 완료 되었다면 젠킨스 이미지 다운로드 합니다. sudo docker pull jenkins/jenkins:lts 젠킨스 컨테이너 띄우기 Jenkins가 설치 된 Docker 컨테이너 실행 명령어 sudo docker run -d -p 8080:8080 -v /jenkins:/var/jenkins_home –name jenkins -u root jenkins/jenkins:lts

-d : 컨테이너를 데몬으로 띄웁니다.
-p 8080:8080 : 컨테이너 외부와 내부 포트를 포워딩합니다. 좌측이 호스트 포트, 우측이 컨테이너 포트입니다.
-v /jenkins:/var/jenkins_home : 도커 컨테이너의 데이터는 컨테이너가 종료되면 휘발됩니다.
도커 컨테이너의 데이터를 보존하기 위한 여러 방법이 존재하는데, 그 중 한 방법이 볼륨 마운트입니다.
이 옵션을 사용하여 젠킨스 컨테이너의 /var/jenkins_home 이라는 디렉토리를 호스트의 /jenkins 와
마운트하고 데이터를 보존할 수 있습니다.
–name jenkins : 도커 컨테이너의 이름을 설정합니다.
-u root : 컨테이너가 실행될 리눅스의 사용자 계정을 root 로 명시합니다. 젠킨스 컨테이너가 실행 되었는지 확인해 봅시다. sudo docker ps -a 컨테이너 ID와 여러 정보를 확인 할 수 있습니다. 여기까지 따라 왔다면 AWS EC2 퍼블릭 ID를 웹 주소창에 입력시 Jenkins 웹 페이지에 접속 가능하십니다. ❗ 여기서 주의 해야할 점은 https가 아닌 http로 접속해야합니다. 그리고 포트번호는 8080 입니다. http 퍼블릭IP :8080 ✔️ Unlock Jenkins에 들어갈 Administrator password는 모바엑스텀에서 아래 명령어로 확인 가능합니다. sudo docker logs jenkins ✔️ 젠킨스의 시간을 서울 기준으로 변경합시다. Jenkins 관리 > Script Console에서 아래 명령어를 입력합니다. System.setProperty(‘org.apache.commons.jelly.tags.fmt.timeZone’, ‘Asia/Seoul’) 여기 까지 오셨다면 Jenkins를 설치 및 초기 설정 완료하신 겁니다. 4. S3 버킷 생성 및 정적 웹 호스팅 진행 S3 설정 전에 React.js CRA방식으로 프로젝트를 생성해줍니다. React 생성 명령어는 아래와 같습니다. npx create-react-app 원하는프로젝트명 원하는 폴더에 리액트 프로젝트를 생성해주시고 npm start를 하면 아래와 같은 화면이 나온다면 생성 완료입니다. 해당 파일을 s3에 업로드하기 위해서는 npm run build 명령어를 통해 build를 해야합니다. 현재는 수동으로 빌드 해줬지만 이후에는 jenkins가 알아서 build를 진행 할 것입니다. 이제 S3 버킷을 생성해보겠습니다. ✔️ S3 – 버킷 만들기 ✔️ AWS 리전 : 서울 ✔️ 버킷 명: 원하는이름( 전세계에서 유일해야합니다. ) ✔️ 모든 퍼블릭 엑세스 차단 해제 위와 같이 만든 버킷의 현재 상태는 “객체를 퍼블릭으로 설정할 수 있음” 상태입니다. 이를 “퍼블릭” 상태로 변경해줘야합니다. 버킷 정책 – 편집에서 아래와 같이 입력해줍니다. {
“Version”: “2012-10-17”,
“Id”: “Policy1704443108899”,
“Statement”: [
{
“Sid”: “Stmt1704443098981”,
“Effect”: “Allow”,
“Principal”: “*”,
“Action”: “s3:GetObject”,
“Resource”: “arn:aws:s3:::soonmin.info/*”
}
]
} 퍼블릭이 되었으므로 이제 파일 업로드를 해줘야하는데 아까 npm run build로 생성된 파일 모두 올리면 됩니다. 대략 아래 사진과 같습니다. ( 참고로 파일이 모두 같진 않습니다. 다르다고 걱정하실 필요 없습니다.) 업로드까지 완료 되었다면 객체 속성에서 맨 밑 “정적 웹 사이트 호스팅” 편집 해주면 됩니다. 링크로 된 주소를 확인 할 수 있는데 클릭하시면 배포가 된 react를 확인 할 수 있습니다. 아까는 npm start로 로컬단에서 실행했었지만 현재는 S3를 통해 웹 페이지에서 접근 가능합니다. 5. 가비아 네임서버 등록 및 route53 이용해서 도메인 입혀주기 ✔️ Route 53으로 이동 후 호스팅 영역 생성 ✔️ 레코드 생성 ✔️ 레코드이름은 S3 버킷과 동일 하게 해줍니다. ✔️ 트래픽 라우팅 대상 – S3 웹 사이트 엔드포인트에 대한 별칭 – 서울 – S3 정적 웹 호스팅 주소(자동으로 검색 됨) ✔️ 여기 까지 완료 되면 가비아에서 도메인 구매하고 네임서버 등록합시다. 저는 soonmin.info로 구매 했습니다. ✔️ 호스팅 영역에서 유형 NS인 행을 보시면 값/트래픽 라우팅 대상에 4가지 주소가 있습니다. 이를 가비아에 등록해줘야합니다. ✔️ My가비아 – 관리 – 네임서버 설정 ✔️ AWS Certificate Manager를 통해 HTTPS 통신을할 수 있도록 할 것입니다. 그러면 http ~로 시작하는게 아닌 https ~가 되겠습니다. 참고로 무료이므로 외부에서 별도로 인증서를 구매하지 하지 않아도 됩니다. ✔️ 도메인 이름 : 도메인 이름 입력해주시면 됩니다. 저는 soonmin.info 입니다. ✔️ 글로벌 서비스이기때문에 버지니아 북부에서 인증서 만드셔야합니다. ✔️ 이 뒤에 인증서가 생성되는데 인증서를 클릭 후 Route S3에서 레코드 생성을 클릭합니다. ✔️ 레코드 생성까지 되면 상태 검증 대기 중이 “발급됨”으로 변경됩니다. 5. CloudFront를 통해 배포 ✔️ CloudFront – 배포 ✔️ 원본 도메인은 S3 웹 정적 호스팅 주소를 가져와 줍니다. 복사 붙여넣기 하면되는데 http://은 빼줍니다. ✔️ 뷰어 프로토콜 정책 : Redirect HTTP to HTTPS ✔️ 캐시 키 및 원본 요청 : Legacy cache settings – 객체 캐싱(Customize) – 기본 TTL( 0으로 설정 ) ✔️ 대체 도메인 생성 – 인증서 선택 (좀 전에 만들었던 것 선택해주면됩니다. 자동으로 뜹니다) 여기까지 완료되었다면 배포가 완료 됩니다. 현재는 Route 53이 S3를 가리키고 있는 상황입니다. 하지만 저는 Route53이 CloudFront를 가리키고 CloudFront가 S3를 가리켜야 합니다. 그럼 아래와 같은 아키텍처가 구성되게 됩니다. Route 53 ▶ CloudFront ▶ S3 최 상단 웹 배포 아키텍처 일부분 ✔️ Route53으로 이동 후 아까 만들었던 S3 정적 웹 호스팅 주소가 포함된 레코드를 편집 합니다. ✔️ 트래픽 라우팅 대상 : CloudFront 배포에 대한 별칭 ✔️ 주소는 아까 만들었던 CloudFront 주소가 자동으로 검색 됩니다. 이후 저장합니다. 여기 까지 완료되면 HTTPS 통신까지 가능한 CloudFront를 통한 웹 배포가 완료되게 됩니다. 저의 경우 https://soonmin.info/ 으로 접속이 가능하겠네요. 이제 http가 아닌 https입니다. 6. CICD를 위한 github와 jenkins 설정 ✔️ Jenkins에서 새로운 Item ( Pipeline )을 생성해줍니다. ✔️ GitHub project 체크 후 해당 프로젝트 github 주소 작성 ✔️ Build Triggers : Github hook tigger for GITScm polling 체크 ( git push 트리거를 위한 설정입니다. ) ✔️ Pipeline 작성해줍니다. 해당 스크립트는 모두 다르므로 제 스크립트는 참고 하시기 바랍니다. pipeline {
agent any
tools {
nodejs “node21.5”
git “git”
}

stages {
stage(‘Git Clone’) {
steps {
git branch: “main”,
credentialsId: “GIT_ACCOUNT”,
url: ‘https://github.com/BTC-LeeSoonMin/resumeCICD.git’
sh ‘ls -al’
}
}
stage(‘Npm Build’) {
steps {
sh ‘npm install –save’
sh ‘CI=false npm run build’
}
}
stage(‘Deploy’) {
steps {
sh ‘ls -al’
sh “aws s3 sync ./build s3://soonmin.info –delete”
sh ‘aws cloudfront create-invalidation –distribution-id E1HTT4JF13FIJM –paths “/*”‘
}
}
}
} 위 스크립트가 낯설어서 어렵겠지만 굉장히 직관적인 스크립트입니다. Stages는 단계를 분리하는 역할입니다. 저는 Git Clone, Npm Build, Deploy라는 3가지 Stage가 있으며 각기 역할이 정해져 있습니다. Git Clone에서는 Github 주소에 접속해서 main브랜치를 Clone 합니다. Npm Build에서는 npm run build 명령어를 수행합니다. ( 아까 위에서 수동으로 입력했던걸 이제 Jenkins가 대신해줍니다.) Deploy 단계에서는 AWS CLI 를 통해 S3 버킷에 정적호스팅이 가능하도록 빌드된 파일을 업로드 하게 됩니다. 이 때 Docker 컨테이너 안에 AWS CLI를 설치하는 과정이 있어야합니다. 6-1. 도커 컨테이너에 AWS CLI 설치 및 메모리 스왑 ✔️ 모바엑스텀에서 “sudo docker ps -a ” 명령어를 통해 컨테이너 ID를 복사합니다. ✔️ “sudo docker exec -itu0 컨테이너ID /bin/bash” 명령어를 통해 컨테이너에 들어가기 ✔️ 아래 명령어들을 순차적으로 실행합니다. apt-get update

curl “https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip” -o “awscliv2.zip”

unzip awscliv2.zip

./aws/install 4가지 명령어를 거쳤다면 aws cli를 설치 완료 했습니다. 설치가 완료 되었는지 확인 하는 명령어는 “aws -version” 입니다. AccessKey와 SecretKey를 등록해야합니다. 우선 Key 생성을 합니다. ✔️ AWS Management Console -> 보안 자격 증명 접속 ✔️ 새 액세스 키 만들기 버튼 클릭 발급받은 AccessKey와 SecretKey를 등록해주기 위해 “aws configure” 명령어를 실행합니다. aws configure

AWS Access Key ID [None]: <위에서 발급한 Key id>
AWS Secret Access Key [None]: <위에서 발급한 Secret Access Key>
Default region name [None]: ap-northeast-2
Default output format [None]: json 차례대로 입력해주는 region name과 format은 굳이 적어주지 않아도 상관없습니다. 여기 까지 완료되었다면 확인하기 위해 “aws s3 ls” 명령어를 입력하면 s3 버킷 목록을 호출 할 수 있습니다. 저는 soonmin.info가 보입니다. 이제 도커 컨테이너를 빠져나옵시다. 명령어는 “exit” 입니다. 그럼 다시 ec2 단으로 복귀하게 됩니다. 메모리 스왑을 할 차례입니다. 메모리 스왑을 이용하는 이유를 설명드리자면 EC2 프리티어 인스턴스인 t2.micro은 RAM이 1GB로 매우 부족한 데, 이 부족한 부분을 디스크의 일부를 대신 사용하도록 설정해줌 으로써 해결할 수 있습니다. 사실 돈이 있다면 t2.micro보다 높은 등급을 사용하면 되는데 저희는 월 1000원 미만으로 웹 배포를 할 것이기 때문에 메모리 스왑을 이용할 것입니다. ✔️ 모바엑스텀에서 EC2단에서 아래 명령어들을 순차적으로 실행합니다. sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=2048

sudo mkswap /mnt/swapfile

sudo swapon /mnt/swapfile 이제 RAM이 2GB인 것으로 동작하지만 실제 RAM만큼의 속도는 아니지만 임시방편으로는 괜찮습니다. AWS CLI설치와 메모리 스왑이 완료되었습니다. 다시 Jenkins 설정으로 돌아가겠습니다. ✔️ Jenkins 관리 – Manage Credentials – Add credentials(global) ✔️ Username과 password는 github 계정 기준으로 작성하시면 됩니다. ✔️ ID : GIT_ACCOUNT ( 위에 작성했던 Pipeline과 동일시 하면 됩니다. ) 필요한 플러그인도 설치와 Git 설정도 하겠습니다. ✔️ Jenkins 관리 – Plugins ✔️ 설치 가능목록에 “node”, “Github integration” 검색 후 다운, 다운이 완료되면 젠킨스 재부팅을 합시다. ✔️ Jenkins 관리 – Tool – Git installations ✔️ Name : git ✔️ Path to Git executable : git ✔️ Install automatically 체크 ✔️ Jenkins 관리 – Tool – NodeJS ✔️ Name : node21.5 ( 위에 작성했던 Pipeline과 동일시 하면 됩니다.) ✔️ Version : Node21.5.0 (버전은 크게 상관없지만 저는 21.5.0 버전을 선택했습니다. ) ✔️ Global npm packages to install : npm install -g ✔️ Global npm packages refresh hours : 72 ✔️깃허브 웹훅 추가 ✔️ Github 프로젝트로 이동 – Setting – Webhooks – Add webhook ✔️ Payload URL : 본인 Jenkins 주소를 입력 한 뒤 뒤에 /github-webhook/을 추가로 입력합니다. 여기까지 하면 마지막챕터까지 완료입니다. 깃허브 레포지토리 main브랜치에 git push를 한다면 Github가 Jenkins에게 알림을 보내고 Jenkins는 저희가 작성 스크립트 대로 Git Clone – Npm Build – deploy를 진행할 것입니다. 제가 원하는 git push만으로 웹 재배포가 완료된 것이죠. 모르시는 부분이 있거나 틀린 부분이 있다면 댓글 남겨주시면 감사하겠습니다. 수고하셨습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다