Skip to content

[26.03.25 / TASK-267] Chore - CD workflow 추가#52

Merged
ooheunda merged 7 commits intomainfrom
feature/cd-workflow
Apr 4, 2026
Merged

[26.03.25 / TASK-267] Chore - CD workflow 추가#52
ooheunda merged 7 commits intomainfrom
feature/cd-workflow

Conversation

@ooheunda
Copy link
Copy Markdown
Member

@ooheunda ooheunda commented Mar 25, 2026

🔥 변경 사항

버저닝을 포함한 CD workflow를 추가하였습니다.

  • 배포 완전 자동화
    • env 변경사항 있을 시엔 서버에서 수동으로 바꿔줘야 함!!
  • 깃 태그 기반 버저닝 적용
    • CD 트리거: git tag v${버전} git push origin v${버전}
    • 해당 버전명으로 도커 이미지가 만들어지고 서버에서도 해당 이미지로 배포됩니다.
    • 롤백시엔 actions 탭에서 롤백 버전을 입력해 수동 실행 해야함

🏷 관련 이슈

X

📸 스크린샷 (UI 변경 시 필수)

X

📌 체크리스트

  • 기능이 정상적으로 동작하는지 테스트 완료
  • 코드 스타일 가이드 준수 여부 확인
  • 관련 문서 업데이트 완료 (필요 시)

Summary by CodeRabbit

  • Chores

    • CI/CD 배포 파이프라인 개선: 태그 푸시(v* 패턴) 및 수동 트리거 지원, 빌드와 배포 단계 분리, 빌드 캐시 적용으로 안정성/효율성 향상.
    • 배포 시 API 버전 자동 해석 및 형식 검증(vX.Y.Z), 배포 대상 서버에서 지정된 태그/브랜치로 체크아웃해 실행.
    • 배포 성공/실패에 따른 Slack 알림 발송(버전, 실행자, 실행 링크 포함).
    • 도커 이미지 태그를 환경 변수로 지정 가능하도록 변경(API_VERSION, 기본값 latest).
  • New Features

    • 배포 실행 시 로그 스트리밍을 선택적으로 활성화하는 -f 플래그 추가.

@ooheunda ooheunda self-assigned this Mar 25, 2026
@ooheunda ooheunda added the enhancement New feature or request label Mar 25, 2026
@notion-workspace
Copy link
Copy Markdown

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Walkthrough

태그 푸시 및 수동 배포를 지원하도록 GitHub Actions CD를 추가/재구성하고, docker-compose.yml의 API 이미지 태그를 환경변수로 동적 처리하며, run.sh에 로그 팔로우 제어 플래그(-f)를 도입했습니다. (50단어 이내)

Changes

Cohort / File(s) Summary
CD 워크플로우
​.github/workflows/api-cd.yaml
태그 푸시(v*) 및 수동 실행(workflow_dispatch) 트리거 추가. docker-deploy 단일 job을 build-and-push(태그 이벤트 전용)와 deploy(빌드 성공/스킵 시 실행)로 분리. 이미지 다중 태깅(latest, ref_name)·캐시 사용·API_VERSION 결정 로직·서버 체크아웃 및 bash run.sh 실행·Slack 알림(성공/실패) 추가.
CI 워크플로우
​.github/workflows/api-ci.yaml
워크플로우 표시명 "Test CI" → "API CI" 변경. 주석/삭제된 Docker Hub 빌드/푸시 job 섹션 제거.
Docker Compose
docker-compose.yml
api 서비스 이미지 태그를 고정 nuung/velog-dashboard-v2-api:latest에서 환경변수 기반 nuung/velog-dashboard-v2-api:${API_VERSION:-latest}로 변경(동적 버전 지정).
배포 실행 스크립트
run.sh
-f 플래그 처리 추가하여 FOLLOW 변수로 로그 스트리밍(docker compose logs -f)을 조건부 실행하도록 변경(이전에는 항상 팔로우).

Sequence Diagram

sequenceDiagram
    participant User as 개발자/태그 발행자
    participant GHA as GitHub Actions
    participant Build as build-and-push Job
    participant DockerHub as Docker Hub
    participant Deploy as deploy Job
    participant Server as 배포 서버
    participant Slack as Slack

    User->>GHA: 태그 푸시(v*) 또는 수동 실행
    GHA->>Build: 빌드/푸시 트리거 (태그 이벤트 시)
    Build->>Build: API_VERSION 결정 (ref_name)
    Build->>DockerHub: 이미지 빌드 & 푸시 (latest, 버전 태그)
    DockerHub-->>Build: 푸시 완료
    Build-->>GHA: 빌드 상태(성공/스킵/실패)
    GHA->>Deploy: 배포 시작 (성공 또는 스킵 조건)
    Deploy->>Deploy: API_VERSION 결정 (입력 or ref_name), 유효성 검사
    Deploy->>Server: SSH 접속 → 태그/브랜치 체크아웃 → `bash run.sh` 실행
    Server-->>Deploy: 배포 완료 응답
    Deploy->>Slack: 성공/실패 알림 전송 (버전·액터·실행 링크)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Jihyun3478

