Skip to content

refactor: prod/stage 환경 nginx 블루/그린 배포 방식 도입#756

Merged
Hexeong merged 4 commits into
developfrom
refactor/607-adjust-blue-green-deploy
Jun 15, 2026
Merged

refactor: prod/stage 환경 nginx 블루/그린 배포 방식 도입#756
Hexeong merged 4 commits into
developfrom
refactor/607-adjust-blue-green-deploy

Conversation

@Hexeong

@Hexeong Hexeong commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

관련 이슈

작업 내용

단일 EC2에서 Nginx upstream 전환 방식으로 무중단 Blue/Green 배포를 구현합니다.

Blue/Green 슬롯 구성

Blue Green
APP_PORT 8080 9080
MANAGEMENT_PORT 8081 (기존 actuator 포트 유지) 9081

docker-compose 슬롯 파라미터화

  • container_name, SERVER_PORT, MANAGEMENT_SERVER_PORT를 슬롯 변수로 매개변수화
  • 동일 호스트에서 blue/green 컨테이너가 격리된 상태로 동시 기동 가능

dev-cd.yml / prod-cd.yml 배포 흐름

  1. upstream.conf 파싱으로 현재 active 슬롯 감지
  2. inactive 슬롯에 신규 컨테이너 기동
  3. /actuator/health 폴링으로 기동 완료 대기 (최대 150초)
  4. upstream.conf 교체 + nginx -s reload로 트래픽 무중단 전환
  5. 구 컨테이너 종료

application.yml

  • management.endpoints.web.exposure.includehealth 추가 (헬스체크용)

특이 사항

  • Blue는 기존 actuator 포트(8081)를 그대로 유지하여 Prometheus scraping 설정 호환성 보장
  • Green은 9080/9081 포트를 사용하므로, Green이 active인 상태에서는 모니터링 스크레이핑 포트 변경 필요 → 인프라 레포 이슈로 별도 트래킹
  • 머지 전 수동 작업 필요: 첫 배포 후 레거시 컨테이너(solid-connection-server, solid-connection-dev) 수동 제거 필요 (레거시 컨테이너가 8080/8081 점유)

리뷰 요구사항 (선택)

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

이번 PR은 dev/prod 두 환경 모두에 Blue/Green 무중단 배포 방식을 도입합니다.

  1. 애플리케이션 설정 변경

    • application.yml에서 actuator 노출 엔드포인트에 health를 추가했습니다.
  2. Docker Compose 슬롯 템플릿화

    • docker-compose.dev.ymldocker-compose.prod.yml 두 파일 모두에서 container_name${SLOT:-blue} 형태로, 포트를 ${APP_PORT} / ${MANAGEMENT_PORT} 환경변수로 주입하도록 변경했습니다.
  3. Dev CD 워크플로우 배포 스크립트 교체 (dev-cd.yml)

    • 기존 단순 compose down/up 재시작 방식이 제거되었습니다.
    • upstream.conf 기반 활성 슬롯 판별 → MySQL 확인 → GHCR 이미지 pull → 신규 슬롯 기동 → /actuator/health 헬스체크(최대 150초, 실패 시 롤백) → nginx reload → 구 슬롯 종료 → 이미지 정리 순서의 스크립트로 교체되었습니다.
  4. Prod CD 워크플로우 배포 스크립트 교체 (prod-cd.yml)

    • sparse-checkout 방식의 설정 파일 전달이 scp 직접 전송으로 교체되었습니다.
    • dev와 동일한 blue/green 배포 흐름이 적용되었으며, image_tag 출력 스텝도 함께 조정되었습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • wibaek
  • whqtker
  • JAEHEE25
  • sukangpunch
  • Gyuhyeok99
  • lsy1307
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항인 Blue/Green 무중단 배포 도입을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed 변경사항이 Issue #607의 모든 TO-BE 요구사항을 충족합니다: Blue/Green 슬롯 구성, 비활성 슬롯 기동, 헬스체크 폴링(150초), upstream.conf 교체, 기존 컨테이너 종료.
Out of Scope Changes check ✅ Passed 모든 변경사항이 Blue/Green 무중단 배포 구현과 직접 관련되어 있으며, 스코프 외 변경사항은 없습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed PR 설명이 필수 섹션(관련 이슈, 작업 내용, 특이 사항, 리뷰 요구사항)을 모두 포함하고 있으며, 블루/그린 배포 전략을 명확하게 설명하고 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/607-adjust-blue-green-deploy

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.

