diff --git a/.DS_Store b/.DS_Store index 3d89e29..1dba6df 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.dockerignore b/.dockerignore index 3661aa8..89537bc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,64 @@ +# VCS .git -.gradle +.gitignore +.gitattributes +.github/ + +# Gradle 로컬 캐시 / 빌드 산출물 (컨테이너 안에서 다시 생성되므로 불필요) +.gradle/ build/ -*.md +out/ +bin/ +HELP.md + +# IDE / OS 자동 생성 파일 +.idea/ +.vscode/ +.settings/ +.classpath +.project +.springBeans +.sts4-cache +.apt_generated +.factorypath +*.iml +*.iws +*.ipr +**/.DS_Store +Thumbs.db + +# 환경변수 / 시크릿 .env -.idea +.env.* +!.env.example +*.pem +*.key +id_rsa* + +# Docker / Compose 정의 (이미지 안에서 안 씀) +Dockerfile +.dockerignore +docker/ +docker-compose.yaml +compose*.yaml +compose*.yml + +# 배포 인프라 정의 (fluent-bit 설정 등) +deploy/ + +# 문서 +*.md +LICENSE +docs/ +CLAUDE.md + +# 테스트 코드 (Dockerfile이 `gradlew build -x test`로 스킵하므로 제외) +src/test/ + +# 로컬 실행 흔적 (데이터/로그) +postgres-data/ +logs/ +*.log + +# Windows 전용 스크립트 (리눅스 컨테이너 무관) +gradlew.bat \ No newline at end of file diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index bd4e756..c6f4c8d 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -12,7 +12,7 @@ jobs: deploy: name: Deploy to Wisoft Server runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' }} + if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main' }} steps: - name: SSH 키 설정 @@ -49,10 +49,10 @@ jobs: echo "$GHCR_PAT" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin echo "=== 최신 이미지 Pull ===" - docker compose -f docker/docker-compose.prod.yml --env-file .env pull app + docker compose -f docker-compose.prod.yml pull app echo "=== 앱 컨테이너 재시작 ===" - docker compose -f docker/docker-compose.prod.yml --env-file .env up -d app + docker compose -f docker-compose.prod.yml up -d app echo "=== Health Check (최대 60초) ===" for i in \$(seq 1 12); do @@ -65,7 +65,7 @@ jobs: done echo "Health check 실패" - docker compose -f docker/docker-compose.prod.yml logs --tail=50 app + docker compose -f docker-compose.prod.yml logs --tail=50 app exit 1 ENDSSH @@ -79,5 +79,8 @@ jobs: TARGET_USER: ${{ secrets.RASPI_TARGET_USER }} run: | ssh -o StrictHostKeyChecking=no -o ProxyJump=$JUMP_USER@$JUMP_HOST:$JUMP_PORT $TARGET_USER@$TARGET_HOST /bin/bash << ENDSSH - docker image prune -af --filter "until=72h" --filter "reference=ghcr.io/wisoft-prepair/backend-java*" || true + docker images 'ghcr.io/wisoft-prepair/backend-java*' --format '{{.ID}}' \ + | awk '!seen[\$0]++' \ + | tail -n +2 \ + | xargs -r docker rmi -f || true ENDSSH diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index db0dbe2..b28a15b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -75,6 +75,9 @@ jobs: - name: 코드 체크아웃 uses: actions/checkout@v4 + - name: QEMU 설정 + uses: docker/setup-qemu-action@v3 + - name: Docker Buildx 설정 uses: docker/setup-buildx-action@v3 diff --git a/Dockerfile b/Dockerfile index 64eb233..31b3a02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,16 +5,13 @@ FROM eclipse-temurin:21 AS builder WORKDIR /app -# Gradle 캐싱을 위해 설정 파일 먼저 복사 COPY gradlew . COPY gradle gradle -COPY build.gradle . COPY settings.gradle . +COPY build.gradle . -# 의존성 다운로드 (캐싱 레이어) RUN ./gradlew dependencies --no-daemon -# 소스코드 복사 후 빌드 COPY src src RUN ./gradlew build -x test --no-daemon @@ -29,12 +26,9 @@ RUN apt-get update && apt-get install -y ffmpeg curl && rm -rf /var/lib/apt/list COPY --from=builder /app/build/libs/*.jar app.jar -# 포트 문서화 EXPOSE 7300 -# 컨테이너 레벨 헬스체크 (Docker daemon의 status 표시용) -HEALTHCHECK --interval=24h --timeout=10s --start-period=40s --retries=3 \ - CMD curl -sf http://localhost:7300/actuator/health || exit 1 +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -sf http://localhost:7300/actuator/health -# 실행 ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml deleted file mode 100644 index 48276ec..0000000 --- a/docker-compose-dev.yml +++ /dev/null @@ -1,58 +0,0 @@ -services: - # Fluent-Bit - fluent-bit: - image: fluent/fluent-bit:latest - container_name: interview-fluent-bit - volumes: - - ./docker/fluent-bit/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro - - ./docker/fluent-bit/parsers.conf:/fluent-bit/etc/parsers.conf:ro - ports: - - "24224:24224" - environment: - - ELASTIC_USERNAME=${ELASTIC_USERNAME} - - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - networks: - - logging-network - - # PostgreSQL - postgres: - image: postgres:17 - container_name: interview-postgres - ports: - - "${POSTGRES_PORT}:5432" - environment: - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - volumes: - - postgres-data:/var/lib/postgresql/data - - # Spring Boot Application - app: - build: - context: . - dockerfile: Dockerfile.prev - container_name: interview-api - ports: - - "7300:7300" - env_file: - - .env - environment: - - SPRING_PROFILES_ACTIVE=prod - - TZ=Asia/Seoul - depends_on: - - fluent-bit - - postgres - logging: - driver: fluentd - options: - fluentd-address: localhost:24224 - tag: interview-service - fluentd-async: "true" - -volumes: - postgres-data: - -networks: - logging-network: - external: true diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..966e33e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,39 @@ +services: + postgres: + image: postgres:17 + container_name: prepair-backend-java-postgres + ports: + - "${POSTGRES_PORT}:5432" + environment: + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + app: + build: + context: . + dockerfile: Dockerfile + container_name: prepair-backend-java-api + ports: + - "7300:7300" + env_file: + - .env + environment: + - SPRING_PROFILES_ACTIVE=prod + - TZ=Asia/Seoul + depends_on: + postgres: + condition: service_healthy + +volumes: + postgres-data: + + + diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml deleted file mode 100644 index a218256..0000000 --- a/docker/docker-compose.prod.yml +++ /dev/null @@ -1,68 +0,0 @@ -services: - fluent-bit: - image: fluent/fluent-bit:3.2 - container_name: interview-fluent-bit - volumes: - - ../fluentbit/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro - - ../fluentbit/parsers.conf:/fluent-bit/etc/parsers.conf:ro - ports: - - "24224:24224" - environment: - - ELASTIC_USERNAME=${ELASTIC_USERNAME} - - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - networks: - - interview-network - - logging-network - restart: unless-stopped - - postgres: - image: postgres:17 - container_name: interview-postgres - environment: - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - volumes: - - postgres-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] - interval: 10s - timeout: 5s - retries: 5 - networks: - - interview-network - restart: unless-stopped - - app: - image: ghcr.io/wisoft-prepair/backend-java:latest - container_name: interview-api - ports: - - "7300:7300" - env_file: - - ../.env - environment: - - SPRING_PROFILES_ACTIVE=prod - - TZ=Asia/Seoul - depends_on: - postgres: - condition: service_healthy - fluent-bit: - condition: service_started - logging: - driver: fluentd - options: - fluentd-address: localhost:24224 - tag: interview-service - fluentd-async: "true" - networks: - - interview-network - restart: unless-stopped - -volumes: - postgres-data: - -networks: - interview-network: - driver: bridge - logging-network: - external: true \ No newline at end of file diff --git a/docker/fluent-bit/fluent-bit.conf b/docker/fluent-bit/fluent-bit.conf deleted file mode 100644 index d38ea45..0000000 --- a/docker/fluent-bit/fluent-bit.conf +++ /dev/null @@ -1,34 +0,0 @@ -[SERVICE] - Flush 1 - Daemon Off - Log_Level info - Parsers_File parsers.conf - -[INPUT] - Name forward - Listen 0.0.0.0 - Port 24224 - -[FILTER] - Name parser - Match * - Key_Name log - Parser json - Reserve_Data On - -[FILTER] - Name modify - Match * - Add source fluent-bit - Add host ${HOSTNAME} - -[OUTPUT] - Name es - Match * - Host elasticsearch - Port 9200 - HTTP_User ${ELASTIC_USERNAME} - HTTP_Passwd ${ELASTIC_PASSWORD} - Index interview-service - Retry_Limit 5 - Suppress_Type_Name On \ No newline at end of file diff --git a/docker/fluent-bit/parsers.conf b/docker/fluent-bit/parsers.conf deleted file mode 100644 index 2293d3f..0000000 --- a/docker/fluent-bit/parsers.conf +++ /dev/null @@ -1,5 +0,0 @@ -[PARSER] - Name json - Format json - Time_Key timestamp - Time_Format %Y-%m-%dT%H:%M:%S.%LZ diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yaml similarity index 100% rename from src/main/resources/application-local.yml rename to src/main/resources/application-local.yaml diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yaml similarity index 100% rename from src/main/resources/application-prod.yml rename to src/main/resources/application-prod.yaml diff --git a/src/main/resources/application.yml b/src/main/resources/application.yaml similarity index 97% rename from src/main/resources/application.yml rename to src/main/resources/application.yaml index 7073ce5..c60e477 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yaml @@ -9,7 +9,7 @@ logging: spring: application: - name: interview-service + name: prepair-backend-java-api profiles: active: local diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index ffc2d4b..d03f44d 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -1,6 +1,6 @@ - +