728x90
반응형
이 글에서는 백엔드 개발 환경을 위한 AWS 인프라 설정과 Blue/Green 배포 전략을 다룰 계획입니다. 이 환경은 운영 환경과 달리 단일 EC2 인스턴스에서 빠른 테스트와 배포 자동화에 중점을 두고 구성했습니다.
구조 설계 개요
- EC2 단일 인스턴스 (t3.medium)
- 퍼블릭 서브넷 배치
- Nginx 기반 리버스 프록시와 포트 스왑 방식의 Blue/Green 배포
- ECR 및 GitHub Actions 활용 CI/CD 구성
DEV 서비는 트래픽이 적고 빠른 개발 적용과 테스트가 중요하므로 복잡한 오토스케일링 구조 없이, 효율적인 배포 자동화에 중점을 두였습니다.
배포 전략: Nginx + Docker 포트 스왑
Nginx의 upstream 기능과 Docker 커테이너를 이용해 8080/8081 포트를 교차 활용하는 배포 방식을 채택했습니다.
Nginx 설정 예시
upstream backend-blue {
server 127.0.0.1:8080;
}
upstream backend-green {
server 127.0.0.1:8081;
}
server {
listen 80;
server_name dev-api.modie.site;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name dev-api.modie.site;
ssl_certificate /etc/letsencrypt/live/dev-api.modie.site/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dev-api.modie.site/privkey.pem;
set $active_backend backend-green;
location /wss {
proxy_pass http://localhost:$active_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
location / {
proxy_pass http://$active_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
- 80 포트 요청은 HTTPS(443)로 리디렉션
- $active_backend 변수로 현재 활성 컨테이너 선택 (blue/green)
Dockerfile (Gradle 기반 빌드)
FROM bellsoft/liberica-openjdk-alpine:17 AS builder
WORKDIR /app
COPY . .
RUN ./gradlew clean build -x test
FROM bellsoft/liberica-openjdk-alpine:17
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
Gradle을 사용해 Spring Boot 프로젝트를 빌드하고, 생성된 .jar 파일을 컨테이너에 포함시켜 8080 포트로 실행합니다.
GitHub Actions - CI/CD 설정 (dev 브랜치)
name: CI/CD Pipeline for ECR and EC2
on:
push:
branches:
- dev
jobs:
build-and-deploy:
name: Build, Push to ECR, Deploy on EC2
runs-on: ubuntu-22.04
env:
ECR_REGISTRY: 418272768555.dkr.ecr.ap-northeast-2.amazonaws.com
ECR_REPOSITORY: modie/modie-be
COMMIT_HASH: "${{ github.sha }}"
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set short COMMIT_HASH
run: echo "COMMIT_HASH=$(echo $GITHUB_SHA | cut -c1-7)" >> $GITHUB_ENV
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
aws-access-key-id: ${{ secrets.ECR_USER_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.ECR_USER_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: Login to Amazon ECR
run: |
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin $ECR_REGISTRY
- name: Build, tag, and push image to Amazon ECR
run: |
set -e # 오류 발생 시 즉시 종료
echo "Building Docker image..."
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$COMMIT_HASH \
-t $ECR_REGISTRY/$ECR_REPOSITORY:dev .
echo "Pushing Docker image to ECR..."
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$COMMIT_HASH &
docker push $ECR_REGISTRY/$ECR_REPOSITORY:dev &
wait # 모든 Push 완료될 때까지 대기
echo "✅ Image pushed successfully!"
- name: Deploy to EC2
uses: appleboy/ssh-action@master
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
script: |
set -e # 🚀 오류 발생 시 즉시 종료
COMMIT_HASH=$(aws ecr describe-images --repository-name modie/modie-be \
--region ap-northeast-2 --query 'sort_by(imageDetails,& imagePushedAt)[-1].imageTags[0]' --output text)
echo "🚀 사용할 Docker 태그: $COMMIT_HASH"
# 로그 디렉토리 생성 및 권한 설정
sudo mkdir -p /var/log/modie/dev
sudo chown $(id -u):$(id -g) /var/log/modie/dev
sudo chmod 755 /var/log/modie/dev
# aws 로그인
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 418272768555.dkr.ecr.ap-northeast-2.amazonaws.com/modie/modie-be
# Pull 최신 이미지
docker pull 418272768555.dkr.ecr.ap-northeast-2.amazonaws.com/modie/modie-be:$COMMIT_HASH
# Nginx 설정 파일 경로
NGINX_CONFIG=/etc/nginx/sites-enabled/default
# 기존 컨테이너 확인 및 포트 설정
PORT=8080
OTHER_PORT=8081
if docker ps | grep -q "0.0.0.0:8081"; then
PORT=8080
OTHER_PORT=8081
BG_MODE=blue
else
PORT=8081
OTHER_PORT=8080
BG_MODE=green
fi
docker run -d -p $PORT:8080 --name modie-be-$PORT \
-e RDS_ENDPOINT=${{ secrets.DB_HOST }} \
-e RDS_PORT=${{ secrets.DB_PORT }} \
-e DB_USERNAME=${{ secrets.DB_USER }} \
-e DB_PASSWORD=${{ secrets.DB_PASSWORD }} \
-e JWT_SECRET=${{ secrets.JWT_SECRET }} \
-e KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY }} \
-e KAKAO_REDIRECT_URI=${{ secrets.KAKAO_REDIRECT_URI }} \
-e DATABASE_NAME=modie \
-e FCM_PROJECT_ID=${{ secrets.FCM_PROJECT_ID }} \
-e FIREBASE_CLIENT_EMAIL=${{ secrets.FIREBASE_CLIENT_EMAIL }} \
-e FIREBASE_CLIENT_ID=${{ secrets.FIREBASE_CLIENT_ID }} \
-e FIREBASE_PRIVATE_KEY_ID=${{ secrets.FIREBASE_PRIVATE_KEY_ID }} \
-e FIREBASE_PRIVATE_KEY=${{ secrets.FIREBASE_PRIVATE_KEY }} \
-e FIREBASE_CLIENT_X509_CERT_URL=${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }} \
-e HASH_KEY=${{ secrets.HASH_KEY }} \
-e SPRING_PROFILES_ACTIVE=dev \
-v /var/log/modie/dev:/var/log/modie/dev \
--restart always \
418272768555.dkr.ecr.ap-northeast-2.amazonaws.com/modie/modie-be:$COMMIT_HASH || {
echo "❌ Docker 컨테이너 실행 실패!"
exit 1
}
echo "⌛ 신규 컨테이너 /health 체크 중..."
for i in {1..20}; do
sleep 3
if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:$PORT/health)" = "200" ]; then
echo "✅ 신규 컨테이너가 정상적으로 기동되었습니다!"
break
fi
if [ "$i" -eq 20 ]; then
echo "❌ /health 체크 실패! 롤백 수행"
docker stop modie-be-$PORT
docker rm modie-be-$PORT
exit 1
fi
done
sudo sed -i "s|set \$active_backend backend-.*;|set \$active_backend backend-$BG_MODE;|g" $NGINX_CONFIG
# 🔥 Nginx 설정 테스트 후 적용 (실패 시 롤백)
if sudo nginx -t; then
sudo systemctl reload nginx
echo "✅ Nginx 설정이 성공적으로 적용되었습니다!"
else
echo "❌ Nginx 설정 오류 발생! 롤백 수행"
docker stop modie-be-$PORT
docker rm modie-be-$PORT
exit 1
fi
# 이전 컨테이너 종료 및 삭제
docker stop modie-be-$OTHER_PORT || true
docker rm modie-be-$OTHER_PORT || true
echo "🚀 배포 완료!"
핵심 기능
- 이미지 버전 관리
커밋 해시(Commit Hash)를 기반으로 Docker 이미지를 태깅하고 관리하여,
어떤 코드가 배포되었는지 명확히 추적할 수 있도록 했습니다. - Blue/Green 포트 스와핑
8080/8081 포트를 교차 사용하여 기존 서비스에 영향 없이 새 컨테이너를 띄운 뒤,
Nginx 설정을 수정해 트래픽을 새로운 컨테이너로 전환하는 방식을 구현했습니다. - 자동 헬스체크 및 롤백 메커니즘
새로 배포한 컨테이너의 /health 엔드포인트를 검사하여,
응답이 실패할 경우 즉시 컨테이너를 중지하고 롤백할 수 있도록 구성했습니다. - Nginx 설정 자동 적용 및 검증
Nginx 설정 변경 후 구문 오류 검사를 수행(nginx -t)하고,
성공할 때만 설정을 reload하여 운영 중 장애를 방지했습니다. - SSH를 통한 EC2 배포 자동화
GitHub Actions를 통해 EC2 인스턴스에 SSH로 접속하여,
Docker 이미지 Pull, 새 컨테이너 실행, 포트 스와핑, 이전 버전 종료까지 전체 배포 과정을 자동화했습니다. - 시크릿 값 관리
데이터베이스 접속 정보, API 키 등 민감한 설정 값은 모두 GitHub Secrets에 안전하게 저장하고,
배포 시 환경 변수로 주입하는 방식으로 보안을 강화했습니다.
마무리
이번 DEV 서버 구축의 핵심 목표는 빠른 배포와 테스트였습니다.
이에 따라 비용 절감이나 확장성 확보보다는, 간결하고 안정적인 자동화 프로세스를 구축하는 데 집중했습니다.
728x90
반응형
'DevOps' 카테고리의 다른 글
[DevOps] 모니터링 환경 구축 Prometeus, Grafana, Loki, Promtail - Modie (0) | 2025.04.11 |
---|---|
[DevOps] 백엔드 Prod 서버 ASG 기반 롤링 배포 자동화 - Modie (0) | 2025.04.08 |
[DevOps] 프로젝트 Dev/Prod 서버 아키텍처 설계 경험 정리 (1) | 2025.04.07 |
GitHub Actions 머리 박치기 (0) | 2024.12.11 |