Poem

🐰 태그 휙! 배포 바람 따라,
이미지는 구르니 버전도 번쩍.
서버에 닿아 조용히 재시작,
슬랙엔 소식이 톡 하고 울리네.
폴짝, -f로 보고 싶은 이여 춤추자!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 CD 워크플로우 추가라는 주요 변경사항을 명확하게 요약하고 있으며, 제공된 코드 변경사항과 PR 목표와 일치합니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/cd-workflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
.github/workflows/api-cd.yaml (1)

61-61: sed ... | bash 패턴의 잠재적 취약성

run.sh를 sed로 필터링하여 bash에 파이프하는 방식은 동작하지만, 스크립트 변경 시 예상치 못한 동작이 발생할 수 있습니다.

대안으로 run.sh--no-logs 플래그를 추가하거나 별도의 배포용 스크립트를 만드는 것을 고려해 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/api-cd.yaml at line 61, The workflow uses a brittle "sed
'/docker compose logs -f/d' run.sh | bash" pattern which risks unexpected
behavior when run.sh changes; modify run.sh to accept a flag like --no-logs (or
create a dedicated deploy script) that disables the "docker compose logs -f"
behavior, then update the workflow step to invoke the script directly (e.g.,
"bash run.sh --no-logs" or call the new deploy script) instead of filtering and
piping via sed; update references in the workflow step that currently contain
the sed pipeline and the run.sh script to use the new flag/script.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/api-cd.yaml:
- Around line 49-75: The current sequential SSH deployment steps "Deploy to
Server 1" and "Deploy to Server 2" may stop the second deploy if the first
fails; update the workflow to ensure resilience by either adding
continue-on-error: true to the failing-prone job steps (the two job steps named
"Deploy to Server 1" and "Deploy to Server 2") so a failure on one host doesn't
block the other, or refactor these into a matrix/strategy job that runs the same
ssh-action in parallel across hosts (use a matrix entry for
DEPLOY_HOST/DEPLOY_SSH_KEY pairs and the existing script block) so both servers
are deployed concurrently and independently.
- Around line 77-90: The workflow uses slackapi/slack-github-action@v1.24.0 in
the "Notify Slack on Success" and "Notify Slack on Failure" steps; update both
uses entries to slackapi/slack-github-action@v3.0.1 and then verify the action's
input/schema changes (e.g., payload format and environment variable names)
against the v3.0.1 docs to adjust the "payload" content and SLACK_WEBHOOK_URL
usage if required so the notifications continue to work.

---

Nitpick comments:
In @.github/workflows/api-cd.yaml:
- Line 61: The workflow uses a brittle "sed '/docker compose logs -f/d' run.sh |
bash" pattern which risks unexpected behavior when run.sh changes; modify run.sh
to accept a flag like --no-logs (or create a dedicated deploy script) that
disables the "docker compose logs -f" behavior, then update the workflow step to
invoke the script directly (e.g., "bash run.sh --no-logs" or call the new deploy
script) instead of filtering and piping via sed; update references in the
workflow step that currently contain the sed pipeline and the run.sh script to
use the new flag/script.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6bf2bfec-0b5b-476b-94dc-257d06b1e112

📥 Commits

Reviewing files that changed from the base of the PR and between ce7501e and 80d14f3.

📒 Files selected for processing (3)
  • .github/workflows/api-cd.yaml
  • .github/workflows/api-ci.yaml
  • docker-compose.yml

Copy link
Copy Markdown
Member

@six-standard six-standard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드 잘 읽었습니다!

좋았던 점

  • 입력값으로 롤백할 버전을 넣을 수 있는 점이 좋았습니다!

Copy link
Copy Markdown
Member

@Nuung Nuung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋았던 점

  • tag 활용해 CI/CD 한 호흡까지 가져가고, CI랑 CD분리 하는 것도 너무 좋았어요.
  • 이게 env 가 분리되면 더 깔끔해질 것 같아요!

아쉬운 점

  • 코멘트 부분만 체크해주세요!
  • x.y.z 으로 버저닝하는걸 시멘틱 버저닝이라고 하잖아요~ 여기에 대한 룰 하나 정도는 있어야 할 것 같아요. 뭘 바꾸면 x 를 바꾸고 뭘 바꾸면 y, 뭘 바꾸면 z 이지? 같은 내용이요.
  • 버전 삭제의 경우 github tag 와 docker 버전 싱크가 안맞을 경우가 있을 것 같다는 리스크 정도/

