Skip to content

feat: 운영(Prod) 서버 CI/CD 구축 및 환경 설정 분리#40

Merged
Juhye0k merged 5 commits intodevfrom
ip
Dec 1, 2025
Merged

feat: 운영(Prod) 서버 CI/CD 구축 및 환경 설정 분리#40
Juhye0k merged 5 commits intodevfrom
ip

Conversation

@Juhye0k
Copy link
Copy Markdown
Contributor

@Juhye0k Juhye0k commented Nov 30, 2025

🚀 1. 개요

  • 기존의 개발(Dev) 환경과 별도로 운영(Prod) 환경을 안정적으로 배포하기 위한 CI/CD 파이프라인을 구축하고, 이에 필요한 Spring Boot 및 Docker 설정을 추가했습니다.

📝 2. 주요 변경 사항

1. 🐳 Docker 인프라 분리 (Port & Volume 격리)

기존 docker-compose.yml을 용도에 따라 두 개로 분리하고, 포트와 데이터 경로를 격리하여 동시 실행이 가능하도록 했습니다.

  • 운영(Prod): docker-compose.prod.yml
    • MySQL: 3311, Redis: 6379
    • 네트워크: geumpumta-net-prod
  • 개발(Dev): docker-compose.dev.yml
    • MySQL: 3313 (기존 3312 충돌 해결), Redis: 6381
    • 네트워크: geumpumta-net-dev
    • 데이터 경로: ./docker-infra/mysql-dev 등 별도 경로 사용

2. 🍃 Spring Boot 설정 분리

application.yml에서 프로필별로 바라보는 DB 주소를 분리하여, 로컬 개발 시 실수로 운영 DB에 접속하는 사고를 방지했습니다.

  • local: localhost:3313 (개발 서버의 Dev DB 포트 사용)
  • dev: Docker 내부망 mysql-geumpumta-dev
  • prod: Docker 내부망 mysql-geumpumta-prod

3. 🚀 CI/CD 파이프라인 고도화 (4-Tier 구조)

기존 단일 워크플로우를4개의 파일로 분리하고, GitHub Environments를 도입했습니다.

  • 파일 구조:
    • dev-ci.yml / prod-ci.yml: 빌드 및 테스트, Docker 이미지 생성 (Artifact로 태그 전달)
    • dev-cd.yml / prod-cd.yml: workflow_run으로 CI 성공 시 트리거, 환경 변수 주입 및 배포
  • 주요 개선점:
    • Artifact 활용: CI와 CD 분리에 따른 버전 정보 공유를 위해 upload/download-artifact 로직 추가
    • 동적 변수: GitHub Environments(development, production)의 WEB_PORT, NETWORK_NAME 변수를 사용하여 스크립트 재사용성 증대
    • 안전한 정리: docker image prune -f를 사용하여 롤백 가능성을 열어두고 안전하게 구버전 이미지 정리

4. 🌐 Nginx 리버스 프록시 분리

외부 요청을 각 환경에 맞는 컨테이너로 연결하도록 Upstream 포트를 분리했습니다.

  • geumpumta.shop32123 (Prod Container)
  • dev.geumpumta.shop32124 (Dev Container)

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 프로덕션 환경 설정 및 지원 추가
  • Chores

    • 개발 및 프로덕션 환경용 자동화 배포 파이프라인 구성
    • 다중 플랫폼 Docker 이미지 빌드 설정 추가
    • 보안 관련 모듈 업데이트

✏️ Tip: You can customize this high-level summary in your review settings.

@Juhye0k Juhye0k requested a review from kon28289 November 30, 2025 02:56
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 30, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

요약 (Walkthrough)

프로덕션 환경을 위한 Spring Boot 설정 파일과 GitHub Actions CI/CD 워크플로우를 추가합니다. 프로덕션 프로필 설정, 개발/프로덕션 배포 파이프라인, 서브모듈 업데이트를 포함하여 멀티 환경 배포 인프라를 구성합니다.

변경 사항 (Changes)

