배포 전 CI 구성은 여기에 정리해두었다.
전체 과정은 wbluke님 블로그를 많이 참고했다.
전체 코드는 깃허브에 정리해두었다.
전체 배포 과정
- 어플리케이션에 배포에 필요한 절차를 AppSpec.yml에 작성한다.
- CodeDeploy에 프로젝트의 특정 버전을 배포해달라고 요청하면, CodeDeploy는 배포를 진행할 EC2 인스턴스에 설치되어 있는 CodeDeploy Agent들에게 요청받은 버전을 배포해달라고 요청
- 요청받은 Agent들은 코드 저장소에서 프로젝트 전체를 다운로드 받고, AppSpec.yml 파일을 읽어 해당 파일의 절차대로 배포를 진행한다.
- Agent는 배포를 진행한 후 CodeDeploy에게 성공/실패 등의 결과를 알려준다.
AWS 리소스 생성
EC2
jar 파일을 실행시킬 OS에 따라 인스턴스를 생성하면 된다.
Amazon Linux 2 AMI를 사용했다.
인바운드 규칙은 HTTPS, HTTP, SSH 포트를 설정해주고 HTTP, HTTPS의 소스는 0.0.0.0/0 SSH는 현재 IP로 설정 해준다. (다음날 로그를 보니 해킹 공격이 있길래 서비스용 인스턴스가 아니라 테스트용이라 HTTP, HTTPS도 현재 내 IP에만 설정해주었다. 😂)
- AMI (Amazone Machine Image)
- 인스턴스 생성에 필요한 모든 소프트웨어 정보를 담고 있는 템플릿 이미지
IAM
ec2가 s3와 codedeploy에 접근할 수 있도록 권한을 생성해야 한다.
정책은 AmazonS3FullAccess, AWSCodeDeployFullAccess를 선택하여 IAM 역할을 생성한다.
생성한 역할을 EC2에 적용해 준다.
S3
AWS에서 지원하는 파일 스토리지 서비스
build한 jar파일을 업로드하고, 배포하는 곳에서 다운로드한다.
IAM으로 접근 권한을 설정하므로 모든 퍼블릭 엑세스 차단을 선택한다.
IAM
github actions가 s3와 codedeploy에 접근할 수 있도록 권한을 생성해야 한다.
프로그래밍 방식 억세스를 통해 IAM 사용자를 생성해준다.
정책은 AmazonS3FullAccess, AWSCodeDeployFullAccess를 선택한다.
생성된 억세스 키 ID와 비밀 엑세스 키는 github actions에서 사용한다.
CodeDeploy
어플리케이션 배포를 자동화하는 AWS의 배포 서비스
nginx를 통해 무중단 배포를 설정할 것이기 때문에 배포 유형은 현재 위치, 배포 구성은 CodeDeployDefault.AllAtOnce로 설정해준다.
IAM
codedeploy가 aws 서비스에 접근할 수 있도록 권한을 생성해야 한다.
정책은 AWSCodeDeployRole를 선택하여 IAM 역할을 생성한다.
생성한 역할을 codedeploy에 적용해 준다.
S3 업로드
Github Actions
name: Java CD with gradle and codedeploy
on:
workflow_dispatch:
env:
S3_BUCKET_NAME: yaini-deploy
PROJECT_NAME: simple
jobs:
build:
...
- name: Build with Gradle
run: ./gradlew build
- name: Make zip file
run: zip -r ./$GITHUB_SHA.zip .
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
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: Upload to S3
run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip
- name: Code Deploy
run: aws deploy create-deployment --application-name yaini-deploy --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name develop --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip
- env
- 변수로 사용할 값을 정의해준다.
- s3 bucket이름과 build할 프로젝트 이름을 지정해주었다.
- Build and Make zip file
- gradle을 사용하여 build한 파일을 압축해준다.
- configure aws credentials
- 생성한 IAM의 억세스 키 ID와 시크릿 억세스 키를 설정해준다.
- 절대절대 하드 코딩으로 설정하면 안되고 github 의 secrets 기능을 통해 설정해준다.
- Upload to S3
- 압축한 zip 파일을 s3의 원하는 경로에 업로드 해준다.
- 이 부분도 cli가 아닌 정의되어 있는 actions가 있지 않을까 했는데 아직 없는 것 같다. (github aws-actions 참조)
- Code Deploy
- code deploy의 이름, 배포 설정, 배포 그룹 이름, s3 경로를 지정해준다.
EC2 설정
# jdk 설치
sudo yum install -y java-1.8.0-openjdk-devel.x86_64
sudo yum update
sudo yum install ruby
sudo yum install wget
# codedeploy 설치
cd /home/ec2-user
wget <https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install>
chmod +x ./install
sudo ./install auto
# nginx 설치
sudo amazon-linux-extras install nginx1
Amazon Linux 2에서 nginx를 설치하려면 amazon-linux-extras 명령어를 사용해야 한다.
원래 java 11을 사용하려고 했는데 해당 OS에선 java 8까지만 설치되어서 변경해주었다😥
CodeDeploy 설정
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/SpringStudy/
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location: scripts/run_new_was.sh
timeout: 180
runas: ec2-user
- location: scripts/health_check.sh
timeout: 180
runas: ec2-user
- location: scripts/switch.sh
timeout: 180
runas: ec2-user
codedeploy agent가 배포를 진행할 때 수행할 명령을 appspec.yml에 정의해준다.
- files
- source: 배포할 파일을 설정해준다. / 는 모든 파일을 배포한다는 뜻이다.
- destination: 파일을 배포할 서버의 경로를 지정해준다.
- permissions
- 가져온 파일들의 권한을 설정해준다.
- hooks
- CodeDeploy의 이벤트에 따라 실행할 명령을 지정해준다.
- EC2, Lamda, ECS에 따라 이벤트의 종류가 다르니 공식문서를 참고하자.
script코드는 깃허브에 정리해두었다.
nginx 설정
이전엔 Apache+Tomcat 을 많이 사용했는데, 최근엔 nginx+Tomcat이 주를 이루는 것 같다.
그러면 Tomcat도 설치해야 하는 것 아닌가?! 할 수 있지만 Spring Boot엔 내장 톰캣이 있기 때문에 추가로 설정하지 않아도 된다.
nginx의 리버스 프록시를 통해 로드 밸런싱, 캐싱, SSL 터미네이션 등 다양한 기능을 사용할 수 있다.
sudo vim /etc/nginx/nginx.conf
nginx 설정을 변경해준다.
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# 추가한 부분
include /home/ec2-user/service_url.inc;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_Host;
proxy_pass $service_url;
}
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
- proxy_pass: nginx에서 요청이 오면 프록시에서 전달해줄 서버의 주소
- proxy_set_header: 헤더들을 재정의 하여 전달할 때 사용한다.
- X-Forwarded-For: 프록시 환경에서 HTTP Server에 요청한 Client의 IP를 식별하기 위한 헤더
- Host: Request의 Host 헤더 값
vim /home/ec2-user/service_url.inc
nginx.conf에서 설정한 service_url 변수를 설정해준다.
set $service_url http://127.0.0.1:8081;
AWS의 ECR이나 Beanstalk을 사용하면 nginx 설정도 ec2에서 직접하지 않아도 레포지토리에서 한번에 설정이 가능한 것 같다. 다음에 설정해봐야 겠다.
nohup java -jar -Dserver.port=8081 /home/ec2-user/SpringStudy/simple/build/libs/simple-0.0.1-SNAPSHOT.jar &
ec2에서 jar 파일을 실행한 뒤 github에 push하면
s3, codedeploy에 배포도 잘 되고
[ec2-user@ip-172-31-35-40 ~]$ ps -ef | grep java
ec2-user 20512 18117 0 22:49 pts/0 00:00:08 java -jar -Dserver.port=8081 /home/ec2-user/SpringStudy/simple/build/libs/simple-0.0.1-SNAPSHOT.jar
ec2-user 28653 1 58 23:03 ? 00:00:06 java -jar -Dserver.port=8082 /home/ec2-user/SpringStudy/simple/build/libs/simple-0.0.1-SNAPSHOT.jar
ec2-user 28873 18117 0 23:03 pts/0 00:00:00 grep --color=auto java
무중단 배포도 잘 실행되는 것을 볼 수 있다!
혼자 처음 ci/cd를 모두 해봤다. 아직 궁금한 것도 많고 부족한 내용도 많지만 나름 뿌듯하다. 🤩
참고
https://wbluke.tistory.com/39
https://www.sunny-son.space/AWS/AWS CodeDeploy와 nginx로 무중단 배포/
https://intrepidgeeks.com/tutorial/spring-guided-nginx-non-disruptive-deployment
'Devops > CD' 카테고리의 다른 글
[Docker] Spring Boot와 MySQL을 docker-compose로 실행하자 (0) | 2022.05.01 |
---|