@Hexeong Hexeong force-pushed the refactor/607-adjust-blue-green-deploy branch from 2207f68 to 3934f41 Compare June 15, 2026 10:42
@Hexeong Hexeong self-assigned this Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
.github/workflows/prod-cd.yml (1)

62-67: ⚡ Quick win

Release 태그 및 수동 입력 태그의 command injection 위험성

정적 분석 도구가 ${{ github.ref_name }}${{ inputs.tag_name }}에서 템플릿 인젝션을 경고하고 있어요.

  1. Release 태그: 악의적인 태그명(예: v1.0.0$(whoami))이 생성될 경우 셸 명령어 실행 위험이 있어요.
  2. 수동 입력: workflow_dispatch 권한이 있는 사용자가 악의적인 입력을 할 수 있어요.

다만 실제 위험도는 낮습니다:

  • Docker 태그 명명 규칙이 특수문자를 제한합니다.
  • Release 생성 권한은 신뢰할 수 있는 사용자로 제한되어야 합니다.
🔐 환경 변수를 통한 안전한 전달 방식 제안
      - name: Define image name and tag
        id: image_meta
+       env:
+         REF_NAME: ${{ github.ref_name }}
+         INPUT_TAG: ${{ inputs.tag_name }}
        run: |
          OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')

          # Trigger가 Release인 경우: Release의 Tag Name (예: v1.0.0) 사용
          if [ "${{ github.event_name }}" == "release" ]; then
-           IMAGE_TAG="${{ github.ref_name }}"
+           IMAGE_TAG="${REF_NAME}"
          # Trigger가 수동(workflow_dispatch)인 경우: 입력받은 tag_name 사용
          else
-           IMAGE_TAG="${{ inputs.tag_name }}"
+           IMAGE_TAG="${INPUT_TAG}"
          fi

환경 변수로 전달하면 셸 문맥에서의 직접 확장을 방지할 수 있어요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-cd.yml around lines 62 - 67, The IMAGE_TAG assignment
in the conditional block uses direct GitHub context variable expansion (`${{
github.ref_name }}` and `${{ inputs.tag_name }}`) within the shell script, which
poses a potential command injection risk through template injection. Refactor
this to use environment variables that are set before the shell command runs,
rather than expanding the GitHub context variables directly within the shell
script itself. This will prevent shell metacharacters from being interpreted as
commands while still correctly capturing the tag value from either the release
event or the manual workflow input.

Source: Linters/SAST tools

.github/workflows/dev-cd.yml (1)

122-128: 💤 Low value

슬롯 판별 로직의 fallback 값 "8081" 일관성 개선

두 워크플로우 모두 upstream.conf를 읽지 못할 때 fallback 값으로 "8081"(management 포트)을 사용하고 있어요. 이 값은 APP 포트(8080/9080)와 혼동될 수 있습니다.

  • .github/workflows/dev-cd.yml#L122-L128: fallback을 "9080"으로 변경하여 "green active → blue 배포" 의도를 명확히
  • .github/workflows/prod-cd.yml#L138-L144: dev와 동일하게 fallback 값 수정
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/dev-cd.yml around lines 122 - 128, The fallback value used
when upstream.conf cannot be read is currently "8081" (a management port) in
both workflow files, which creates confusion about which application slot is
active. In `.github/workflows/dev-cd.yml` at lines 122-128, change the fallback
value in the UPSTREAM_PORT grep command from "8081" to "9080" to clarify the
default assumption that green slot is active and blue will be deployed to. Apply
the same change in `.github/workflows/prod-cd.yml` at lines 138-144 to maintain
consistency across both workflows.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/dev-cd.yml:
- Around line 143-145: The docker login command in the GHCR authentication step
may be recorded in the remote server's shell history when executed via SSH,
creating a security risk where the GitHub token could be exposed. Verify and
configure the remote server's shell history settings to prevent sensitive
commands from being recorded by checking the HISTCONTROL and HISTFILE
environment variables, and configure them appropriately (such as setting
HISTCONTROL=ignorespace or HISTFILE=/dev/null) before executing the docker login
command. Alternatively, ensure the command is run with history expansion
disabled or use a secure authentication method that doesn't rely on piping
credentials through shell commands.

---

Nitpick comments:
In @.github/workflows/dev-cd.yml:
- Around line 122-128: The fallback value used when upstream.conf cannot be read
is currently "8081" (a management port) in both workflow files, which creates
confusion about which application slot is active. In
`.github/workflows/dev-cd.yml` at lines 122-128, change the fallback value in
the UPSTREAM_PORT grep command from "8081" to "9080" to clarify the default
assumption that green slot is active and blue will be deployed to. Apply the
same change in `.github/workflows/prod-cd.yml` at lines 138-144 to maintain
consistency across both workflows.

In @.github/workflows/prod-cd.yml:
- Around line 62-67: The IMAGE_TAG assignment in the conditional block uses
direct GitHub context variable expansion (`${{ github.ref_name }}` and `${{
inputs.tag_name }}`) within the shell script, which poses a potential command
injection risk through template injection. Refactor this to use environment
variables that are set before the shell command runs, rather than expanding the
GitHub context variables directly within the shell script itself. This will
prevent shell metacharacters from being interpreted as commands while still
correctly capturing the tag value from either the release event or the manual
workflow input.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5203ee5a-31e7-41df-8f0f-237e94da7a4f

📥 Commits

Reviewing files that changed from the base of the PR and between 0186e89 and 2207f68.

📒 Files selected for processing (5)
  • .github/workflows/dev-cd.yml
  • .github/workflows/prod-cd.yml
  • docker-compose.dev.yml
  • docker-compose.prod.yml
  • src/main/resources/application.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🧹 Nitpick comments (2)
.github/workflows/prod-cd.yml (1)

62-67: ⚡ Quick win

Release 태그 및 수동 입력 태그의 command injection 위험성

정적 분석 도구가 ${{ github.ref_name }}${{ inputs.tag_name }}에서 템플릿 인젝션을 경고하고 있어요.

  1. Release 태그: 악의적인 태그명(예: v1.0.0$(whoami))이 생성될 경우 셸 명령어 실행 위험이 있어요.
  2. 수동 입력: workflow_dispatch 권한이 있는 사용자가 악의적인 입력을 할 수 있어요.

다만 실제 위험도는 낮습니다:

  • Docker 태그 명명 규칙이 특수문자를 제한합니다.
  • Release 생성 권한은 신뢰할 수 있는 사용자로 제한되어야 합니다.
🔐 환경 변수를 통한 안전한 전달 방식 제안
      - name: Define image name and tag
        id: image_meta
+       env:
+         REF_NAME: ${{ github.ref_name }}
+         INPUT_TAG: ${{ inputs.tag_name }}
        run: |
          OWNER_LOWERCASE=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')

          # Trigger가 Release인 경우: Release의 Tag Name (예: v1.0.0) 사용
          if [ "${{ github.event_name }}" == "release" ]; then
-           IMAGE_TAG="${{ github.ref_name }}"
+           IMAGE_TAG="${REF_NAME}"
          # Trigger가 수동(workflow_dispatch)인 경우: 입력받은 tag_name 사용
          else
-           IMAGE_TAG="${{ inputs.tag_name }}"
+           IMAGE_TAG="${INPUT_TAG}"
          fi

환경 변수로 전달하면 셸 문맥에서의 직접 확장을 방지할 수 있어요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prod-cd.yml around lines 62 - 67, The IMAGE_TAG assignment
in the conditional block uses direct GitHub context variable expansion (`${{
github.ref_name }}` and `${{ inputs.tag_name }}`) within the shell script, which
poses a potential command injection risk through template injection. Refactor
this to use environment variables that are set before the shell command runs,
rather than expanding the GitHub context variables directly within the shell
script itself. This will prevent shell metacharacters from being interpreted as
commands while still correctly capturing the tag value from either the release
event or the manual workflow input.

Source: Linters/SAST tools

.github/workflows/dev-cd.yml (1)

122-128: 💤 Low value

슬롯 판별 로직의 fallback 값 "8081" 일관성 개선

두 워크플로우 모두 upstream.conf를 읽지 못할 때 fallback 값으로 "8081"(management 포트)을 사용하고 있어요. 이 값은 APP 포트(8080/9080)와 혼동될 수 있습니다.

  • .github/workflows/dev-cd.yml#L122-L128: fallback을 "9080"으로 변경하여 "green active → blue 배포" 의도를 명확히
  • .github/workflows/prod-cd.yml#L138-L144: dev와 동일하게 fallback 값 수정
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/dev-cd.yml around lines 122 - 128, The fallback value used
when upstream.conf cannot be read is currently "8081" (a management port) in
both workflow files, which creates confusion about which application slot is
active. In `.github/workflows/dev-cd.yml` at lines 122-128, change the fallback
value in the UPSTREAM_PORT grep command from "8081" to "9080" to clarify the
default assumption that green slot is active and blue will be deployed to. Apply
the same change in `.github/workflows/prod-cd.yml` at lines 138-144 to maintain
consistency across both workflows.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/dev-cd.yml:
- Around line 143-145: The docker login command in the GHCR authentication step
may be recorded in the remote server's shell history when executed via SSH,
creating a security risk where the GitHub token could be exposed. Verify and
configure the remote server's shell history settings to prevent sensitive
commands from being recorded by checking the HISTCONTROL and HISTFILE
environment variables, and configure them appropriately (such as setting
HISTCONTROL=ignorespace or HISTFILE=/dev/null) before executing the docker login
command. Alternatively, ensure the command is run with history expansion
disabled or use a secure authentication method that doesn't rely on piping
credentials through shell commands.

---

Nitpick comments:
In @.github/workflows/dev-cd.yml:
- Around line 122-128: The fallback value used when upstream.conf cannot be read
is currently "8081" (a management port) in both workflow files, which creates
confusion about which application slot is active. In
`.github/workflows/dev-cd.yml` at lines 122-128, change the fallback value in
the UPSTREAM_PORT grep command from "8081" to "9080" to clarify the default
assumption that green slot is active and blue will be deployed to. Apply the
same change in `.github/workflows/prod-cd.yml` at lines 138-144 to maintain
consistency across both workflows.

In @.github/workflows/prod-cd.yml:
- Around line 62-67: The IMAGE_TAG assignment in the conditional block uses
direct GitHub context variable expansion (`${{ github.ref_name }}` and `${{
inputs.tag_name }}`) within the shell script, which poses a potential command
injection risk through template injection. Refactor this to use environment
variables that are set before the shell command runs, rather than expanding the
GitHub context variables directly within the shell script itself. This will
prevent shell metacharacters from being interpreted as commands while still
correctly capturing the tag value from either the release event or the manual
workflow input.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5203ee5a-31e7-41df-8f0f-237e94da7a4f

📥 Commits

Reviewing files that changed from the base of the PR and between 0186e89 and 2207f68.

📒 Files selected for processing (5)
  • .github/workflows/dev-cd.yml
  • .github/workflows/prod-cd.yml
  • docker-compose.dev.yml
  • docker-compose.prod.yml
  • src/main/resources/application.yml
🛑 Comments failed to post (1)
.github/workflows/dev-cd.yml (1)

143-145: ⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 원격 서버에서 shell history가 비활성화되어 있는지, 
# 또는 HISTCONTROL 설정이 민감한 명령어를 제외하는지 확인이 필요합니다.
# 이 스크립트는 일반적인 확인 사항을 제시합니다.

echo "GitHub Actions의 기본 동작:"
echo "1. secrets.* 값은 로그에서 자동 마스킹됨"
echo "2. SSH 세션 내 명령어는 원격 서버의 history 설정에 따름"
echo ""
echo "권장 사항: 원격 서버에서 HISTCONTROL=ignorespace 또는 HISTFILE=/dev/null 설정 확인"

Repository: solid-connection/solid-connect-server

Length of output: 240


🏁 Script executed:

cat -n .github/workflows/dev-cd.yml | sed -n '130,160p'

Repository: solid-connection/solid-connect-server

Length of output: 1962


🏁 Script executed:

# Check for any deployment scripts or configurations that handle shell history
find . -type f \( -name "*.sh" -o -name "*.bash" -o -name "*.yml" -o -name "*.yaml" \) | xargs grep -l "HISTCONTROL\|HISTFILE\|history" 2>/dev/null | head -20

Repository: solid-connection/solid-connect-server

Length of output: 63


🏁 Script executed:

# Check the full deploy job context and permissions
grep -A 50 "deploy:" .github/workflows/dev-cd.yml | head -70

Repository: solid-connection/solid-connect-server

Length of output: 2259


GHCR 인증 시 원격 서버 shell history 보안 고려가 필요합니다.

현재 구현은 표준적인 방식이지만, 보안 관점에서 다음 사항들을 검토해 보세요:

  1. 토큰 노출 경로 확인

    • echo ... | docker login 명령은 GitHub Actions에서 마스킹되지만, SSH를 통해 원격 서버에서 실행되므로 서버의 shell history에 기록될 수 있습니다.
  2. 원격 서버 설정 확인

    • 원격 서버의 HISTCONTROL 또는 HISTFILE 설정을 확인해 토큰을 포함한 민감한 명령어가 history에 남지 않도록 해야 합니다.
    • 예: HISTCONTROL=ignorespace 또는 HISTFILE=/dev/null 설정 권장
  3. 권한 범위 검증

    • 현재 deploy job의 permissions: { packages: read }는 GHCR 접근에 충분히 제한적이고 적절합니다.

추가 검토는 원격 인프라 설정에 따라 결정하시면 됩니다.

🧰 Tools
🪛 zizmor (1.25.2)

[error] 144-144: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/dev-cd.yml around lines 143 - 145, The docker login
command in the GHCR authentication step may be recorded in the remote server's
shell history when executed via SSH, creating a security risk where the GitHub
token could be exposed. Verify and configure the remote server's shell history
settings to prevent sensitive commands from being recorded by checking the
HISTCONTROL and HISTFILE environment variables, and configure them appropriately
(such as setting HISTCONTROL=ignorespace or HISTFILE=/dev/null) before executing
the docker login command. Alternatively, ensure the command is run with history
expansion disabled or use a secure authentication method that doesn't rely on
piping credentials through shell commands.

Source: Linters/SAST tools

@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

sudo sed -i "s|server 127.0.0.1:[0-9]*;|server 127.0.0.1:${NEW_PORT};|" /etc/nginx/conf.d/upstream.conf

P1 Badge Verify upstream rewrites before stopping active slots

When /etc/nginx/conf.d/upstream.conf uses a valid upstream server directive with parameters after the address (NGINX supports parameters such as max_fails/fail_timeout), this substitution does not match because it requires the semicolon immediately after the port; for example, server 127.0.0.1:8080 max_fails=3; remains unchanged. The workflow still reloads nginx and then stops the old slot, so production traffic can continue pointing at the now-stopped port; please update the regex to preserve optional parameters and verify the file changed before stopping the active slot. The dev workflow has the same pattern.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@Hexeong Hexeong added 인프라 진행 중 자유롭게 merge 가능 labels Jun 15, 2026
@Hexeong Hexeong merged commit 615f17a into develop Jun 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

인프라 진행 중 자유롭게 merge 가능

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: solid-connection 애플리케이션 무중단 배포 전략 적용

1 participant