FDE Capstone: The Warehouse Got the Same Order Three Times
1.5.0 showed Accepted, but the warehouse had no orders
한국어 원문으로 표시합니다.
목표
releases/<판> 과 current 심링크로 배포하고, 헬스체크와 스모크 시험을 거쳐 실패하면 직전 판으로 되돌려 기록하는 스크립트를 만든다. 보존 정리에서 current 와 직전 판을 지킨다.
왜 중요한가
헬스체크는 프로세스가 살아 있는지를, 스모크는 업무 한 건이 끝까지 되는지를 묻는다. 고객사의 지난 배포는 앞의 질문만 하고 끝나 밤새 주문을 잃었다. 판을 디렉터리로 나누고 심링크 하나로 전환하면 되돌림도 같은 동작 하나가 된다. 다만 전환하는 순간 current 가 사라지거나, 옛 프로세스가 대신 초록불을 켜거나, 정리 스크립트가 돌아갈 판을 지우면 구조가 있어도 사고가 난다. 채점기는 매번 다른 판 번호로 정상·스모크에서만 깨지는·뜨자마자 죽는·늦게 뜨는 빌드를 만들어 여러분의 스크립트를 실행하고, 로그 문구가 아니라 실제로 응답하는 판과 심링크를 다시 잰다.
예상 60분이다. 기본 세션이 끝나기 전에 +시간으로 연장하자(최대 180분). 세션이 끝나면 /root 의 파일은 사라지니 스크립트는 따로 보관한다.
단계
- 빌드 orderapp-1.4.0.tar.gz 를 /root/site/releases/1.4.0 에 풀고, /root/site/current 심링크가 그 디렉터리를 가리키게 한다.
- /root/release/switch.sh ROOT 판 을 만든다. 없는 판은 거부하고, current 가 사라지는 순간 없이 한 번에 전환한다.
- /root/release/deploy.sh ROOT 빌드 PORT 를 만든다. 풀기·전환·옛 프로세스 교체·헬스체크(version 확인)·스모크 주문·deploy.log 기록까지 정상 빌드로 통과시킨다.
- deploy.sh 가 스모크에 실패하면 current 를 직전 판으로 되돌리고 그 판을 다시 띄운 뒤 rolled_back·reason smoke 를 기록하고 종료 코드 2 로 끝나게 한다.
- deploy.sh 의 헬스체크가 뜨자마자 죽는 판은 health 로 되돌리고, 2.5초 늦게 뜨는 정상 판은 기다려서 배포하게 한다. 대기 상한은 10초다.
- /root/release/prune.sh ROOT KEEP 을 만든다. mtime 최신 KEEP 개를 남기되 current 와 직전 판은 지울 목록에서 뺀다.
- 네 번 배포(마지막은 스모크 실패)와 보존 1 정리를 이어서 해도 기록·남은 판·응답하는 판이 맞는지 확인한다.
- 고객의 1.5.0 을 deploy.sh 로 /root/site(포트 8480)에 배포해 결과를 보고, /root/release/incident.json 에 사건을 기록한다.
참고
- 재료: 실행 계약 /opt/lab/p1a-release/CONTRACT.md, 고객 메모 /opt/lab/p1a-release/README.md, 빌드 /opt/lab/p1a-release/builds/
- 빌드 안 보기: tar -tzf 빌드 파일, tar -xzOf 빌드 파일 VERSION
- 심링크 확인: readlink /root/site/current, ls -la /root/site
- 직접 시험: bash /root/release/deploy.sh /tmp/try /opt/lab/p1a-release/builds/orderapp-1.4.0.tar.gz 18480; cat /tmp/try/deploy.log
- 흔한 실수: ln -sf 에서 -n 빠뜨리기, 앱을 띄울 때 출력을 파일로 돌리지 않기(스크립트가 끝나지 않는다), 헬스체크에서 판을 확인하지 않기, 최신순으로만 정리하기.
- 시험용으로 띄운 앱은 끝나면 그 배포 루트의 shared/app.pid 로 골라 끈다.
첫 판을 손으로 풀고 current 걸기
빌드 1.4.0 을 /root/site/releases/1.4.0 에 풀고 /root/site/current 심링크가 그곳을 가리키게 하세요.
tar 의 -C 로 풀 자리를 정합니다. current 는 디렉터리 복사가 아니라 ln -s 로 만든 링크여야 합니다. 상대 경로 링크(releases/판)는 배포 루트를 통째로 옮겨도 깨지지 않습니다.
current 를 한 번에 바꾸는 전환 스크립트
/root/release/switch.sh ROOT 판 을 만드세요. 없는 판은 0 이 아닌 코드로 거부하고 current 는 사라지는 순간 없이 바뀌어야 합니다.
rename(2) 는 기존 이름을 원자적으로 교체합니다. 같은 디렉터리에 임시 링크를 만들고 current 위로 옮기세요. mv 에는 대상이 디렉터리를 가리켜도 그 안으로 넣지 않게 하는 옵션이 있습니다. ln 을 쓴다면 -n 을 빠뜨리지 마세요.
풀고 바꾸고 띄우고 스모크까지
/root/release/deploy.sh ROOT 빌드 PORT 를 만들어 정상 빌드를 배포하고 deploy.log 에 deployed 한 줄을 남기세요.
판은 tar 안의 VERSION 에서 읽습니다. 옛 프로세스는 shared/app.pid 로 내리고, 새 앱은 nohup 과 출력 리디렉션으로 백그라운드에 띄웁니다. 헬스체크 응답의 version 이 새 판인지, 스모크 주문(SMOKE- 접두어)이 GET 으로 되읽히는지 봅니다. 기록은 jq -cn 으로 만들면 따옴표 실수가 없습니다.
스모크가 깨지면 직전 판으로 되돌리기
/root/release/deploy.sh 가 스모크 실패 시 직전 판으로 전환·재기동하고 rolled_back(reason smoke)을 기록한 뒤 종료 코드 2 로 끝나게 하세요.
전환 전에 current 가 가리키던 판을 previous 로 기억해 두어야 되돌릴 곳을 압니다. 되돌림도 switch.sh 로 합니다. 실패한 releases/판 은 조사용으로 남깁니다.
죽은 판은 빨리, 늦은 판은 기다려서
/root/release/deploy.sh 의 헬스체크가 최대 10초를 기다리되 프로세스가 죽으면 곧바로 health 로 되돌리게 하세요.
한 번 실패했다고 포기하면 늦게 뜨는 정상 판을 되돌립니다. 반대로 이미 죽은 프로세스를 10초 기다릴 이유는 없습니다. kill -0 PID 는 신호를 보내지 않고 프로세스가 있는지만 확인합니다.
정리하다 돌아갈 판을 지우지 않기
/root/release/prune.sh ROOT KEEP 을 만드세요. mtime 최신 KEEP 개 외에는 지우되 current 와 직전 판은 남깁니다.
되돌림 뒤에는 current 가 최신이 아닙니다. 직전 판은 deploy.log 에서 result 가 deployed 이고 version 이 current 인 마지막 줄의 previous 입니다. ls -t 는 수정 시각 최신순으로 나열합니다.
네 번 배포하고 정리해도 맞는 기록
/root/release/deploy.sh 와 /root/release/prune.sh 가 배포 네 번(마지막은 스모크 실패)과 보존 1 정리를 이어서 해도 기록과 실제 상태가 맞게 하세요.
되돌린 배포의 previous 는 되돌아간 판입니다. 그 뒤 정리하면 최신 1개(실패한 판)와 current·직전 판이 남아야 합니다. 임시 배포 루트에서 직접 이어서 돌려 보세요.
고객의 1.5.0 을 배포하고 사건 기록 남기기
deploy.sh 로 1.5.0 을 /root/site(포트 8480)에 배포하고, 결과를 /root/release/incident.json 에 failed_version·restored_version·reason·deploy_log_line·health_passed 로 적으세요.
deploy_log_line 은 /root/site/deploy.log 에서 되돌림 기록이 몇 번째 줄인지(1부터)입니다. health_passed 는 이 판이 헬스체크를 통과했는지를 적습니다 — app.log 와 기록의 reason 으로 판단하세요. 채점기는 incident.json 을 deploy.log·current·빌드 원본과 대조합니다.