본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

창 하나, 버전 아님

~12 min · deploy, contracts, operations, discipline

Level 0흩어진 부품
0 XP0/48 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

배포 순서, 그리고 그게 순서인 이유

배선 계약이 바뀌면 배포가 한 순서로 돌아. 필드의 어휘든, 응답 봉투든, 라우트 모양이든. 소유 서비스 먼저, 그다음 공용 층, 그다음 소비자 엔진들, 그다음 전부를 같은 창에서 재시작.

순서는 의존 방향을 따르고, 사람들이 건너뛰는 건 마지막 단계야. 전부 디스크에 있어도 여전히 틀릴 수 있어. 도는 프로세스는 시작할 때 임포트한 코드를 쥐고 있으니까. 재시작 없는 배포는 파일이 하나를 말하고 동작이 다른 걸 말하는 시스템을 만들고, 그게 있을 수 있는 제일 헷갈리는 상태야.

버전 섞인 유예 기간이 없는 이유

버전 붙은 API 는 정확히 이 창을 위해 설계돼. 옛것과 새것이 계약으로 공존하고, 소비자는 자기 일정으로 올리고, 섞인 상태가 지원돼.

이 가족엔 그게 하나도 없고 원하지도 않아. 버전 어긋남이 공용 저장소가 없애려고 존재하는 결함이야. 여기서 버전 고정은 어긋남을 버그가 아니라 지원되는 기능으로 만들어. 결과는 섞인 창이 지원되는 상태가 아니라는 거야. 그동안 부품마다 시스템 절반이 아직 안 채택한 계약에 대고 올바르게 굴어.

그게 깨진 창보다 나빠. 깨진 창은 스스로 알리거든. 절반 배포된 창에선 대체로 돌아가. 요청은 통과하고, 응답은 파싱되고, 필드 하나가 받는 쪽 가정이랑 살짝 다른 뜻일 뿐이야. 그 창에서 테스트하면 결과는 몇 분 뒤엔 존재하지 않을 구성을 묘사해.

절반 배포된 창은 가짜 확신을 찍어내니까, 거기서 절대 테스트하지 말고 시연도 하지 마. 전부 개별로는 맞고 전체로는 틀리고, 그러면 나중에 존재할 시스템이랑 아무 관계도 없는 그럴듯한 결과가 나와. 창을 끝내고, 전부 재시작하고, 그다음에 봐. 배포 중에 얻은 초록불은 약한 증거가 아냐. 아무도 안 돌릴 시스템에 대한 증거야.

실무적 결과 둘

첫째, 창을 짧고 스크립트로 유지해. 순서를 길게 만드는 건 뭐든 시스템이 지원 안 되는 상태에 있는 간격을 늘려. 손으로 하는 단계든, 확인이든, 재시작하기 전에 찾아야 하는 서비스든. 그 순서는 전부를 하는 명령 하나여야 해.

둘째, 내려앉은 변경이 배포된 변경은 아냐. 레지스트리든 라우트 표든 정책 지도든, 모듈 수준 데이터를 바꾸는 커밋은 오래 도는 프로세스한텐 그 프로세스가 재시작할 때에야 닿아. 전부 커밋되고 푸시되고 디스크에서 맞는 채로, 도는 시스템은 무기한 옛날 방식으로 굴 수 있어. 그다음 오는 실패는 방향 감각을 잃게 해. 코드를 읽으면 관찰하고 있는 것의 반대를 말해주고, 다들 믿는 게 코드거든.

Code

창, 스크립트로 — 그리고 지원 안 되는 그 상태·bash
#!/usr/bin/env bash
# One command, one window. Every manual step lengthens the interval
# during which the system is in a state nobody designed for.
set -euo pipefail

# 1. the OWNER first - it defines the contract. Two lines, not
#    "deploy && restart": set -e ignores a failure in any command
#    of an && list except the last, so a failed deploy there would
#    roll straight on into the consumers.
deploy owner-service
restart owner-service

# 2. the shared layer - vendored copies into every consumer
python sync/kit_sync.py

# 3. each consumer engine
for app in app-a app-b app-c; do deploy "$app"; done

# 4. RESTART THEM TOGETHER. The step people skip, and the one that
#    makes the previous three real: a running process holds the code
#    it imported at STARTUP, so files on disk change nothing until
#    this line runs.
for app in app-a app-b app-c; do restart "$app"; done

# Only now is it meaningful to look at anything.
for app in app-a app-b app-c; do healthcheck "$app"; done


# THE STATE THAT IS NOT SUPPORTED - and why it is worse than broken:
#
#   owner    deployed   speaks the NEW vocabulary
#   shared   deployed   translates for the NEW vocabulary
#   app-a    deployed   but NOT RESTARTED -> still runs the OLD code
#   app-b    not yet deployed
#
# Nothing crashes. Requests go through, responses parse, and one
# field means something slightly different than the receiver
# assumes. Any test run here describes a configuration that will
# not exist in five minutes.


# THE DIAGNOSTIC for "landed but not deployed", which is the same
# failure at rest: compare the process start time against the
# modification time of the file whose behavior you are expecting.
#
#   ps -o lstart= -p "$(pgrep -f app-a)"
#   stat -f '%Sm' app-a/registry.py
#
# A file NEWER than the process is the whole explanation, and it is
# the fastest way out of an argument with your own source code.

External links

Exercise

네가 운영하는 서비스에서 요청마다가 아니라 임포트나 시작 때 로드되는 설정을 하나 찾아. 레지스트리, 라우트 표, 기능 지도. 그다음 거기 대한 변경이 지금 어떻게 운영에 닿는지, 그리고 네 배포에 프로세스를 재시작하는 게 있는지 확인해. 없으면 증상이 뭘지, 사람들이 어디부터 볼지 적어.
Hint
증상은 언제나 "코드는 X 라는데 시스템은 Y 를 한다"고, 사람들이 제일 먼저 보는 자리는 코드야. 그래서 이 부류의 실패가 마땅한 시점보다 훨씬 오래 살아남아. 진단 단계를 런북에 명시해서 두 번째 질문이 소스 말고 프로세스에 대한 게 되게 해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.