Skip to content

Conversation

@Hexeong
Copy link
Contributor

@Hexeong Hexeong commented Nov 4, 2025

관련 이슈

작업 내용

  • 기존 CD 스크립트는 jar 파일을 포함, nginxalloy 등의 설정 파일을 ssh copy(scp) 명령으로 전송 이후, 원격 호스트에서 build하고 docker-compose를 실행하는 방식을 사용해왔습니다.
  • 해당 리팩토링에서는 GHCR(Github Container Registry)를 사용하여 Github Action에서 이미지 빌드 이후, GHCR로 push/원격호스트에서 pull하는 방식으로 docker-compose를 실행하도록 리팩토링했습니다.
  • 해당 과정에서 테스트를 해본 결과, 기존 CD process는 평균 3분이었지만, 현재는 평균 2분 15초25% 단축시킨 결과가 나왔습니다. (테스트 결과는 아래 첨부파일에 올려두겠습니다.)
  • 또한, 이미지에 대한 버전 관리를 위해 현재 이미지:날짜-시간 방식으로 태그가 되어 있는 상태입니다. 하지만 prod 인스턴스는 릴리즈 버전으로 관리될 수 있기에 여러분의 의견을 듣고 내용 반영하여 추가 commit 올리겠습니다!
  • 추가적으로 GHCR과 dev 인스턴스에 대한 최신 이미지 5개 유지로 이미지 버전 관리를 도입했습니다.

특이 사항

  1. 기존 jar파일을 container로 만들기 위해 base imageopenjdk:17-jdk를 사용해왔지만, 해당 base image가 deprecated되어 공식 대체 이미지인 eclipse-temurin:17-jdk로 변경하였습니다.

리뷰 요구사항 (선택)

  • dev 인스턴스 목적이 개발용이기에, 많은 image가 생성되어 버전으로 관리되지 못할 것 같아 현재 solid-connection-dev 이미지의 tag를 날짜-시간으로 설정해두었습니다.
  • 하지만, prod의 경우 release 버전으로 이미지가 관리될 수 있다 보니, 이미지 태그에 대한 내용을 스크립트 상에 하드코딩할 수 없는 상태입니다.
  • 이에 생각되는 대안으로 다음 2가지를 가져왔는데, 여러분의 의견을 듣고 싶습니다!
    1. lastet로 prod-cd.yml을 하드코딩하여 이미지를 태깅한다.
    2. 버전 릴리스에 대한 Code Deploy를 자동이 아닌 수동 실행으로 변경한다. 수동 배포시 version에 대해 입력이 가능하기에 이미지의 태그를 배포하는 버전으로 지정할 수 있습니다.

첨부파일(테스트 결과)

image

@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

Walkthrough

1.  GitHub Actions 워크플로우 전환.
- 로컬 파일 전송·SSH 기반 배포에서 Docker Buildx로 이미지를 빌드·캐시·GHCR로 푸시하고 원격에서 이미지를 풀해 재시작하는 흐름으로 전환되었습니다.
2.  이미지 메타데이터 생성 및 노출.
- 워크플로우 단계에서 image_name과 image_tag를 계산해 후속 단계와 원격에서 사용하도록 노출합니다.
3.  Docker 중심 빌드·푸시 및 캐시 적용.
- docker/build-push-action@v5와 buildx 캐시를 사용해 이미지 빌드·푸시·캐싱이 추가되었습니다.
4.  원격 배포 통합 스크립트.
- 여러 SCP/SSH 단계가 단일 파일 전송 후 인라인 SSH 스크립트로 통합되어 이미지 풀, nginx 교체·리로드, docker-compose 재시작 및 호스트 측 이미지 정리를 수행합니다.
5.  docker-compose 이미지 소스 변경.
- dev/prod compose 파일의 일부 서비스가 로컬 빌드 대신 ghcr.io의 사전 빌드된 이미지 참조로 변경되었습니다.
6.  Dockerfile 및 서브모듈 변경.
- 베이스 이미지를 openjdk:17-jdk에서 eclipse-temurin:17-jdk로 변경했고 서브모듈 포인터만 업데이트되었습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • 주의할 파일/영역:
    • .github/workflows/dev-cd.yml 및 .github/workflows/prod-cd.yml의 권한, buildx 및 캐시 설정, GHCR 로그인 흐름.
    • image_name/image_tag 생성 로직과 워크플로우 간 변수 전달 경로.
    • 인라인 SSH 스크립트의 에러 처리, 권한·토큰 사용, nginx 교체·reload 및 docker-compose 업데이트 로직.
    • docker-compose.dev.yml 및 docker-compose.prod.yml에서 IMAGE_TAG 기본값과 환경 변수 주입 동작.
    • Dockerfile의 베이스 이미지 변경에 따른 런타임 호환성 및 빌드 결과 검증.

Suggested reviewers

  • wibaek

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 제목 'refactor: CD 성능 개선'은 변경사항의 핵심을 명확히 전달하며, PR의 주요 목표인 CD 시간 단축을 잘 요약하고 있습니다.
Description check ✅ Passed PR 설명이 저장소의 템플릿 구조를 따르고 있으며, 관련 이슈, 작업 내용, 특이 사항, 리뷰 요구사항을 모두 포함하고 있습니다.
Linked Issues check ✅ Passed PR은 #529의 주요 목표인 CD 시간 단축 및 서버 부하 감소를 달성하기 위해 GHCR 기반의 Docker 이미지 빌드/푸시/풀 방식을 적용했으며, 테스트 결과 25% 성능 개선을 달성했습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #529 목표 범위 내입니다. CD 워크플로우 개선(dev-cd.yml, prod-cd.yml), Dockerfile 베이스 이미지 업데이트, docker-compose 파일 수정, 서브모듈 업데이트가 모두 배포 프로세스 개선과 직접 관련이 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b57e74 and 7ae94b1.

📒 Files selected for processing (2)
  • .github/workflows/dev-cd.yml (2 hunks)
  • .github/workflows/prod-cd.yml (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T01:55:03.827Z
Learnt from: Hexeong
Repo: solid-connection/solid-connect-server PR: 552
File: .github/workflows/prod-cd.yml:40-45
Timestamp: 2025-11-10T01:55:03.827Z
Learning: GHCR (GitHub Container Registry)에서는 docker login 시 사용자명의 대소문자를 구분하지 않으며, 레지스트리 경로명은 항상 소문자로 정규화됩니다. 따라서 `github.repository_owner`를 그대로 사용해도 이미지 경로와의 대소문자 불일치 문제가 발생하지 않습니다.

Applied to files:

  • .github/workflows/prod-cd.yml
  • .github/workflows/dev-cd.yml
🔇 Additional comments (13)
.github/workflows/prod-cd.yml (8)

11-13: 권한 설정이 올바르게 구성되었습니다.

GHCR에 이미지를 푸시하기 위해 packages: write 권한이 추가되었네요. Gradle 빌드와 Docker 이미지 관리 단계에 필요한 권한이 모두 포함되어 있습니다.


22-33: Java/Gradle 설정이 일관되게 정리되었습니다.

dev 워크플로우와 동일하게 Temurin 분포로 통일했고, bootJar 타스크로 JAR을 빌드하는 구조가 깔끔합니다. 다음 항목들이 잘 구성되어 있습니다:

  1. setup-java로 JDK 17 설정
  2. setup-gradle로 Gradle 액션 활용
  3. Gradle wrapper 실행 권한 부여
  4. bootJar 태스크로 최적화된 JAR 생성

35-54: Docker 빌드 및 메타데이터 생성 로직이 체계적입니다.

Docker Buildx 설정, GHCR 로그인, 이미지 메타데이터 생성이 순서대로 잘 정렬되어 있습니다. 다음 구성을 확인했습니다:

  1. docker/setup-buildx-action@v3로 arm64 플랫폼 지원
  2. GHCR 로그인 (대소문자 처리는 GHCR 측에서 자동 정규화됨 — 레깅 학습 확인 ✅)
  3. 이미지 이름과 태그를 워크플로우 출력으로 노출
    • IMAGE_TAG: 날짜-시간 형식 (%Y%m%d-%H%M%S)
    • 이 값들이 이후 단계에서 참조되도록 설계됨

56-65: Docker 이미지 빌드 및 캐싱 전략이 효율적입니다.

docker/build-push-action@v5에서 다음이 구현되었습니다:

  1. arm64 플랫폼 지정으로 프로덕션 환경 호환성 확보
  2. 레지스트리 캐시(buildcache 태그) 활용으로 반복 빌드 가속화
  3. 이미지 메타데이터의 출력값을 활용한 동적 태그 지정

CD 시간 단축이라는 PR 목표에 부합합니다.


76-86: 설정 파일 전송이 최적화되었습니다.

기존의 여러 SCP 단계를 단일 명령어로 통합했습니다. 다음 개선사항이 확인됩니다:

  1. StrictHostKeyChecking=no 문법 수정 ✅ (과거 이슈 반영)
  2. docker-compose.prod.yml, nginx 설정, alloy 설정만 전송
  3. 불필요한 JAR 및 중간 산물 제외로 네트워크 부하 경감

이전 리뷰에서 지적한 SSH 옵션 문법 오류가 모두 수정되었습니다. 👍


68-73: GitHub App 토큰 생성이 추가되었으나, 권한 확인이 필요합니다.

GitHub App 토큰을 생성하고 (Line 70) 이를 원격 GHCR 로그인에 사용합니다 (Line 106). 다만 다음을 확인해야 합니다:

  • GitHub App의 권한 설정: 생성된 토큰이 GHCR에 대한 read:packages 권한을 가지고 있는지 확인 필요
  • 토큰 만료 시간: GitHub App 토큰의 유효 기간이 배포 완료까지 충분한지 확인

기존의 secrets.GITHUB_TOKEN 대신 App 토큰을 사용하는 이유가 있다면 (예: 세밀한 권한 제어), 해당 설정이 정확히 구성되어 있는지 검증해 주세요.


98-138: 원격 배포 스크립트가 잘 구조화되어 있습니다.

인라인 SSH 스크립트에서 다음 단계들이 순서대로 실행됩니다:

  1. 환경변수 설정 (Lines 101-103): 이미지 이름과 태그를 동적으로 조합
  2. GHCR 로그인 (Line 106): 이미지 pull을 위한 인증
  3. 이미지 Pull (Line 110): 워크플로우에서 빌드한 이미지 다운로드
  4. Nginx 설정 적용 (Lines 113-120): 설정 파일 이동, 검증, 재로드
  5. Docker Compose 재시작 (Lines 123-131): 안정성을 위해 stop → down → up 순서로 실행
  6. 호스트 측 정리 (Lines 134-135): 사용하지 않는 이미지 제거

set -e로 에러 발생 시 즉시 중단되도록 설정되어 있어 안정성이 높습니다. ✅

다만 다음 항목들을 검증해 주세요:

1. 변수 인용(Quoting) 강화
Line 102에서 IMAGE_TAG_ONLY 변수가 인용되지 않았습니다. 타임스탬프(20231115-143022)는 특수문자를 포함하지 않지만, 안정성을 위해 인용하는 것이 좋습니다.

2. docker-compose.prod.yml 변수 참조 일관성
Line 131에서 환경변수 IMAGE_TAG를 전달합니다:

OWNER_LOWERCASE=$OWNER_LOWERCASE IMAGE_TAG=$IMAGE_TAG_ONLY docker compose ...

docker-compose.prod.yml 파일에서 ${IMAGE_TAG}${OWNER_LOWERCASE}를 참조하는지 확인해 주세요.

3. 원격 디렉토리 구조 검증
스크립트는 /home/${{ secrets.USERNAME }}/solid-connection-prod 디렉토리가 이미 존재한다고 가정합니다. 배포 초기에 이 디렉토리가 생성되는 프로세스가 있는지 확인이 필요합니다.


141-151: GHCR 이미지 보존 정책이 명확하게 설정되었습니다.

과거 리뷰에서 지적된 이미지명 불일치(solid-connection-prod vs solid-connection-server)가 수정되었습니다. ✅

현재 설정:

  1. 이미지명: solid-connection-server (실제 푸시 이미지명과 일치)
  2. 보존 개수: 최신 5개 유지
  3. 정리 대상: 태그 없는 이미지 및 7일 이상 된 이미지
  4. 조직 범위: 레지스트리 정책이 조직 수준에서 적용

dev-cd.yml의 dev 이미지 정리 정책과 일관되게 구성되어 있습니다. 프로덕션 환경에서 필요한 이미지 개수(현재 5개)가 충분한지 팀과 확인하면 좋겠습니다.

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

11-33: ✅ 권한 및 기본 설정이 올바릅니다.

다음 개선사항들이 잘 적용되었습니다:

  1. packages: write 권한 추가 (Line 13)

    • GHCR 푸시에 필요한 최소 권한으로 설정됨
  2. Gradle 공식 Action 사용 (Line 29)

    • gradle/actions/setup-gradle@v3로 통합되어 유지보수성 향상
  3. 의존성 명확화

    • JDK 17(temurin) + Gradle이 명시적으로 설정됨

35-55: ✅ Docker 빌드 파이프라인이 체계적으로 구성되었습니다.

세 가지 핵심 단계가 명확히 분리되어 있습니다:

  1. Buildx 및 GHCR 인증 (Lines 36-45)

    • linux/arm64 플랫폼: dev 환경이 ARM 기반이므로 정확한 선택
    • GitHub App 토큰으로 GHCR 로그인: 기존 GITHUB_TOKEN보다 보안 우수
  2. 이미지 메타데이터 사전 정의 (Lines 49-55)

    • OWNER_LOWERCASE: 레지스트리 경로명 소문자 정규화 (GHCR 정책 준수)
    • 태그 형식 %Y%m%d-%H%M%S: 정렬 가능하고 실행마다 고유함
    • $GITHUB_OUTPUT로 후속 단계에 노출: 변수 재계산 제거 가능

선택 사항: 플랫폼을 linux/amd64,linux/arm64로 확장하면 더 다양한 환경을 지원할 수 있습니다. (dev 전용이므로 현재 설정도 충분함)


57-67: ✅ Docker 빌드 및 푸시 단계가 최적화되었습니다.

캐시 전략이 CD 시간 단축에 기여합니다:

  1. Registry 기반 캐시 (Lines 66-67)

    • buildx 캐시를 GHCR에 저장하여 재빌드 시 레이어 재사용
    • 원본 PR 목표인 "CD 시간 약 25% 단축"에 직접 기여
  2. 멀티스테이지 빌드 효율화

    • 이전 방식(원격 빌드): 매번 처음부터 빌드
    • 현재 방식(GitHub Actions + 캐시): 변경된 레이어만 재빌드

69-140: ✅ SSH 배포 스크립트가 명확하고 안전하게 구현되었습니다.

세 가지 단계로 정리한 배포 프로세스가 효율적입니다:

  1. GitHub App 토큰 기반 인증 (Lines 70-75)

    • 머신 사용자 / 특정 개인 계정 종속성 제거
    • 기존 피드백(lsy1307 제안) 반영
  2. 최소한의 파일만 전송 (Lines 78-88)

    • docker-compose.dev.yml, nginx.dev.conf, config.alloy만 SCP
    • JAR 파일 전송 제거 → 네트워크 효율화
  3. SSH 배포 자동화 (Lines 91-140)

    • set -e (Line 97): 오류 시 즉시 중단 → 부분 배포 방지
    • GHCR 이미지 Pull (Line 109): 사전 빌드된 이미지 사용
    • Nginx 설정 재적용 (Lines 117-119): 단일 다운타임 내 완료
    • Docker Compose 재시작 (Line 124): IMAGE_TAG 환경 변수 전달
    • 이미지 정리 (Lines 127-137):
      docker images ... | sort -r | tail -n +6 | xargs -I {} docker rmi ...
      → 최신 5개 이미지만 유지, 오래된 태그 자동 삭제

참고: 이전 리뷰 피드백이 잘 반영되었습니다.

  • OWNER_LOWERCASE 변수를 SSH 컨텍스트에서 재계산하는 이유: GitHub Actions 변수(${{ }})는 SSH 쉘 진입 시점에 이미 평가되므로, 쉘 내에서 재계산하여 변수 투명성 확보

142-152: ✅ GHCR 이미지 정리 정책이 체계적으로 구성되었습니다.

호스트 정리(Lines 132-137)와 GHCR 정리(Lines 143-152)가 일관된 보존 정책을 유지합니다:

  1. 일관된 보존 정책

    • 호스트: 최신 5개 이미지 유지
    • GHCR: 최신 5개 태그 유지 (keep-n-tags: 5)
  2. 저장소 효율성

    • delete-untagged: true: 댕글링 이미지 자동 삭제
    • cut-off: '7 days ago UTC': 7일 이상 오래된 이미지 고려
    • 기존 GHCR 무료 요금제 용량 문제(500MB) 완화

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

@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: 3

🧹 Nitpick comments (2)
.github/workflows/prod-cd.yml (1)

82-130: 원격 배포 SSH 스크립트의 복원력 개선이 필요합니다.

현재 SSH 스크립트는 여러 단계를 순차적으로 실행하며 set -e로 에러 시 중단하도록 설정되어 있습니다. 그러나 다음과 같은 시나리오에서 개선이 필요합니다:

상황별 개선 사항:

  1. docker pull 실패 시 처리

    • 현재: 에러 발생 시 그냥 중단
    • 제안: 기존 이미지로 서비스 유지 또는 명확한 실패 알림
  2. docker compose up -d 실패 시 처리

    • Line 123에서 새 컨테이너 시작 실패 시, 이전 컨테이너가 이미 down된 상태
    • 제안: 스크린샷 또는 파일로 이전 상태 백업 후 롤백 가능하도록 개선
  3. Nginx 설정 적용 실패 시 처리

    • Line 111의 nginx -t 검증 후 reload가 실패하면 서비스 불가
    • 제안: 구 설정 백업 후 검증 실패 시 복구

아래와 같이 개선하는 것을 권장합니다:

# Nginx 설정 변경 전 백업
cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup

# 새 설정 적용
sudo cp ./nginx/default.conf /etc/nginx/conf.d/default.conf

# 검증 실패 시 복구
if ! sudo nginx -t; then
  echo "Nginx configuration test failed. Reverting..."
  sudo cp /etc/nginx/conf.d/default.conf.backup /etc/nginx/conf.d/default.conf
  sudo nginx -t
  exit 1
fi

sudo nginx -s reload

또한 Docker Compose도 이전 상태 보존을 고려하세요:

# 이전 상태 백업
docker compose -f docker-compose.prod.yml cp solid-connection-server:/var/log/spring ./logs.backup

# 새 버전으로 재시작
OWNER_LOWERCASE=$OWNER_LOWERCASE IMAGE_TAG=$IMAGE_TAG_ONLY docker compose -f docker-compose.prod.yml up -d

# 헬스 체크 (옵션)
sleep 5
curl -f http://localhost:8080/health || { echo "Health check failed"; exit 1; }
.github/workflows/dev-cd.yml (1)

118-119: Docker Compose 정리 명령어가 prod 워크플로우와 불일치합니다.

현재 dev-cd.yml:

  • Line 118: docker compose -f docker-compose.dev.yml down

prod-cd.yml (더 견고함):

  • Line 117-120:
    • docker compose ... stop (graceful shutdown)
    • docker compose ... down --remove-orphans (고아 네트워크/볼륨 정리)

차이점:

  1. prod는 명시적으로 stop → down 순서 진행
  2. prod는 --remove-orphans 플래그 사용 (고아 리소스 제거)
  3. dev는 이 두 가지를 누락

이로 인해 dev 환경에서 장기 실행 후 불필요한 네트워크/볼륨이 쌓일 수 있습니다.

prod 워크플로우와 일치하도록 수정하세요:

  # 6. Docker Compose 재시작
  echo "Restarting Docker Compose with tag: $IMAGE_TAG_ONLY"
- docker compose -f docker-compose.dev.yml down
+ echo "Stopping containers gracefully..."
+ docker compose -f docker-compose.dev.yml stop
+ 
+ echo "Removing old containers and networks..."
+ docker compose -f docker-compose.dev.yml down --remove-orphans
+ 
+ echo "Starting new containers..."
  OWNER_LOWERCASE=$OWNER_LOWERCASE IMAGE_TAG=$IMAGE_TAG_ONLY docker compose -f docker-compose.dev.yml up -d
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 093b6af and 898d594.

📒 Files selected for processing (6)
  • .github/workflows/dev-cd.yml (2 hunks)
  • .github/workflows/prod-cd.yml (1 hunks)
  • Dockerfile (2 hunks)
  • docker-compose.dev.yml (2 hunks)
  • docker-compose.prod.yml (1 hunks)
  • src/main/resources/secret (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-25T10:59:42.734Z
Learnt from: whqtker
Repo: solid-connection/solid-connect-server PR: 481
File: docs/infra-config/nginx.conf:36-36
Timestamp: 2025-08-25T10:59:42.734Z
Learning: When using network_mode: host in Docker Compose, containers share the host's network namespace, so nginx proxy_pass should target localhost:PORT instead of container service names like solid-connection-server:PORT.

Applied to files:

  • docker-compose.prod.yml
⏰ 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)
src/main/resources/secret (1)

1-1: 서브모듈 포인터 업데이트 확인 필요합니다.

src/main/resources/secret 서브모듈이 ae3e90efb2fb5167로 업데이트되었습니다. 다음 사항을 확인해 주세요:

  1. 변경 내용 검증: 새 커밋 (b2fb5167)에서 어떤 변경사항이 발생했는지 확인 필요
  2. CD 리팩터링 연관성: 현재 PR의 CD 워크플로우 개선(GitHub Actions → GHCR → docker-compose)과 이 서브모듈 업데이트의 관계 확인
  3. 보안 영향도: secret 디렉토리 특성상 민감한 정보 포함 여부 및 변경에 따른 보안 영향 검토

이 서브모듈이 실제로 이 PR의 범위에 포함되어야 하는지, 아니면 별도로 관리되어야 하는지 명확히 해 주시기 바랍니다.

docker-compose.dev.yml (1)

21-21: 환경 변수 전달 방식 확인이 필요합니다.

Line 21의 이미지 참조가 OWNER_LOWERCASEIMAGE_TAG 환경 변수에 의존하고 있습니다. 워크플로우에서 OWNER_LOWERCASE=$OWNER_LOWERCASE IMAGE_TAG=$IMAGE_TAG_ONLY docker compose로 명시적으로 전달하고 있으므로 동작하겠지만, 로컬 개발 환경에서도 이 변수들을 설정하고 실행해야 합니다.

로컬에서 수동 테스트 시 아래 명령어로 실행되는지 확인하세요:

export OWNER_LOWERCASE=<your-org>
export IMAGE_TAG=<tag-or-leave-for-latest>
docker compose -f docker-compose.dev.yml up -d
docker-compose.prod.yml (1)

21-21: 환경 변수 전달 방식 일관성 유지 필요합니다.

docker-compose.dev.yml과 마찬가지로 Line 21의 이미지 참조는 OWNER_LOWERCASEIMAGE_TAG 환경 변수에 의존합니다. 워크플로우에서 올바르게 전달되고 있으나, 배포 전 로컬 테스트 시에도 동일한 방식으로 환경 변수를 설정해야 합니다.

추가적으로, nginx 설정(prod-cd.yml의 라인 107~112)이 이 compose 파일 외부에서 관리되므로, nginx 컨테이너가 bridge network을 사용하는 경우 proxy_pass http://solid-connection-server:8080 형식으로 서비스명을 사용해야 함을 확인하세요.

Dockerfile (1)

2-2: Base image 변경이 적절합니다.

OpenJDK는 더 이상 유지보수되지 않으며, Eclipse Temurin은 공식 대체 이미지입니다. 변경 자체는 명확하고 문제없어 보입니다.

다만 한 가지 검증 항목:

  1. 빌드 플랫폼 호환성: 워크플로우에서 linux/arm64 플랫폼을 지정하고 있습니다. Eclipse Temurin:17-jdk가 이 플랫폼에서 정상 작동하는지 확인하세요.
.github/workflows/prod-cd.yml (1)

106-112: Nginx 설정 변경 시 권한 문제 검토가 필요합니다.

원격 SSH 명령어에서 Nginx 설정을 다음과 같이 처리하고 있습니다:

  1. 로컬에서 nginx.prod.conf를 scp로 전송 (Line 76)
  2. 원격에서 ./nginx/default.conf로 이동 (Line 107)
  3. sudo로 /etc/nginx/conf.d/default.conf에 복사 (Line 110)
  4. sudo nginx -t 및 sudo nginx -s reload 실행 (Lines 111-112)

문제점:

  1. sudo 명령어 실행 시 비밀번호 입력 없이 동작해야 하는데, 배포 키 기반 SSH에서 이것이 보장되는지 확인 필요
  2. Nginx 설정 검증 실패 시 에러 처리 미흡 (set -e가 있으므로 중단되지만, 롤백 로직 없음)

다음을 확인하세요:

  1. 배포 서버의 sudoers 파일에서 해당 사용자(${{ secrets.USERNAME }})가 비밀번호 없이 nginx 명령어를 실행할 수 있도록 설정되었는지 확인
  2. nginx -t 검증 실패 시 이전 설정으로 복구하는 로직 추가 검토
.github/workflows/dev-cd.yml (1)

130-139: 이미지 보관 정책의 이미지명을 검토하세요.

Line 134에서 image-names: solid-connection로 지정되어 있으나, 실제 푸시된 이미지명은:

  • Line 54: ghcr.io/${OWNER_LOWERCASE}/solid-connection-dev

또한 prod-cd.yml의 보관 정책과도 불일치:

  • prod: image-names: solid-connection-prod
  • dev: image-names: solid-connection

다음을 확인하고 수정하세요:

  1. 실제 GHCR에 푸시되는 이미지명 확인
  2. 보관 정책의 이미지명을 정확하게 매칭:
- image-names: solid-connection
+ image-names: solid-connection-dev

또는 조직 수준의 보관 정책을 사용하려면 와일드카드 지원 여부 확인 필요.

export FULL_IMAGE_NAME="ghcr.io/${OWNER_LOWERCASE}/solid-connection-dev:${IMAGE_TAG_ONLY}"
# 2. 서버가 GHCR에 로그인 (pull 받기 위해)
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
Copy link

@coderabbitai coderabbitai bot Nov 4, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

GHCR 로그인 시 사용자명 대소문자 불일치 문제가 있습니다.

dev-cd.yml도 prod-cd.yml과 동일한 문제가 있습니다.

Line 100에서 docker login 시 사용자명으로 ${{ github.repository_owner }}를 사용하고 있으나, 이미지 태그는 소문자로 변환되어 있습니다:

  • Line 52: OWNER_LOWERCASE=$(echo ... | tr '[:upper:]' '[:lower:]')
  • Line 54: 이미지명에 OWNER_LOWERCASE 사용

GitHub 저장소 소유자명에 대문자가 있으면 로그인 실패 또는 권한 오류 발생 가능성이 있습니다.

수정 방법:

  # 2. 서버가 GHCR에 로그인 (pull 받기 위해)
- echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
+ OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${OWNER_LOWERCASE} --password-stdin
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${OWNER_LOWERCASE} --password-stdin
🤖 Prompt for AI Agents
.github/workflows/dev-cd.yml around line 100: the docker login uses ${{
github.repository_owner }} which may contain uppercase letters while the image
tag and OWNER_LOWERCASE are used elsewhere, causing GHCR auth failures; change
the login username to the already-computed OWNER_LOWERCASE variable (or compute
a lowercase owner just before login) so the docker login user matches the
lowercase image namespace, and apply the same change in prod-cd.yml.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai

이전에 lower_case로 통일하도록 이미지 메타데이터 정의 스텝에서 전처리를 해두었기에 문제가 나지 않을 것 같은데?

심지어 내 닉네임이 Hexeong인데, fork 레포에서 깃헙 액션을 실행시켰을 때에 문제가 발생하지 않았었어

Copy link

Choose a reason for hiding this comment

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

@Hexeong

맞습니다! 제가 잘못 지적했네요. 죄송합니다. 🙇

  1. Docker/GHCR 로그인은 사용자명 대소문자를 구분하지 않습니다.

    • Line 100의 docker login ghcr.io -u ${{ github.repository_owner }}Hexeong이든 hexeong이든 정상적으로 인증됩니다.
  2. 이미지 경로는 올바르게 소문자를 사용하고 있습니다.

    • Line 95에서 OWNER_LOWERCASE를 재계산하고
    • Line 97에서 FULL_IMAGE_NAME에 소문자 버전을 사용하여
    • Line 103의 docker pull이 정상 작동합니다.

실제 테스트에서 문제가 없었다는 것이 이를 증명하네요. 불필요한 수정 제안을 드려 혼란을 드려 죄송합니다!

Comment on lines +40 to +45
- name: Log in to GitHub Container Registry (GHCR)
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
Copy link

@coderabbitai coderabbitai bot Nov 4, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

GHCR 로그인 시 사용자명 대소문자 불일치 문제가 있습니다.

워크플로우를 자세히 보면:

  1. GitHub Actions에서 GHCR 로그인 (Line 44): ${{ github.repository_owner }} (원본 대소문자 유지)
  2. 이미지 메타데이터 정의 (Lines 51-54): OWNER_LOWERCASE=$(echo ... | tr '[:upper:]' '[:lower:]')로 소문자 변환
  3. 원격 서버 GHCR 로그인 (Line 98): ${{ github.repository_owner }} (원본 대소문자 유지)

문제: GitHub 저장소 소유자명이 대문자를 포함하면, docker pull 시 이미지명과 로그인 사용자명의 대소문자 불일치로 인증 오류가 발생할 수 있습니다.

아래와 같이 수정하세요:

  # 2. 서버가 GHCR에 로그인 (pull 받기 위해)
- echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
+ OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${OWNER_LOWERCASE} --password-stdin

Also applies to: 98-100

🤖 Prompt for AI Agents
.github/workflows/prod-cd.yml lines 40-45 (also applies to lines 98-100): GHCR
login uses `${{ github.repository_owner }}` which can contain uppercase letters
causing mismatch with the image names that are converted to lowercase; update
the workflow to pass a lowercased owner username to docker/login-action (use the
same lowercase transformation used for OWNER_LOWERCASE) so both GHCR login steps
use the lowercase repository owner; ensure the environment variable or step
output holding the lowercase owner is referenced in both login steps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

레지스트리 경로명은 항상 소문자로 유지되기에 repository_owner가 대문자가 포함되어 있어도 이후에 문제가 되지 않아!

Copy link

Choose a reason for hiding this comment

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

@Hexeong, 설명 감사합니다! GHCR의 인증 과정에서 사용자명은 대소문자를 구분하지 않고, 레지스트리 경로는 자동으로 소문자로 정규화되는 점을 확인해주셨네요. 제가 너무 신중하게 접근했습니다. 현재 구현이 문제없이 작동하겠네요! 👍


✏️ Learnings added
Learnt from: Hexeong
Repo: solid-connection/solid-connect-server PR: 552
File: .github/workflows/prod-cd.yml:40-45
Timestamp: 2025-11-10T01:55:03.812Z
Learning: GHCR (GitHub Container Registry)에서는 docker login 시 사용자명의 대소문자를 구분하지 않으며, 레지스트리 경로명은 항상 소문자로 정규화됩니다. 따라서 `github.repository_owner`를 그대로 사용해도 이미지 경로와의 대소문자 불일치 문제가 발생하지 않습니다.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines 133 to 142
- name: Clean up old image versions from GHCR
if: success()
uses: snok/container-retention-policy@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
image-names: solid-connection-prod
delete-untagged: true
keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
Copy link

@coderabbitai coderabbitai bot Nov 4, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

이미지 보관 정책의 이미지명 불일치가 있습니다.

Line 138에서 image-names: solid-connection-prod로 지정되어 있으나, 실제 푸시된 이미지는:

  • Line 53: ghcr.io/${OWNER_LOWERCASE}/solid-connection-server

동일하게 dev-cd.yml (Line 134)는 image-names: solid-connection로 지정되어 있습니다.

두 워크플로우 간 네이밍 정책이 일관되지 않습니다. 정리해야 할 항목:

  1. prod 워크플로우: solid-connection-prod vs 실제 푸시 이미지명 solid-connection-server
  2. dev 워크플로우: solid-connection vs 실제 푸시 이미지명 solid-connection-dev

이미지 보관 정책이 정확한 이미지명과 일치하도록 수정하세요:

  - name: Clean up old image versions from GHCR
    ...
    with:
      token: ${{ secrets.GITHUB_TOKEN }}
-     image-names: solid-connection-prod
+     image-names: solid-connection-server
      delete-untagged: true
      keep-n-tags: 5
      account-type: org
      org-name: ${{ github.repository_owner }}
      cut-off: '7 days ago UTC'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Clean up old image versions from GHCR
if: success()
uses: snok/container-retention-policy@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
image-names: solid-connection-prod
delete-untagged: true
keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
- name: Clean up old image versions from GHCR
if: success()
uses: snok/container-retention-policy@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
image-names: solid-connection-server
delete-untagged: true
keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
🤖 Prompt for AI Agents
In .github/workflows/prod-cd.yml around lines 133-142 the image-names value is
set to "solid-connection-prod" but the pushed image is
"ghcr.io/${OWNER_LOWERCASE}/solid-connection-server" (see push at line 53);
update image-names to "solid-connection-server". Also update the dev workflow
(.github/workflows/dev-cd.yml around line 134) where image-names is
"solid-connection" to the actual pushed name "solid-connection-dev". Ensure the
container-retention-policy steps in both workflows use the exact image names
that are pushed to GHCR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai 해당 내용 반영했어!

Copy link

Choose a reason for hiding this comment

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

@Hexeong 수정 감사합니다! 이미지명이 이제 실제 푸시되는 이미지와 일치하여 컨테이너 보관 정책이 올바르게 작동할 것입니다. 🎉


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

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

하지만, prod의 경우 release 버전으로 이미지가 관리될 수 있다 보니, 이미지 태그에 대한 내용을 스크립트 상에 하드코딩할 수 없는 상태입니다.

이게 어떤 말씀이신가요~?? release tag 활용하면 되는 건 아닌가요!?

sudo nginx -t
sudo nginx -s reload
scp -i deploy_key.pem \
-o StrictHostKeyChecking:no \
Copy link
Contributor

Choose a reason for hiding this comment

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

@coderabbitai
StrictHostKeyChecking:no
StrictHostKeyChecking=no

이거 문법 뭐가 맞아?

Copy link

Choose a reason for hiding this comment

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

@Gyuhyeok99,

올바른 문법은 StrictHostKeyChecking=no 입니다 (등호 사용).

SSH 옵션은 -o option=value 형식을 사용해야 합니다. 콜론(:)이 아니라 등호(=)를 사용해야 합니다.

현재 파일에서 두 곳 모두 수정이 필요합니다:

  1. Line 71 (scp 명령어):

    scp -i deploy_key.pem \
      -o StrictHostKeyChecking=no \
  2. Line 83 (ssh 명령어):

    ssh -i deploy_key_ssh.pem \
     -o StrictHostKeyChecking=no \

콜론을 사용하면 SSH가 옵션을 올바르게 인식하지 못할 수 있습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@Hexeong
Copy link
Contributor Author

Hexeong commented Nov 5, 2025

하지만, prod의 경우 release 버전으로 이미지가 관리될 수 있다 보니, 이미지 태그에 대한 내용을 스크립트 상에 하드코딩할 수 없는 상태입니다.

이게 어떤 말씀이신가요~?? release tag 활용하면 되는 건 아닌가요!?

아하 제가 릴리즈 기능을 써본 적이 없어서 몰랐던 것 같습니다;; 이 방식으로 배포할 경우 ${{ github.ref_name }}으로 릴리즈 태그를 CD 스크립트에서 쓸 수 있는 거군요! 좋은 정보 감사합니다~ ㅎㅎ

Copy link

@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: 2

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

40-45: ⚠️ GHCR 로그인 시 소유자명 대소문자 불일치 문제 (이전 리뷰의 미해결 항목)

앞선 리뷰에서 지적되었던 문제가 여전히 남아 있습니다.

문제:

  1. Line 44의 GHCR 로그인: ${{ github.repository_owner }} (원본 대소문자 유지)
  2. Line 53의 이미지명: ghcr.io/${OWNER_LOWERCASE}/solid-connection-server (소문자 변환)

리포지토리 소유자명에 대문자가 포함되면, 로그인 사용자명과 이미지명의 대소문자 불일치로 인증 오류가 발생합니다.

수정:

  - name: Log in to GitHub Container Registry (GHCR)
    uses: docker/login-action@v3
    with:
      registry: ghcr.io
-     username: ${{ github.repository_owner }}
+     username: ${{ steps.image_meta.outputs.owner_lowercase }}
      password: ${{ secrets.GITHUB_TOKEN }}

그리고 Line 48-54의 메타데이터 정의 단계에 다음 줄을 추가하세요:

  - name: Define image name and tag
    id: image_meta
    run: |
      OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
+     echo "owner_lowercase=${OWNER_LOWERCASE}" >> $GITHUB_OUTPUT
      IMAGE_TAG=$(date +'%Y%m%d-%H%M%S')
      echo "image_name=ghcr.io/${OWNER_LOWERCASE}/solid-connection-server" >> $GITHUB_OUTPUT
      echo "image_tag=${IMAGE_TAG}" >> $GITHUB_OUTPUT

98-98: ⚠️ 원격 서버의 GHCR 로그인에서도 소유자명 대소문자 불일치 (이전 리뷰의 미해결 항목)

Line 40-45의 GHCR 로그인 문제와 동일하게, 원격 서버에서의 docker login 명령어에서도 대소문자 불일치가 발생합니다.

문제:

  • Line 93: export OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')로 소문자 변수 생성
  • Line 98: docker login ghcr.io -u ${{ github.repository_owner }} (원본 대소문자 사용)

변수가 정의되었으므로 일관성 있게 사용하세요.

수정:

  # 2. 서버가 GHCR에 로그인 (pull 받기 위해)
- echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${OWNER_LOWERCASE} --password-stdin
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a76e446 and 59c0116.

📒 Files selected for processing (1)
  • .github/workflows/prod-cd.yml (1 hunks)
🔇 Additional comments (4)
.github/workflows/prod-cd.yml (4)

1-34: 기본 설정 단계가 잘 구성되었습니다.

Java, Gradle 설정과 초기 빌드 단계가 명확하게 작성되었습니다. 특히 submodule 체크아웃과 Gradle wrapper 권한 설정이 적절합니다.


56-65: Docker 빌드 및 푸시 구성이 잘 설계되었습니다.

메타데이터를 올바르게 활용하고, 레지스트리 캐시를 통해 빌드 성능 최적화를 구현한 점이 좋습니다. 멀티 플랫폼 지원도 명시되어 있습니다.


100-130: 배포 로직이 견고하게 구성되었습니다.

이미지 pull부터 Nginx 재시작, Docker Compose 재시작, 정리까지의 단계가 명확하고, set -e를 통한 에러 처리와 graceful shutdown 로직이 좋습니다. 위의 문법 오류들을 수정하면 배포 흐름이 안정적으로 작동할 것입니다.


132-143: GHCR 이미지 정리 정책이 올바르게 수정되었습니다. ✅

이전 리뷰에서 지적했던 image-names 불일치 문제(solid-connection-prod → solid-connection-server)가 이미 반영되었습니다. 실제 푸시된 이미지명과 일치하며, 5개 태그 유지 및 7일 이상 된 이미지 정리 정책이 적절합니다.

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

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

이미지를 pull하는 방식으로 잘 개선해주신 거 같네요! 고생하셨습니다!
길게 잡고 점진적으로 개선해나가봐요!

keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
cut-off: '7 days ago UTC'
Copy link
Contributor

Choose a reason for hiding this comment

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

개행 하나 부탁드려요~

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
cut-off: '7 days ago UTC'
Copy link
Contributor

Choose a reason for hiding this comment

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

여기도요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

Dockerfile Outdated

# 볼륨 설정
VOLUME /tmp
VOLUME /tmp
Copy link
Contributor

Choose a reason for hiding this comment

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

개행!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

- ./docs/infra-config/config.alloy:/etc/alloy/config.alloy:ro
environment:
- ALLOY_ENV=dev
- ALLOY_ENV=dev
Copy link
Contributor

Choose a reason for hiding this comment

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

개행!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

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

깊게 아는 부분은 아니라, 감안하고 리뷰 봐주시면 감사하겠습니다 🙇‍♂️ 고생하셨습니다 !

image

추가로 dev 인스턴스에 인성 님 이름으로 이미지가 몇 개 있던데, 이 이미지들 또한 이미지 자동 삭제 트리거에 영향을 받나요 ? 만약 아니라면, PR 머지 시 삭제 부탁드리겠습니다 !

- name: Define image name and tag
id: image_meta
run: |
OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
Copy link
Member

Choose a reason for hiding this comment

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

github.repository_owner 로 설정하면 레포 소유자 이름(e.g. whqtker)으로 고정되는 건가요 ? 만약 그렇다면, solid-connection 과 같이 사용자에 종속되지 않는 이름으로 설정하는 게 좋을 거 같습니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Organization에 속한 레포의 워크플로우 실행의 경우 github.repository_owner은 Organization 이름인 solid-connection이 됩니다! 때문에 사용자 이름으로 종속되지는 않아 해당 문제는 없을 것 같아요!

Copy link
Member

Choose a reason for hiding this comment

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

확인했습니다 ~!

uses: snok/container-retention-policy@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
image-names: solid-connection
Copy link
Member

Choose a reason for hiding this comment

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

image-namessolid-connection-dev 로 설정해야 할 듯 합니다 ~!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

@Hexeong
Copy link
Contributor Author

Hexeong commented Nov 6, 2025

깊게 아는 부분은 아니라, 감안하고 리뷰 봐주시면 감사하겠습니다 🙇‍♂️ 고생하셨습니다 !

image 추가로 dev 인스턴스에 인성 님 이름으로 이미지가 몇 개 있던데, 이 이미지들 또한 이미지 자동 삭제 트리거에 영향을 받나요 ? 만약 아니라면, PR 머지 시 삭제 부탁드리겠습니다 !

어 그럼 이부분도 GHCR Clean up 작업처럼 하나 추가해서 최신 이미지 10개만 유지하게 할까요?? 어떻게 생각하시나용 @whqtker

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

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

이번 pr만큼은 수연님도 리뷰해주시면 좋겠습니다~

Copy link
Contributor Author

@Hexeong Hexeong left a comment

Choose a reason for hiding this comment

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

반영사항들 반영해두었습니다! pending 처리되어있던 걸 제가 늦게 확인했네요;; 빠르게 stage 인스턴스에 대해서도 old 이미지를 지우는 step도 추가해두겠습니다!

export FULL_IMAGE_NAME="ghcr.io/${OWNER_LOWERCASE}/solid-connection-dev:${IMAGE_TAG_ONLY}"
# 2. 서버가 GHCR에 로그인 (pull 받기 위해)
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai

이전에 lower_case로 통일하도록 이미지 메타데이터 정의 스텝에서 전처리를 해두었기에 문제가 나지 않을 것 같은데?

심지어 내 닉네임이 Hexeong인데, fork 레포에서 깃헙 액션을 실행시켰을 때에 문제가 발생하지 않았었어

Comment on lines 133 to 142
- name: Clean up old image versions from GHCR
if: success()
uses: snok/container-retention-policy@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
image-names: solid-connection-prod
delete-untagged: true
keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai 해당 내용 반영했어!

Comment on lines +40 to +45
- name: Log in to GitHub Container Registry (GHCR)
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

레지스트리 경로명은 항상 소문자로 유지되기에 repository_owner가 대문자가 포함되어 있어도 이후에 문제가 되지 않아!

sudo nginx -t
sudo nginx -s reload
scp -i deploy_key.pem \
-o StrictHostKeyChecking:no \
Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

- name: Define image name and tag
id: image_meta
run: |
OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Organization에 속한 레포의 워크플로우 실행의 경우 github.repository_owner은 Organization 이름인 solid-connection이 됩니다! 때문에 사용자 이름으로 종속되지는 않아 해당 문제는 없을 것 같아요!

keep-n-tags: 5
account-type: org
org-name: ${{ github.repository_owner }}
cut-off: '7 days ago UTC'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

Dockerfile Outdated

# 볼륨 설정
VOLUME /tmp
VOLUME /tmp
Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

- ./docs/infra-config/config.alloy:/etc/alloy/config.alloy:ro
environment:
- ALLOY_ENV=dev
- ALLOY_ENV=dev
Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

Copy link

@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

🧹 Nitpick comments (3)
.github/workflows/dev-cd.yml (3)

69-80: SCP 전송 시 에러 처리 추가 권장.

현재 설정 파일 전송 단계에서 scp 명령이 실패해도 워크플로우가 계속 진행됩니다. 예를 들어, 파일이 존재하지 않거나 네트워크 오류가 발생할 경우를 대비하세요.

다음과 같이 개선하면 실패 시 명확히 감지할 수 있습니다:

  - name: Copy config files to remote
    run: |
      echo "${{ secrets.DEV_PRIVATE_KEY }}" > deploy_key.pem
      chmod 600 deploy_key.pem
      
+     set -e  # 에러 발생 시 즉시 중단
+     
      scp -i deploy_key.pem \
        -o StrictHostKeyChecking=no \
        ./docker-compose.dev.yml \
        ./docs/infra-config/config.alloy \
        ./docs/infra-config/nginx.dev.conf \
        ${{ secrets.DEV_USERNAME }}@${{ secrets.DEV_HOST }}:/home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/

118-119: docker compose 명령어 실행 시 환경 변수 설정 방식 정리.

현재 Line 119에서:

OWNER_LOWERCASE=$OWNER_LOWERCASE IMAGE_TAG=$IMAGE_TAG_ONLY docker compose -f docker-compose.dev.yml up -d

이 패턴은 작동하지만, OWNER_LOWERCASE가 Line 95에서 이미 export되었다면 불필요한 중복입니다. 일관성을 위해 다음 중 하나로 정리하세요:

옵션 1: 환경 변수를 명시적으로 선언하고 재사용

export OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
export IMAGE_TAG_ONLY=${{ steps.image_meta.outputs.image_tag }}
# ...
docker compose -f docker-compose.dev.yml up -d

옵션 2: 매번 인라인으로 설정 (현재 방식이지만 정리)

IMAGE_TAG=${{ steps.image_meta.outputs.image_tag }} \
OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') \
docker compose -f docker-compose.dev.yml up -d

129-132: 오래된 이미지 정리 로직이 정상 작동하는지 검증 권장.

Line 129-132의 이미지 정리 로직은 수동으로 태그를 정렬하고 삭제합니다:

docker images "${IMAGE_NAME_BASE}" --format "{{.Tag}}" | \
sort -r | \
tail -n +6 | \
xargs -I {} docker rmi "${IMAGE_NAME_BASE}:{}" || true

주의할 점:

  1. docker images 출력에 <none> 태그가 포함될 수 있으며, 이는 정렬 순서에 영향을 미칠 수 있습니다.
  2. 태그가 6개 미만이면 아무것도 삭제되지 않습니다 (의도한 동작이라면 문제 없음).
  3. || true로 인해 삭제 실패가 무시되므로, 실제 오류를 인지하기 어려울 수 있습니다.

개선 제안:

GHCR의 자동 정리 정책(Line 137-147, 최신 5개 유지)과의 일관성을 위해, 로컬 환경에서도 같은 개수를 유지하되, 더 명확한 로직을 고려하세요:

docker images "${IMAGE_NAME_BASE}" --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | \
tail -n +2 | \
sort -k2 -r | \
tail -n +6 | \
awk '{print $1}' | \
xargs -r docker rmi

또는 snok/container-retention-policy 같은 로컬 도구도 검토해볼 가치가 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab3054e and 01114c3.

📒 Files selected for processing (1)
  • .github/workflows/dev-cd.yml (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T01:55:03.812Z
Learnt from: Hexeong
Repo: solid-connection/solid-connect-server PR: 552
File: .github/workflows/prod-cd.yml:40-45
Timestamp: 2025-11-10T01:55:03.812Z
Learning: GHCR (GitHub Container Registry)에서는 docker login 시 사용자명의 대소문자를 구분하지 않으며, 레지스트리 경로명은 항상 소문자로 정규화됩니다. 따라서 `github.repository_owner`를 그대로 사용해도 이미지 경로와의 대소문자 불일치 문제가 발생하지 않습니다.

Applied to files:

  • .github/workflows/dev-cd.yml
🔇 Additional comments (2)
.github/workflows/dev-cd.yml (2)

47-55: 이미지 메타데이터 정의 로직이 명확하고 효율적입니다.

이미지 이름과 태그를 GitHub Actions Output으로 미리 정의하여 여러 단계에서 재사용할 수 있게 설계된 점이 좋습니다. 특히:

  • OWNER_LOWERCASE로 소문자 정규화: GHCR 이미지명 규칙 준수 ✓
  • 타임스탬프 기반 태깅(YYYYMMDD-HHMMSS): 버전 추적 명확함 ✓
  • 이미지명 포맷 표준화: 코드 가독성 향상 ✓

137-147: GHCR 자동 정리 정책이 체계적으로 구성되었습니다.

snok/container-retention-policy를 활용한 정책이 명확합니다:

  1. 태그 없는 이미지 자동 삭제 (delete-untagged: true)
  2. 최신 5개 버전 유지 (keep-n-tags: 5)
  3. 7일 이상 오래된 버전 정리 (cut-off: 7 days ago)
  4. 성공 시에만 실행 (if: success()) — 안정성 고려 ✓

로컬 정리 정책(Line 129-132)과도 일관성이 있어, 스토리지 관리가 예측 가능합니다.

Copy link

@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: 0

🧹 Nitpick comments (1)
.github/workflows/dev-cd.yml (1)

137-147: GHCR 이미지 정리 정책과 로컬 호스트 정리 정책의 일관성이 확인됩니다.

GHCR에서 최신 5개 태그를 유지(keep-n-tags: 5)하고, Line 131에서 로컬 호스트에서도 동일하게 최신 5개를 유지하는 방식으로 정책이 일관되어 있습니다. delete-untagged: true로 미태그 이미지도 정리하므로 레지스트리 저장소 효율이 좋습니다.

다만, 다음 사항을 확인해주세요:

  • 정기적 정리 스케줄: 현재 구조에서는 성공적인 배포 시에만 정리가 실행됩니다(if: success()). 배포가 자주 이루어지지 않는 기간이 있다면 저장소가 누적될 수 있으니, 필요시 별도의 정기 정리 워크플로우(예: 주 1회 스케줄 실행)도 고려해보세요.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01114c3 and 229d9aa.

📒 Files selected for processing (1)
  • .github/workflows/dev-cd.yml (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T01:55:03.812Z
Learnt from: Hexeong
Repo: solid-connection/solid-connect-server PR: 552
File: .github/workflows/prod-cd.yml:40-45
Timestamp: 2025-11-10T01:55:03.812Z
Learning: GHCR (GitHub Container Registry)에서는 docker login 시 사용자명의 대소문자를 구분하지 않으며, 레지스트리 경로명은 항상 소문자로 정규화됩니다. 따라서 `github.repository_owner`를 그대로 사용해도 이미지 경로와의 대소문자 불일치 문제가 발생하지 않습니다.

Applied to files:

  • .github/workflows/dev-cd.yml
🔇 Additional comments (3)
.github/workflows/dev-cd.yml (3)

47-67: 이미지 메타데이터 정의 및 빌드 캐시 전략이 명확합니다.

이미지명과 태그를 명시적으로 정의하고, Docker BuildX의 레지스트리 캐시(type=registry)를 활용해 빌드 성능을 개선하는 구조가 좋습니다. 타임스탬프 기반 태그(YYYYMMDD-HHMMSS)는 dev 환경에서 이미지 버전 추적에 효과적입니다.


87-135: SSH 배포 스크립트의 변수 관리와 함께 일관성 개선을 제안합니다.

현재 구조는 다음과 같은 점에서 정상 작동하지만, 명확성을 위해 몇 가지 정리를 제안합니다:

  1. 로그인 사용자명 일관성

    • Line 95에서 OWNER_LOWERCASE를 계산하지만, Line 100의 docker login에서는 여전히 ${{ github.repository_owner }}를 사용 중입니다.
    • 비록 GHCR에서 대소문자를 구분하지 않지만, 코드의 일관성과 가독성 차원에서 이미 계산한 OWNER_LOWERCASE 변수를 사용하는 것이 좋습니다.
  2. 환경 변수 전달 방식

    • Line 119의 IMAGE_TAG=$IMAGE_TAG_ONLY docker compose ...는 정상 작동합니다(IMAGE_TAG 환경변수가 해당 명령 실행 시에만 유효).
    • 다만, OWNER_LOWERCASE는 Line 95에서 이미 export되었으므로, docker-compose.dev.yml이 두 변수를 모두 필요로 한다면 현재 구조로 충분합니다.
  3. 오래된 이미지 정리 로직 확인

    • Lines 129-132에서 sort -r | tail -n +6으로 최신 5개를 제외한 나머지를 삭제하는 로직은 정확합니다.
    • 다만, 타임스탬프 기반 정렬(이미지 태그 문자열)과 실제 생성 시간의 순서가 일치하는지 확인이 필요합니다. (현재 YYYYMMDD-HHMMSS 형식이므로 문자열 정렬과 시간 순서가 일치함)

다음 개선안을 고려해주세요:

  # 2. 서버가 GHCR에 로그인 (pull 받기 위해)
- echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${OWNER_LOWERCASE} --password-stdin

이렇게 하면 로그인 사용자명이 이미지명 계산 부분과 완전히 일치하게 됩니다.


69-80: 설정 파일 전송 단계가 효율적으로 통합되었습니다.

기존의 개별 파일 전송 방식에서 하나의 scp 명령으로 여러 파일을 전송하도록 통합되어, 워크플로우 단계가 간결해졌습니다. SSH 보안 설정(StrictHostKeyChecking=no, 권한 600)도 적절합니다.

Copy link
Contributor

@lsy1307 lsy1307 left a comment

Choose a reason for hiding this comment

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

개인적으로 on push 방식으로 release태그를 사용하는 게 배포 자동화 면에서 이점이 있다고 생각합니당

Comment on lines 85 to 86
echo "${{ secrets.DEV_PRIVATE_KEY }}" > deploy_key_ssh.pem
chmod 600 deploy_key_ssh.pem
Copy link
Contributor

Choose a reason for hiding this comment

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

위에서 생성한 key를 그대로 사용해도 되지 않나요..?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

해당 내용 반영했습니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

이름도 바꿔주셔야 할 것 같아요! deploy_key.pem으로 저장이 되었는데 deploy_key_ssh.pem을 사용하고 있습니당

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다!

export FULL_IMAGE_NAME="ghcr.io/${OWNER_LOWERCASE}/solid-connection-dev:${IMAGE_TAG_ONLY}"
# 2. 서버가 GHCR에 로그인 (pull 받기 위해)
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
Copy link
Contributor

Choose a reason for hiding this comment

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

지금 쓰기가 가능한 토큰이 서버로 전송되는데 보안상 취약하지 않을까요...? 읽기 전용 토큰을 따로 발급해서 사용하는 게 좋아보입니당

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SSH를 통해 GHCR에 접근하는 이 방식을 적용하려면, SUBMODULE_ACCESS_TOKEN처럼 Fine-grained Token을 사용하는 것이 보안상 가장 좋다고 생각합니다!

하지만 이 방식을 사용하려면 GHCR 읽기 권한이 있는 분이 직접 토큰을 발급해야 하는데, 이는 특정 계정에 파이프라인이 의존하게 되는 문제가 있어 망설여졌습니다.

그래서 일단은 임시로 GITHUB_TOKEN을 사용하는 방식을 적용했습니다만, 혹시 이 '개인 종속성' 문제를 해결할 더 좋은 방법이 있다면 의견 부탁드립니다! @lsy1307

Copy link
Contributor

Choose a reason for hiding this comment

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

machine user 개념으로 CI/CD용 계정을 따로 만들어서 사용하는 건 어떨까요? Github Docs에도 나와있는 내용입니다!
참고 : https://docs.github.com/en/get-started/learning-about-github/types-of-github-accounts#user-accounts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

machine user 대신 최근 Github 추천하는 방식인 Github App을 사용해서 Repository에서 임시 토큰을 발급할 수 있도록 개선했습니다! 해당 문제는 해결된 것 같아요!

Copy link

@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: 0

🧹 Nitpick comments (1)
.github/workflows/dev-cd.yml (1)

83-132: SSH 스크립트 내 환경 변수 정의 정리 제안.

현재 설계:

  1. 라인 52–55에서 GitHub Actions 컨텍스트에서 OWNER_LOWERCASEIMAGE_TAG 생성
  2. 라인 92–94에서 SSH 스크립트 내부에서 동일한 값을 재계산

재계산은 작동하지만 불필요한 중복입니다. GitHub Actions 출력값이 이미 SSH 스크립트 내 ${{ steps.image_meta.outputs.* }} 구문으로 전달되므로, 아래처럼 간소화할 수 있습니다:

export OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
export IMAGE_TAG_ONLY=${{ steps.image_meta.outputs.image_tag }}
export FULL_IMAGE_NAME="ghcr.io/${OWNER_LOWERCASE}/solid-connection-dev:${IMAGE_TAG_ONLY}"

이미 export된 OWNER_LOWERCASE를 재계산할 필요 없이 일관성 있게 사용할 수 있으며, 라인 116의 IMAGE_TAG=$IMAGE_TAG_ONLY docker compose... 부분도 명확해집니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 229d9aa and 1b57e74.

📒 Files selected for processing (2)
  • .github/workflows/dev-cd.yml (2 hunks)
  • src/main/resources/secret (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/resources/secret
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T01:55:03.812Z
Learnt from: Hexeong
Repo: solid-connection/solid-connect-server PR: 552
File: .github/workflows/prod-cd.yml:40-45
Timestamp: 2025-11-10T01:55:03.812Z
Learning: GHCR (GitHub Container Registry)에서는 docker login 시 사용자명의 대소문자를 구분하지 않으며, 레지스트리 경로명은 항상 소문자로 정규화됩니다. 따라서 `github.repository_owner`를 그대로 사용해도 이미지 경로와의 대소문자 불일치 문제가 발생하지 않습니다.

Applied to files:

  • .github/workflows/dev-cd.yml
🔇 Additional comments (4)
.github/workflows/dev-cd.yml (4)

11-33: 좋은 권한 설정과 표준 Gradle 스택.

권한에 packages: write를 추가했고, 최신 공식 Gradle 액션(v3)을 사용하여 정확하게 구성되었습니다. JDK 17 temurin 배포판도 Dockerfile 변경과 일치합니다.


35-67: Docker 빌드·푸시 단계가 잘 설계됨.

Docker Buildx 멀티 아키텍처 지원, GHCR 로그인, 이미지 메타데이터(이름·태그) 사전 정의, 레지스트리 캐시 전략까지 모두 CD 성능 개선 목표와 일치합니다. 특히 buildcache 태그를 활용한 레이어 캐싱은 반복 배포 시 빌드 시간 단축에 효과적입니다.


103-132: 원격 배포 및 정리 로직이 효율적으로 구성됨.

다음 항목들이 잘 구성되었습니다:

  1. Config 파일 재배치: Nginx 설정을 정확한 경로(/etc/nginx/conf.d/default.conf)로 이동
  2. Nginx 검증 및 재시작: nginx -t로 사전 검증 후 재시작 (무중단 배포)
  3. Docker Compose 재시작: downup -d로 깔끔한 순환
  4. 이미지 정리: docker image prune -f로 이미지 제거 및 로컬 태그 이미지 최신 5개 유지
  5. GHCR 정리: snok/container-retention-policy로 오래된 이미지 자동 삭제

특히 로컬(라인 122–129)과 GHCR(라인 135–144) 양쪽에서 정리 정책을 일관되게 적용(최신 5개 유지)하는 것이 스토리지 효율성과 유지보수성 면에서 좋습니다.


70-81: Deploy 키 파일명 오류.

라인 72에서 deploy_key.pem을 생성하고 있으나, 라인 85의 SSH 명령어에서 deploy_key_ssh.pem을 참조하고 있습니다. 이는 파일을 찾을 수 없는 오류로 인해 배포가 실패합니다.

라인 85를 다음과 같이 수정하세요:

- ssh -i deploy_key_ssh.pem \
+ ssh -i deploy_key.pem \

Likely an incorrect or invalid review comment.

Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

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

제가 남긴 리뷰들에 대해서 해소가 되어서 approve하겠습니다 !
다른 분들까지 approve되면 머지 진행해주시면 될 거 같습니다. 고생하셨어요 !!

@Hexeong Hexeong merged commit 4dbc07e into solid-connection:develop Nov 15, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: CD 시간 단축 및 OCI 배포시 부하 개선

4 participants