본문으로 건너뛰기
버전: 1.0.3

4.8. 배포 전략

어떨 때 읽는 장인가

  • 새 버전을 배포하는데 서비스가 끊기면 안 될 때
  • 문제가 생기면 즉시 되돌려야 할 때
  • 새 버전을 일부 사용자에게 먼저 보여 주고 싶을 때
  • 어느 방식을 쓸지 정해야 할 때

앞의 4.2~4.7 장이 무엇으로 배포하는가(Console·Jenkins·Bastion·CI 도구·ArgoCD)를 다뤘다면, 이 장은 어떤 순서로 바꾸는가 를 다룹니다. 어느 경로로 배포하든 여기서 고른 방식을 그대로 쓸 수 있습니다.

네 가지 비교

롤링 업데이트Recreate 는 Deployment 설정으로 끝나지만, 블루그린카나리 는 Deployment 설정이 아닙니다. Service 를 어떻게 두느냐로 만드는 방식 입니다.

전략무엇으로 하는가서비스 중단되돌리는 속도필요한 자원
롤링 업데이트Deployment 설정없음롤백에 시간이 걸림조금 더
RecreateDeployment 설정있음다시 배포해야 함그대로
블루그린Deployment 2개 + Service 선택 조건 전환없음즉시두 배
카나리Deployment 2개 + 복제본 비율없음빠름조금 더

Kubernetes 가 직접 지원하는 것은 앞의 둘뿐입니다. 뒤의 둘은 이미 있는 기능(Service 의 레이블 선택)을 조합해 만듭니다.

롤링 업데이트 — 하나씩 바꾸기

기본값입니다. 새 파드를 하나 띄우고 준비되면 옛 파드를 하나 내리는 식으로 교체합니다. 설정은 Deployment 의 strategy 한 곳에서 끝납니다(3.2 장).

좋은 점아쉬운 점
설정이 간단합니다교체 중에 두 버전이 동시에 돕니다
자원을 조금만 더 씁니다되돌리려면 다시 롤링으로 바꿔야 해 시간이 걸립니다
서비스가 끊기지 않습니다문제를 늦게 발견하면 이미 대부분 교체된 뒤입니다

대부분의 경우 이것으로 충분합니다. 아래 방식은 이것으로 안 되는 사정이 있을 때 씁니다.

Recreate — 다 내리고 다시 띄우기

옛 파드를 전부 내린 뒤 새 파드를 띄웁니다. 그 사이 서비스가 멈춥니다.

두 버전이 동시에 돌면 안 될 때만 씁니다.

쓰는 경우이유
데이터베이스 스키마가 바뀜옛 버전이 새 스키마를 읽지 못합니다
같은 파일을 쓰는데 ReadWriteOnce두 파드가 동시에 붙을 수 없습니다(6.1 장)
배타적 잠금을 쓰는 프로그램둘이 동시에 돌면 충돌합니다

블루그린 — 다 띄운 뒤 한 번에 옮기기

같은 애플리케이션을 Deployment 두 개로 나눠 두고, Service 가 어느 쪽을 볼지 바꾸는 방식입니다. Service 는 레이블로 파드를 고르므로(5.1 장) 그 조건만 바꾸면 트래픽이 통째로 옮겨 갑니다.

COP 는 이 구성을 샘플 애플리케이션에 미리 만들어 둡니다. egov 네임스페이스를 보면 Deployment 가 넷 있습니다.

Deployment레이블용도
egovapplication: egov기본 배포
egov-blueapplication: egov-blue블루그린의 한쪽
egov-greenapplication: egov-green블루그린의 다른 쪽
egov-hpaapplication: egov-hpa자동 확장 시연용(3.5 장)

아래 설명은 이 실제 구성을 그대로 따릅니다.

