Feat/#141 GitHub Actions 기반 백엔드 CI/CD 구축 - #142
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new deployment pipeline and environment configuration, including a simplified single-stage Dockerfile, a Docker Compose setup with health checks, a robust backend deployment script with automatic rollback capabilities, and comprehensive deployment documentation. Additionally, Spring Boot Actuator is integrated to expose a health endpoint. The review feedback highlights two critical issues: first, calling setExecutable(true) on the Gradle wrapper will fail on non-POSIX filesystems like Windows, breaking local development; second, the base JRE image lacks wget, which will cause the Docker Compose health check to fail and trigger an unintended rollback.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| Path gradleWrapper = destination.resolve(GRADLE_WRAPPER_FILE_NAME); | ||
| if (!gradleWrapper.toFile().setExecutable(true)) { | ||
| throw new IOException("Failed to make Gradle wrapper executable: " + gradleWrapper); | ||
| } |
There was a problem hiding this comment.
Calling setExecutable(true) on a File object can fail and return false on non-POSIX filesystems (such as Windows). This will throw an IOException and break local development or testing for developers on Windows. To ensure cross-platform compatibility, we should check if the filesystem supports POSIX attributes before throwing an exception, or log a warning on non-POSIX systems.
Path gradleWrapper = destination.resolve(GRADLE_WRAPPER_FILE_NAME);
java.io.File file = gradleWrapper.toFile();
if (file.exists() && !file.setExecutable(true)) {
if (java.nio.file.FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
throw new IOException("Failed to make Gradle wrapper executable: " + gradleWrapper);
} else {
log.warn("[AndroidThemeGenerator] Failed to set executable permission on non-POSIX filesystem: {}", gradleWrapper);
}
}| WORKDIR /app | ||
| # install docker | ||
| RUN apk add --no-cache docker-cli | ||
| # run backend container | ||
| COPY --from=builder ./app.jar app.jar | ||
| ENTRYPOINT ["java", "-jar", "app.jar"] No newline at end of file | ||
|
|
||
| COPY build/libs/kakao-theme-maker.jar app.jar |
There was a problem hiding this comment.
The base image eclipse-temurin:17-jre-jammy is based on a minimal Ubuntu image which does not pre-install wget. Since the healthcheck in docker-compose.yml relies on wget, the healthcheck will always fail, causing the deployment to fail and trigger an automatic rollback. Please install wget in the Dockerfile.
RUN apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY build/libs/kakao-theme-maker.jar app.jar
kym8821
left a comment
There was a problem hiding this comment.
내부적으로 검증 로직이 상세하게 작성되어 있고 안정성을 고려한 부분은 좋다고 생각합니다.
다만 일부 검증 항목의 경우 현재 저희 서비스 운영 환경에서는 불필요하게 복잡도를 높이거나, 개발/배포 과정에서 관리 포인트를 증가시킬 수 있는 부분이 있어 보입니다.
해당 검증들이 필요한 이유와 실제 운영 환경에서 기대하는 효과를 함께 확인 부탁드립니다.
There was a problem hiding this comment.
현재 파일 기준으로는 gradle wrapper에 실행 권한을 부여한 것으로 이해했습니다. 기존에 말씀드렸던 ./gradlew 실행 권한 문제는 해당 방식으로 해결 가능한 것으로 보입니다.
이번 변경에서는 ./gradlew 파일 자체에 실행 권한을 부여하는 방향으로 개선할 예정이니, 해당 방식으로 진행해도 문제가 없는지 확인 부탁드립니다. 문제가 없다면 현재 변경 사항은 롤백해주시면 좋을 것 같습니다.
There was a problem hiding this comment.
현재 테스트는 Spring Boot Actuator가 제공하는 health check 엔드포인트의 정상 응답을 검증하는 것으로 보입니다.
개인적으로는 해당 엔드포인트는 Spring Boot에서 제공하는 기능이고, 별도의 커스텀 로직이 포함되어 있지 않다면 테스트의 실효성이 크지 않을 수 있다고 생각합니다. 오히려 프레임워크 구현에 대한 테스트가 되어 유지보수 대상만 늘어날 가능성도 있다고 봅니다.
혹시 이 테스트를 추가하신 특별한 의도가 있다면 공유해주시면 감사하겠습니다
There was a problem hiding this comment.
테스트 환경에서만 사용하는 설정이지만, 현재 내용만 봤을 때는 공개 저장소에 위치해야 하는 이유를 잘 이해하지 못했습니다.
혹시 application-test.yml을 private 서브모듈이 아닌 공개 저장소로 옮긴 이유가 있을까요? 의도가 있다면 설명 부탁드립니다.
There was a problem hiding this comment.
기존에 개발 환경에서 사용하던 docker-compose를 제거하고 있는데 차라리 CICD용 docker-compose를 별도로 관리하는 것이 좋을 것 같습니다. 현재 변경 사항은 docker-compose를 개발 환경에서 실행하고 싶은 상황에 문제가 발생할 수 있습니다.
There was a problem hiding this comment.
현재 CI/CD 설정을 확인해보니 application.yml에서 관리 가능한 설정값들도 대부분 서버 환경변수로 다시 주입하는 구조로 되어 있는 것 같습니다.
개인적으로는 설정 책임을 조금 더 명확하게 분리하는 방향이 좋을 것 같습니다. application.yml은 애플리케이션 설정 및 환경별 차이를 관리하고, DB password, API key 등 민감한 정보만 GitHub Secrets 또는 서버 환경변수로 관리하는 방식이 적절해 보입니다.
현재처럼 application.yml의 대부분 값을 환경변수로 관리하게 되면 실제 설정 정보가 application.yml, CI/CD 설정, 서버 환경변수로 분산되어 관리 포인트가 증가하고, 어떤 설정이 실제 운영에 적용되는지 추적하기 어려워질 수 있을 것 같습니다.
Spring Profile(dev/prod 등)과 별도 설정 파일 관리를 활용하면 환경별 설정도 충분히 분리 가능해 보이며, 현재 구조에서는 모든 설정을 외부 환경변수화하는 것은 관리 복잡도 대비 과한 설계일 수 있다고 생각합니다.
민감 정보와 배포 환경에서만 필요한 값 위주로 환경변수 관리를 적용하는 방향을 검토해보면 좋을 것 같습니다.
| redis: | ||
| image: redis:6.2.6-alpine | ||
| ports: | ||
| - 6379:6379 | ||
| options: >- | ||
| --health-cmd "redis-cli ping" | ||
| --health-interval 5s | ||
| --health-timeout 5s | ||
| --health-retries 10 |
There was a problem hiding this comment.
현재 저희 서비스에서는 테스트 환경에서 embedded-redis를 사용하고 있습니다. embedded-redis는 테스트 실행 시 Redis 서버 바이너리를 이용하여 테스트용 Redis 프로세스를 별도로 실행하는 방식으로 동작하는 것으로 알고 있습니다.
다만 현재 GitHub Actions 스크립트에서는 Redis 컨테이너를 별도로 띄우고 있는 것으로 보이는데, embedded-redis와 별개로 Redis 컨테이너가 필요한 이유가 궁금합니다.
| (.services.database.environment | | ||
| has("CI_BACKEND_ENV_SENTINEL") | not) and | ||
| (.services.backend.environment.CI_BACKEND_ENV_SENTINEL == | ||
| "loaded-from-backend-env") and |
There was a problem hiding this comment.
jq를 이용해 config 파일 내 필수 요소들의 존재 여부를 검증하는 부분으로 이해했습니다.
다만 CI_BACKEND_ENV_SENTINEL에 대한 검증 로직이 어떤 목적으로 추가되었는지는 명확하게 이해되지 않습니다.
해당 값이 단순히 환경 파일이 정상적으로 생성되었는지 확인하기 위한 sentinel 값인지, 혹은 특정 환경 설정 누락을 방지하기 위한 검증인지 설명 부탁드립니다.
PR 타입
작업 내용
관련 이슈: #141
테스트 결과
배포 관련 주의사항
머지 후 작업
이 PR은
dev대상입니다. 이 PR이 merge되어도 GHCR image 게시와 개발 서버 자동 배포는 실행되지 않습니다.1.
devmerge 직후devpush의CI verify성공 확인dev,masterbranch protection에서 PR 필수 설정CI verify지정master는dev -> master승격 PR만 허용CI verify가 required check 목록에 보이지 않으면, 이 PR을dev에 merge해 workflow가 한 번 실행된 뒤 다시 설정합니다.2. GitHub Development Environment 설정
Settings -> Environments -> development에서 다음을 설정합니다.master로 제한Variables:
DEV_HOST=158.180.72.167DEV_SSH_PORT=22DEV_SSH_USER=ubuntuSecrets:
DEV_SSH_PRIVATE_KEYDEV_SSH_KNOWN_HOSTS3. 서버 사전 준비
ubuntu가 Docker 명령을 실행할 수 있는지 확인read:packagestoken으로 로그인~/.docker/config.json권한을600으로 설정backend.env생성backend.env권한을600으로 설정deploy-backend.sh권한을700으로 설정backend.env에COMPOSE_PROJECT_NAME설정SPRING_JPA_HIBERNATE_DDL_AUTO=update설정devprofile 비활성 확인서버
backend.env에만 저장할 값:기존 노출 가능성이 있는 값은 최초 배포 전에 회전하고 새
backend.env에 반영합니다.4.
dev -> masterPR merge 전docker/FIRST_DEPLOYMENT.md전체 절차 검토developmentEnvironment 승인자가 대기 가능한지 확인위 준비가 끝나기 전에는
dev -> masterPR을 merge하지 않습니다.5.
mastermerge 후, 배포 승인 전mastermerge 후에는 다음 순서로 진행합니다.CI verify성공Deploy developmentjob이 Environment 승인 대기승인 전에 확인할 항목:
@sha256:digest 확보arm64인지 확인mysql_datavolume이 기존에 존재하지 않는지 확인docker compose --env-file backend.env config --quiet통과모든 검증이 끝날 때까지
Deploy development를 승인하지 않습니다.6. 최초 배포 승인 후
/actuator/health가{"status":"UP"}만 반환healthyarm64PR 체크리스트