Skip to content

chore: CI 도입 및 Docker 빌드 레이어 캐시 최적화 [#SEOUL-151] - #2

Open
kdy4049067 wants to merge 1 commit into
mainfrom
feature/SEOUL-151-ci-and-docker-cache
Open

chore: CI 도입 및 Docker 빌드 레이어 캐시 최적화 [#SEOUL-151]#2
kdy4049067 wants to merge 1 commit into
mainfrom
feature/SEOUL-151-ci-and-docker-cache

Conversation

@kdy4049067

Copy link
Copy Markdown
Contributor

작업 내용

  • .github/workflows/ci.yml신규. 이 저장소에는 워크플로가 하나도 없었다
    • test job: PR·main push에서 테스트 32개 실행
    • image job: 이미지 빌드. PR에서는 빌드만, main에서만 GHCR push
  • Dockerfile — builder 스테이지 레이어 재배치 (의존성 다운로드를 COPY src 위로)
  • build.gradle.ktsresolveDependencies 태스크 추가

변경 이유

① EC2 배포에서 이 저장소의 이미지는 반드시 레지스트리를 거쳐야 한다.
docker-compose-local.yml의 어드민 서비스는 context: ../seoulection-admin으로 형제 저장소를 가리킨다. EC2에 seoulection-server만 클론하면 그 경로가 없어서 compose가 실패한다. 그래서 어드민은 GHCR에 올라가 있어야 하고, 운영 compose는 build: 대신 image:로 참조한다.

② Dockerfile에 api-server와 같은 문제가 있었다.
COPY src가 의존성 다운로드보다 위에 있어, 소스를 한 줄만 고쳐도 그 아래 레이어가 전부 무효화됐다. Gradle 배포판(151MB)부터 의존성 전체를 매번 다시 받는다.

Gradle에는 "의존성만 받기"에 해당하는 명령이 없다(해석이 lazy라 compileJava가 클래스패스를 요구하는 시점에야 받는다). 그래서 configuration을 직접 resolve하는 태스크를 추가했다. ⚠️ gradlew dependencies로 대체하면 안 된다 — 그건 의존성 그래프만 렌더링해서 POM 메타데이터만 받고 jar는 받지 않는다.

테스트 방법

로컬 실측 (소스 한 줄만 수정한 뒤 재빌드):

빌드 시간
변경 전 48s
변경 후 7s
  • 양쪽 모두 --no-cache로 캐시를 비워 채운 뒤 측정 (2회차라 빨랐던 것이 아님을 확인)
  • 새 구조 2차 빌드에서 RUN ./gradlew resolveDependencies 레이어가 CACHED로 뜨는 것 확인
  • 최종 이미지 605MB로 동일 (레이어 순서만 바뀌었을 뿐 결과물은 같음)
  • ./gradlew test 32개 전부 통과

리뷰어가 알아둘 것

seoulection-server의 같은 이름 워크플로와 짝이지만, 의도적으로 다른 부분이 있다:

seoulection-server 이 저장소
경로 필터 api-server/** (모노레포) 없음 — 단일 프로젝트라 불필요
working-directory api-server 없음gradlew가 루트
이미지 이름 seoulection-api-server seoulection-admin

경로 필터가 없어서 부수 효과가 하나 있다 — 브랜치 보호에서 이 CI를 "필수 체크"로 지정해도 안전하다. seoulection-server 쪽은 경로 필터 때문에 job이 스킵되면 PR이 영원히 대기 상태가 되는 함정이 있는데, 여기엔 그게 구조적으로 없다.

⚠️ 머지 후 main 실행에서 처음으로 GHCR push가 일어난다. 조직 설정이 Actions의 패키지 쓰기를 막고 있으면 여기서 403이 날 수 있다(seoulection-server에서는 통과했으므로 문제없을 가능성이 높다).

이 저장소에는 워크플로가 하나도 없었다. PR과 main push에서 테스트 32개를
자동 실행하고, main에서는 이미지를 GHCR에 올린다.

EC2 배포에서 이 저장소의 이미지가 반드시 레지스트리를 거쳐야 한다.
compose의 context가 ../seoulection-admin(형제 저장소)이라, EC2에
seoulection-server만 클론하면 그 경로가 없어 빌드가 불가능하기 때문이다.

Dockerfile은 api-server와 같은 문제를 갖고 있었다. COPY src가 의존성
다운로드보다 위에 있어 소스를 한 줄만 고쳐도 Gradle 배포판부터 전부
다시 받았다. 실측(소스 한 줄만 수정 후 재빌드): 48s -> 7s.

PR에서는 이미지 빌드까지만 하고 push는 main에서만 한다. 머지 후에야
처음 도는 job이면 깨졌을 때 main이 빨간불이 된다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant