-
Notifications
You must be signed in to change notification settings - Fork 1
feat: GitHub Actions 배포 워크플로우 Matrix 전략 도입 및 Blue-Green 배포 구조 개선 #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…servability 모듈로 마이그레이션
Walkthrough관찰성 모듈(observability) 추가, MDC 로깅 필터를 공통 추상으로 이동 및 인증용/단순 구현 추가, 여러 모듈에 observability 의존성 추가, Dockerfile 및 프로필/포트 변경, 개발용 Dockerfile 삭제, CI/CD 워크플로우를 모듈별 배포로 재구성했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Gateway as Gateway\n(Security Filter Chain)
participant SecFilter as SecurityMdcLoggingFilter
participant BaseFilter as BaseMdcLoggingFilter
participant Service as Backend Service
Client->>Gateway: HTTP Request (헤더 포함)
Gateway->>SecFilter: 필터 체인 진입
SecFilter->>BaseFilter: MDC 공통 필드 설정 (traceId, clientIp, requestInfo)
SecFilter->>SecFilter: SecurityContext에서 userId 추출(Jwt.subject 또는 principal)
SecFilter->>Service: 요청 전달 (MDC 세팅 유지)
Service-->>SecFilter: 응답/예외
SecFilter->>BaseFilter: MDC 정리 (finally)
SecFilter-->>Client: HTTP Response
sequenceDiagram
participant GH as GitHub Workflow
participant Detect as detect-changes
participant Matrix as build-push-and-deploy (matrix)
participant DeployAction as ./.github/actions/deploy-module
participant Remote as Remote Server
GH->>Detect: 변경 모듈 감지 (apis,batch,any)
Detect-->>Matrix: 모듈별 플래그 전달
Matrix->>DeployAction: 모듈별 빌드·푸시·배포 실행 (입력: module,image-prefix,ssh 등)
DeployAction->>Remote: SSH로 배포 스크립트 실행 및 알림 전송
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Dockerfile (1)
1-45: 선택 사항: Dockerfile 보안 및 운영 개선 사항정적 분석에서 제안한 다음 항목들을 고려하세요:
HEALTHCHECK 추가: 컨테이너 헬스 상태를 주기적으로 확인하여 자동 재시작 메커니즘을 활성화할 수 있습니다.
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \ CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1비루트 사용자: 보안 취약성을 줄이기 위해 전용 비루트 사용자를 추가하세요.
RUN useradd -m -u 1000 appuser USER appuserobservability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt (1)
5-8: 설정 속성에 기본값 제공 권장
basePath속성이 non-nullable이고 기본값이 없어서, 설정 파일에management.endpoints.web.base-path값이 누락되면 애플리케이션 시작 시 바인딩 오류가 발생할 수 있습니다.다음 중 하나의 방법을 적용하는 것을 권장합니다:
옵션 1: 기본값 제공
@ConfigurationProperties(prefix = "management.endpoints.web") data class ActuatorProperties( - val basePath: String + val basePath: String = "/actuator" )옵션 2: Nullable 타입으로 변경
@ConfigurationProperties(prefix = "management.endpoints.web") data class ActuatorProperties( - val basePath: String + val basePath: String? )
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (23)
.github/workflows/ci-pr.yml(1 hunks)Dockerfile(3 hunks)Dockerfile-dev(0 hunks)admin/build.gradle.kts(1 hunks)admin/src/main/kotlin/org/yapp/admin/AdminApplication.kt(1 hunks)admin/src/main/resources/application.yml(1 hunks)apis/build.gradle.kts(1 hunks)apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt(1 hunks)apis/src/main/resources/application.yml(1 hunks)batch/build.gradle.kts(1 hunks)batch/src/main/kotlin/org/yapp/batch/BatchApplication.kt(1 hunks)batch/src/main/resources/application.yml(1 hunks)buildSrc/src/main/kotlin/Dependencies.kt(1 hunks)gateway/build.gradle.kts(1 hunks)gateway/src/main/kotlin/org/yapp/gateway/filter/MdcLoggingFilter.kt(0 hunks)gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt(1 hunks)gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt(3 hunks)observability/build.gradle.kts(1 hunks)observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt(1 hunks)observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt(1 hunks)observability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt(1 hunks)observability/src/main/resources/application-observability.yml(1 hunks)settings.gradle.kts(1 hunks)
💤 Files with no reviewable changes (2)
- Dockerfile-dev
- gateway/src/main/kotlin/org/yapp/gateway/filter/MdcLoggingFilter.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-16T06:56:28.923Z
Learnt from: hoonyworld
PR: YAPP-Github/Reed-Server#96
File: gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt:0-0
Timestamp: 2025-08-16T06:56:28.923Z
Learning: In Spring Security filter chain, MdcLoggingFilter should be placed after BearerTokenAuthenticationFilter because it depends on SecurityContextHolder to extract userId from the authenticated principal. Placing it before authentication filters would result in null or anonymous authentication context.
Applied to files:
gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.ktgateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt
🧬 Code graph analysis (1)
observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt (2)
gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt (2)
resolveUserId(14-29)resolveUserId(21-28)observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt (2)
resolveUserId(11-18)resolveUserId(17-17)
🪛 Checkov (3.2.334)
Dockerfile
[low] 1-45: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
[low] 1-45: Ensure that a user for the container has been created
(CKV_DOCKER_3)
⏰ 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-validation
🔇 Additional comments (29)
.github/workflows/ci-pr.yml (1)
61-65: 좋은 개선: 빌드 명령 문서화로 가독성 향상Gradle 명령을 다중 라인 형식으로 변경하고 각 플래그의 목적을 명확히 설명하는 한국어 주석을 추가한 것은 팀의 유지보수성 측면에서 긍정적입니다. 특히
fullCheck작업이 여러 모듈(apis, admin, batch, gateway 등)을 포함한다는 점이 명확해졌습니다.다만, 다음 항목을 확인해 주시기 바랍니다:
fullCheckGradle 작업이 실제로 PR에서 추가되는 새로운 observability 모듈을 포함하도록 설정되었는지 확인- 멀티 애플리케이션 배포 지원을 위해 다른 워크플로우 파일(예: CD, 배포 워크플로우)도 유사한 업데이트가 필요한지 검토
Dockerfile (1)
3-4: 멀티 모듈 빌드를 위한 MODULE 파라미터화가 잘 구현되었습니다.MODULE ARG를 필수 요구 사항으로 변경하고 명확한 주석을 추가하여 각 모듈별 빌드 시 명시적으로 모듈을 지정하도록 강제하는 것이 좋은 설계입니다. 빌드 단계와 실행 단계에서 일관되게 사용되고 있습니다.
Also applies to: 28-29
observability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt (1)
1-1: 패키지 이동이 올바르게 완료됨검증 결과, ActuatorProperties 클래스의 패키지 이동이 완전하게 처리되었습니다. 새로운 패키지 경로(
org.yapp.observability.metrics.config)로의 import가 SecurityConfig.kt에서 올바르게 업데이트되었으며, 기존 패키지 경로에 대한 참조는 남아있지 않습니다. 클래스가 SecurityConfig에서 정상적으로 사용 중입니다.batch/src/main/resources/application.yml (4)
15-15:observability프로필이 모든 환경에서 일관되게 추가됨Observability 모듈 통합이 dev, prod, test 프로필에 균일하게 적용되었습니다. 다음을 확인해주세요:
application-observability.yml또는 해당 프로필 파일이 존재하는지 확인- 다른 모듈(admin, apis, gateway)의
application.yml에서도 동일한observability프로필이 추가되었는지 확인하여 일관성 보장Also applies to: 21-21, 27-27
22-27:test프로필의 순서가dev,prod와 다름 - 의도 확인 필요프로필 로딩 순서가 다릅니다:
- dev, prod: persistence → crosscutting → redis → external → observability
- test: persistence → crosscutting → redis → external → observability
현재는 순서가 동일하게 보이지만, 추가 검토 시 다음을 확인하세요:
- 프로필 순서가 설정 오버라이드에 영향을 주지 않는지 확인
- 각 환경별로 프로필 로딩 순서가 의도한 대로인지 검증
28-31: 멀티파트 최대 요청 크기 30MB 설정 - 용도 확인
max-request-size: 30MB이 신규 추가되었습니다. Batch 모듈에서 이 설정이 필요한 이유를 확인해주세요:
- Batch 모듈이 HTTP 요청으로 파일 업로드를 받는 엔드포인트를 가지고 있는지 확인
- 30MB 크기가 실제 사용 케이스에 적절한지 검증
- 메모리 및 성능 영향도 검토 (특히 동시 요청 시)
일반적인 배치 작업은 주기적 스케줄링이나 메시지 큐로 트리거되므로, HTTP 멀티파트 요청이 필요한지 재검토하세요.
10-27: <!-- [scratchpad_start]
[task review_verification_1/3]
Profile consistency across modules - COMPLETED[observations]
- batch/src/main/resources/application.yml: dev/prod/test all have [persistence, crosscutting, redis, external, observability]
- apis/src/main/resources/application.yml: dev/prod/test all have [persistence, crosscutting, jwt, redis, external(dev/prod only), observability]
- admin/src/main/resources/application.yml: dev/prod/test all have [persistence, crosscutting, jwt, redis, external(dev/prod only), observability]
- gateway module: NOT FOUND in search results
- crosscutting profile: present consistently across all 3 modules
- observability profile: present consistently across all 3 modules (batch, apis, admin)
[analysis]
- Profile structure IS synchronized across modules
- crosscutting migration appears successful (replaced jwt in some contexts)
- observability added consistently to all modules
- Discrepancy: batch uses [external + observability] while apis/admin use [external + jwt + observability]
→ This is intentional: batch doesn't need jwt (no API authentication), apis/admin need it[pending]
- Dockerfile parameterization verification (NOT in script scope)
- CI/CD workflow changes verification (NOT in script scope)
- Gateway module location/status (not found)
[actions]
- Generate final response noting completed verifications
- Flag incomplete verifications requiring manual review
- Use tag due to incomplete verification scope
[done]
Profile consistency check complete. Remaining items require manual review or different verification approach.
[scratchpad_end]
-->프로필 일관성 검증 완료 - 추가 확인 필요
프로필 그룹의 모듈 간 일관성 검증이 완료되었습니다.
✓ 확인된 사항:
- Crosscutting 프로필: batch, apis, admin 모듈 모두에서 dev/prod/test 환경에 일관되게 적용됨
- Observability 프로필: batch, apis, admin 모듈 모두에서 dev/prod/test 환경에 일관되게 적용됨
- 구조 일관성: 모든 모듈이 동기화되어 있으며, 각 모듈의 특성에 맞게 프로필 구성됨 (batch는 jwt 미포함, apis/admin은 jwt 포함)
⚠ 확인 불가능한 항목:
- Gateway 모듈: 검색 결과에서 찾을 수 없음 (해당 모듈 확인 필요)
- Dockerfile 파라미터화: 프로필 구조와의 연결 관계를 직접 확인하려면 Dockerfile 및 배포 스크립트의 수동 검토 필요
- CI/CD 워크플로우: 새로운 observability 프로필로 인한 빌드 단계 변경사항의 수동 검토 필요
settings.gradle.kts (1)
10-11: LGTM! 새로운 observability 모듈 추가observability 모듈이 프로젝트에 정상적으로 추가되었습니다. trailing comma 사용도 좋은 패턴입니다.
buildSrc/src/main/kotlin/Dependencies.kt (1)
33-33: LGTM! OBSERVABILITY 상수 추가새로운 observability 모듈 참조를 위한 상수가 올바르게 추가되었습니다. 기존 패턴을 따르고 있습니다.
admin/src/main/resources/application.yml (1)
8-30: LGTM! 프로필 그룹 업데이트모든 환경(dev, prod, test)에
crosscutting과observability프로필이 일관되게 추가되었습니다. 기존 프로필들은 유지되면서 새로운 관심사를 추가하는 방식으로 안전한 변경입니다.apis/src/main/resources/application.yml (1)
10-30: 웹 프로필 존재 여부 확인 불가검색 결과, 코드베이스에
application-web.yml파일이나web프로필 관련 설정이 발견되지 않았습니다. 다음을 확인해야 합니다:
- 원래
web프로필이 실제로 존재했는지 여부observability모듈의management.endpoints.web설정(actuator 엔드포인트)이 기존 web 프로필의 모든 설정을 포함하는지 여부- 변경 전 상태와의 비교 검증
현재 상태:
apis,admin,batch모두 모든 환경에서observability프로필을 활성화하고 있습니다.admin/src/main/kotlin/org/yapp/admin/AdminApplication.kt (1)
8-10: JpaRepositoriesAutoConfiguration 제외는 의도된 아키텍처 설계로 정당화됩니다검증 결과, Admin 모듈의 JPA 제외는 명시적이고 필요한 설정입니다:
- Admin 모듈 자체: JPA 리포지토리 직접 사용 없음
- 대체 구성:
@EnableInfraBaseConfig([InfraBaseConfigGroup.JPA])를 통해 INFRA 모듈의 JpaConfig를 활성화- INFRA 모듈: 모든 JPA 엔티티(
BookEntity,ReadingRecordEntity)와 리포지토리(JpaBookRepository,JpaReadingRecordRepository)를 관리- 아키텍처: Spring Boot 기본 JPA 자동 구성을 제외하고 INFRA 모듈의 커스텀 JPA 설정을 명시적으로 로드하는 다중 모듈 구조
이는 데이터베이스 관심사를 INFRA 모듈에 집중시키는 단일 책임 원칙을 따르는 설계입니다.
batch/src/main/kotlin/org/yapp/batch/BatchApplication.kt (1)
8-10: JpaRepositoriesAutoConfiguration 제외는 적절함 - 우려사항 없음infra 모듈이
JpaConfig.kt에서 명시적으로@EnableJpaRepositories(basePackages = ["org.yapp.infra"])를 구성하고 있습니다. batch 모듈의@ComponentScan이 infra 모듈을 포함하므로,JpaRepositoriesAutoConfiguration을 제외하더라도 infra의 명시적 설정이 JPA 리포지토리 스캔을 처리합니다. batch 모듈은 직접 JPA 리포지토리를 정의하지 않으므로, 이 구성은 올바릅니다.observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt (4)
21-31: 상수 정의가 명확하고 적절합니다.MDC 키와 헤더 이름을 상수로 정의하여 재사용성과 유지보수성을 높였습니다.
DEFAULT_GUEST_USER상수를 통한 폴백 처리도 적절합니다.
33-46: MDC 정리를 finally 블록에서 수행하여 안전합니다.필터 체인 실행 후
MDC.clear()를 finally 블록에서 호출하여 예외 발생 시에도 MDC 컨텍스트가 정리되도록 보장합니다.OncePerRequestFilter사용도 요청당 한 번만 실행되도록 보장하여 적절합니다.
74-86: 클라이언트 IP 추출 로직이 올바릅니다.프록시 환경을 고려한 헤더 우선순위 처리가 적절합니다:
X-Forwarded-For(첫 번째 IP 추출)X-Real-IPrequest.remoteAddr
52-56: TraceId 형식 변경에 대한 외부 시스템 영향 없음 - 검증 완료코드베이스 전체 검증 결과, traceId는 로컬 로깅 컨텍스트(MDC)에서만 사용되며 외부 시스템으로 전파되지 않습니다.
검증 내용:
- traceId는 X-Request-ID 헤더에서만 읽음
- MDC에만 저장되고 log4j2 로깅 패턴에 사용됨
- HTTP 인터셉터나 아웃바운드 헤더 설정 없음
- 분산 추적 라이브러리(Sleuth, Jaeger 등) 미사용
- RestClient 빈에서 traceId 전파 로직 없음
따라서 UUID에서 하이픈을 제거하는 설계는 표준 형식 호환성이나 외부 시스템 통합에 영향을 주지 않습니다. 이는 로그 포맷을 간결하게 유지하기 위한 로컬 설계 결정입니다.
Likely an incorrect or invalid review comment.
batch/build.gradle.kts (1)
7-7: Observability 모듈 의존성 추가가 적절합니다.Batch 모듈에서 MDC 로깅 및 메트릭 기능을 사용할 수 있도록 observability 의존성이 추가되었습니다.
apis/build.gradle.kts (1)
8-8: Observability 모듈 의존성 추가가 적절합니다.APIs 모듈에서 MDC 로깅, 메트릭, 모니터링 기능을 사용할 수 있도록 observability 의존성이 추가되었습니다.
admin/build.gradle.kts (1)
7-7: Observability 모듈 의존성 추가가 적절합니다.Admin 모듈에서 MDC 로깅 및 메트릭 기능을 사용할 수 있도록 observability 의존성이 추가되었습니다.
observability/build.gradle.kts (2)
3-16: Observability 모듈의 의존성 구성이 적절합니다.메트릭, 모니터링, 로깅을 위한 필수 의존성들이 올바르게 포함되어 있습니다:
- Web: 필터 구현을 위한 서블릿 API 제공
- Actuator: 헬스체크 및 메트릭 엔드포인트
- Prometheus: 메트릭 수집
- Kotlin Logging: 구조화된 로깅
18-21: 라이브러리 모듈 설정이 올바릅니다.Jar는 활성화하고 BootJar는 비활성화하여 observability를 다른 모듈에서 의존할 수 있는 라이브러리로 올바르게 구성했습니다.
gateway/build.gradle.kts (2)
5-6: Observability 의존성 통합이 적절합니다.Actuator와 Prometheus 의존성을 observability 모듈로 이동시키고, 해당 모듈을 의존성으로 추가하여 중복을 제거하고 관찰성 기능을 중앙화했습니다.
14-17: 추가 정보를 수집해야 합니다. 다음을 확인하겠습니다:추가 정보가 필요합니다. 다음 항목들을 확인하겠습니다:
이제 추가 검증을 위해 다음 항목들을 확인하겠습니다:
Gateway 모듈의 BootJar 비활성화 설정이 올바르게 구현되었습니다.
BootJar를 비활성화하고 Jar를 활성화하는 구성은 Spring Boot 라이브러리 모듈을 만드는 표준 패턴입니다.
검증 결과:
- ✓ APIs 모듈이 Gateway에 의존 -
apis/build.gradle.kts에서Dependencies.Projects.GATEWAYimport 확인- ✓ 라이브러리 모듈로 올바른 구성 - 실행 가능한 über-jar를 만드는 것이 목적이 아닌 라이브러리의 경우 Spring Boot 플러그인이 불필요합니다.
- ✓ 멀티 모듈 멀티 애플리케이션 패턴에 적합 - Spring Boot의 멀티 모듈 구조는 애플리케이션의 여러 부분을 독립적으로 관리할 수 있으며 코드를 모듈화된 상태로 유지합니다.
그러나 다음 사항들을 최종 확인하시기 바랍니다:
- Admin 모듈도 Gateway를 의존성으로 포함하는지 확인
- 기존 독립 실행형 Gateway 애플리케이션(있었다면)이 제거되었는지 확인
- CI/CD 파이프라인에서 apis, admin 모듈이 개별적으로 배포되도록 구성되었는지 검토
gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt (1)
14-29: 필터 체인 순서 검증 완료 - 모든 우려사항이 올바르게 처리됨SecurityConfig의 필터 체인 설정을 확인한 결과, SecurityMdcLoggingFilter가 BearerTokenAuthenticationFilter 이후에 정확히 배치되어 있습니다 (64번 라인). 따라서
resolveUserId()메서드 실행 시점에 SecurityContext에서 인증된 JWT principal을 안정적으로 읽을 수 있습니다.코드 변경사항은 문제가 없습니다.
observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt (1)
11-18: SimpleMdcLoggingFilter의 조건부 등록 설정 필요검증 결과 원래 리뷰 의견이 타당합니다. 현재 설정의 문제점:
SimpleMdcLoggingFilter는@Component만 적용되어 조건부 등록 로직이 없음- 모든 모듈이
@ComponentScan(basePackages = ["org.yapp"])를 사용하므로, SimpleMdcLoggingFilter가 모든 모듈에서 bean으로 등록됨- SimpleMdcLoggingFilter의 JavaDoc에서 "Batch 애플리케이션이나 인증이 없는 내부 서비스"라고 명시했으나, 현재는 gateway 모듈(인증 필요)에서도 bean이 생성됨
- 웹 검색 결과에 따르면 같은 타입의 여러 Filter bean이 존재할 때 Spring에서 예외가 발생할 수 있음
권장 사항:
SimpleMdcLoggingFilter에 다음 중 하나를 적용하세요:
@ConditionalOnProperty나@Profile로 batch/unauthenticated 환경에서만 활성화- 각 모듈별
@ComponentScan범위를 제한하여 필터별로 명시적으로 등록- FilterRegistrationBean 패턴으로 변경하여 명시적 관리
gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt (3)
14-15: LGTM: Observability 모듈로의 올바른 모듈화MdcLoggingFilter를 SecurityMdcLoggingFilter로 교체하고 ActuatorProperties를 observability 모듈에서 임포트하는 변경사항이 적절합니다. 이는 관찰성 관련 기능을 별도 모듈로 분리하는 리팩토링의 일부로 보입니다.
24-24: LGTM: 생성자 파라미터 변경 적절함
MdcLoggingFilter에서SecurityMdcLoggingFilter로의 타입 변경과 변수명 변경이 일관되게 적용되었습니다.
64-64: 필터 순서 및 구현 검증 완료 - LGTM
SecurityMdcLoggingFilter가BearerTokenAuthenticationFilter다음에 올바르게 배치되어 있습니다. SecurityContext가 이미 인증 필터에 의해 채워진 후 실행되므로,SecurityContextHolder.getContext().authentication에서 JWT 주제(userId)를 안전하게 추출할 수 있습니다.검증 내용:
- ✓ 필터 체인 순서가 올바름 (BearerTokenAuthenticationFilter 이후)
- ✓
resolveUserId()가 SecurityContext에서 JWT subject 추출- ✓ JWT가 아닌 principal에 대한 폴백 처리 (
principal.toString())- ✓ 인증 없을 경우 GUEST로 처리됨
- ✓ MDC.clear()를 통한 적절한 정리
기존
MdcLoggingFilter기능이 완전히 유지되고 있습니다.
| management: | ||
| server: | ||
| port: 8081 | ||
| port: 1234 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트 프로필의 관리 포트 번호 검토 권장
테스트 환경의 management server port가 1234로 설정되어 있습니다. 이는 일반적인 관리 포트 번호(8081, 9090 등)와 다른 임의의 값으로 보입니다. 다른 서비스나 테스트와 충돌 가능성이 없는지, 또는 더 표준적인 포트 번호를 사용하는 것이 좋지 않은지 검토가 필요합니다.
🤖 Prompt for AI Agents
observability/src/main/resources/application-observability.yml lines 19-21: the
management.server.port is hard-coded to 1234 for the test profile; review and
change it to avoid collisions by either (a) switching to a standard management
port used by your infra (e.g., 8081 or 9090) or (b) making it configurable via
an environment/property (e.g., ${MANAGEMENT_PORT:0}) or profile-specific value
so tests can use a random/ephemeral port; update the YAML to remove the fixed
1234 and document the chosen approach in the profile config.
There was a problem hiding this 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
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
.github/actions/deploy-module/action.yml(1 hunks).github/workflows/dev-ci-cd.yml(1 hunks).github/workflows/prod-ci-cd.yml(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-validation
🔇 Additional comments (8)
.github/workflows/dev-ci-cd.yml (3)
14-14:IMAGE_PREFIX환경 변수의 모듈별 이미지 태그 전략 확인 필요이미지 접두사가 모든 모듈에 동일하게 적용되므로, 모듈별 이미지 구분이
deploy-module액션의${{ inputs.module }}에만 의존합니다. 배포 스크립트와 Docker 이미지 레지스트리에서 이 명명 규칙이 일관되게 사용되고 있는지 확인하세요.모듈별 이미지 태그 정책이 일관되는지 검증하기 위해 배포 스크립트를 확인해 주세요:
deploy-dev.sh파일에서IMAGE_TAG환경 변수 사용 확인- Docker Hub에
ninecraft0523/ninecraft-apis,ninecraft0523/ninecraft-batch이미지가 실제로 존재하는지 확인
81-85: "Skip if no changes" 로직이 명확하지만, matrix 스트래티지의 실행 흐름 검증 권장조건부 스킵 단계가 올바르게 구현되어 변경이 없는 모듈은 배포되지 않습니다. 다만,
matrix.changed != 'true'일 때exit 0으로 종료하므로, 실제로는 다른 모듈이 계속 배포될 수 있습니다. 이것이 의도된 동작인지 확인하세요. 만약 어떤 모듈도 변경이 없다면 전체 작업이 스킵되어야 한다면,if: needs.detect-changes.outputs.any == 'true'조건이 올바르게 작동하는지 테스트하세요.
91-109:deploy-module액션에 전달된 모든 입력이 올바르게 정의되어 있는지 검증12개 이상의 입력 파라미터가
deploy-module액션으로 전달됩니다. 특히image-prefix,image-tag-type,deploy-script등 새로운 파라미터들이 액션의 입력 정의(.github/actions/deploy-module/action.yml)와 일치하는지 확인이 필수입니다. 또한,matrix.module과matrix.port값이 실제로 배포 스크립트와 Dockerfile에서 올바르게 사용되는지 검증하세요..github/actions/deploy-module/action.yml (2)
114-147: Discord 알림 조건 로직이 올바르지만, 환경 변수 참조 일관성 확인 필요개발/프로덕션 환경별로 조건이 구분되어 알림이 전송됩니다. 다만 라인 129, 146에서
embed-color: 65280(초록색)과embed-color: 16711680(빨간색)이 하드코딩되어 있습니다. 테스트 시 색상이 올바르게 렌더링되는지 확인하세요.또한
continue-on-error: true가 라인 117, 134, 151, 167에서 사용되므로, 알림 전송 실패가 배포 결과에 영향을 주지 않습니다. 이는 의도된 동작이지만, 알림 전송 실패 로그를 모니터링할 수 있는 별도 메커니즘이 있는지 확인하세요.
96-112: SSH 액션을 통한 기밀정보 노출 위험 검증 필요현재 코드에서
appleboy/ssh-action@v1.2.2를 사용하여 secrets를 환경 변수로 내보내고 있습니다. appleboy/ssh-action v1.2.2는 자체 시크릿 마스킹 로직을 구현하지 않으며, GitHub Actions 러너가 시크릿 마스킹을 처리합니다. 그러나 문제는 script 속성에 secrets를 직접 보간할 경우 이들 값이 GitHub Actions 로그에 노출될 수 있다는 점입니다.구체적인 위험 요소:
GitHub Actions 로그 노출:
export DOCKERHUB_TOKEN="${{ inputs.dockerhub-token }}"같은 export 문이 SSH 액션에 전송되는 script의 일부로 표시되며, 이는 GitHub Actions 로그에 기록됩니다.원격 서버 로그: 배포 스크립트(deploy-dev.sh, deploy.sh)가
set -x모드로 실행되거나 환경 변수를 echo하면, 원격 서버에서 기밀정보가 기록될 수 있습니다.동적 값 마스킹 부재:
IMAGE_TAG는 동적으로 생성되므로 GitHub Actions의 자동 마스킹 대상이 아닙니다.권장사항:
- 동적으로 생성되거나 디코딩된 시크릿에 대해
::add-mask::명령어를 사용하여 GitHub Actions가 로그를 마스킹하도록 설정하세요- 원격 서버의 deploy-dev.sh 및 deploy.sh 스크립트가
set +x를 사용하고 기밀정보를 출력하지 않는지 확인하세요.github/workflows/prod-ci-cd.yml (3)
14-14: 프로덕션 환경의IMAGE_PREFIX이 개발 환경과 동일한 Docker Hub 계정을 사용하는지 검증dev-ci-cd.yml과 prod-ci-cd.yml 모두
ninecraft0523/ninecraft을 이미지 접두사로 사용합니다. 프로덕션과 개발이 동일한 Docker Hub 계정을 사용한다면 문제없지만, 다른 레지스트리나 계정을 사용해야 한다면 환경별로 다른 접두사가 필요합니다. 특히 프로덕션 이미지의 접근 권한을 더 엄격하게 관리해야 하는 경우, 별도의 레지스트리 설정이 필요할 수 있습니다.
36-55: 배포 스크립트는 리포지토리가 아닌 서버의 ~/deploy 디렉토리에 존재해야 함 — 검토 의견 재평가
.github/actions/deploy-module/action.yml분석 결과, 배포 스크립트는 저장소 내에 포함되지 않습니다. 대신 "Deploy to Server" 단계에서 다음과 같이 실행됩니다:cd ~/deploy chmod +x ./${{ inputs.deploy-script }} ./${{ inputs.deploy-script }}이는
deploy-dev.sh와deploy.sh가 각 서버(개발/프로덕션)의~/deploy디렉토리에 별도로 관리되는 서버 측 배포 스크립트임을 의미합니다. SSH를 통해 원격으로 실행되는 구조이므로, 저장소 내에 존재하지 않는 것이 정상이고 설계 의도입니다.따라서 원본 검토 의견의 검증 요청(저장소에서 스크립트 확인)은 배포 아키텍처에 대한 잘못된 가정을 바탕으로 합니다. 실제 검증이 필요하다면 각 서버의
~/deploy디렉토리에서 스크립트 존재 여부와 일관성을 확인해야 합니다.Likely an incorrect or invalid review comment.
52-54: 프로덕션 이미지 태그 전략 — 구문 유효하나 실제 릴리스 태그 형식 검증 필요라인 52-54의
type=semver,pattern={{version}}과type=raw,value=production-latest구문은 docker/metadata-action이 지원하는 유효한 Handlebars 템플릿입니다.다만 현재 저장소에는 기존 Git 릴리스 태그가 없어 실제 GitHub Release 프로세스에서 사용될 태그 형식을 검증할 수 없습니다. 향후 릴리스를 생성할 때 Git 태그가 semver 형식(예:
v1.0.0,v1.2.3-rc1)을 정확히 따르도록 주의하세요.
.github/workflows/prod-ci-cd.yml
Outdated
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - module: apis | ||
| port: 8080 | ||
| # - module: admin # TODO: Uncomment when admin module is ready | ||
| # port: 8081 | ||
| - module: batch | ||
| port: 8082 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
프로덕션 환경에서는 경로 필터가 없으므로, 모든 모듈이 항상 배포됩니다 — 의도 확인 필수
Matrix 전략이 include 섹션에서 apis(8080)과 batch(8082)를 명시하고 있으며, dev-ci-cd.yml의 path-filter와는 다르게 조건부 실행이 없습니다. Release 이벤트는 의도적으로 모든 모듈을 배포하려는 설계로 보이지만, 특정 모듈의 릴리스만 필요한 경우 대응 불가능합니다. Release workflow에서도 변경된 모듈만 배포하도록 path-filter를 추가할 것을 고려하세요.
또한 matrix fail-fast: false는 한 모듈 배포 실패 시에도 다른 모듈을 계속 배포하므로, 부분 배포 시나리오가 발생할 수 있습니다.
🤖 Prompt for AI Agents
In .github/workflows/prod-ci-cd.yml around lines 21-30, the release matrix
currently unconditionally includes apis and batch so every release deploys all
modules; update the workflow to only include changed modules by adding a
path-filter step (or use actions/changed-files / peter-evans/changed-files to
detect modified module paths) and then dynamically generate the matrix.include
from those results instead of hardcoding modules; additionally, decide whether
to set strategy.fail-fast: true (to stop remaining module jobs on first failure)
or document why false is required and keep it as-is.
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
batch/src/main/resources/application.yml (1)
28-31: 배치 모듈에 대한 서블릿 멀티파트 설정 재검토 권장
servlet.multipart.max-request-size: 30MB설정이 추가되었습니다. 배치 모듈은:
- REST 컨트롤러나 HTTP 엔드포인트를 노출하지 않음
- 멀티파트 파일 업로드 요청을 처리하지 않음
이 설정은 APIs/Admin 모듈과 같은 HTTP API 서버에서만 필요합니다. 배치 모듈에 필요한지 재검토하고, 불필요하다면 제거하는 것이 좋습니다.
일반적인 배치 모듈 특성:
- 파일 처리가 필요하면 로컬 파일시스템 또는 원격 스토리지(S3 등)에서 읽음
- HTTP 요청이 아닌 내부 로직으로 운영됨
Dockerfile (1)
36-36: 기밀 정보가 Docker 이미지에 포함되는 보안 결함 — 긴급 수정 필수라인 36의
COPY --from=build /app/secret ./secret/는 런타임 이미지에 기밀 정보를 영구적으로 포함시킵니다. 빌드 컨텍스트에.dockerignore가 없어서application-*-secret.properties와AuthKey.p8이 이미지 레이어에 저장되어 Docker 분석 도구로 노출될 수 있습니다.필수 해결 방안:
- 프로젝트 루트에
.dockerignore파일 생성:secret/
- Dockerfile 라인 36 제거 및 런타임 기밀 정보 주입으로 변경:
기밀 정보는 빌드 시점이 아닌 컨테이너 실행 시점에 주입해야 합니다. 배포 시스템(Kubernetes Secret, Docker secrets, 또는 환경 변수)에서 기밀 정보를 마운트하도록 수정하세요. 예를 들어 Docker Compose나 Kubernetes에서 볼륨 마운트나 secret 객체를 활용하면 됩니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
.github/actions/deploy-module/action.yml(1 hunks).github/workflows/dev-ci-cd.yml(1 hunks).github/workflows/prod-ci-cd.yml(1 hunks)Dockerfile(3 hunks)batch/src/main/resources/application.yml(2 hunks)
🧰 Additional context used
🪛 Checkov (3.2.334)
Dockerfile
[low] 1-46: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
[low] 1-46: Ensure that a user for the container has been created
(CKV_DOCKER_3)
⏰ 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-validation
🔇 Additional comments (7)
batch/src/main/resources/application.yml (3)
1-2: 배치 모듈의 포트 번호 변경 검증 필요서버 포트가 8080에서 8082로 변경되었습니다. 배치 모듈은 백그라운드 스케줄링 작업을 수행하며 외부 HTTP 엔드포인트를 노출하지 않으므로, 이 포트 변경의 의도를 확인하세요:
- Actuator 관리 포트 용도인지
- Docker/Kubernetes 배포 시 포트 충돌 회피 목적인지
- 인프라 설정 전체에서 일관성 있게 적용되었는지 (docker-compose.yml, Dockerfile, 로드밸런서 설정 등)
14-14:external프로필 추가 사용성 확인모든 환경(dev, prod, test) 프로필 그룹에
external프로필이 추가되었습니다. 배치 모듈이 외부 서비스 통합(알림 전송, 외부 API 호출 등)을 필요로 한다면 이는 적절합니다. 다음을 확인하세요:
application-external.yml(또는 유사한 설정 파일)이 모든 모듈에 존재하는지- 배치 모듈에서 실제로 외부 프로필 설정을 사용하는지 (예: 알림 서비스 연동)
Also applies to: 20-20, 26-26
12-12: 프로필 그룹 통합 승인 —crosscutting/observability로 마이그레이션 완료모든 환경에서
jwt프로필을crosscutting과observability프로필로 통합했습니다. 이는 이전 검토 지적사항을 올바르게 해결하고 아키텍처 요구사항을 충족합니다:
- ✅ JWT 인증 제외: 배치 모듈은 HTTP 엔드포인트를 노출하지 않으므로 인증이 불필요 (이전 리뷰 확인됨)
- ✅
crosscutting프로필: 로깅, 캐싱, 모니터링 등 횡단 관심사 포함- ✅
observability프로필: 배치 작업 모니터링, 메트릭 수집, MDC 로깅 적용- ✅
external프로필: 알림(Batch Module Dockerlizing 목표) 등 외부 서비스 통합멀티 애플리케이션 배포 기능과 일관성 있게 각 모듈의 역할에 맞춰 프로필을 구성했습니다.
Also applies to: 18-18, 24-24
Dockerfile (1)
46-46: ✅ ENTRYPOINT 개선 — 신호 처리 및 기본값 설정
exec명령 추가와${SERVER_PORT:-8080}기본값 설정이 올바르게 적용되었습니다. 이제 SIGTERM 신호가 정상적으로 Java 프로세스에 전달되어 graceful shutdown이 가능하고, SERVER_PORT 환경 변수 미설정 시에도 안전한 기본값으로 동작합니다..github/workflows/dev-ci-cd.yml (1)
17-59: ✅ 멀티 모듈 배포 감지 로직 — 잘 설계된 조건부 실행
detect-changes작업이 경로 필터를 통해 각 모듈별 변경 여부를 정확하게 감지하고,build-push-and-deploy작업의 매트릭스 전략에서 이를 활용해 필요한 모듈만 배포합니다. 특히if: matrix.changed == 'true'조건으로 불필요한 배포를 방지하고 있습니다.
observability경로가 모든 모듈 필터에 포함된 것은 관찰성 기반 시설이 런타임 동작에 직접 영향을 주므로, 버전 일관성을 유지하기 위한 의도된 설계입니다..github/actions/deploy-module/action.yml (1)
56-65: ✅ 런타임 기밀 정보 주입 — 보안 우수 사례라인 56-65에서 기밀 정보를 런타임에 동적으로 생성하고
chmod 600으로 파일 권한을 제한하는 방식은 올바른 보안 관행입니다. 다만, Dockerfile 라인 36의COPY --from=build /app/secret ./secret/부분이 이미지에 기밀을 포함시키지 않도록 반드시 수정되어야 합니다 (Dockerfile 리뷰 참고)..github/workflows/prod-ci-cd.yml (1)
21-27: 프로덕션 배포 모듈 선택성 — 의도 확인 필요현재 프로덕션 매트릭스 전략(라인 21-27)은
dev-ci-cd.yml의 경로 필터와 달리 모든 모듈(apis, batch)을 조건 없이 배포합니다. Release 이벤트의 특성상 모든 모듈을 항상 배포하려는 의도로 보이지만, 특정 모듈만 릴리스해야 하는 경우에 대응하지 못합니다.현재 설계 검토 사항:
- 의도: Release는 항상 전체 모듈을 배포하는가?
fail-fast: false는 한 모듈 배포 실패 시에도 다른 모듈을 계속 배포하는 설계인가?개발 환경처럼 선택적 배포가 필요하다면,
detect-changes작업과 경로 필터를 추가하여 변경된 모듈만 배포하도록 수정할 수 있습니다.현재 설계가 의도된 것인지 확인하고, 필요시 경로 기반 필터링을 추가하시기 바랍니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
.github/workflows/prod-ci-cd.yml (1)
21-27: 프로덕션 배포 시 변경된 모듈만 선택적으로 배포하도록 경로 필터 추가 필요현재 구현은 릴리스 이벤트 발생 시
apis와batch모듈을 항상 배포합니다.fail-fast: false는 한 모듈 배포 실패 시에도 다른 모듈을 계속 배포하므로 부분 배포 시나리오가 발생할 수 있습니다. 개발 환경(dev-ci-cd.yml)에서는 경로 필터로 조건부 실행을 하고 있는 만큼, 프로덕션에서도 변경된 모듈만 배포하도록 다음 중 하나를 고려하세요:
- 동적 매트릭스 생성:
actions/changed-files또는peter-evans/changed-files액션으로 변경된 모듈을 감지한 후 동적으로 matrix를 생성- fail-fast 동작 명확화:
fail-fast: true로 변경하여 모듈 배포 실패 시 다른 모듈 배포도 중단하거나, false인 이유를 문서화🤖 AI Agent Prompt
.github/workflows/prod-ci-cd.yml의 release 워크플로우를 수정하여: 1. actions/changed-files를 사용해 modules/ 디렉토리의 변경을 감지 2. 변경된 모듈 목록 (예: apis, batch)을 matrix.include에 동적으로 설정 3. fail-fast 전략 명확화 (true/false 선택 및 이유 문서화)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
.github/actions/deploy-module/action.yml(1 hunks).github/workflows/prod-ci-cd.yml(1 hunks)
🔇 Additional comments (3)
.github/workflows/prod-ci-cd.yml (1)
33-50: 모든 필수 입력값이 올바르게 전달되고 있습니다
deploy-module액션 호출이 올바르게 구성되어 있으며, 환경 변수, 모듈명, 도커 자격증명, 배포 스크립트 등 모든 필수 입력값이 명시적으로 전달되고 있습니다..github/actions/deploy-module/action.yml (2)
97-112: 원격 서버 배포 단계가 올바르게 구성됨SSH 액션을 통한 배포 구성이 적절합니다. 환경 변수가 원격 배포 스크립트에 명시적으로 전달되고 있으며, 권한 설정도 올바릅니다(
chmod +x).
83-95: docker/build-push-action의secrets매개변수 사용을 검토해야 합니다.
docker/build-push-action@v6이secrets입력을 지원하는 것은 맞습니다. 그러나 이 제안을 적용하려면 추가 고려사항이 있습니다:
- 현재 워크플로우는 첫 번째 단계(11-14줄)에서 여전히
./secret/디렉토리에 파일을 생성합니다.docker/build-push-action의secrets매개변수를 사용하려면 Dockerfile이RUN --mount=type=secret,id=...구문을 지원하도록 수정되어야 합니다.- 제안된 방식이 현재 파일 생성 단계를 대체하는지, 아니면 추가되는지 명확히 해야 합니다.
다음을 검증하세요:
- Dockerfile이 BuildKit 시크릿 마운트 구문을 지원하는가?
- 파일 생성 단계(11-14줄)를 제거하고
secrets매개변수로만 진행해야 하는가?- 전체 구현의 일관성을 확인하고 필요시 Dockerfile도 함께 수정하는가?
| deploy-script: | ||
| description: 'Deploy script name (deploy-dev.sh or deploy-prod.sh)' | ||
| required: true | ||
| default: 'deploy-dev.sh' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
deploy-script 입력 설명 명확화 제안
라인 51의 기본값 'deploy-dev.sh'는 설명과 실제 사용이 혼동을 줄 수 있습니다. 의존 워크플로우(dev-ci-cd.yml, prod-ci-cd.yml)에서 명시적으로 deploy-dev.sh 또는 deploy-prod.sh를 전달하고 있으므로, 설명을 다음과 같이 개선하면 좋습니다:
deploy-script:
description: 'Deploy script name (deploy-dev.sh for dev, deploy-prod.sh for prod) - 워크플로우에서 명시적으로 전달됨'
required: true
default: 'deploy-dev.sh'이렇게 하면 향후 독자가 기본값이 개발 환경 용도임을 알 수 있고, 워크플로우에서 의도적으로 override됨을 이해할 수 있습니다.
🤖 Prompt for AI Agents
.github/actions/deploy-module/action.yml around lines 48 to 51: the input
description for `deploy-script` is ambiguous given the default value
'deploy-dev.sh' and workflows explicitly pass either deploy-dev.sh or
deploy-prod.sh; update the description to state that the default is for dev and
that workflows explicitly override it (e.g., "Deploy script name (deploy-dev.sh
for dev, deploy-prod.sh for prod) - workflows explicitly override this value"),
keep required: true and default: 'deploy-dev.sh' unchanged.
| - name: Inject application-secret.properties from Secrets | ||
| shell: bash | ||
| run: | | ||
| mkdir -p ./secret | ||
| echo "$SECRET_CONTENT" > ./secret/application-${{ inputs.environment }}-secret.properties | ||
| echo "$APPLE_KEY_CONTENT" > ./secret/AuthKey.p8 | ||
| chmod 600 ./secret/* | ||
| env: | ||
| SECRET_CONTENT: ${{ inputs.secret-properties }} | ||
| APPLE_KEY_CONTENT: ${{ inputs.apple-auth-key }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🔴 기밀정보 보안: 런타임 시크릿 마운트로 마이그레이션 필수
현재 구현은 GitHub Actions 런너에 기밀정보 파일을 작성한 후 Docker 빌드 컨텍스트에 포함시켜 최종 이미지 레이어에 영구적으로 저장합니다. 빌드 인자와 환경 변수는 최종 이미지에 지속되므로 부적절하며, 대신 시크릿 마운트나 SSH 마운트를 사용해야 합니다.
필수 수정 사항:
docker/build-push-action@v6단계에 secrets 매개변수 추가 (라인 83-95):
secrets: |
"application_secret=${{ inputs.secret-properties }}"
"apple_auth_key=${{ inputs.apple-auth-key }}"-
Dockerfile 업데이트:
RUN --mount=type=secret플래그를 사용하여 시크릿을 빌드 컨테이너 내/run/secrets/<id>경로에 임시로 마운트합니다. -
기밀정보 파일 작성 단계 제거 (라인 56-65): 런타임에 시크릿을 주입하도록 변경하면,
./secret디렉토리 생성이 불필요해집니다.
BuildKit은 tmpfs를 사용하여 시크릿을 임시 파일로 마운트하므로 이미지에 시크릿이 남지 않습니다.
시크릿 보안 문제를 검증하기 위해 필요한 정보를 수집하겠습니다.
---
🏁 Script executed:
```shell
#!/bin/bash
# Find and examine the action.yml file
echo "=== Examining action.yml ==="
find . -name "action.yml" -path "*deploy-module*" -type f | head -5
Length of output: 71
🏁 Script executed:
#!/bin/bash
# Look for Dockerfile in the repository
echo "=== Finding Dockerfile ==="
fd -t f -i dockerfile | head -10Length of output: 37
🏁 Script executed:
#!/bin/bash
# Search for docker/build-push-action usage
echo "=== Searching for docker/build-push-action usage ==="
rg -n "build-push-action" --type yaml -A 10 -B 2 | head -50Length of output: 52
🏁 Script executed:
#!/bin/bash
# Check for secrets parameter in build-push-action or any secret handling
echo "=== Checking for secrets parameter in workflows ==="
rg -n "secrets:" --type yaml -A 5 -B 5 | head -80Length of output: 51
🏁 Script executed:
#!/bin/bash
# Examine the specific action.yml file
echo "=== Full action.yml content ==="
cat ".github/actions/deploy-module/action.yml" 2>/dev/null || echo "File not found at standard path"Length of output: 6582
Dockerfile를 검증하여 시크릿 처리 방식을 확인하겠습니다.
---
🏁 Script executed:
```shell
#!/bin/bash
# Examine the Dockerfile
echo "=== Examining Dockerfile ==="
cat ./Dockerfile
Length of output: 1310
🏁 Script executed:
#!/bin/bash
# Search for secret-related patterns in Dockerfile
echo "=== Checking for ./secret references in Dockerfile ==="
grep -n "secret\|SECRET" ./Dockerfile || echo "No secret references found"Length of output: 124
🏁 Script executed:
#!/bin/bash
# Check for RUN --mount=type=secret pattern
echo "=== Checking for secret mount patterns ==="
grep -n "mount=type=secret" ./Dockerfile || echo "No secret mount patterns found"Length of output: 73
🏁 Script executed:
#!/bin/bash
# Check if .dockerignore exists and what it contains
echo "=== Checking .dockerignore ==="
cat ./.dockerignore 2>/dev/null || echo ".dockerignore not found"Length of output: 54
🔴 중대 보안 이슈: 시크릿이 Docker 이미지 레이어에 영구 저장됨
현재 구현은 시크릿 데이터가 최종 Docker 이미지에 영구적으로 저장되는 심각한 취약점을 가지고 있습니다. 검증 결과:
- action.yml (56-65줄):
./secret디렉토리에 시크릿 파일 작성 - Dockerfile (36줄):
COPY --from=build /app/secret ./secret/명령으로 시크릿을 최종 이미지로 복사 - docker/build-push-action@v6 (83-95줄):
secrets파라미터 부재 → 시크릿이 빌드 컨텍스트에 노출 - .dockerignore: 존재하지 않음 →
./secret폴더 제외 불가능
결과적으로 시크릿이 Docker Hub에 푸시된 이미지 레이어에 평문으로 저장됩니다.
필수 수정 사항:
-
docker/build-push-action@v6에 secrets 파라미터 추가 (83-95줄):
with: context: . file: ./Dockerfile platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha,scope=${{ inputs.module }} cache-to: type=gha,mode=max,scope=${{ inputs.module }} build-args: | MODULE=${{ inputs.module }} secrets: | "application_secret=${{ inputs.secret-properties }}" "apple_auth_key=${{ inputs.apple-auth-key }}"
-
Dockerfile 업데이트 (36줄 제거 및 시크릿 마운트 사용):
- 기존:
COPY --from=build /app/secret ./secret/제거 - 변경:
RUN --mount=type=secret=application_secret --mount=type=secret=apple_auth_key사용하여 런타임에/run/secrets/경로에서 접근
- 기존:
-
action.yml의 시크릿 파일 작성 단계 제거 또는 조건부 처리 (56-65줄):
- BuildKit 시크릿 마운트 사용 시
./secret디렉토리 생성 불필요
- BuildKit 시크릿 마운트 사용 시
| - name: Send Discord notification on success (Development) | ||
| uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645 | ||
| if: success() && inputs.environment == 'dev' | ||
| continue-on-error: true | ||
| shell: bash | ||
| with: | ||
| webhook-url: ${{ inputs.discord-webhook-url }} | ||
| embed-title: "✅ [${{ github.repository }}] Development Deploy Succeeded - ${{ inputs.module }}" | ||
| embed-description: | | ||
| **Module**: `${{ inputs.module }}` | ||
| **Commit**: `${{ github.sha }}` | ||
| **Author**: `${{ github.actor }}` | ||
| **Message**: `${{ github.event.head_commit.message }}` | ||
| [View Committed Changes](https://github.com/${{ github.repository }}/commit/${{ github.sha }}) | ||
| embed-color: 65280 | ||
|
|
||
| - name: Send Discord notification on success (Production) | ||
| uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645 | ||
| if: success() && inputs.environment == 'prod' | ||
| continue-on-error: true | ||
| shell: bash | ||
| with: | ||
| webhook-url: ${{ inputs.discord-webhook-url }} | ||
| content: "🚀 **Production Deploy Succeeded!**" | ||
| embed-title: "✅ [${{ github.repository }}] Production Deploy Succeeded - ${{ inputs.module }}" | ||
| embed-description: | | ||
| **Module**: `${{ inputs.module }}` | ||
| **Deployed by**: `${{ github.actor }}` | ||
| The new version has been successfully deployed to production. | ||
| [View Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ||
| embed-color: 65280 | ||
|
|
||
| - name: Send Discord notification on failure (Development) | ||
| uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645 | ||
| if: failure() && inputs.environment == 'dev' | ||
| continue-on-error: true | ||
| shell: bash | ||
| with: | ||
| webhook-url: ${{ inputs.discord-webhook-url }} | ||
| embed-title: "❌ [${{ github.repository }}] Development Deploy Failed - ${{ inputs.module }}" | ||
| embed-description: | | ||
| **Module**: `${{ inputs.module }}` | ||
| **Commit**: `${{ github.sha }}` | ||
| **Author**: `${{ github.actor }}` | ||
| An error occurred during the workflow execution. | ||
| [View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ||
| embed-color: 16711680 | ||
|
|
||
| - name: Send Discord notification on failure (Production) | ||
| uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645 | ||
| if: failure() && inputs.environment == 'prod' | ||
| continue-on-error: true | ||
| shell: bash | ||
| with: | ||
| webhook-url: ${{ inputs.discord-webhook-url }} | ||
| content: "🚨 **Production Deploy Failed!**" | ||
| embed-title: "❌ [${{ github.repository }}] Production Deploy Failed - ${{ inputs.module }}" | ||
| embed-description: | | ||
| **Module**: `${{ inputs.module }}` | ||
| **Deployed by**: `${{ github.actor }}` | ||
| An error occurred during the production deployment workflow. | ||
| [View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ||
| embed-color: 16711680 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Discord 알림 구성이 적절하며 선택적 개선 가능
환경별 성공/실패 알림이 명확하게 구분되어 있고, continue-on-error: true로 설정되어 웹훅 실패가 배포 프로세스에 영향을 주지 않습니다.
선택사항: 코드 중복 감소
네 개의 Discord 알림 단계에서 상당한 중복이 있습니다. 조건문을 활용하여 단일 단계로 통합할 수 있습니다:
- name: Send Discord notification
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
if: always()
continue-on-error: true
shell: bash
with:
webhook-url: ${{ inputs.discord-webhook-url }}
content: ${{ (success() && inputs.environment == 'prod') && '🚀 **Production Deploy Succeeded!**' || (failure() && inputs.environment == 'prod') && '🚨 **Production Deploy Failed!**' || '' }}
embed-title: |
${{ success() && '✅' || '❌' }} [${{ github.repository }}]
${{ inputs.environment == 'prod' && 'Production' || 'Development' }} Deploy
${{ success() && 'Succeeded' || 'Failed' }} - ${{ inputs.module }}
embed-description: |
**Module**: `${{ inputs.module }}`
...
embed-color: ${{ success() && 65280 || 16711680 }}그러나 현재 구현도 명확하고 유지보수하기 좋습니다.
🤖 Prompt for AI Agents
.github/actions/deploy-module/action.yml lines 114-176: the four nearly
identical Discord webhook steps (dev/prod × success/failure) can be consolidated
to remove duplication; replace them with a single step (keep continue-on-error:
true) that runs always() and sets webhook inputs (content, embed-title,
embed-description, embed-color) using GitHub Actions expressions that branch on
success()/failure() and inputs.environment to produce the correct text/color for
dev vs prod and success vs failure, preserving all existing fields (module,
sha/actor/run links) and ensuring empty content when not needed.
|


🔗 관련 이슈
📘 작업 유형
📙 작업 내역
📋 Summary
apis,batch)의 CI/CD 파이프라인을 Matrix strategy 기반으로 리팩토링하여 Dev 환경 병렬 배포, Prod 환경 순차 배포를 구현하고, Blue-Green 배포 안정성을 강화했습니다.🎯 Motivation
기존 문제점
domain,infra,global-utils,observability) 변경 시 모든 모듈을 순차적으로 배포하여 배포 시간이 길어짐해결 방안
dorny/paths-filter로 변경된 모듈만 선택적 배포 (Dev)🚀 Key Changes
1. Dev 환경 - 병렬 배포 (
.github/workflows/dev-ci-cd.yml)변경 감지 기반 선택적 배포
Matrix 병렬 배포
배포 시나리오:
apis/**변경 →apis만 배포batch/**변경 →batch만 배포domain/**변경 →apis+batch병렬 배포2. Prod 환경 - 순차 배포 (
.github/workflows/prod-ci-cd.yml)Semantic Versioning + Release 트리거
Matrix 순차 배포로 버전 일관성 보장
주요 특징:
max-parallel: 1로apis→batch순서 보장v1.2.3) 사용3. Blue-Green 배포 스크립트
Dev 배포 스크립트 (
deploy-dev.sh)주요 로직 (
deploy-dev.sh:389-534):docker compose up --no-deps)rollback_deployment())Prod 배포 스크립트 (
deploy-prod.sh)# Dev 대비 추가 기능 + Pre-deployment checks (디스크 공간, Docker 데몬) + 배포 메타데이터 JSON 추적 (timestamp, version, status) + RDS 연동 (USE_RDS=true) + Post-deployment validation (컨테이너 상태, 메모리 사용량) + 강화된 타임아웃 (MAX_RETRIES=60, sleep 5초) + Nginx 설정 백업 (타임스탬프별 디렉토리) + 최종 시스템 상태 리포트메타데이터 예시 (
deploy-prod.sh:216-228):{ "deployment_timestamp": "20250125_143022", "module": "apis", "release_version": "v1.2.3", "image_tag": "ninecraft0523/ninecraft-apis:1.2.3", "container": "apis-green", "deployment_type": "BLUE_TO_GREEN", "status": "SUCCESS", "completion_timestamp": "20250125_143245" }4. 재사용 가능한 배포 액션 (
.github/actions/deploy-module/action.yml)모듈별 공통 배포 프로세스를 단일 액션으로 추상화:
📊 Architecture Diagram
flowchart TB %% ====================== %% GitHub Actions %% ====================== subgraph GA["GitHub Actions Workflow"] direction LR %% -------- Dev -------- subgraph DEV["Dev Environment (Parallel Deployment)"] direction TB D1["Detect Changes (paths-filter)"] D2["Matrix Strategy (fail-fast: false)"] subgraph DEV_MTX["Services"] direction LR D_APIS["apis (8080)"] D_BATCH["batch (8082)"] end D1 --> D2 --> DEV_MTX D_APIS -->|"Blue-Green Deploy"| DEV_DEPLOY1["Deploy"] D_BATCH -->|"Blue-Green Deploy"| DEV_DEPLOY2["Deploy"] end %% -------- Prod -------- subgraph PROD["Prod Environment (Sequential Deployment)"] direction TB P1["Release Published (Semantic Version)"] P2["Matrix Strategy (max-parallel: 1, fail-fast: true)"] P_APIS["apis"] P_BATCH["batch"] P1 --> P2 P2 --> P_APIS -->|"Blue-Green Deploy"| P_BATCH end end %% ====================== %% Server %% ====================== subgraph EC2["Server (EC2)"] direction TB subgraph NGINX["Nginx (Reverse Proxy: apis only)"] direction TB N1["Client Request"] N_APIS_B["apis-blue :8080"] N_APIS_G["apis-green :8080"] N1 --> N_APIS_B N1 --> N_APIS_G end B_BLUE["batch-blue :8082"] B_GREEN["batch-green :8082"] DB["MySQL / Redis"] end %% ====================== %% Connections %% ====================== DEV --> EC2 PROD --> EC2 %% ====================== %% Styles (AREA FOCUS) %% ====================== classDef devArea fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px classDef prodArea fill:#FCE4EC,stroke:#C2185B,stroke-width:2px classDef infraArea fill:#ECEFF1,stroke:#455A64,stroke-width:2px classDef neutral fill:#FFFFFF,stroke:#B0BEC5 %% Apply area styles class DEV devArea class PROD prodArea class EC2,NGINX infraArea %% Neutral nodes class D1,D2,DEV_MTX,D_APIS,D_BATCH,DEV_DEPLOY1,DEV_DEPLOY2 neutral class P1,P2,P_APIS,P_BATCH neutral class N1,N_APIS_B,N_APIS_G,B_BLUE,B_GREEN,DB neutral🔍 Technical Details
Actuator 포트 매핑
병렬 배포 시 포트 충돌 방지를 위한 actuator 포트 분리를 진행했습니다.
헬스체크 전략
Dev & Prod (apis)
http://localhost:${ACTUATOR_PORT}/actuator/health(컨테이너 내부)https://api.{domain}/actuator/health(Nginx를 통한 외부)Dev & Prod (batch)
http://localhost:${ACTUATOR_PORT}/actuator/health의존성 서비스 헬스체크
질문 1 답변: 의존성 서비스(MySQL, Redis)는 모든 모듈 배포 시 체크합니다.
domain모듈이 공유 엔티티를 포함하므로apis와batch모두 DB 연결 필요--no-deps플래그로 공유 서비스 재시작 방지deploy-dev.sh:210-250)질문 2 답변: Actuator 포트 충돌 없습니다.
롤백 메커니즘
자동 롤백 트리거
nginx -t)롤백 프로세스 (
deploy-dev.sh:330-380)ROLLBACK_COMPLETED)📈 Performance & Reliability Improvements
Dev 환경
fail-fast: false로 한 모듈 실패해도 다른 모듈 배포 계속Prod 환경
max-parallel: 1로 순차 배포,fail-fast: true로 즉시 중단공통
📝 Migration Guide
기존 워크플로우 대비 변경사항
develop브랜치 푸시 시 변경 감지 후 선택적 병렬 배포~/deploy/deploy-dev.sh,~/deploy/deploy-prod.sh위치 확인 필요서버 설정 확인사항
📚 Additional Notes
Future Improvements
🎨 스크린샷 또는 시연 영상 (선택)
✅ PR 체크리스트
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
새로운 기능
버그 수정/개선
리팩토링
기타