
무중단 배포란?
무중단 배포(Zero-downtime deployment)는 소프트웨어 업데이트나 변경을 수행할 때 서비스의 가용성을 유지하면서 사용자에게 서비스를 중단시키지 않고 배포하는 전략이다. 이는 사용자가 웹 사이트나 애플리케이션을 사용하는 동안 일시적으로 서비스를 중단시키지 않고도 업데이트를 적용할 수 있다는 소리이다. 때문에 실제 운영환경에서 새 버전을 배포하거나 기존 내용을 수정할 때 많이 활용한다.
그리고 쿠버네티스에서는 이러한 무중단 배포를 실현하기 위해 다양한 전략들이 존재한다.
아래는 가장 많이 활용되는 대표적인 무중단 배포 전략이다.
- Rolling Updates (롤링 업데이트)
- Blue-Green Deployment (블루-그린 배포)
- Canary Deployment (카나리 배포)
해당 방법들을 통해 어떤 식으로 무중단 배포가 이루어지는지 알아보자.
Rolling Updates(롤링 업데이트)
쿠버네티스에서 기본값으로 사용하는 배포 방식으로, 이전 애플리케이션 버전을 새 버전으로 하나씩 대체하는 방식이다.
새 버전의 애플리케이션이 정상적으로 배포된 걸 확인한 후에 기존 애플리케이션을 내리는 방식이라 사용자 입장에서는 서비스가 중단되지 않고 계속 사용할 수 있게 된다.
위 사진을 보면 기존 애플리케이션(V1)이 배포되어 있는 상태에서 신규 애플리케이션(V2)을 추가로 배포하면서 V1에서 V2로 서비스가 순차적으로 대체되는 것을 볼 수 있다. 이런 식으로 모든 배포가 진행되면 최종적으로 모든 애플리케이션이 신규 버전(V2)으로 업데이트되는 것이다.
Blue-Green Deployment (블루-그린 배포)
블루-그린 배포는 두 개의 별도 환경을 사용하여 버전을 관리한다.
위 사진을 보면 현재 운영 중인 블루 환경(V1)과는 별도로 새로운 버전의 애플리케이션을 그린 환경 (V2)에 추가로 배포한다. 그리고 그린 환경에 대한 테스트 및 검증이 완료되면 기존 블루 환경으로 가던 트래픽을 그린 환경으로 전환하여 서비스를 지속시킨다.
새로운 배포에 대한 검증이 완료된 후에 트래픽을 전환시키는 만큼 안전하게 새 버전을 배포할 수 있다는 장점이 있다. 만약 검증 과정에서 발견하지 못한 오류가 있었다고 해도 이전 버전으로 간단하게 롤백할 수 있다는 것도 블루-그린 배포의 특징이다.
다만, 새 배포를 위한 별도의 환경을 사용하기 때문에 리소스는 평소의 2배를 사용하게 된다는 점에서 비용보다 안정성을 우선하는 환경에서 쓰기 좋은 배포 전략이다.
Canary Deployment (카나리 배포)
카나리 배포는 전체 트래픽의 일부만을 새로운 버전의 애플리케이션으로 전환하는 배포 전략이다.
기존 애플리케이션(V1)을 배포해 둔 상태에서 새 애플리케이션(V2)을 추가로 배포한 후에, 소규모 그룹 또는 일부 사용자를 대상으로 새로운 버전의 애플리케이션으로 전환되도록 서비스를 배포하고 상태를 모니터링한다.
새로운 버전에 문제가 발생해도 전체 시스템에 영향을 미치지 않는다는 장점이 있다. 때문에 카나리 배포는 성능 및 안정성 테스트에 유용하며 새 버전에 대한 안정성이 검증되면 상황에 따라 전체 트래픽으로 전환하거나 비중을 늘려가는 등 유동적으로 배포 전략을 수립할 수 있다.
배포 테스트
커스텀한 Nginx 이미지를 활용하여 위에서 소개한 배포 전략 3가지를 적용해 본다.
커스텀 이미지 빌드
먼저 배포 테스트에 사용될 테스트용 이미지를 제작한다. 베이스 이미지는 Docker hub에 있는 Nginx를 활용하여 배포한 애플리케이션 버전을 쉽게 알아볼 수 있도록 내용을 변경한 후 재빌드한다.
- V1용 Dockerfile
FROM docker.io/nginx:latest
RUN echo The version of this app is V1 > /usr/share/nginx/html/index.html
EXPOSE 80
- V2용 Dockerfile
FROM docker.io/nginx:latest
RUN echo The version of this app is V2 > /usr/share/nginx/html/index.html
EXPOSE 80
이후 빌드된 이미지를 Docker hub에 업로드하여 클러스터에서 받아올 수 있도록 만든다.
Rolling Updates 배포 테스트
1. manifest 작성 후 배포(deployment, service, ingress)
strategy
옵션을 이용하여 type을 rolling update
로 지정 후 상세설정을 해준다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 4
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: lhjwj1179/deploy-nginx:v1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
ingressClassName: nginx
rules:
- host: nginx.hokka.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: nginx-service
port:
number: 80
yaml에서 정의된 롤링 업데이트 옵션은 maxSurge: 25%, maxUnavailable: 25%이다.
maxSurge
는 롤링 업데이트를 할 때 현재 정의된 replicas에서 몇 개의 pod가 추가로 배포되는 것을 허용할지 정하는 옵션이다. 현재는 replicas를 4로 정의했기 때문에 최대 1개의 pod가 추가되는것을 허용한다.
maxUnavailable
은 replicas 기준 최대 허용하는 비가용 pod의 개수를 의미한다. 이 또한 replicas가 4이므로 25%인 1개까지 pod가 Terminating
되는것을 허용한다.
결론적으로 위 두 가지 옵션이 모두 반영되어 V1에서 V2로 롤링 업데이트가 진행된다.
2. 배포된 애플리케이션의 버전을 확인
$ curl nginx.hokka.com
The version of this app is V1
curl을 통해 확인해 보면 V1
이미지가 배포된 걸 확인할 수 있다.
3. V2 이미지로 롤링 업데이트
아래 명령어를 통해 V1
에서 V2
로 deployment의 이미지를 변경한다.
$ kubectl set image deployment/nginx-deployment nginx=lhjwj1179/deploy-nginx:v2
이미지가 변경되면 기존 deployment는 yaml에 정의된 대로 업데이트를 진행한다.
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-5c5dbf95bd-4whb8 1/1 Running 0 88s
nginx-deployment-5c5dbf95bd-kdtzs 1/1 Running 0 88s
nginx-deployment-5c5dbf95bd-pcvsr 1/1 Running 0 86s
nginx-deployment-5c5dbf95bd-pv986 1/1 Terminating 0 85s
nginx-deployment-5f75d85f45-g645f 0/1 ContainerCreating 0 0s
nginx-deployment-5f75d85f45-gn2w9 0/1 ContainerCreating 0 0s
4. 새로 배포된 애플리케이션의 버전을 확인
$ curl nginx.hokka.com
The version of this app is V2
이전에 V1
이였던 응답이 새로 배포된 pod에 의해 V2
로 변경된 걸 볼 수 있다.
Blue-Green Deployment 배포 테스트
1. manifest 작성 후 배포(deployment, service, ingress)
version
이라는 label을 추가하여 v1
값을 가진 deployment를 배포하고 이를 service와 연결한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-v1
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: lhjwj1179/deploy-nginx:v1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
version: v1
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
ingressClassName: nginx
rules:
- host: nginx.hokka.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: nginx-service
port:
number: 80
2. 배포된 애플리케이션의 버전을 확인
$ curl nginx.hokka.com
The version of this app is V1
curl을 통해 확인해 보면 V1
이미지가 배포된 걸 확인할 수 있다.
3. 새 애플리케이션 배포
업데이트를 위해 V2
이미지를 사용하는 deployment를 추가로 배포한다. label의 값은 기존 애플리케이션과 구분을 위해 v2
로 설정해 준다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-v2
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v2
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- name: nginx
image: lhjwj1179/deploy-nginx:v2
ports:
- containerPort: 80
그리고 새 버전의 애플리케이션으로 트래픽을 전환시키기 위해 service selector에 label 값을 v1
에서 v2
로 수정해 준다.
kubectl patch service nginx-service -p '{"spec": {"selector": {"app": "nginx", "version": "v2"}}}'
4. 새로 배포된 애플리케이션의 버전을 확인
$ curl nginx.hokka.com
The version of this app is V2
이전에 V1
이였던 응답이 새로 배포된 pod에 의해 V2
로 변경된 걸 볼 수 있다.
Canary Deployment 배포 테스트
1. manifest 작성 후 배포(deployment, service, ingress)
서로 다른 버전의 이미지를 배포하는 2개의 deployment를 배포하되 공통된 라벨 값(app:nginx
)을 가지도록 yaml을 정의한다.
그리고 service에서 selector
의 label을 app:nginx
로 설정해 주어 2개 버전의 deployment를 모두 연결할 수 있도록 설정한다.
# V1 애플리케이션
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-v1
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: lhjwj1179/deploy-nginx:v1
ports:
- containerPort: 80
---
# V2 애플리케이션
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-v2
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v2
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- name: nginx
image: lhjwj1179/deploy-nginx:v2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
ingressClassName: nginx
rules:
- host: nginx.hokka.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: nginx-service
port:
number: 80
2. 배포된 애플리케이션의 버전을 확인
$ curl nginx.hokka.com
The version of this app is V2
$ curl nginx.hokka.com
The version of this app is V1
$ curl nginx.hokka.com
The version of this app is V1
$ curl nginx.hokka.com
The version of this app is V2
$ curl nginx.hokka.com
The version of this app is V1
$ curl nginx.hokka.com
The version of this app is V2
curl을 통해 확인해 보면 기존 버전인 V1
에 연결됨과 동시에 특정 트래픽들은 V2
로도 연결되는 것을 볼 수 있다.
'Kubernetes' 카테고리의 다른 글
CoreDNS 알아보기 (0) | 2024.03.24 |
---|---|
Configmap과 Secret으로 데이터 관리하기 (0) | 2024.03.17 |
쿠버네티스 볼륨 알아보기 (0) | 2024.02.03 |
쿠버네티스 대시보드로 클러스터 모니터링하기 (1) | 2024.01.23 |
Kubeadm으로 쿠버네티스 설치하기 (0) | 2024.01.14 |