스택큐힙리스트

자바 스프링 개발 시작하기 - 14일차 배포 자동화 & 운영 본문

개발

자바 스프링 개발 시작하기 - 14일차 배포 자동화 & 운영

스택큐힙리스트 2025. 7. 21. 15:39
반응형

왜 지금 ‘배포 자동화’에 집중해야 할까?

서비스가 성장할수록 “서버 잠깐 내릴게요!”라는 말은 곧 사용자 이탈로 직결됩니다. 배포가 초 단위로 끝나더라도, 그 사이에 접속한 유저에게는 502 Bad Gateway가 찍히죠. 이번 14일차에서는 Docker + GitHub Actions + AWS EC2를 이용해 코드 push → 빌드 → 테스트 → 배포 → 헬스체크까지 전 과정을 자동화하고, 트래픽 손실 없는 무중단 배포(블루-그린 패턴)를 구축합니다.


1️⃣ Dockerfile: 가볍고 안전하게 빌드하기

# 1단계: 컴파일
FROM eclipse-temurin:21 as builder
WORKDIR /app
COPY . .
RUN ./mvnw -Dmaven.test.skip=true package

# 2단계: 실행 전용 이미지
FROM eclipse-temurin:21-jre-alpine
ARG JAR_FILE=/*.jar
COPY --from=builder /app/target/${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:8080/actuator/health | grep UP || exit 1
  • 멀티-스테이지 빌드로 이미지 용량을 최소화.
  • HEALTHCHECK를 포함해 컨테이너 내부에서 애플리케이션 상태를 주기적으로 확인.

2️⃣ GitHub Actions Workflow: ‘push = 배포’ 파이프라인

name: CI/CD

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      # 1) 테스트
      - name: Run Unit Tests
        run: ./mvnw test

      # 2) Docker 이미지 빌드 & 푸시
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build & Push
        run: |
          docker build -t ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }} .
          docker push ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}

      # 3) EC2에 SSH 접속하여 신규 컨테이너 배포
      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1.0.4
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ec2-user
          key: ${{ secrets.EC2_KEY }}
          script: |
            ./deploy.sh ${{ github.sha }}
  • 빌드 성공 후 바로 deploy.sh를 호출해 EC2 내에서 블루-그린 스위칭을 수행합니다.

3️⃣ 무중단 배포 스크립트(deploy.sh) 핵심 로직

 
#!/usr/bin/env bash
IMAGE_TAG=$1
BLUE_PORT=8081
GREEN_PORT=8082

# ① 현재 서비스 중인 포트 조회
ACTIVE_PORT=$(curl -s http://localhost/port)

if [ "$ACTIVE_PORT" = "$BLUE_PORT" ]; then
  IDLE_PORT=$GREEN_PORT
  COLOR="GREEN"
else
  IDLE_PORT=$BLUE_PORT
  COLOR="BLUE"
fi

echo "▶ 새 버전을 $COLOR($IDLE_PORT) 환경에 배포"

# ② 새 컨테이너 실행
docker pull myrepo/spring-app:${IMAGE_TAG}
docker rm -f app-${COLOR} || true
docker run -d --name app-${COLOR} -p ${IDLE_PORT}:8080 myrepo/spring-app:${IMAGE_TAG}

# ③ 헬스체크 (30초 내 200 OK 확인)
for i in {1..30}; do
  STATUS=$(curl -s http://localhost:${IDLE_PORT}/actuator/health | grep UP)
  if [ -n "$STATUS" ]; then
    echo "Health check passed"
    break
  fi
  echo "Waiting for app… ($i/30)"
  sleep 1
done

# ④ Nginx 업스트림 스위치 & 설정 리로드
sudo sed -i "s/port=.*/port=${IDLE_PORT}/" /etc/nginx/conf.d/upstream.conf
sudo nginx -s reload
echo "✔ 트래픽 전환 완료. $IDLE_PORT 가 동작 중"

# ⑤ 기존 컨테이너 정리
docker rm -f app-${ACTIVE_PORT} || true
  • 현재 서비스 포트와 대기 포트를 번갈아 가며 사용해 끊김 없이 배포.
  • 헬스체크 실패 시 트래픽 전환을 중단해 장애를 예방.

4️⃣ 스프링 Boot Actuator로 헬스 체크 강화하기

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      probes:
        enabled: true
      group:
        readiness:
          include: diskSpace, ping
  • readiness 그룹을 활용해 디스크 용량, DB 연결 등도 함께 검증.
  • 컨테이너 재시작 시 쿠버네티스·ECS로 옮겨가도 그대로 재사용 가능.

5️⃣ 운영 팁

  • 로그 롤링: docker logs 대신 Filebeat + ELK를 붙여 장기간 보존.
  • 보안: AWS Systems Manager Session Manager로 SSH Key 없이 접속, 액세스 로그 남기기.
  • 비용 절감: Auto-Scaling Group + Spot Instance로 유휴 시간대 비용 40 % 절약.

✨ 마무리

오늘은 “push 한 번으로 끝나는” 무중단 배포 파이프라인을 완성했습니다. 다음 배포부터는 콘솔 대신 커밋 메시지로 배포 기록이 남고, 장애 없는 업데이트가 가능해집니다. 내일은 모니터링을 곁들인 로그 & 메트릭 수집으로 더 탄탄한 운영 환경을 만들어 봅시다!

반응형
Comments