파일 그룹 / 파일 변경 요약
Spring Boot 프로덕션 설정
src/main/resources/application-prod.yml, src/main/resources/application.yml
프로덕션 환경 설정 파일 추가 및 프로필 매핑 설정. MySQL, Redis, Hibernate 설정과 파일 업로드 크기 제한(10MB/100MB) 구성
보안 서브모듈
src/main/resources/security
서브모듈 커밋 포인터 업데이트
개발 환경 CI/CD
.github/workflows/dev-ci.yml, .github/workflows/dev-cd.yml
개발 브랜치 대상 CI 빌드 파이프라인 및 CD 배포 워크플로우 추가. Redis 서비스, Docker 멀티 플랫폼 이미지 빌드/푸시 포함
프로덕션 환경 CI/CD
.github/workflows/prod-ci.yml, .github/workflows/prod-cd.yml
메인 브랜치 대상 워크플로우로 전환. 트리거 이름 및 배포 환경 업데이트, 프로덕션 설정 적용

예상 코드 리뷰 난이도 (Estimated code review effort)

🎯 3 (보통) | ⏱️ ~20분

  • 주의 사항:
    • GitHub Actions 워크플로우 트리거 조건 및 브랜치 필터 검증 필요
    • 프로덕션/개발 프로필 설정의 보안 관련 구성 확인 (데이터베이스, Redis 자격증명)
    • CI 파이프라인의 아티팩트 및 이미지 태그 처리 로직 검토
    • 서브모듈 업데이트가 빌드에 영향을 주지 않는지 확인

관련 PR (Possibly related PRs)

  • feat: 스웨거 적용 #5: Swagger 설정 파일(application-swagger.yml)을 Spring 프로필 YAML 임포트에 추가하는 공통 설정 변경

제안 레이블 (Suggested labels)

enhancement

축하 시 (Poem)

🐰 프로덕션으로 향하는 길,
CI/CD 파이프라인 완성!
안전한 배포와 자동화의 춤,
개발과 본격 서비스,
이제 우리도 준비 완료! 🚀

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 핵심을 명확하게 반영합니다. '운영(Prod) 서버 CI/CD 구축 및 환경 설정 분리'는 프로덕션 환경의 CI/CD 파이프라인 구축과 설정 분리라는 주요 변경사항을 정확하게 요약합니다.
Description check ✅ Passed PR 설명이 리포지토리 템플릿의 모든 필수 섹션(개요, 주요 변경사항)을 포함하며, 변경사항을 구체적이고 명확하게 설명합니다. 각 섹션이 충실하게 작성되었습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

29-63: 아티팩트 업로드 구성을 확인하세요.