Comment on lines +49 to +75
- name: Deploy to Server 1
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST_1 }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY_1 }}
command_timeout: 10m
script: |
export GIT_SSH_COMMAND='ssh -i ~/.ssh/velog-dashboard-v2-api'
export API_VERSION=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}
cd ${{ secrets.DEPLOY_PATH }}
git pull origin main
sed '/docker compose logs -f/d' run.sh | bash

- name: Deploy to Server 2
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST_2 }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY_2 }}
command_timeout: 10m
script: |
export GIT_SSH_COMMAND='ssh -i ~/.ssh/velog-dashboard-v2-api'
export API_VERSION=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}
cd ${{ secrets.DEPLOY_PATH }}
git pull origin main
sed '/docker compose logs -f/d' run.sh | bash
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matrix strategy 라는게 있는데, 이렇게 step 으로 쪼개지 말고 yaml 에서 특정 블록을 만들어서, 진행할 수 있거든요! 찾아보시면 좋을 것 같아요. 더욱이 server1 만 터지면 어쩌지? 라는 생각도 들어요.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인사이트 감사합니다! matrix strategy 찾아보았는데 이런 이슈(matrix 값에 secrets 사용불가) 때문에 현재 스크립트에 적용은 어려울 것 같습니다.
그리고 matrix strategy를 사용하면 기본적으로 병렬 실행이 되는 것 같은데, 저희같이 무중단 배포 중인 경우에도 병렬 배포를 해도 괜찮나요??

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞네요! 제가 디테일을 잘 못 생각했네요! 원래 방향 설계가 나을 것 같아요. @ooheunda

Comment on lines +49 to +75
- name: Deploy to Server 1
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST_1 }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY_1 }}
command_timeout: 10m
script: |
export GIT_SSH_COMMAND='ssh -i ~/.ssh/velog-dashboard-v2-api'
export API_VERSION=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}
cd ${{ secrets.DEPLOY_PATH }}
git pull origin main
sed '/docker compose logs -f/d' run.sh | bash

- name: Deploy to Server 2
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST_2 }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY_2 }}
command_timeout: 10m
script: |
export GIT_SSH_COMMAND='ssh -i ~/.ssh/velog-dashboard-v2-api'
export API_VERSION=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}
cd ${{ secrets.DEPLOY_PATH }}
git pull origin main
sed '/docker compose logs -f/d' run.sh | bash
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사견으로 이거 그냥 스크립트만 실행하게 그냥 run.sh 를 바꾸든, 자동 배포용 스크립트를 만들든 하는게 가장 좋을 것 같아요

@Nuung
Copy link
Copy Markdown
Member

Nuung commented Apr 4, 2026

추가적인 사견

@ooheunda

  1. 저희 docs 도 업데이트가 되었으면 해요!

https://www.notion.so/nuung/26-01-28-1be6299fd666805d873ad181123e30b7
image

  1. 시멘틱 버저닝의 경우 저는 아래와 같은 룰을 사용했었습니다.
image

# dockerfile: Dockerfile
# image: velog-dashboard-v2-api
image: nuung/velog-dashboard-v2-api:latest
image: nuung/velog-dashboard-v2-api:${API_VERSION:-latest}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 가물가물해서 그런데 이 환경변수 깃헙에서 자동으로 tag 버전 가져오는거 맞나요? 저는 GITHUB_REF_NAME 를 썻었던 것 같아서

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 지정한 임의 환경변수입니다!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 오케이 전역 변수 중 있는 줄

Copy link
Copy Markdown
Contributor

@Jihyun3478 Jihyun3478 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코멘트가 너무 늦어져서 죄송해요🥲 고생하셨습니다!!

좋았던 점

  • CI와 CD를 각각의 파일로 분리해주셔서 각 워크플로우의 책임이 명확해진 부분이 좋았습니다.
  • 성공 및 실패 시 Slack 알림이 오도록 추가해주신 부분이 좋았습니다!

궁금한 점

  • 현재 API_VERSION 환경변수를 export로 설정한 뒤 docker-compose.yml에서 참조하는 구조인데, 해당 변수가 실제로 잘 전달되고 있는지 여쭤보고 싶습니다!

@ooheunda
Copy link
Copy Markdown
Member Author

ooheunda commented Apr 4, 2026

궁금한 점

  • 현재 API_VERSION 환경변수를 export로 설정한 뒤 docker-compose.yml에서 참조하는 구조인데, 해당 변수가 실제로 잘 전달되고 있는지 여쭤보고 싶습니다!

