Skip to content

blue-green 슬롯 판정·전환 공용 블록 신설 - #38

Merged
m-a-king merged 2 commits into
mainfrom
feat/bluegreen-slot-blocks
Aug 11, 2026
Merged

blue-green 슬롯 판정·전환 공용 블록 신설#38
m-a-king merged 2 commits into
mainfrom
feat/bluegreen-slot-blocks

Conversation

@m-a-king

@m-a-king m-a-king commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Situation

  • blue-green 배포 오케스트레이션이 core 와 extractor 의 deploy.yml 에 각각 복제돼 있다. extractor 이식 과정에서 복제의 비용이 실사고 2건으로 실증됐다:
    • 포트 추출 함정: 상태 파일 server 127.0.0.1:18090; 에서 무차별 숫자 grep 은 127 을 먼저 문다. core 는 localhost 표기라 우연히 무사했을 뿐 같은 코드가 잠복해 있다.
    • 전환 실패 시 원복 부재: 상태 파일만 새 슬롯을 가리키고 실제 서빙은 옛 슬롯인 괴리가 남으면, 다음 배포가 서빙 중인 슬롯을 비활성으로 오판해 제거한다. extractor 이행 배포(약 10분 다운타임)에서 실증됐고, 원복 로직은 지금 extractor 에만 있다.
  • 한쪽에서 고친 것이 다른 쪽에 전파되지 않는 구조라, "블록 수정이 별도 커밋 없이 모든 소비자에 반영"이라는 infra 블록의 SSOT 의도에 부합하는 추출 대상이다.

Task

  • 두 소비자가 공유하는 범용 골격만 블록으로 추출한다: 슬롯 판정과 원복 가능한 전환. 값(포트·이름·URL)과 서비스별 단계(nginx 프로비저닝·TLS·로그 백업)는 호출부에 남긴다.

Action

블록 책임 실패 계약
slot_decide.sh 상태 파일에서 ACTIVE/INACTIVE 슬롯 판정 (콜론 뒤 포트만 추출, 모르는 상태 = 부트스트랩) 판정은 실패하지 않음, 인자 오류만 2
slot_switch.sh 이전 상태 보존 → 갱신 → nginx -t → reload(정지 시 restart 폴백) → 선택 verify 어느 단계 실패든 이전 upstream 원복 후 1
  • 값 무소유: 슬롯 이름·포트(--slot-a blue:18090)·상태 파일 경로·검증 명령 전부 default 없는 필수/명시 인자다 (conventions/blocks.md 2번).
  • decide 의 소비 계약: eval 가능한 한 줄을 출력하되, eval "$(...)" 직결은 치환 실패가 빈 문자열 eval(성공)로 위장되므로 "할당 후 eval" 2단계를 헤더에 문서화했다.
  • switch 의 verify 를 원복 경계 안에: 전환 후 프론트 경유 헬스체크까지 이 블록의 원복 대상이다. 부트스트랩(이전 상태 없음)은 원복 대상이 없어 exit 1 로만 알린다고 명시했다.
  • 셀프 테스트 28케이스: 판정 11(부트스트랩·양슬롯·127 함정 회귀·eval 소비 계약) + 전환 17(단계별 실패마다 상태 파일이 원복되는지, PATH 스텁으로 nginx·systemctl·sudo 대체). CI block-test job 에 등록.

Result

  • 후속 2단계로 소비 전환한다: extractor deploy 먼저(방금 실배포 검증된 구현이라 등가 확인 용이), 그다음 core deploy - core 는 이때 원복 로직을 획득한다.
  • 검증: 셀프 테스트 전 케이스 통과 + shellcheck 클린 (로컬 실행).

연관 이슈

Summary by CodeRabbit

  • 새 기능

    • 두 배포 슬롯의 현재 활성 상태를 자동으로 판별합니다.
    • 트래픽 전환 시 설정 검증, Nginx 재로드 및 필요 시 재시작을 수행합니다.
    • 전환 또는 사후 검증에 실패하면 이전 상태와 설정을 자동으로 복원합니다.
    • 초기 상태에서도 안전한 기본 슬롯을 선택할 수 있습니다.
  • 버그 수정

    • 잘못된 인자와 슬롯 형식을 감지해 명확한 오류 코드로 처리합니다.
  • 테스트

    • 정상 전환, 오류 복원, 초기화 실패 및 검증 실패 시나리오를 자동으로 확인합니다.

- core·extractor 에 복제된 blue-green 오케스트레이션에서 범용 골격 둘을 블록으로 추출: 상태 파일 기반 슬롯 판정(slot_decide)과 실패 시 원복하는 upstream 전환(slot_switch). 복제 구현에서 실증된 두 사고가 동인: 포트 추출이 host 의 127 을 먼저 무는 함정, 전환 실패 시 상태·현실 괴리로 다음 배포가 서빙 슬롯을 오제거하는 결함(#37)
- 블록 원칙 준수: 포트·이름·URL 은 default 없는 필수 인자(값 무소유), 순수 bash(실행 위치 중립), sudo 는 비루트일 때만
- decide 출력은 eval 가능한 한 줄 - 소비는 할당 후 eval 2단계로 문서화 (eval "$(...)" 직결은 치환 실패가 성공으로 위장)
- switch 의 verify-cmd 까지 원복 경계 안에 둔다 - 전환 후 검증 실패도 이전 슬롯으로 복귀
- 셀프 테스트 28케이스(판정 11 + 전환 17, PATH 스텁으로 nginx·systemctl 대체) + CI 등록
@m-a-king m-a-king added the feat 외부 가시적 새 기능 label Aug 11, 2026
@m-a-king m-a-king self-assigned this Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Blue-green 슬롯 관리

Layer / File(s) Summary
슬롯 판정 계약과 구현
blocks/slot_decide.sh, blocks/slot_decide.test.sh
NAME:PORT 슬롯 인자와 nginx upstream 상태 파일을 검증합니다. 활성 슬롯과 배포 대상 슬롯을 출력합니다. 오류, 부트스트랩, eval 출력 계약을 자체 테스트합니다.
Upstream 전환과 복구
blocks/slot_switch.sh, blocks/slot_switch.test.sh, .github/workflows/ci.yml
upstream 상태를 변경하고 nginx -t, reload, restart 및 선택적 검증 명령을 실행합니다. 실패 시 이전 상태를 복원합니다. 자체 테스트를 CI에 추가합니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

  • TeamPiKi/infra#37: Blue-green 슬롯 판정·전환 블록과 자체 테스트를 구현하는 변경입니다.

Possibly related PRs

  • TeamPiKi/infra#5: .github/workflows/ci.ymlblock-test 작업에 셸 자체 테스트를 추가한다는 점에서 직접 관련됩니다.

Sequence Diagram(s)

sequenceDiagram
  participant slot_switch.sh
  participant StateFile
  participant nginx
  participant systemctl
  slot_switch.sh->>StateFile: 새 upstream 상태 기록
  slot_switch.sh->>nginx: nginx -t 실행
  nginx-->>slot_switch.sh: 설정 검증 결과 반환
  slot_switch.sh->>systemctl: nginx reload 요청
  systemctl-->>slot_switch.sh: reload 결과 반환
  slot_switch.sh->>slot_switch.sh: 선택적 검증 명령 실행
  slot_switch.sh->>StateFile: 실패 시 이전 상태 복원
  slot_switch.sh->>systemctl: 복원 후 nginx reload 요청
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 blue-green 배포의 슬롯 판정·전환 공용 블록을 신설하는 주요 변경 사항을 정확하고 간결하게 설명합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@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: 4

🤖 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 `@blocks/slot_decide.sh`:
- Around line 38-40: 통합된 옵션 인자 검사를 blocks/slot_decide.sh 38-40과
blocks/slot_switch.sh 38-40의 각 옵션 처리부에 추가하십시오. `shift 2` 전에 필요한 값이 실제로 존재하는지
확인하고, 없으면 오류 메시지를 출력한 뒤 반드시 종료 코드 2로 종료하게 하며, blocks/slot_switch.sh의
`--verify-cmd`가 마지막 인자인 경우도 동일하게 처리하십시오.
- Around line 52-60: Update parse_slot and the output path that serializes slot
names so values consumed by eval in blocks/slot_decide.test.sh are Bash-safe,
preferably by encoding each emitted value with printf '%q' while preserving
valid names and ports. Add an eval regression case covering malicious slot names
containing spaces, semicolons, or command-substitution characters, and verify
they are not executed.
- Around line 63-66: After parsing both slots in the slot-decision flow,
validate that NAME_A differs from NAME_B and PORT_A differs from PORT_B; when
either value is duplicated, print an appropriate error and exit with status 2.
Add regression coverage for both identical names and identical ports, while
preserving valid distinct-slot behavior.

In `@blocks/slot_switch.sh`:
- Around line 61-63: Update the verify-failure rollback path in slot_switch.sh
so writing PREV to STATE_FILE is followed by nginx -t and a reload, with a
restart fallback when reload fails. Track rollback application success, emit a
separate error when restoration fails, and only print “restored previous
upstream” after nginx successfully applies it; add coverage in the rollback
tests for reload failure, success-log suppression, and the actual nginx state.
🪄 Autofix

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 Plus

Run ID: 85259b8e-af2b-4bd0-a5d8-f175ed803013

📥 Commits

Reviewing files that changed from the base of the PR and between f359993 and 89cdebf.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • blocks/slot_decide.sh
  • blocks/slot_decide.test.sh
  • blocks/slot_switch.sh
  • blocks/slot_switch.test.sh

Comment thread blocks/slot_decide.sh
Comment on lines +38 to +40
--state-file) STATE_FILE="${2:-}"; shift 2;;
--slot-a) SLOT_A="${2:-}"; shift 2;;
--slot-b) SLOT_B="${2:-}"; shift 2;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

값이 없는 옵션의 종료 코드 계약을 두 스크립트에서 통일하십시오.

두 스크립트는 옵션 값이 없을 때 shift 2를 실행합니다. 이 동작은 set -e로 인해 문서화한 인자 오류 코드 2를 보장하지 않습니다.

  • blocks/slot_decide.sh#L38-L40: 각 옵션에서 shift 2 전에 인자 개수를 확인하고, 값이 없으면 오류 메시지와 함께 exit 2 하십시오.
  • blocks/slot_switch.sh#L38-L40: 동일한 검사를 적용하고, --verify-cmd가 마지막 인자인 경우도 종료 코드 2로 처리하십시오.
📍 Affects 2 files
  • blocks/slot_decide.sh#L38-L40 (this comment)
  • blocks/slot_switch.sh#L38-L40
🤖 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 `@blocks/slot_decide.sh` around lines 38 - 40, 통합된 옵션 인자 검사를
blocks/slot_decide.sh 38-40과 blocks/slot_switch.sh 38-40의 각 옵션 처리부에 추가하십시오.
`shift 2` 전에 필요한 값이 실제로 존재하는지 확인하고, 없으면 오류 메시지를 출력한 뒤 반드시 종료 코드 2로 종료하게 하며,
blocks/slot_switch.sh의 `--verify-cmd`가 마지막 인자인 경우도 동일하게 처리하십시오.

Comment thread blocks/slot_decide.sh
Comment on lines +52 to +60
PARSED_NAME="${value%%:*}"
PARSED_PORT="${value##*:}"
if [ "$value" = "$PARSED_NAME" ] || [ -z "$PARSED_NAME" ] || [ -z "$PARSED_PORT" ]; then
echo "$arg_name must be NAME:PORT (got: $value)" >&2
exit 2
fi
case "$PARSED_PORT" in
*[!0-9]*) echo "$arg_name port must be numeric (got: $value)" >&2; exit 2;;
esac

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

eval 출력에 슬롯 이름을 안전하게 직렬화하십시오.

parse_slot은 슬롯 이름의 공백, 세미콜론, 명령 치환 문자를 허용합니다. Line 74~78은 그 값을 그대로 출력하고, blocks/slot_decide.test.sh Line 70은 출력을 eval합니다. 예를 들어 조작된 --slot-a 이름은 소비자 셸에서 명령으로 재해석될 수 있습니다.

모든 출력 값을 Bash 안전 형식으로 인코딩하십시오. 예를 들어 printf '%q'를 사용하십시오. 또는 슬롯 이름을 안전한 문자 집합으로 제한하십시오. 이 경우 eval 회귀 테스트에 악성 문자 입력도 추가하십시오.

🤖 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 `@blocks/slot_decide.sh` around lines 52 - 60, Update parse_slot and the output
path that serializes slot names so values consumed by eval in
blocks/slot_decide.test.sh are Bash-safe, preferably by encoding each emitted
value with printf '%q' while preserving valid names and ports. Add an eval
regression case covering malicious slot names containing spaces, semicolons, or
command-substitution characters, and verify they are not executed.

Source: Linters/SAST tools

Comment thread blocks/slot_decide.sh
Comment on lines +63 to +66
parse_slot "--slot-a" "$SLOT_A"
NAME_A="$PARSED_NAME" PORT_A="$PARSED_PORT"
parse_slot "--slot-b" "$SLOT_B"
NAME_B="$PARSED_NAME" PORT_B="$PARSED_PORT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

두 슬롯의 이름과 포트가 서로 다르도록 검증하십시오.

--slot-a blue:18090 --slot-b green:18090이 현재 통과합니다. 이 상태에서 INACTIVE_PORT도 활성 포트인 18090이 됩니다. 배포 대상이 현재 서빙 슬롯과 분리되지 않습니다.

NAME_A != NAME_BPORT_A != PORT_B를 확인하고, 중복이면 종료 코드 2를 반환하십시오. 동일 포트와 동일 이름의 회귀 테스트도 추가하십시오.

🤖 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 `@blocks/slot_decide.sh` around lines 63 - 66, After parsing both slots in the
slot-decision flow, validate that NAME_A differs from NAME_B and PORT_A differs
from PORT_B; when either value is duplicated, print an appropriate error and
exit with status 2. Add regression coverage for both identical names and
identical ports, while preserving valid distinct-slot behavior.

Comment thread blocks/slot_switch.sh
Comment on lines +61 to +63
printf '%s\n' "$PREV" | run_priv tee "$STATE_FILE" >/dev/null
run_priv systemctl reload nginx 2>/dev/null || true
echo "switch FAILED at $stage - restored previous upstream" >&2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

nginx 원복 실패를 성공으로 처리하지 마십시오.

verify 실패 경로에서는 새 upstream이 이미 Line 72에서 적용되었습니다. 이후 Line 62의 reload가 실패하면 상태 파일만 이전 값으로 돌아가고 nginx는 새 upstream을 계속 사용할 수 있습니다. 그러나 현재 코드는 실패를 무시하고 "restored previous upstream"을 출력합니다.

원복 상태 파일을 쓴 뒤 nginx -treload, 필요 시 restart를 수행하십시오. 원복 적용이 실패하면 별도 오류를 출력하고 성공 복원 로그를 출력하지 마십시오. blocks/slot_switch.test.sh에는 원복 reload 실패 시 성공 로그와 실제 적용 상태를 검증하는 케이스를 추가하십시오.

🤖 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 `@blocks/slot_switch.sh` around lines 61 - 63, Update the verify-failure
rollback path in slot_switch.sh so writing PREV to STATE_FILE is followed by
nginx -t and a reload, with a restart fallback when reload fails. Track rollback
application success, emit a separate error when restoration fails, and only
print “restored previous upstream” after nginx successfully applies it; add
coverage in the rollback tests for reload failure, success-log suppression, and
the actual nginx state.

@m-a-king
m-a-king merged commit 3a29452 into main Aug 11, 2026
3 checks passed
@m-a-king
m-a-king deleted the feat/bluegreen-slot-blocks branch August 11, 2026 06:00
m-a-king added a commit to TeamPiKi/extractor that referenced this pull request Aug 11, 2026
- 인라인 구현을 TeamPiKi/infra#38 의 블록 호출로 대체 (-56/+25줄). 판정·원복 로직의 정본이 infra 로 옮겨져 core 와 공유된다 (infra#37 3단계)
- 값(슬롯 이름·포트·상태 파일·검증 명령)은 종전대로 이 호출부가 소유 - 블록 무값 원칙
- 이행 1회차 전용이던 레거시 단일 컨테이너 분기 제거 (2026-08-10 이행 완료로 도달 불가)
- 부트스트랩(ACTIVE 빈 값) teardown 에 가드 추가 - 종전엔 빈 이름으로 no-op 호출이 나갔다
- 검증: 머지된 실제 블록으로 decide→eval→switch→verify(healthcheck 중첩 인용 포함) 로컬 통합 시뮬레이션 통과, yaml·bash -n·shellcheck 클린
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 외부 가시적 새 기능

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant