From 70041a1207628c42b47ec7b2e16d82ae1d5d63df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=ED=98=84?= Date: Sun, 17 May 2026 21:10:05 +0900 Subject: [PATCH 1/3] =?UTF-8?q?chore:=20=ED=99=88=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=85=80=ED=94=84=ED=98=B8=EC=8A=A4=ED=8A=B8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile 멀티스테이지화 (gradle:8.5-jdk17 -> eclipse-temurin:17-jre) 호스트에서 사전 빌드 없이 docker build만으로 산출물 생성 - deploy.yml 추가: 셀프호스트 러너(macmini-runner-todaysound)에서 git fetch -> docker compose build -> up -d -> caddy 경유 헬스체크 - cicd.yml 제거: EC2 SSH 기반 배포 종료 (AWS 이관 완료) --- .github/workflows/cicd.yml | 127 ----------------------------------- .github/workflows/deploy.yml | 62 +++++++++++++++++ Dockerfile | 14 ++-- 3 files changed, 71 insertions(+), 132 deletions(-) delete mode 100644 .github/workflows/cicd.yml create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml deleted file mode 100644 index 8905b26..0000000 --- a/.github/workflows/cicd.yml +++ /dev/null @@ -1,127 +0,0 @@ -name: TodaySound Server CI/CD - -on: - push: - branches: - - main - - dev - workflow_dispatch: - -env: - IMAGE_NAME: todaysound-server - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: "17" - - - name: Cache Gradle - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - - - name: Build Spring Boot app (skip tests) - run: ./gradlew clean bootJar -x test -x asciidoctor - env: - SPRING_PROFILES_ACTIVE: ci - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and tag Docker image - run: | - GIT_SHA=${GITHUB_SHA::7} - docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest -t ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${GIT_SHA} . - - - name: Push Docker image - run: | - GIT_SHA=${GITHUB_SHA::7} - docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest - docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${GIT_SHA} - - - name: Deploy to EC2 via SSH - uses: appleboy/ssh-action@v1.0.3 - env: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - IMAGE_NAME: ${{ env.IMAGE_NAME }} - DB_URL: ${{ secrets.DB_URL }} - DB_USERNAME: ${{ secrets.DB_USERNAME }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} - FCM_JSON: ${{ secrets.FCM_JSON }} - with: - host: ${{ secrets.EC2_HOST }} - username: ${{ secrets.EC2_USERNAME }} - key: ${{ secrets.EC2_PRIVATE_KEY }} - script_stop: true - envs: DOCKER_USERNAME,IMAGE_NAME,DB_URL,DB_USERNAME,DB_PASSWORD,FCM_JSON - script: | - set -e - - cd ~/todaysound-be - - cat > .env << EOF - DOCKER_USERNAME=${DOCKER_USERNAME} - DB_URL=${DB_URL} - DB_USERNAME=${DB_USERNAME} - DB_PASSWORD=${DB_PASSWORD} - FCM_JSON=${FCM_JSON} - EOF - - sudo docker-compose down - sudo docker-compose pull app - sudo docker-compose up -d - - docker image prune -f - - # Health check (local) - try basic endpoint first - echo "Waiting for app to start..." && sleep 45 - - # Show container status - APP_NAME="todaysound-server" - PORT=8080 - sudo docker ps -a | grep ${APP_NAME} || true - - # Try basic health check first (disable set -e for this section) - set +e - HEALTH_CHECK_PASSED=false - for i in {1..15}; do - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${PORT}/actuator/health 2>/dev/null) - if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then - echo "Health check passed! HTTP code: $HTTP_CODE" - HEALTH_CHECK_PASSED=true - break - else - echo "Attempt $i failed, HTTP code: $HTTP_CODE, retrying in 5s..." - sleep 5 - fi - done - set -e - - if [ "$HEALTH_CHECK_PASSED" = "false" ]; then - echo "Health check failed after 15 attempts" - sudo docker logs ${APP_NAME} --tail 50 || true - exit 1 - fi - - # Optional domain health check (non-blocking) - curl -f http://today-sound.com/ || echo "Domain check failed, but continuing..." diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..ac046e5 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,62 @@ +name: Deploy to homeserver + +on: + push: + branches: [main] + paths: + - "src/**" + - "build.gradle" + - "settings.gradle" + - "gradle/**" + - "Dockerfile" + - ".github/workflows/deploy.yml" + workflow_dispatch: + +concurrency: + group: deploy-homeserver-todaysound + cancel-in-progress: false + +jobs: + deploy: + runs-on: [self-hosted, homeserver] + timeout-minutes: 25 + + env: + REPO_DIR: /Users/parkparkjihyeon/homeserver/services/todaysound/repo + APP_DIR: /Users/parkparkjihyeon/homeserver/services/todaysound + + steps: + - name: Sync repo on host + run: | + cd "$REPO_DIR" + git fetch --all --prune + git reset --hard origin/main + + - name: Build backend image + run: | + cd "$APP_DIR" + DOCKER_BUILDKIT=1 docker compose build + + - name: Roll out + run: | + cd "$APP_DIR" + docker compose up -d + docker image prune -f + + - name: Smoke test (e2e via caddy with correct Host) + run: | + for i in 1 2 3 4 5 6 7 8 9 10; do + code=$(docker exec caddy wget -S -q -O- \ + --header "Host: today-sound.com" \ + http://localhost/actuator/health 2>&1 \ + | awk '/HTTP\//{print $2; exit}') + if [ "$code" = "200" ] || [ "$code" = "401" ] || [ "$code" = "403" ]; then + echo "Backend OK ($code)" + exit 0 + fi + echo "Retry $i (code=$code)..." + sleep 6 + done + echo "Backend healthcheck failed" + docker logs todaysound-server --tail 100 || true + exit 1 diff --git a/Dockerfile b/Dockerfile index b252e82..a5673fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,14 @@ -FROM eclipse-temurin:17-jre - -WORKDIR /app +# syntax=docker/dockerfile:1.7 -COPY build/libs/*SNAPSHOT*.jar /app/app.jar +FROM gradle:8.5-jdk17 AS build +WORKDIR /src +COPY --chown=gradle:gradle . . +RUN --mount=type=cache,target=/home/gradle/.gradle \ + gradle --no-daemon clean bootJar -x test -x asciidoctor +FROM eclipse-temurin:17-jre +WORKDIR /app +COPY --from=build /src/build/libs/*SNAPSHOT*.jar /app/app.jar ENV SPRING_PROFILES_ACTIVE=prod EXPOSE 8080 - ENTRYPOINT ["java","-jar","/app/app.jar"] From 0eaae8d42e3bfd4c10843cc2ae8878ced80647ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=ED=98=84?= Date: Sun, 17 May 2026 21:18:02 +0900 Subject: [PATCH 2/3] =?UTF-8?q?chore:=20deploy.yml=20=ED=8A=B8=EB=A6=AC?= =?UTF-8?q?=EA=B1=B0=EB=A5=BC=20dev=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main은 운영에서 거의 사용하지 않으므로 dev 푸시 시점에 배포되도록 변경. - on.push.branches: main -> dev - 러너의 git reset --hard origin/main -> origin/dev --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ac046e5..78d8c2e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,7 +2,7 @@ name: Deploy to homeserver on: push: - branches: [main] + branches: [dev] paths: - "src/**" - "build.gradle" @@ -30,7 +30,7 @@ jobs: run: | cd "$REPO_DIR" git fetch --all --prune - git reset --hard origin/main + git reset --hard origin/dev - name: Build backend image run: | From 904d515370e4261ff2f3c9153c3266a3ba270616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=ED=98=84?= Date: Sun, 17 May 2026 21:56:47 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20Dockerfile=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EB=8B=A8=EA=B3=84=EB=A5=BC=20gradle=20wrapper=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FROM gradle:8.5-jdk17 -> eclipse-temurin:17-jdk + ./gradlew - gradle-wrapper.properties가 8.14.3을 지정하므로 wrapper를 따라야 로컬/CI 환경 버전이 일치 - -x test -x asciidoctor 옵션은 의도적으로 유지 (기존 cicd.yml과 동일, swagger-ui는 springdoc 어노테이션 기반이라 asciidoctor와 무관) --- Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index a5673fb..26ab971 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,11 @@ # syntax=docker/dockerfile:1.7 -FROM gradle:8.5-jdk17 AS build +FROM eclipse-temurin:17-jdk AS build WORKDIR /src -COPY --chown=gradle:gradle . . -RUN --mount=type=cache,target=/home/gradle/.gradle \ - gradle --no-daemon clean bootJar -x test -x asciidoctor +COPY . . +RUN --mount=type=cache,target=/root/.gradle \ + chmod +x gradlew && \ + ./gradlew --no-daemon clean bootJar -x test -x asciidoctor FROM eclipse-temurin:17-jre WORKDIR /app