@Jihyun3478 일단 테스트상으론 문제 없긴 했습니다! 말씀주셔서 다시 검토해보니 괜찮을 것 같습니다!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/api-cd.yaml:
- Around line 59-60: The workflow currently always runs "git pull origin main"
which mixes the selected image tag with whatever is currently on main; instead,
fetch the repo and checkout the exact ref you intend to deploy (the tag or
commit referenced by the workflow) before running docker-compose/run.sh. Replace
the unconditional "git pull origin main" steps with a fetch + checkout sequence
that uses the workflow's deployment ref variable (e.g., the tag or
GITHUB_REF/GITHUB_SHA or the action input you use for rollbacks) so the working
tree matches the image version, and ensure the checkout is forced/clean to avoid
leftover changes.
- Line 55: 현재 설정된 command_timeout 값을 늘리거나 헬스체크 재시도 예산을 줄여 SSH 타임아웃 문제를 해결하세요: CI
워크플로의 command_timeout 설정(현재 10m)을 15m 이상으로 변경(워크플로의 모든 command_timeout 항목에
적용)하거나, 대신 run.sh 스크립트에서 호출되는 wait_for_service 함수의 재시도 횟수/초기 지연 시간을 낮춰 헬스체크 총 소요
시간을 줄이세요(문제가 되는 wait_for_service 호출을 수정).
- Around line 8-11: The workflow accepts a raw input named "version" and injects
it unescaped into shell commands and JSON payloads, creating shell-injection and
JSON-break risks; fix by quoting the variable when used in shell (e.g., export
API_VERSION="${{ inputs.version }}" in the step that sets it) and/or add a
dedicated validation step that uses a strict regex (semantic-version or
allowed-pattern) to validate/normalize inputs and then expose the validated
value as a step output which all downstream steps (the ones currently inserting
the version into commands and JSON) must consume instead of the raw
inputs.version; ensure all JSON payload assembly also uses the validated value
and proper quoting/escaping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0a214871-19c9-4040-8112-37db681db0cb

📥 Commits

Reviewing files that changed from the base of the PR and between 80d14f3 and 1b74cc6.

📒 Files selected for processing (2)
  • .github/workflows/api-cd.yaml
  • run.sh

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
.github/workflows/api-cd.yaml (1)

59-72: ⚠️ Potential issue | 🟡 Minor

git checkout 실패 가능성에 대비하세요.

현재 git checkout "$API_VERSION"이 로컬 변경사항이 있을 경우 실패할 수 있습니다. 배포 서버에서 예기치 않은 파일 수정이 있을 때 배포가 중단될 수 있으므로, 강제 체크아웃을 고려하세요.

♻️ 강제 체크아웃 적용 예시
             git fetch --tags
-            git checkout "$API_VERSION"
+            git checkout -f "$API_VERSION"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/api-cd.yaml around lines 59 - 72, The deployment script's
git checkout step (git checkout "$API_VERSION") can fail if there are local
changes; update the script block to perform a forced, clean checkout before
running run.sh by fetching tags, forcing checkout of "$API_VERSION" (or
resetting to the remote ref) and removing untracked files so local modifications
won't block deployment—locate the script section containing git fetch --tags and
git checkout "$API_VERSION" and replace the checkout with a robust sequence that
forces the branch/tag and cleans untracked files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/api-cd.yaml:
- Around line 74-87: The "Deploy to Server 2" step (uses:
appleboy/ssh-action@v1) currently runs git checkout "$API_VERSION" without
forcing; update the script in that step to use a forced checkout (git checkout
-f "$API_VERSION") just like the Server 1 change so uncommitted changes don't
block deployment — locate the "Deploy to Server 2" step and replace the git
checkout command in its script block accordingly.

---

Duplicate comments:
In @.github/workflows/api-cd.yaml:
- Around line 59-72: The deployment script's git checkout step (git checkout
"$API_VERSION") can fail if there are local changes; update the script block to
perform a forced, clean checkout before running run.sh by fetching tags, forcing
checkout of "$API_VERSION" (or resetting to the remote ref) and removing
untracked files so local modifications won't block deployment—locate the script
section containing git fetch --tags and git checkout "$API_VERSION" and replace
the checkout with a robust sequence that forces the branch/tag and cleans
untracked files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3ecc109-4f01-4b00-a0f6-bbe34107283a

📥 Commits

Reviewing files that changed from the base of the PR and between 1b74cc6 and 462540b.

📒 Files selected for processing (1)
  • .github/workflows/api-cd.yaml

@ooheunda ooheunda merged commit bf6ef51 into main Apr 4, 2026
1 of 5 checks passed
@ooheunda ooheunda deleted the feature/cd-workflow branch April 4, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants