이 글은 아래 링크의 블로그를 따라하며 작성했습니다.
----
이 글의 목표
git pull > npm run build > npx pm2 reload all 로 이뤄지는 배포 프로세스 자동화
이 글에서 구현할 배포 자동화/무중단 배포 로드맵
- IDE에서 깃 레파지토리로 코드 push
- 깃 레파지토리가 업데이트 되면, 연동되어 있는 Travis CI에서 이를 감지
- Travis CI는 업데이트된 레파지토리를 빌드/테스트
- 빌드/테스트된 코드를 AWS S3에 업로드
- Travis CI는 EC2 인스턴스의 CodeDeploy에게 배포 이벤트 트리거
- CodeDeploy는 AWS S3에서 업로드된 파일을 가져옴
- 가져온 파일을 EC2 인스턴스에 저장
- 배포 스크립트(blue-green 무중단 배포)를 실행해서 배포 완료 --> NginX 로드밸런싱 처리
- 결론적으로 개발자는 레파지토리에 push만 하면, 빌드/테스트/배포까지 모두 자동화되어 동작한다.
개발 환경
- MacOS
- VSCode
- AWS EC2 인스턴스: RedHat Enterprise Linux
1. 버킷 생성
프로젝트의 파일들을 업로드 할 버킷을 생성합니다.
- https://s3.console.aws.amazon.com/s3/buckets
- 버킷 만들기 버튼 클릭 후 버킷 생성
- 설정은 따로 건드릴 필요 없음
2. 사용자 추가
외/내부에서 AWS 서비스를 이용할 수 있는 권한을 가진 사용자를 추가합니다.
- https://us-east-1.console.aws.amazon.com/iamv2/home
- 왼쪽 메뉴 > 엑세스 관리 > 사용자 > 사용자 추가 버튼 클릭
- "액세스 허용 - 프로그래밍 방식 액세스" 체크 후 다음
- 기존 정책 직접 연결 클릭
- AmazonS3FullAccess 체크
- CodeDeployFullAccess 체크
- 태그는 건너뛰고 완료
- .csv 파일 다운로드
3. AWS 역할 만들기
CodeDeploy 사용을 위해 EC2 인스턴스에 액세스할 수 있는 권한을 부여하기 위한 IAM 역할을 만듭니다.
IAM이란 AWS 리소스에 대한 액세스를 제어할 수 있는 서비스입니다.
- https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-2#/roles$new?step=type
- AWS 서비스 클릭
- 일반 사용 사례 > EC2 클릭 > 다음
- AmazonEc2RoleforAWSCodeDeploy 정책 체크
- 태그는 건너뛰고, 역할 이름 입력 후 완료
- 역할 하나 더 만들기
- AWS 서비스 클릭
- 사용 사례에서 CodeDeploy 클릭 후 다음 >>>> 완료
4. EC2에 역할 적용하기
EC2 인스턴스에 3번에서 만든 역할을 적용합니다. 인스턴스가 없다면 생성해서 적용해주세요.
- https://ap-northeast-2.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2#Instances:instanceState=running
- 인스턴스 우클릭 > 보안 > IAM 역할 수정
- 3번에서 만든 역할 선택 > 저장
5. EC2에 CodeDeploy Agent 설치하기
CodeDeploy는 애플리케이션 배포를 자동화하는 AWS의 배포 서비스입니다.
CodeDeploy를 설치하려면 먼저, EC2 인스턴스에 터미널로 접속해서 awscli를 설치해야 합니다.
아래 예제는 레드햇 리눅스 환경에서 설치한 예제입니다.
$ sudo dnf install python3-pip
$ sudo pip3 install awscli
$ aws --version
설치가 완료되면 configure 명령어로 awscli를 설정해 주세요.
$ aws configure
- Acess Key ID / Secret Acess Key : 다운받았던 .csv 파일 내용을 입력합니다.
- Default region name : 요청을 전달할 서버의 지역을 입력합니다. ( 서울은 ap-northeast-2 )
- Default output format : 기본 출력 포맷을 지정합니다. ( json으로 설정함 )
설정 후 CodeDeploy 설치 파일을 다운로드 합니다.
$ aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
위 명령어를 입력하면 현재 경로에 "install"이라는 파일이 다운로드 되고, 이 파일로 CodeDeploy를 설치합니다.
설치 파일 실행에는 Ruby가 필요하기 때문에 Ruby를 먼저 설치해야 합니다.
# 실행 권한 부여
$ sudo chmod +x ./install
# Ruby 설치
$ sudo dnf install ruby
# CodeDeploy 설치
$ sudo ./install auto
CodeDeploy를 설치한 후 CodeDeploy를 실행합니다.
$ sudo service codedeploy-agent start
$ sudo service codedeploy-agent status
6. 도커 설치
애플리케이션을 도커 이미지로 배포할 예정이기 때문에 위해 도커를 설치했습니다.
# yum에 도커 레파지토리 추가
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# container-selinux 설치
$ sudo yum install -y http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.33-1.git86f33cd.el7.noarch.rpm
# docker-ce 설치
$ sudo yum install -y docker-ce --nobest
# docker 서비스 시작
$ sudo systemctl enable --now docker
# docker 서비스 상태 확인
$ sudo systemctl status docker
# 도커 버전 확인
$ docker -v
7. NginX 설치
리버스 프록시 서버로 사용할 NginX를 설치합니다.
프록시 서버에 대한 내용은 아래 링크의 글에서 정리했습니다.
https://sty110357.tistory.com/90
$ sudo dnf install nginx
$ sudo systemctl start nginx
$ sudo systemctl status nginx
리버스 프록시 예로써, 아래처럼 80번 포트로 요청이 오면 3000번 포트로 전달해주도록 설정할 수 있습니다.
기본 설정 파일은 nginx.conf이지만 sites-available, sites-enables 폴더를 만들어서 관리하는 것이 권장됩니다.
$ sudo vi /etc/nginx/nginx.conf
# /etc/nginx/nginx.conf
... (생략)
http {
... (생략)
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
location / {
proxy_pass http://127.0.0.1:3000;
}
... (생략)
}
}
- "location /" 블록에 proxy_pass를 사용하면, 요청을 내부의 다른 서버로 전달해줄 수 있습니다.
만약 리버스 프록시가 동작하지 않으면 SELinux 설정을 변경해 주세요.
https://sty110357.tistory.com/92
8. dockerfile
dockerfile은 docker 컨테이너의 빌드를 자동화해주는 파일입니다.
프로젝트 최상단에 dockerfile을 생성합니다.
FROM node:alpine
WORKDIR /usr/app
RUN npm install --global pm2
COPY ./package*.json ./
RUN npm install --production
COPY ./ ./
RUN npm run build
EXPOSE 3000
USER node
CMD [ "pm2-runtime", "start", "npm", "--", "start" ]
- FROM : 컨테이너에 설치할 이미지 명시
- WORKDIR : 경로 이동(= cd)
- RUN : 도커 이미지를 빌드하는 동안 명령어 실행
- COPY : 첫 번째 인자=dockerfile이 위치한 경로 기준의 복사할 파일, 두 번째 인자=복사된 파일을 저장할 컨테이너 상의 경로
- EXPOSE : 외부에 공개할 컨테이너의 포트 번호 명시(명시 뿐이라서 실제론 docker run -p 를 사용해 됨)
- CMD : 생성된 도커 이미지를 시작하는 동안 명령어 실행, 둘 이상의 CMD 명령이 있으면 마지막을 제외한 명령은 무시됨
9. Docker-Compose
Docker-Compose는 yaml 파일을 사용하여, 여러 개의 컨테이너로 이루어진 서비스를 구축하고, 실행하는 순서를 자동화하는 기능입니다.
Docker-Compose를 설치하는 명령어는 아래와 같습니다.
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# .sh 파일이 docker-compose 명령어를 사용할 수 있도록 심볼릭 링크 생성
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
10. 무중단 배포
무중단 배포하는 방법으로 blue-green 방식을 사용했습니다. 코드가 수정되어 애플리케이션이 재배포되면, blue와 green이 번갈아가며 서버를 실행해주는 방법입니다.
docker-compose를 사용해서 dockerfile을 빌드해야 하기 때문에 프로젝트 최상단에 docker-compose.blue.yml, docker-compose.green.yml 파일을 생성해줍니다.
# docker-compose.blue.yml
version: '3'
services:
nextjs:
build: ./
restart: unless-stopped
ports:
- 3001:3000
# docker-compose.green.yml
version: '3'
services:
nextjs:
build: ./
restart: unless-stopped
ports:
- 3002:3000
- blue는 3001번 포트, green은 3002번 포트에서 실행됩니다.
NginX에서 80번 포트로 들어온 요청을 3001, 3002 포트로 로드밸런싱 해주기 위해 nginx.conf 파일을 수정해줍니다.
# /etc/nginx/nginx.conf
... (생략)
http {
upstream next-build-test {
least_conn;
server 127.0.0.1:3001 weight=5 max_fails=3 fail_timeout=10s;
server 127.0.0.1:3002 weight=10 max_fails=3 fail_timeout=10s;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
location / {
proxy_pass http://next-build-test;
}
}
... (생략)
}
마지막으로, 무중단 배포를 실행할 deploy.sh 파일을 프로젝트 최상단에 만들어 줍니다.
#!/bin/bash
DOCKER_APP_NAME=node-koa-server
EXIST_BLUE=$(docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml ps | grep Up)
if [ -z "$EXIST_BLUE" ]; then
echo "blue up"
docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml up -d
sleep 10
docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml down
else
echo "green up"
docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml up -d
sleep 10
docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml down
fi
- blue가 실행 중이면, green을 시작하고 blue를 종료합니다. 반대도 마찬가지입니다.
11. AWS CodeDeploy Application 생성하기
- https://ap-northeast-2.console.aws.amazon.com/codesuite/codedeploy/applications?region=ap-northeast-2
- (region에 따라 애플리케이션 생성 위치가 달라지니 주의)
- 애플리케이션 생성 클릭
- 이름을 입력하고, 컴퓨팅 플랫폼엔 EC2/온프레미스를 선택
생성을 완료하고, 배포 그룹 탭의 배포 그룹 생성을 클릭합니다.
배포 그룹 생성 화면
- 배포 그룹 이름: 적절한 배포 그룹 이름 입력
- 서비스 역할 : 3번에서 만들었던 역할을 선택
- 배포 유형 : 현재 위치
- 환경 구성 : Amazon EC2 인스턴스
- 태그 그룹 입력
- 배포 설정 : CodeDeployDefault.AllAtOnce 선택
- 배포 그룹 생성 클릭
- 로드 밸런서 : 체크 해제
12. Travis CI 설정 파일에 deploy 설정하기
Travis CI는 깃 레파지토리와 연동하여 코드가 push 되면 이를 감지하여 빌드와 테스트를 해주는 CI(Continuous Integration) 툴입니다. CI 툴이기 때문에 직접 배포까진 하지 않고, CodeDeploy에게 이벤트를 트리거 합니다.
Travis CI를 사용하기 위한 사전 세팅
- https://app.travis-ci.com/ 에서 회원가입
- 이메일 인증
- 깃 레파지토리를 연동
- 계정에 사용 플랜 설정(체험판인 Trial 플랜으로 결제 카드를 등록하면 10,000 크레딧 제공)
- Trial 플랜 설정 시 1,200원 상당의 비용이 발생
설정을 마치면, 프로젝트 최상단에 ".travis.yml" 파일을 만들어서 배포에 대한 설정을 해야 합니다.
language: node_js
node_js:
- 14 #노드 버전
branches:
only:
- main
before_deploy:
- rm -rf node_modules
- zip -r next-deploy-test * #zip 파일 이름 명시
- mkdir -p deploy #deploy폴더 생성
- mv next-deploy-test.zip deploy/next-deploy-test.zip #만든 zip 파일을 deploy 폴더로 이동
deploy:
- provider: s3
access_key_id: $AWS_ACCESS_KEY #추후에 travis에서 설정
secret_access_key: $AWS_SECRET_KEY #추후에 travis에서 설정
bucket: next-deploy-test-jaynull #s3 버킷 이름
region: ap-northeast-2
skip_cleanup: true
local_dir: deploy
wait-until-deployed: true
on:
repo: sty1103/next-deploy-test #본인의 repository
branch: main
- provider: codedeploy
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_KEY
bucket: next-deploy-test-jaynull #s3 버킷 이름
key: next-deploy-test.zip
bundle_type: zip
application: code-deploy-application #CodeDeploy에서 생성한 애플리케이션 이름
deployment_group: code-deploy-group #이전 단계에서 설정한 CodeDeploy 배포 그룹명
region: ap-northeast-2
wait-until-deployed: true
on:
repo: sty1103/next-deploy-test #본인의 repository
branch: main
notifications:
email:
recipients:
- sty110357@gmail.com #본인의 이메일
이제 레파지토리에 push하면, Travis가 S3에 소스를 업로드 하고, CodeDeploy에 이벤트를 트리거합니다.
이 때 CodeDeploy가 어떻게 해야할지 정의해야 하는데, 이는 appspec.yml로 정의할 수 있습니다.
이 파일도 마찬가지로 프로젝트 최상단에 만들어야 합니다.
version: 0.0
os: linux
files:
- source: /
destination: /home/next-deploy-test #S3에서 가지고온 파일을 저장할 폴더
hooks:
AfterInstall: #배포 후 실행할 명령
- location: execute-deploy.sh
timeout: 240
appspec.yml에서 설정했던, 배포 후 실행할 명령인 execute-deploy.sh을 프로젝트 최상단에 만들어 줍니다.
#!/bin/bash
cd /home/next-deploy-test #본인의 EC2 폴더 구조에 따라 변경
sudo sh ./deploy.sh
마지막으로, .travis.yml 파일에 필요했던 Travis 환경변수를 설정하면 됩니다.
- AWS_ACCESS_KEY, AWS_SECRET_KEY를 .csv 파일의 내용으로 추가합니다.
13. 마무리
이제 코드를 push 하면 배포가 자동적으로 이뤄집니다. 단, push 하기 전에 아래 사항을 먼저 확인해주세요.
- AWS EC2, S3의 region이 서울로 되어있는지 확인
- .travis.yml 파일에서 region을 서울로 지정했기 때문
- region을 잘못 만들었다면, 다음 링크의 글을 확인 : https://ndb796.tistory.com/257
- EC2 인스턴스에서 codedeploy-agent 서비스 재시작 해주기($ sudo systemctl restart codedeploy-agent)
- IAM이 변경되면, agent를 재시작해줘야 적용되기 때문
- execute-deploy.sh 파일에서 deploy.sh 파일을 실행하는 명령어 확인
이제 코드를 push 해서 배포가 잘 되는지 확인해보죠!
Travis CI에서 빌드/테스트/배포 트리거 성공 확인
CodeDeploy에서 배포 성공 확인
잠시 후 EC2 인스턴스에서 dockerfile이 build되면, 3001/3002 포트가 열렸는지 확인(처음엔 3001번이 정상)
NginX의 로드밸런싱이 잘 적용됐는지 확인
14. 마치며..
블로그 글을 따라하며 같은 환경을 구축해봤지만, 제 인스턴스의 OS가 레드햇 기반이라 명령어가 일부 달랐고, 특히 EC2 인스턴스의 region을 미국 동부로 해놔서(...) 인스턴스를 서울로 이전하는 등의 삽질을 많이 했습니다. 삽질한 만큼 알게된 게 많았고, 새로 배운 것도 많았던 경험이었습니다.
'DevOps > CI, CD' 카테고리의 다른 글
Docker Ubuntu 22.04에 Jenkins 설치 (0) | 2023.09.05 |
---|---|
GKE에 GitLab CI/CD 적용하기 (0) | 2023.01.11 |