1. Deployment 를 두 벌 만듭니다. 레이블 하나로 구분합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
name: egov-blue
namespace: egov
spec:
replicas: 2
selector:
matchLabels:
application: egov-blue # 이 값으로 Service 가 고릅니다
template:
metadata:
labels:
application: egov-blue
spec:
containers:
- name: egov-blue
image: registry.example.com/apps/egov:1.0.0

egov-green 은 위와 같고 이름과 레이블만 egov-green, 이미지는 새 버전으로 둡니다.

COP 는 두 벌을 각각 빌드·배포할 수 있게 스크립트 디렉터리도 나눠 둡니다. Bastion 의 workspaces/apps/egov/ 아래에 egov-blue·egov-green 디렉터리가 따로 있고, 각각의 env.sh 에 자기 Deployment 이름이 들어 있습니다(4.5 장). 그래서 한쪽만 새 버전으로 배포할 수 있습니다.

2. Service 는 한쪽만 가리킵니다.

apiVersion: v1
kind: Service
metadata:
name: egov-blue-green
namespace: egov
labels:
application: egov-blue-green
spec:
type: ClusterIP
selector:
application: egov-blue # 지금은 blue 로 보냅니다
ports:
- protocol: TCP
port: 8080
targetPort: 8080

Ingress 는 이 Service 하나만 가리키므로 외부 주소는 전환해도 바뀌지 않습니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: egov-blue-green
namespace: egov
spec:
ingressClassName: default
rules:
- host: egov-blue-green.apps.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: egov-blue-green # Service 는 그대로, 뒤만 바뀝니다
port:
number: 8080

3. 새 버전을 먼저 띄워 확인합니다. egov-green 은 Service 가 가리키지 않으므로 사용자 트래픽을 받지 않습니다. 확인용 Service 를 하나 더 두면 미리 시험할 수 있습니다.

apiVersion: v1
kind: Service
metadata:
name: egov-preview # 확인 전용
namespace: egov
spec:
selector:
application: egov-green
ports:
- port: 8080
targetPort: 8080

4. 전환합니다. Service 의 선택 조건을 egov-green 으로 바꿉니다. Console 의 Service 상세에서 편집하거나 YAML 편집기로 고칩니다(1.2 장).

spec:
selector:
application: egov-green # egov-blue → egov-green

되돌릴 때는 이 값을 egov-blue 로 돌리면 끝입니다. 옛 버전 파드가 그대로 떠 있으므로 몇 초 안에 원래대로 돌아갑니다. 이것이 블루그린의 가장 큰 이점입니다.

Jenkins 작업이 이 전환을 대신합니다(4.4 장). 전환 대상을 고르면 위 Service 선택 조건 변경을 수행합니다. 실제로 하는 일은 이 한 가지뿐입니다.

좋은 점아쉬운 점
되돌리기가 즉시 됩니다두 버전이 동시에 떠 있어 자원이 두 배 필요합니다
새 버전을 미리 확인할 수 있습니다데이터베이스를 공유하면 두 버전이 함께 쓸 수 있어야 합니다
두 버전이 섞여 돌지 않습니다관리할 자원이 늘어납니다

전환 뒤 옛 버전을 바로 지우지 마십시오. 문제가 늦게 드러날 수 있으므로 하루 이틀 남겨 두었다가 정리하는 것이 안전합니다. 자원이 아까우면 복제본을 0 으로 줄여 둡니다.

카나리 — 일부에게만 먼저

새 버전을 소수의 파드로만 띄워 일부 사용자에게 먼저 보여 주고, 문제가 없으면 비율을 늘려 갑니다. 광부가 유해 가스를 알아채려고 데려간 새에서 온 이름입니다.

COP 가 미리 만들어 주지는 않습니다. 블루그린과 달리 준비된 구성이 없어 직접 만들어야 합니다. 다만 자원 구성은 블루그린과 거의 같고 Service 가 양쪽을 모두 가리킨다 는 점만 다릅니다.

1. 두 Deployment 에 공통 레이블을 하나 둡니다.

# 안정 버전
apiVersion: apps/v1
kind: Deployment
metadata:
name: egov-stable
namespace: egov
spec:
replicas: 9
selector:
matchLabels:
application: egov-canary # 공통 — Service 가 이것을 봅니다
track: stable # 구분용
template:
metadata:
labels:
application: egov-canary
track: stable
spec:
containers:
- name: egov
image: registry.example.com/apps/egov:1.0.0
# 새 버전 — 처음에는 1개만
apiVersion: apps/v1
kind: Deployment
metadata:
name: egov-canary
namespace: egov
spec:
replicas: 1
selector:
matchLabels:
application: egov-canary # 같은 값
track: canary # 여기만 다릅니다
template:
metadata:
labels:
application: egov-canary
track: canary
spec:
containers:
- name: egov
image: registry.example.com/apps/egov:1.1.0

2. Service 는 공통 레이블만 봅니다.

apiVersion: v1
kind: Service
metadata:
name: egov-canary
namespace: egov
spec:
selector:
application: egov-canary # track 은 넣지 않습니다
ports:
- port: 8080
targetPort: 8080

track 을 빼는 것이 핵심입니다. 넣으면 한쪽만 대상이 되어 블루그린과 같아집니다.

3. 복제본 비율로 단계를 올립니다.

단계egov-stableegov-canary새 버전이 받는 비율
시작9110%
1차 확대7330%
2차 확대5550%
완료010100%

Console 의 복제본 수 조절로 단계를 올립니다(위 "복제본 수 조절"). 문제가 보이면 카나리 쪽을 0 으로 내리면 즉시 멈춥니다.

좋은 점아쉬운 점
문제가 있어도 일부 사용자만 겪습니다비율이 파드 수로만 정해집니다
실제 트래픽으로 확인할 수 있습니다두 버전이 동시에 돌므로 호환되어야 합니다
자원을 조금만 더 씁니다단계를 사람이 올려야 합니다

비율이 정확하지 않습니다. 이 방식의 트래픽 비율은 파드 개수 비율입니다. "5% 에게만" 처럼 세밀하게 나누려면 파드가 20개는 있어야 합니다.

카나리 — 요청 비율로 나누기 (HAProxy)

앞의 방식은 파드 개수로만 비율이 정해집니다. 요청 수를 기준으로 정확히 나누려면 COP 가 쓰는 HAProxy Ingress Controller 의 route-acl 기능을 씁니다(5.1 장).

Service 에 조건을 하나 붙이면 그 조건에 맞는 요청만 그 Service 로 갑니다. 파드가 몇 개든 상관없습니다.

구성

1. Service 두 개를 따로 둡니다. 앞 절과 달리 각자 자기 파드만 가리킵니다.

apiVersion: v1
kind: Service
metadata:
name: egov-stable
namespace: egov
spec:
selector:
application: egov
track: stable
ports:
- port: 8080
targetPort: 8080
apiVersion: v1
kind: Service
metadata:
name: egov-canary
namespace: egov
annotations:
haproxy.org/route-acl: rand(100) lt 20 # 요청의 20% 만 이리로
spec:
selector:
application: egov
track: canary
ports:
- port: 8080
targetPort: 8080

rand(100) lt 20요청마다 0~99 중 하나를 뽑아 20 미만이면 참 이라는 뜻입니다. 숫자를 바꾸면 비율이 바뀝니다 — lt 5 는 5%, lt 50 은 50% 입니다.

2. Ingress 를 두 개 만듭니다. 같은 주소·같은 경로로 둡니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: egov-canary
namespace: egov
spec:
ingressClassName: default
rules:
- host: egov.apps.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: egov-canary # 조건이 붙은 쪽
port:
number: 8080

안정 버전 Ingress 는 같은 내용에 egov-stable 을 가리키면 됩니다.

route-acl 을 붙여도 Ingress 는 반드시 있어야 합니다. 주석만으로는 아무 일도 일어나지 않습니다.

이렇게 동작합니다

컨트롤러가 만드는 규칙은 이런 형태이고, 조건이 붙은 규칙이 일반 규칙보다 앞에 놓입니다.

use_backend egov_svc_egov-canary_8080 if { 주소 일치 } { 경로 일치 } { rand(100) lt 20 }
use_backend <일반 라우팅>

요청이 오면 위에서부터 확인합니다. 20% 는 첫 줄에 걸려 카나리로 가고, 나머지 80% 는 아래로 내려가 안정 버전으로 갑니다.

비율 올리기

Service 의 주석 값만 바꿉니다. 파드 수는 건드리지 않습니다.

단계주석 값카나리가 받는 비율
시작rand(100) lt 55%
1차rand(100) lt 2020%
2차rand(100) lt 5050%
완료카나리 Ingress 를 지우고 안정 버전을 새 이미지로 배포

멈출 때는 카나리 Ingress 를 지우면 됩니다. 조건이 사라져 모든 요청이 안정 버전으로 갑니다.

비율 말고 다른 조건도 됩니다

route-acl 에는 HAProxy 의 조건식을 그대로 씁니다. 무작위 비율 대신 특정 대상에게만 새 버전을 보여 줄 수도 있습니다.

목적조건식
요청의 N%rand(100) lt 20
특정 쿠키를 가진 사용자cookie(canary) -m found
특정 헤더를 보낸 요청req.hdr(X-Canary) -m str yes
사내 주소 대역에서만src 10.0.0.0/8

내부 검증에는 쿠키나 헤더 조건이 더 낫습니다. 무작위 비율은 같은 사용자가 요청마다 다른 버전을 받아 화면이 오락가락할 수 있지만, 쿠키 조건은 그 사용자만 계속 새 버전을 봅니다.

두 방식 비교

복제본 비율route-acl
비율 단위파드 개수로만 정해집니다요청 단위로 자유롭게 정합니다
5% 로 맞추려면파드가 20개 필요숫자만 lt 5
조정 방법복제본 수 변경주석 값 변경
같은 사용자요청마다 달라질 수 있음조건에 따라 고정 가능(쿠키·헤더)
필요한 자원비율만큼 파드 필요카나리 파드 1개면 충분
적용 범위어느 환경에서나HAProxy Ingress Controller 를 쓰는 환경

한 Ingress 에 경로를 여럿 두지 마십시오. 여러 경로가 같은 요청에 맞을 수 있는 Ingress 에서는 이 기능이 의도대로 동작하지 않습니다. 카나리용 Ingress 는 경로 하나만 둡니다.

비율은 무작위입니다. 요청 수가 적으면 실제 분배가 지정한 값과 차이가 납니다. 100건 정도로는 20% 가 정확히 20건이 되지 않습니다.

무엇을 고를 것인가

상황권장
특별한 사정이 없다롤링 업데이트
두 버전이 동시에 돌면 안 된다Recreate
문제가 생기면 즉시 되돌려야 한다블루그린
자원 여유가 두 배 있다블루그린
실제 트래픽으로 먼저 확인하고 싶다카나리
자원 여유가 적다롤링 업데이트 또는 카나리

어느 방식이든 준비 상태 검사가 있어야 합니다(3.1 장). 새 파드가 준비되었는지 판단하지 못하면 롤링은 멈추고, 블루그린·카나리는 아직 뜨지 않은 파드로 트래픽을 보냅니다.

블루그린과 카나리는 옛 버전과 새 버전이 같은 데이터를 다룰 수 있어야 합니다. 스키마가 바뀌는 배포에는 쓸 수 없습니다 — 그때는 Recreate 를 쓰거나, 두 버전이 함께 쓸 수 있도록 스키마를 단계적으로 바꿉니다.