[AWS] Elastic Beanstalk + Github Actions 사용해서 Spring boot CI/CD 파이프라인 구축하기
동아리 스터디의 일환으로 시작된
Beanstalk 으로 CI/CD 파이프라인 구축하기
처음 써보는거라 확실히 어렵지만
EC2 생성하고 RDS 생성하고 https 설정해주고... 하던 시절과 비교하면
확실히 배포 속도가 엄청 빠르긴 하다
물론 그 과정에서 삽질을 엄청나게 하긴 했지만...
하지만 결론적으로는 빌드 시간이 많이 오래 걸리기도 하고
배포서버와 개발서버 그리고 혹시 모르지만 웹까지
하나의 EC2 환경에서 배포하고 싶어서
JAR 환경에서 Docker 환경으로 이동할 것이다
JAR 이나 WAR 로 배포하는 것은 물론 좋지만
여러 EC2 인스턴스를 사용하기엔 돈이 모자란 ^^.... 가난한 대학생에겐
하나의 EC2에서 멀티 컨테이너로 돌리는게 더 나을 것이라는 판단이다
그래도 공부한게 아까우니까 까먹기 전에 과정을 올려본다
🚀 Elastic Beanstalk 환경 생성
Elastic Beanstalk 은 EC2 를 한번 더 추상화 한 개념이다
하나의 환경 내에 여러 EC2 인스턴스를 두고 Auto Scaling 으로 관리도 할 수 있다
S3나 RDS, ELB 등 여러 요소를 하나의 환경에서 관리할 수 있다
환경 -> 새 환경 생성 -> 웹 서버 환경 선택하고 다음으로
어플리케이션 이름 지어주면
환경 이름은 첫 문자를 대문자로 바꾼 것에 -env 가 붙어서 자동으로 만들어진다
물론 바꿀 수도 있다
아래 도메인은 기본 도메인이 생성되는데
나중에 구매해서 연결할 도메인이랑 별개이니까 놔둬도 상관 없다
관리형 플랫폼 -> Java -> 자바 버전 선택 -> 플랫폼 버전 선택
환경에서 사용할 플랫폼을 지정해준다
JAR 파일을 그대로 사용하려면 Java 플랫폼을 선택해주면 된다
Docker, Go, Node.js, Python 등등 웹서버로 많이 활용하는 플랫폼도 당연히 있다
Java 는 8, 11, 17이 지원되는데
보통 8이나 11버전을 많이 사용하더라
나는 간지나게 새삥 17을 선택했다
그 다음 중요한 추가 옵션 구성으로 들어가준다
새 인스턴스를 생성하고 삭제하는 것을 통해 CI 환경을 만들기 때문에
단일 인스턴스 대신 사용자 지정 구성을 선택
그리고 EC2 프리티어 기준이
한달 EC2의 총합 사용 시간이 750시간을 안넘기면 되기 때문에
여러 EC2 인스턴스가 존재하는 것은 사실 상관이 없다
어차피 배포가 끝나면 바로 삭제되기 때문
그리고 EC2 보안 그룹을 설정해야 하는데
환경 구성 메뉴에서는 설정을 할 수가 없다
그러니까 EC2 서비스 -> 보안 그룹으로 가서 설정해야한다
새 탭 열어서 HTTP, HTTPS 포트 여는 등 설정을 끝내고 다시 오자
용량 설정에서는 Auto Scaling 을 중요하게 보면 된다
왜냐면 돈과 직결되는 부분이기 때문
최소 최대 인스턴스를 모두 1로 설정해주면
Auto Scaling 이 동작하지 않아서
새 EC2 인스턴스가 생기지 않기 때문에 과금 우려가 없어진다
인스턴스 유형에서 t2.small 기본 선택이 되어있을텐데
프리티어인 t2.micro 만 빼고 없애버린다
우리는 가난한 대학생이니까...
HTTPS 설정이 필요하다면 리스너 -> 리스너 추가에서
HTTPS 와 SSL 인증서를 달아주면 된다
인증서가 없다면 Route 53 서비스를 이용하는 조건으로
무료 인증서를 발급받을 수 있다
(무료라면서 Route 53 에 도메인 하나 연결 당 매달 500원 정도 나온다)
다음으로 중요한 롤링 업데이트와 배포 설정
배포 방식을 추가 배치를 사용한 롤링으로 바꿔준다
배포할 때마다 새 EC2 인스턴스를 생성한 후 배포 작업을 진행하며
배포 작업이 끝나면 로드밸런서가 새 EC2 인스턴스를 가리키게 하여
CI 환경을 구성하는 것이다
이후 구 버전 EC2 인스턴스를 삭제하여 배포 작업을 끝낸다
그 다음 중요한 보안 수정
서비스 역할과 IAM 인스턴스 프로파일은 IAM 서비스에서 정의하고
EC2 키 페어는 EC2 서비스로 들어가서 만들어야 한다
이미 있다면 선택 후 저장
없다면 IAM 서비스로 진입
IAM -> 엑세스 관리 -> 역할 -> 역할 만들기 메뉴로 진입해서
위와 같은 권한을 주었다
AdministratorAccess 만 추가했을 때는
권한이 없다면서 안되어서 몇 개 더 넣었더니 되더라
그 다음은 사용자 추가
엑세스 키를 선택하고 이름 지정
나는 이 2가지의 권한을 주었다
다 만들면 이렇게 엑세스 키 ID 와 비밀 엑세스 키가 제공되는데
이 다음에는 더 이상 값을 보여주지 않기 때문에 이를 메모해두자
Github Actions 정의할 때 필요하다
여기까지 하고 환경 생성
문제가 없다면 이런식으로 상태에 파란 불이 들어와야 한다
보통 처음에 안되는건 업로드한 서버 파일 문제 또는 권한 문제
📚 Beanstalk 배포 세팅
Beanstalk 배포 설정을 위해서 내 프로젝트에 파일을 추가해야 한다
📝 .ebextensions/00-makeFile.config
files:
"/sbin/appstart" :
mode: "000755"
owner: webapp
group: webapp
content: |
#!/usr/bin/env bash
JAR_PATH=/var/app/current/application.jar
# run app
killall java
java -Dfile.encoding=UTF-8 -Dspring.profiles.active=$SPRING_PROFILE -jar $JAR_PATH
Beanstalk 내부에서 배포 작업 중 실행할 명령어를 정의했다
현재 실행중인 자바를 종료하고 배포시키는 명령어이다
SPRING_PROFILE 이라는 환경 변수는 Beanstalk 의 소프트웨이 -> 환경 속성에 정의했다
📝 ./Procfile
web: appstart
아까 .ebextensions 에서 정의한 명령어를 배포 작업이 종료되면 즉시 실행한다
📝 .platform/nginx/nginx.conf
user nginx;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 33282;
events {
use epoll;
worker_connections 1024;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
include conf.d/*.conf;
map $http_upgrade $connection_upgrade {
default "upgrade";
}
upstream springboot {
server localhost:8080;
keepalive 1024;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
location / {
proxy_pass http://springboot;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
access_log /var/log/nginx/access.log main;
client_header_timeout 60;
client_body_timeout 60;
keepalive_timeout 60;
gzip off;
gzip_comp_level 4;
# Include the Elastic Beanstalk generated locations
include conf.d/elasticbeanstalk/healthd.conf;
}
}
Nginx 설정 정의 파일
Elastic Beanstalk은 기본적으로 Nginx 를 통해 호스팅 해주는데
Nginx 의 특장점중 하나인 리버스 프록시를 통해
80번 포트 -> 8080 포트로 전달해주는 설정이다
Beanstalk 에 내장된 Nginx 는
기본 설정으로 localhost:5000 포트로 전달해주기 때문에
재정의를 통해 8080 포트로 전달해주도록 하는 것이다
🚀 Github Actions 정의
📝 .github/workflows/deploy.yml
name: beanstalk-springboot-deploy
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
#JDK Setting
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
#Create dotenv file
- name: Make env properties
run: |
touch ./src/main/resources/application-dev.yml
touch ./src/main/resources/application-prod.yml
echo "$ENV_PROPERTIES_DEV" > ./src/main/resources/application-dev.yml
echo "$ENV_PROPERTIES_PROD" > ./src/main/resources/application-prod.yml
env:
ENV_PROPERTIES_DEV: ${{ secrets.ENV_PROPERTIES_DEV }}
ENV_PROPERTIES_PROD: ${{ secrets.ENV_PROPERTIES_PROD }}
#Grant gradlew Permission
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
- name: Build with Gradle
run: ./gradlew clean build
shell: bash
- name: Get current time
uses: 1466587594/get-current-time@v2
id: current-time
with:
format: YYYY-MM-DDTHH-mm-ss
utcOffset: "+09:00"
- name: Show Current Time
run: echo "CurrentTime=$"
shell: bash
- name: Generate deployment package
run: |
mkdir -p deploy
cp build/libs/*.jar deploy/application.jar
cp Procfile deploy/Procfile
cp -r .ebextensions deploy/.ebextensions
cp -r .platform deploy/.platform
cd deploy && zip -r deploy.zip .
- name: Beanstalk Deploy
uses: einaregilsson/beanstalk-deploy@v20
with:
aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
application_name: naechinso
environment_name: Naechinso-env-1
version_label: github-action-${{ steps.current-time.outputs.formattedTime }}
region: ap-northeast-2
deployment_package: deploy/deploy.zip
github actions 는 반드시 루트 폴더의 .github/workflows 내부에 정의해야 한다
나는 민감한 변수들을 application.yml 을 나누어 관리하기 때문에
빌드 과정에서 가상 환경에 생성하는 방식으로 만들었다
마지막 application_name 과 environment_name 은
아까 beanstalk 에서 구성한 어플리케이션 이름과 환경 이름을 넣어주면 된다
secrets 변수들은 내 레포지토리 -> setting -> secrets -> actions 에서 관리한다
여기에 아까 만든 IAM 프로파일 값을 넣어주고 사용한다
설정에 따라 main 브랜치에 푸시를 하면 액션 실행
배포가 진행된다
✨ Log 보는 법
Elastic Beanstalk 이 이렇게 설정하는 것이 간단하지만
답답한 것중 하나가 로그를 직접 보지 못한다는 것...
로그는 환경 -> 로그에서 로그 요청을 한 후에 다운받아서 볼 수 있다
다만 이것도 배포 과정 중에 실시간으로 볼 수 없고
기다렸다가 아니면 중지해야지만 볼 수 있다
이래서 나는 너무 추상화 된 것을 안좋아한다...
이제 Docker 로 새 환경 구성하러 가야겠다