build/libs/*.jarDockerfile이 업로드되고 있으나, CD 워크플로우가 이를 성공적으로 다운로드할 수 있도록 download-artifact 단계의 구성과 일치하는지 확인하십시오. 또한 아티팩트 보관 기간이 설정되지 않은 경우 저장소 용량 누적을 고려하여 명시적으로 설정하는 것이 좋습니다.

아티팩트 구성을 명확히 하기 위해 다음을 추가하세요:

       - name: Upload Build Artifacts
         uses: actions/upload-artifact@v4
         with:
           name: build-libs
+          retention-days: 1
           path: |
             build/libs/*.jar
             Dockerfile
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3dd8e9 and ccbbc98.

📒 Files selected for processing (5)
  • .github/workflows/cd.yml (2 hunks)
  • .github/workflows/ci.yml (2 hunks)
  • src/main/resources/application-prod.yml (1 hunks)
  • src/main/resources/application.yml (1 hunks)
  • src/main/resources/security (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
src/main/resources/security (1)

1-1: Based on my investigation, I've confirmed the key findings. The security submodule repository referenced in .gitmodules (https://github.com/Geumpumta/backend-security.git) is not accessible through public GitHub APIs or web access. The backend repository itself is public, so this suggests the security submodule is either private, doesn't exist, or the URL is incorrect.

Here is the rewritten review comment:


Security submodule update cannot be verified — submodule repository is not accessible

The submodule pointer has been updated from fd01d78390303b12d3459d9db53578fb16bc53ca to 24fc3c905e4cc5f26e4140bbc7d4da51ec14ea44, but the referenced repository (https://github.com/Geumpumta/backend-security.git) is not publicly accessible. Unable to verify:

  • What changes were introduced in the submodule
  • Compatibility with new production configuration (application-prod.yml, production workflows)
  • Any breaking changes or security implications
  • Alignment with production CI/CD objectives

Action required: Verify the submodule changes directly in the backend-security repository or provide documentation of what changed in this update.

.github/workflows/ci.yml (1)

1-7: 워크플로우 트리거 및 이름이 Prod 환경에 올바르게 정렬되어 있습니다.

CI 워크플로우가 prod 브랜치에서만 트리거되도록 변경되었으며, CD 워크플로우에서 이 워크플로우를 참조할 때 일관성이 유지됩니다 (cd.yml 라인 5).

src/main/resources/application-prod.yml (1)

1-11: 프로필 그룹 및 임포트 구조가 명확합니다.

보안 및 애플리케이션 설정 모듈을 프로필별로 분리하는 것이 좋습니다. 그러나 이러한 파일들(security/application-*.yml)이 존재하고 변수(geumpumta.mysql.*, geumpumta.redis.*)가 배포 환경에서 설정되는지 확인하세요.

다음을 확인하세요:

  • 모든 임포트된 파일이 존재하는가
  • 환경 변수 geumpumta.mysql.url, geumpumta.mysql.username, geumpumta.mysql.password, geumpumta.redis.host, geumpumta.redis.port, geumpumta.redis.password가 CD 파이프라인에서 설정되는가
.github/workflows/cd.yml (2)

1-16: 프로덕션 CD 워크플로우 트리거 및 환경 설정이 올바르게 구성되어 있습니다.

워크플로우가 prod 브랜치의 성공적인 CI 완료 후에만 실행되도록 제한되며, GitHub Environment를 통해 프로덕션 환경 변수를 분리하여 관리합니다. 이는 환경 간 설정 분리 목표와 일치합니다.


62-78: Verify that database and Redis configuration variables are properly injected into the container.

The CD workflow correctly uses production GitHub Environment with secrets.WEB_PORT and vars.NETWORK_NAME (lines 91, 90). However, the docker run command (lines 88-94) only sets SPRING_PROFILES_ACTIVE=prod and does not explicitly inject the database and Redis connection variables that the application requires:

  • application-prod.yml references ${geumpumta.mysql.url}, ${geumpumta.mysql.username}, ${geumpumta.mysql.password}, ${geumpumta.redis.host}, ${geumpumta.redis.port}, and ${geumpumta.redis.password}
  • These variables must be available at runtime either via environment variables, self-hosted runner configuration, or GitHub Secrets/Vars

Ensure one of the following:

  1. Add -e flags to the docker run command to inject these variables (e.g., -e GEUMPUMTA_MYSQL_URL=${{ secrets.GEUMPUMTA_MYSQL_URL }})
  2. Or verify these variables are pre-configured on the self-hosted runner environment
  3. Or configure them in the production GitHub Environment if using that approach

Comment on lines +86 to +94
- name: Clean up old container and image
run: |
docker rm -f ${{ vars.CONTAINER_NAME }} || true
docker images "ghcr.io/${{ github.repository }}" \
--format "{{.Repository}}:{{.Tag}}" \
| grep -v "${{ needs.build.outputs.image-tag }}" \
| xargs -r docker rmi -f || true
docker image prune -f

# 새로운 이미지로 새로운 컨테이너 생성하여 실행
- name: Run New Container
- name: Run New Container (Prod)
run: |
docker run -d \
--name ${{ vars.CONTAINER_NAME }} \
--network ${{ vars.NETWORK_NAME }} \
-p ${{ secrets.DEV_WEB_PORT }}:8080 \
-e SPRING_PROFILES_ACTIVE=dev \
-p ${{ secrets.WEB_PORT }}:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e TZ=Asia/Seoul \
${{ needs.build.outputs.image-tag }}
${{ needs.build.outputs.image-tag }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Docker 이미지 정리 및 컨테이너 배포 구성을 개선하세요.

현재 설정의 잠재적 문제:

  1. 이미지 정리 (라인 84): docker image prune -f는 모든 미사용 이미지를 삭제합니다. 여러 애플리케이션이 같은 호스트에서 실행 중인 경우 의도치 않게 다른 애플리케이션의 이미지를 삭제할 수 있습니다.

  2. 컨테이너 재시작 정책 부재: 컨테이너가 중단되었을 때 자동으로 다시 시작되도록 설정하지 않았습니다.

  3. 배포 실패 처리: docker run 실패 시 워크플로우가 계속 진행될 수 있습니다.

다음과 같이 개선하세요:

       - name: Clean up old container and image
         run: |
           docker rm -f ${{ vars.CONTAINER_NAME }} || true
-          docker image prune -f
+          docker image prune -f --filter "until=72h"

       - name: Run New Container (Prod)
         run: |
           docker run -d \
             --name ${{ vars.CONTAINER_NAME }} \
             --network ${{ vars.NETWORK_NAME }} \
+            --restart unless-stopped \
             -p ${{ secrets.WEB_PORT }}:8080 \
             -e SPRING_PROFILES_ACTIVE=prod \
             -e TZ=Asia/Seoul \
             ${{ needs.build.outputs.image-tag }}
+        set -e

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In .github/workflows/cd.yml around lines 81 to 94, tighten image cleanup and
make deployment safer: replace the broad `docker image prune -f` with a scoped
cleanup (e.g., prune only dangling images with `docker image prune -f --filter
"dangling=true"` or remove the previous release image by name/tag) so other
apps' images aren’t removed; add a restart policy to the docker run command
(`--restart unless-stopped`) so the container is automatically restarted on
failure; and ensure the run step fails the workflow on error by removing any `||
true` suppression and enabling exit-on-error at the top of the run script (e.g.,
`set -e`) so a failed `docker run` stops the job.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
.github/workflows/prod-cd.yml (1)

81-94: Docker 이미지 정리 및 컨테이너 배포 안전성을 개선하세요.

현재 설정에 세 가지 중대한 문제가 있습니다:

  1. 광범위한 이미지 정리 (84줄): docker image prune -f는 모든 미사용 이미지를 삭제합니다. 호스트에서 여러 애플리케이션이 실행 중이면 다른 앱의 이미지도 의도치 않게 삭제될 수 있습니다.

  2. 컨테이너 재시작 정책 부재: 컨테이너가 중단되면 자동으로 다시 시작되지 않습니다.

  3. 에러 처리 부재: docker run 실패 시 워크플로우가 계속 진행될 수 있습니다.

필터(--filter)를 사용하여 정확히 제어할 수 있습니다. 다음과 같이 개선하세요:

       - name: Clean up old container and image
         run: |
+          set -e
           docker rm -f ${{ vars.CONTAINER_NAME }} || true
-          docker image prune -f 
+          docker image prune -f --filter "until=72h"

       - name: Run New Container (Prod)
         run: |
           docker run -d \
             --name ${{ vars.CONTAINER_NAME }} \
             --network ${{ vars.NETWORK_NAME }} \
+            --restart unless-stopped \
             -p ${{ secrets.WEB_PORT }}:8080 \
             -e SPRING_PROFILES_ACTIVE=prod \
             -e TZ=Asia/Seoul \
             ${{ needs.build.outputs.image-tag }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f69212 and 7cc0e3e.

📒 Files selected for processing (4)
  • .github/workflows/dev-cd.yml (1 hunks)
  • .github/workflows/dev-ci.yml (1 hunks)
  • .github/workflows/prod-cd.yml (2 hunks)
  • .github/workflows/prod-ci.yml (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
.github/workflows/prod-ci.yml (2)

1-49: 프로덕션 CI 워크플로우가 잘 구성되었습니다.

메인 브랜치로 트리거, 필요한 권한 설정, Redis 헬스 체크, 그리고 의존성 캐싱 등이 모두 적절히 구성되어 있습니다.


60-63: Dockerfile를 아티팩트에 포함한 점이 좋습니다.

프로덕션 CD 워크플로우에서 이미지를 빌드하는 데 필요하므로 이 설정이 필수적입니다.

.github/workflows/prod-cd.yml (1)

1-20: GitHub Environment와 workflow_run 트리거가 올바르게 구성되었습니다.

프로덕션 환경 변수 주입과 CI 성공 시에만 배포하는 조건이 적절합니다.

.github/workflows/dev-cd.yml (1)

13-33: 아티팩트 다운로드 및 이미지 태그 추출 패턴이 적절합니다.

workflow_run 이벤트 간 데이터 전달을 위해 파일 기반 아티팩트를 사용하는 방식이 올바릅니다.

.github/workflows/dev-ci.yml (2)

1-60: 개발 CI 워크플로우의 기본 구조가 잘 설계되었습니다.

JDK 21, Gradle 캐싱, Redis 헬스 체크, 권한 설정 등이 모두 적절합니다.


94-102: 이미지 태그를 파일로 내보내고 아티팩트로 전달하는 방식이 우수합니다.

workflow_run 이벤트를 통한 워크플로우 간 통신에서 이 패턴은 표준적이고 신뢰할 수 있는 방식입니다.

Comment on lines +45 to +58
- name: Clean up old container
run: |
docker rm -f ${{ vars.CONTAINER_NAME }} || true
docker image prune -f

- name: Run New Container (Dev)
run: |
docker run -d \
--name ${{ vars.CONTAINER_NAME }} \
--network ${{ vars.NETWORK_NAME }} \
-p ${{ secrets.WEB_PORT }}:8080 \
-e SPRING_PROFILES_ACTIVE=dev \
-e TZ=Asia/Seoul \
${{ steps.get-tag.outputs.IMAGE_TAG }} No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Docker 이미지 정리 및 컨테이너 배포를 개선하세요.

개발 환경에서도 동일한 안전성 개선이 필요합니다:

  1. 광범위한 이미지 정리: docker image prune -f가 다른 컨테이너 이미지를 삭제할 수 있습니다.
  2. 컨테이너 재시작 정책 부재: 장애 발생 시 자동 복구 기능이 없습니다.
  3. 에러 처리 부재: 배포 실패가 워크플로우 실패로 이어지지 않습니다.

다음 diff를 적용하세요:

       - name: Clean up old container
         run: |
+          set -e
           docker rm -f ${{ vars.CONTAINER_NAME }} || true
-          docker image prune -f 
+          docker image prune -f --filter "until=72h"

       - name: Run New Container (Dev)
         run: |
           docker run -d \
             --name ${{ vars.CONTAINER_NAME }} \
             --network ${{ vars.NETWORK_NAME }} \
+            --restart unless-stopped \
             -p ${{ secrets.WEB_PORT }}:8080 \
             -e SPRING_PROFILES_ACTIVE=dev \
             -e TZ=Asia/Seoul \
             ${{ steps.get-tag.outputs.IMAGE_TAG }}
🤖 Prompt for AI Agents
.github/workflows/dev-cd.yml lines 45-58: the workflow currently runs a broad
docker image prune, lacks a container restart policy, and swallows errors which
prevents CI from failing on real deployment problems; change the image prune to
only remove dangling images (e.g. docker image prune -f --filter
"dangling=true"), add a restart policy to the docker run command (e.g. --restart
unless-stopped), and make error handling strict by removing the unconditional
silencing (replace the current docker rm -f ... || true with a check that only
skips removal when the container is absent, or run a conditional existence check
before removal) so genuine failures cause the workflow to fail.

@Juhye0k Juhye0k requested a review from kon28289 December 1, 2025 00:09
@Juhye0k Juhye0k changed the base branch from main to dev December 1, 2025 01:26
Copy link
Copy Markdown
Contributor

@kon28289 kon28289 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!

@Juhye0k Juhye0k merged commit ba331fd into dev Dec 1, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants