
kb 부트캠프 마지막 프로젝트가 시작했다.
배포를 시작부터 전부 하고 가고 싶어 cicd랑 배포를 전부 마무리하고 시작했다.
은행권 프로젝트이다보니, 보안을 최대한 신경써서 아키텍쳐를 구성했다.
아키텍쳐의 간단한 설명을 진행하도록 하겠다.
아키텍쳐 설명
우선 우리가 돌리는 서버는 총 5개다.
메인 서버 2개
카프카 서버 1개
로그 전송 서버 1개
터널링용 서버 1개
보안을 위해서 메인서버, 카프카 서버, 로그 전송 서버, RDS, elastic cahce는 모두 private 서브넷에 배포했다.
private 서브넷은 인터넷에 연결되어 있지 않기 때문에, 외부에서 접속할 수도, 내부 트래픽을 외부로 내보낼 수도 없다.
보안에 굉장히 강력한 옵션이 될 수 있다.
하지만 우리가 로컬에서 테스트할 때 RDS나 메인서버에 접속을 할 수도 있으니 Tunneling 전용 서버를 public 서브넷에 파서
Tunneling용 서버를 통해 프라이빗 서브넷에 배포한 서버들에 접근할 수 있도록 구성했다.
또한 메인 서버는 외부 인터넷에 api를 요청해서 환율 정보나 상품 정보를 받아와야 하기 때문에 Nat Gateway를 통해
프라이빗 서브넷에서 외부 인터넷으로 나가는 경로만 뚫어줬다.
라우팅 테이블에 내부 메인서버와 Internet Gateway를 연결시켜줬다.
KMS 사용
데이터베이스 및 캐시에 저장되는 모든 정보를 private subnet에 배포하긴 했으나, 추가적인 보안으로
KMS를 이용해 aws 내부망에 키를 생성하고 해당 키로 모든 데이터를 암호화 해서 저장시키도록 했다.
클라우드 워치를 통해 수집하는 모든 로그 또한 내부망에서 kms를 통해 암호화된 후 전송된다.
배포 방식
우선 S3에 docker-compose.yml 파일과 .env 파일을 저장시켰다.
S3에 접근할 수 있는 권한은 오로지 메인 서버에서만 가능하게 했고, 외부에서는 S3에 접근이 불가능하다.
이제 배포가 이뤄질 때 launch template에서 S3의 환경변수 파일과 도커 컴포즈 파일을 가져와 그를 통해 war를 만들고 tomcat에 붙여 서버가 구동될 것이다.
런치 템플릿
#!/bin/bash
# --- 시스템 업데이트 및 필수 패키지 설치 ---
sudo yum update -y
sudo yum install -y docker aws-cli
# --- Docker 서비스 시작 및 부팅 시 자동 실행 설정 ---
sudo service docker start
sudo systemctl enable docker
# Docker 명령어를 ec2-user가 사용할 수 있도록 설정
sudo usermod -aG docker ec2-user
# --- 작업 디렉토리 준비 ---
DEPLOY_DIR="/home/ec2-user/deploy"
sudo mkdir -p $DEPLOY_DIR
sudo chown ec2-user:ec2-user $DEPLOY_DIR
# --- S3에서 Docker Compose 파일과 .env 파일 다운로드 ---
BUCKET_NAME="버킷 이름"
COMPOSE_FILE="도커 컴포즈 파일"
ENV_FILE="환경변수 파일 이름"
AWS_REGION="지역"
# AWS CLI를 사용할 때 EC2 IAM 역할에 S3 접근 권한이 필요합니다.
aws s3 cp s3://$BUCKET_NAME/$COMPOSE_FILE $DEPLOY_DIR/ --region $AWS_REGION
aws s3 cp s3://$BUCKET_NAME/$ENV_FILE $DEPLOY_DIR/ --region $AWS_REGION
# 파일이 성공적으로 다운로드되었는지 확인
if [ ! -f "$DEPLOY_DIR/$COMPOSE_FILE" ] || [ ! -f "$DEPLOY_DIR/$ENV_FILE" ]; then
echo "Failed to download $COMPOSE_FILE or $ENV_FILE from S3. Exiting." >> /var/log/user-data.log
exit 1
fi
# --- Docker Compose 설치 ---
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Docker Compose 버전 확인 (디버깅 용도)
docker-compose --version >> /var/log/user-data.log
# --- Docker Compose 실행 ---
cd $DEPLOY_DIR
sudo docker-compose pull
sudo docker-compose up -d >> $DEPLOY_DIR/deployment.log 2>&1
if [ $? -ne 0 ]; then
echo "Docker Compose failed to start. Check $DEPLOY_DIR/deployment.log for details." >> /var/log/user-data.log
exit 1
fi
# --- Docker 이미지 정리 ---
sudo docker image prune -f
# --- 완료 로그 출력 ---
echo "Deployment completed successfully on $(date)" >> $DEPLOY_DIR/deployment.log
이렇게 런치 템플릿을 설정해서 S3에서 도커 컴포즈 파일과 환경변수 파일을 가져와 도커 이미지를 실행하도록 구성했다.
깃허브 액션에서 도커 이미지만 빌드해 도커 허브 리포지토리에 넣어주고,
오토 스케일링 그룹을 트리거 시킨다면
오토 스케일링 그룹에서 자동으로 런치템플릿을 통해 새 서버를 만들 것이다.
깃허브 액션
name: Java CI/CD with Gradle
on:
push:
branches: [ "main" ]
permissions:
contents: read
jobs:
build-docker-image:
runs-on: ubuntu-latest
steps:
# 소스코드 체크아웃
- uses: actions/checkout@v3
# JDK 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# Gradle 실행 권한 부여
- name: Grant Execute Permission For Gradlew
run: chmod +x gradlew
# Gradle 캐싱
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
# Gradle 빌드 (WAR 파일 생성)
- name: Build With Gradle
run: ./gradlew clean build -x test
# Docker 이미지 빌드 (WAR → Tomcat 이미지에 복사)
- name: docker image build
run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/dolfin-main .
# Docker Hub 로그인
- name: docker login
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Docker Hub로 이미지 푸시
- name: docker Hub push
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/dolfin-main
# AWS 자격 증명 주입
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.SECRET_KEY }}
aws-region: ${{ secrets.REGION }}
# S3로 Docker Compose 업로드
- name: Upload Docker Compose to S3
run: |
aws s3 cp docker-compose.yml s3://${{ secrets.BUCKET_NAME }}/docker-compose.yml --region ${{ secrets.REGION }}
# 오토스케일링 그룹 롤링 재배포
- name: Trigger ASG Rolling Update
run: |
aws autoscaling start-instance-refresh \
--auto-scaling-group-name ${{ secrets.ASG_NAME }} \
--region ${{ secrets.REGION }} \
--preferences '{"MinHealthyPercentage": 50}'
깃허브 액션을 다음과 같이 구성해서 도커 이미지를 레포지토리에 올리고 오토 스케일링 그룹을 실행하도록 했다.
오토 스케일링의 재배포는 최소 가용율을 50%로 만들어서, 최소 1개의 서버가 가용성을 가지도록 구성했고
기본 서버가 2개기 때문에 롤링 업데이트 방식을 활용한 무중단 배포가 구성됐다고 할 수 있다.
수평 스케일링 전략
수평 스케일링 전략은 cpu 활용률이 60% 이상이 되었을 때 클라우드 워치에서 감지해서 ec2 서버 개수를 한 개 늘리도록 구성했다.
AWS 공식문서 기반 cpu 활용률이 60% 이상이 되었을 때 수평 스케일링을 고려할 수 있다고 나와있다.
다음의 공식문서를 참고했다.
Amazon EC2 Auto Scaling의 단계별 조정 및 단순 조정 정책 - Amazon EC2 Auto Scaling
PercentChangeInCapacity - 그룹의 현재 용량을 지정된 퍼센트만큼 늘리거나 줄입니다. 양의 값은 용량을 늘리고, 음의 값은 용량을 줄입니다. 예: 현재 용량이 10개이고 조절이 10%인 경우, 이 정책이 수
docs.aws.amazon.com
실시간 모니터링
실시간 모니터링은 로그 서버에서 클라우드 워치 SDK를 통해 바로 클라우드 워치 logs에 로그를 전송하도록 만들었다.
로그는 ERROR 알림이 1회 이상 울리면 바로 슬랙 알림을 울리도록,
WARNING 알림이 1초내 10회 이상 울리면 바로 슬랙 알림을 울리도록 구성했다.
내부 망에서 전송되는 로그도 모두 KMS에서 관리하는 키를 사용해 암호화 하도록 구성했다.
메인서버 또는 로그 서버 접근 방법
우리는 터널링 서버를 통해 접근해야 하기 때문에 ssh를 두 번 사용해서 서버에 접근해야 한다.
우선 터널링 서버의 퍼블릭 ip 주소를 확인한다.

다음 퍼블릭 주소를 확인하고 터널링 서버에 접속하기 위한 pem키를 이용해
ssh -i 명령어를 통해 터널링 서버에 접속한다.
이후 접속하고 싶은 서버의 private 주소를 찾는다.
private V4ip 주소를 찾고 나서 해당 서버에 접속하기 위한 펨키를 이용해
ssh -i 명령어를 통해 접속하고 싶은 서버에 접속한다.
로컬에 있는 pem 키는 내가 터널링 서버에 복사해뒀으니 그냥 터널링 서버 접속해서 pem 키 사용해서 접속하면 된다.
ssh -i [펨키 이름] ec2-user@[접속하고 싶은 주소]
다음 명령어로 접속하면 된다.
로컬에서 RDS 접속 방법
엘라스틱 캐시는 가격이 비싸서 로컬에서는 연결 안 시킬 것이다.
RDS도 가격 비싸서 로컬에서 RDS 연결하지 말고, 그냥 모니터링 툴로 데이터 잘 들어갔는지 정도 확인하는 용도로 사용하자.

우선 인텔리제이에서 mysql 데이터 소스 생성하자

호스트에는 우리 rds 주소를 입력하고
유저랑 비밀번호는 우리 환경변수에 적어놓은 값을 입력해주면 됩니다.
데이터베이스 이름은 dolfin 입니다.
그 옆에 SSH/SSL 탭을 클릭해주세요

use SSH tennel 클릭해주고, ssl configuration 오른쪽 ... 클릭해주세요

+ 눌러서 페이지 하나 만들고
Host에 터널링 퍼블릭 ip 주소 넣어주세요
유저 네임은 아마존 리눅스 기본 값 ec2-user로 입력해주세요
그리고 프라이빗 키 파일에 다운받은 pem 키 넣어주시고 apply 누르고 나와서
다시 원래 페이지 돌아가서 테스트 커넥션 눌러서 되면 어플라이 해주고 나오세요

잘 되네요 굿
이렇게 연결하시면 됩니다.