Conversation
There was a problem hiding this comment.
Pull request overview
운영(prod) 환경 CD 파이프라인을 구축하고, 환경별(local/dev/prod) Docker Compose 및 환경변수 샘플을 정리하는 PR입니다.
Changes:
- prod/dev/local 환경별
docker-compose파일을 분리·정비하고 리소스/JVM/헬스체크 설정을 구체화 - GitHub Actions 기반 배포 워크플로우(prod/dev) 추가·수정
.env.{local,dev,prod}.sample추가 및 Dockerfile 단순화
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| docker-compose-prod.yml | 운영 프로필용 compose 추가(이미지 pull, 리소스/헬스체크/autoheal 설정) |
| docker-compose-local.yml | 로컬 compose 구조/변수/로그/헬스체크 설정 정리 및 네이밍 변경 |
| docker-compose-dev.yml | 개발 서버용 compose 신규 추가 |
| docker-compose-dev.yaml | 기존 dev compose 파일 삭제 |
| Dockerfile | 멀티스테이지 제거 후 “외부에서 빌드된 jar COPY” 방식으로 변경 |
| .github/workflows/push-cd-prod.yml | prod CI/CD 워크플로우 신규 추가(이미지 빌드/푸시, SSH 배포, 헬스체크) |
| .github/workflows/push-cd-dev.yml | dev 배포 트리거를 PR→push로 변경 |
| .env.prod.sample | 운영 환경 샘플 env 추가 |
| .env.local.sample | 로컬 환경 샘플 env 추가 |
| .env.dev.sample | 개발 환경 샘플 env 추가 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # ======================================== | ||
| # Stage 1: Build | ||
| # ======================================== | ||
| FROM eclipse-temurin:21-jdk-alpine AS builder | ||
|
|
||
| # Stage 1: Build Stage | ||
| FROM gradle:8.5-jdk21 AS builder | ||
|
|
||
| # 작업 디렉토리 설정 | ||
| WORKDIR /app | ||
|
|
||
| # Gradle 의존성 다운로드를 위한 파일만 먼저 복사 (레이어 캐싱 최적화) | ||
| COPY gradle gradle | ||
| # Gradle wrapper 및 설정 파일 먼저 복사 (의존성 캐싱) | ||
| COPY gradlew . | ||
| COPY gradle gradle | ||
| COPY build.gradle.kts . | ||
| COPY settings.gradle.kts . | ||
|
|
There was a problem hiding this comment.
런타임 컨테이너가 기본 root 사용자로 동작합니다(기존 Dockerfile에서는 non-root로 전환). 운영 환경에서는 권한 상승/컨테이너 탈출 리스크를 줄이기 위해 non-root 사용자 생성 후 USER로 전환하고, 필요한 디렉터리 권한만 부여하는 방식으로 되돌리는 것이 좋습니다.
| on: | ||
| pull_request: | ||
| branches: [ dev, main ] | ||
| types: [ opened, synchronize, reopened ] | ||
| push: | ||
| branches: [ dev ] # Only main branch, 서비스 시작 후 dev 제거 |
There was a problem hiding this comment.
트리거를 push로 변경하면서, 이 워크플로우가 실제로 서버에 배포까지 수행하게 됩니다. 그런데 워크플로우 본문에서는 여전히 docker-compose-dev.yaml을 서버로 복사/실행하도록 되어 있어(현재 PR에서 해당 파일이 삭제됨) 배포 단계가 실패합니다. docker-compose-dev.yml로 참조를 변경하거나 파일명을 원복해야 합니다.
| devnogi-gateway-dev: | ||
| build: | ||
| context: . | ||
| dockerfile: Dockerfile | ||
| image: ${DOCKER_USERNAME}/${DOCKER_REPO}:${DOCKER_IMAGE_TAG:-latest} | ||
| container_name: devnogi-gateway-app-dev |
There was a problem hiding this comment.
dev compose에서만 서비스 키가 devnogi-gateway-dev로 되어 있고(local/prod는 spring-app) 동일한 애플리케이션임에도 이름이 달라 운영/스크립트/문서에서 혼동이 생길 수 있습니다. 특별한 이유가 없다면 세 환경에서 동일한 서비스 키로 통일하는 편이 유지보수에 유리합니다.
| devnogi-gateway-dev: | |
| build: | |
| context: . | |
| dockerfile: Dockerfile | |
| image: ${DOCKER_USERNAME}/${DOCKER_REPO}:${DOCKER_IMAGE_TAG:-latest} | |
| container_name: devnogi-gateway-app-dev | |
| spring-app: | |
| build: | |
| context: . | |
| dockerfile: Dockerfile | |
| image: ${DOCKER_USERNAME}/${DOCKER_REPO}:${DOCKER_IMAGE_TAG:-latest} | |
| container_name: spring-app |
| - name: Build and push Docker image | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| push: true | ||
| tags: | | ||
| ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:latest | ||
| ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:prod | ||
| ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:prod-${{ github.sha }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| build-args: | | ||
| BUILDKIT_INLINE_CACHE=1 |
There was a problem hiding this comment.
현재 Dockerfile이 build/libs/*.jar 를 COPY하도록 변경되었는데, 이 워크플로우에서는 Gradle로 bootJar(또는 build)를 실행하는 단계가 없어 Docker 이미지 빌드가 실패합니다. Docker build 전에 JAR을 생성하는 step을 추가하거나, Dockerfile을 다시 멀티스테이지(Gradle 빌드 포함)로 되돌려 CI에서 소스만으로 빌드 가능하게 해주세요.
.github/workflows/push-cd-prod.yml
Outdated
| on: | ||
| pull_request: | ||
| branches: [ dev, main ] | ||
| types: [ opened, synchronize, reopened ] # 테스트용 |
There was a problem hiding this comment.
프로덕션 배포 워크플로우가 pull_request 이벤트( dev/main 대상 )에서 실행되도록 되어 있어 PR이 열릴 때마다 프로덕션 서버에 배포를 시도하게 됩니다. 이는 운영 안정성/보안 측면에서 위험하고, fork PR에서는 secrets가 제공되지 않아 동작 자체도 실패할 가능성이 큽니다. push(예: main) 또는 workflow_dispatch + GitHub Environments 승인(필요 시)로 트리거를 제한하는 쪽으로 조정이 필요합니다.
| ssh -i ~/.ssh/my-key.pem -p ${{ secrets.PROD_SERVER_PORT }} ${{ secrets.PROD_SERVER_USER }}@${{ secrets.PROD_SERVER_HOST }} << 'EOF' | ||
| cd /opt/devnogi-gateway | ||
|
|
||
| # Write .env.prod content to .env |
There was a problem hiding this comment.
주석이 실제 동작과 불일치합니다. 여기서는 .env가 아니라 .env.prod 파일을 쓰고 있으니(다음 줄), 주석을 .env.prod로 맞춰 혼동을 줄여주세요.
| # Write .env.prod content to .env | |
| # Write production env content to .env.prod |
| COPY gradlew . | ||
| COPY gradle gradle |
There was a problem hiding this comment.
이 Dockerfile은 build/libs/*.jar가 빌드 컨텍스트에 이미 존재한다고 가정합니다. 하지만 docker compose up --build(local/dev)나 현재 prod 워크플로우처럼 Gradle 빌드를 선행하지 않으면 COPY build/libs/*.jar에서 실패합니다. (1) 멀티스테이지로 Gradle 빌드를 포함하거나, (2) compose/CI에서 ./gradlew bootJar를 반드시 실행하도록 사용법/워크플로우를 함께 맞춰주세요.
| COPY gradle gradle | ||
| COPY build.gradle.kts . | ||
| COPY settings.gradle.kts . | ||
|
|
There was a problem hiding this comment.
Using sh -c with java ${JAVA_OPTS} -jar /app/app.jar makes the container entrypoint vulnerable to shell injection via the JAVA_OPTS environment variable. If any value that ends up in JAVA_OPTS contains shell metacharacters (for example ;, &&, or backticks) and an attacker can influence those environment variables (such as via a .env file or orchestration config), they can execute arbitrary shell commands in the container before the JVM starts. Avoid routing this through a shell; instead invoke java directly with its arguments or strictly validate and whitelist the characters allowed in JAVA_OPTS so only safe JVM options are accepted.
📋 상세 설명