無知

갈 길이 먼 공부 일기

기술 공부/쿠버네티스

쿠버네티스 (9) | 디플로이먼트

moozii 2022. 3. 3. 18:12
앞으로의 스터디 내용은 <Kubernetes in Action>을 기반으로 진행합니다.
자세한 내용은, 해당 책을 확인해주세요! 
http://www.yes24.com/Product/Goods/89607047 
  • 파드를 최신 버전으로 교체하고 업데이트하기
  • 파드를 선언적으로 업데이트하기
  • 롤링 업데이트 기능 배워보기
  • 잘못된 버전 롤아웃 방지하기
  • 롤아웃 속도를 제어하기
  • 이전 버전으로 파드 롤백시키기

 

애플리케이션 변경 시 업데이트하는 방법과, 무중단 업데이트 프로세스의 작동 방식에 대해 공부해보자. 

쿠버네티스는 레플리카셋 기능을 활용하는 디플로이먼트 리소스를 통해 선언적으로 업데이트를 진행한다.

 

실행 중인 애플리케이션 업데이트

파드가 v1 태그 이미지를 기반으로 애플리케이션 버전1을 실행 중이라고 할 때, 최신 이미지인 v2 이미지가 개발되어 이미지 저장소에 푸시되었다고 하자. 그러면 기존에 실행 중이던 파드를 새로운 버전으로 업데이트해야 하는데, 파드 생성 이후 기존 이미지를 변경하기 어려우므로 기존 파드를 삭제하고 재생성해야 한다.

 

기존 파드를 삭제하고 신규 파드를 생성하는 업데이트 과정은 순서에 따라 2~3가지 경우가 있다.

  • 기존 파드를 모두 삭제한 후 새 파드를 시작 : 잠시 애플리케이션 사용 불가
  • 새 파드를 시작 후 기동 완료 시 기존 파드를 삭제 : 데이터 저장소의 경우 신규 버전이 이전 버전을 손상시킬 수 있는 데이터 스키마나 데이터 수정 등이 불가
    • 한번에 새 파드를 모두 시작 후 기존 파드를 모두 삭제
    • 점진적으로 새 파드를 추가하고 기존 파드를 제거

1. 레플리케이션 컨트롤러로 탬플릿 변경 후 기존 파드 삭제

레플리케이션 컨트롤러 파드 탬플릿 업데이트 시 이전 파드 인스턴스를 삭제하면 쉽게 교체 가능하다.

하지만 짧은 다운타임이 허용되므로 이를 고려해서 사용해야 한다.

 

2. 프로세스 전환해 새 파드 모두 기동 후 이전 파드 삭제

잠시 2배의 파드가 생성되어 존재한다는 측면에서 더 많은 하드웨어 리소스가 필요한 문제가 발생하는 방법이다.

 

"블루-그린 디플로이먼트"라 불리는 아래의 방법으로 진행된다.

1. 구조 상 파드 앞쪽에 서비스를 배치하고, 이전 버전 파드들을 서비스에 연결한다.

2. 새로운 파드를 실행한다

3. 서비스의 레이블 셀렉터를 변경하여 서비스를 새로운 파드로 전환한다. 이때 kubectl set selector 명령어를 수행한다.

4. 이전 파드를 삭제한다.

 

3. 롤링 업데이트

파드를 단계적으로 교체하는 방법을 의미한다.

레플리케이션 컨트롤러를 천천히 스케일 다운하고 새 파드를 스케일 업한다.

수동으로 진행할 경우 많은 명령어 실행이 필요하므로 쿠버네티스를 통한 자동 롤링 업데이트 방법을 익혀보자.

 

Step 1. 애플리케이션 초기 버전(v1) 실행하기

$ cat v1/app.js
const http = require('http');
const os = require('os');

console.log("Kubia server starting...");

var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end("This is v1 running in pod " + os.hostname() + "\n");
};

var www = http.createServer(handler);
www.listen(8080);

해당 애플리케이션은 HTTP 응답에서 파드의 호스트 이름을 반환하는 간단한 애플리케이션이다.

애플리케이션 호출을 위해 레플리케이션 컨트롤러와 로드밸런서 서비스를 생성해서 외부 노출을 진행하자.

실습에서는 하나의 명세에 함께 정의해 한번에 명령어로 게시, 생성한다. 

$ cat kubia-rc-and-service-v1.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia-v1
spec:
  replicas: 3
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
      - image: luksa/kubia:v1
        name: nodejs
---
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  type: LoadBalancer
  selector:
    app: kubia
  ports:
  - port: 80
    targetPort: 8080
$ kubectl create -f kubia-rc-and-service-v1.yaml
replicationcontroller/kubia-v1 created
service/kubia created

$ kubectl get svc
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP        36d
kubia        LoadBalancer   10.101.120.106   <pending>     80:31123/TCP   23s

$ kubectl get po
NAME             READY   STATUS    RESTARTS   AGE
kubia-v1-5lwfg   1/1     Running   0          3m13s
kubia-v1-frdjg   1/1     Running   0          3m13s
kubia-v1-r2cm7   1/1     Running   0          3m13s

$ minikube tunnel
Password:
Status:
	machine: minikube
	pid: 87240
	route: 10.96.0.0/12 -> 192.168.59.100
	minikube: Running
	services: [kubia]
    errors:
		minikube: no errors
		router: no errors
		loadbalancer emulator: no errors
Last login: Wed Mar  2 15:41:11 on ttys000
$ kubectl get svc
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1        <none>           443/TCP        36d
kubia        LoadBalancer   10.101.120.106   10.101.120.106   80:31123/TCP   4m58s

$ while true
while> do curl http://10.101.120.106
while> done
This is v1 running in pod kubia-v1-5lwfg
This is v1 running in pod kubia-v1-frdjg
...

 

이제 업데이트의 대상이 될 v2를 확인하자.

$ cat v2/app.js
const http = require('http');
const os = require('os');

console.log("Kubia server starting...");

var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end("This is v2 running in pod " + os.hostname() + "\n");
};

var www = http.createServer(handler);
www.listen(8080);

response.end("This is v2 running in pod " + os.hostname() + "\n"); 부분 변경이 있었다.

 

자 이제 해당 js를 기반으로 한 이미지로 롤링 업데이트를 쿠버네티스 명령어를 통해 진행할 수 있다. 

앞에서 while 반복문으로 계속 curl 요청을 반복 실행하며 현황을 체크하면서 별도의 터미널로 업데이트를 진행해 그 변화 상황을 바로 확인해보자. 

$ kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
Error: unknown command "rolling-update" for "kubectl"
Run 'kubectl --help' for usage.

그런데 업데이트 명령문이 적용되지 않는다!

이유를 찾아보자..

 

Update: kubectl rolling-update has been deprecated and the replacement command is kubectl rollout. Also note that since I wrote the original answer the Deployment resource has been added and is a better choice than ReplicaSets as the rolling update is performed server side instead of by the client.
https://stackoverflow.com/questions/30184856/how-to-update-a-set-of-pods-running-in-kubernetes#:~:text=Update%3A%20kubectl%20rolling%2Dupdate%20has,instead%20of%20by%20the%20client. 

kubectl rolling-update has been deprecated and hidden since April 2018 (#61285) in v1.11, and has been replaced with kubectl rollout. Let's remove it completely from kubectl and tests, and then the website and any docs.
https://github.com/kubernetes/kubernetes/issues/88051

해당 명령어는 사용하지 않고, rollout으로 진행할 수는 있지만, 사실 디플로이먼트 리소스의 정책을 통한 해결이 가장 적합하다.

https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/

 

Performing a Rolling Update

Objectives Perform a rolling update using kubectl. Updating an application Users expect applications to be available all the time and developers are expected to deploy new versions of them several times a day. In Kubernetes this is done with rolling update

kubernetes.io

https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

 

Deployments

A Deployment provides declarative updates for Pods and ReplicaSets. You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new Rep

kubernetes.io

 

일단 기존 kubectl rolling-update의 메커니즘은 다음과 같다. 

1. 교체할 기존 레플리케이션 컨트롤러를 알려준다.

2. 새로운 레플리케이션 컨트롤러의 이름을 지정한다.

3. 원래 이미지를 교체할 새 이미지를 지정한다. 

 

그러면 아래의 방식과 같이 작동한다.

1. 새로운 레플리케이션 컨트롤러가 즉시 만들어진다.

1-1. 기존 레플리케이션 컨트롤러 아래 파드 레이블을 변경한다.

1-2. 기존 레플리케이션 컨트롤러의 레이블 셀렉터를 변경한다.

1-3. 기존 레플리케이션 컨트롤러의 파드 탬플릿에서 이미지 부분과 레이블 셀렉터 등을 변경해 신규 rc를 만든다.

(파드가 아직 기동되지는 않고 기존 레플리케이션컨트롤러와 공존)

(새로운 레플리케이션 컨트롤러의 의도 레플리카 수는 0으로 기본 설정)

 

2. 신규 컨트롤러를 스케일업 하고 기존 것은 스케일다운해 파드를 하나씩 교체한다.

2-1. 신규 컨트롤러가 첫 파드를 생성한다.

2-2. 이전 레플리케이션 컨트롤러를 스케일 다운한다.

-> 이 과정을 전체 파드가 교체될 때까지 반복한다.

 

kubectl rolling-update deprecated 이유 분석

1. 쿠버네티스가 임의로 파드와 레플리케이션 컨트롤러의 레이블 및 레이블 셀렉터를 수정한다는 점이 그렇다.

 

2. 모든 단계를 kubectl client가 수행하는 것도 문제다. 서버가 아닌 클라이언트가 업데이트 프로세스를 수행한다는 것은, 중도에 네트워크 연결이 중단될 경우 업데이트 프로세스도 중단되어 업데이트 상태와 기존 상태의 중간에 해당하는 애매한 지점에서 머무르게 된다. 

 

3. 쿠버네티스의 <선언적 방식> 철학에 어긋난다. 실제 명령을 지시하기보다 원하는 것을 지정해주면 알아서 변경해줘야 하는 것이 핵심 지론인데, 이번에 진행한 롤링업데이트 명령어는 직접 업데이트를 명령하는 행위이기 때문에 바람직하지 않다.

 

따라서 이러한 단점을 극복하기 위해 사용하는 것이 디플로이먼트이다.

 

 

디플로이먼트 리소스를 통한 선언적 업데이트

디플로이먼트를 생성하면 -> 레플리카셋을 생성하고 -> 레플리카셋이 파드를 관리하는 구조이다.

굳이 단계적으로 레플리카셋의 상위 리소스 오브젝트를 도입한 이유는, 롤링 업데이트 과정에서 두개의 컨트롤러/레플리카셋이 동시에 작동해서 여러 개를 한번에 관리하는 리소스가 필요하기 때문이다.

 

https://jayendrapatil.com/tag/statefulsets/

 

 

디플로이먼트 매니페스트를 생성해보자.

$ cat kubia-deployment-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubia
spec:
  replicas: 3
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
      - image: luksa/kubia:v1
        name: nodejs
  selector:
    matchLabels:
      app: kubia
$ kubectl create -f kubia-deployment-v1.yaml --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/kubia created

$ kubectl get deployment
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
kubia   3/3     3            3           22s

$ kubectl rollout status deployment kubia
deployment "kubia" successfully rolled out

$ kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
kubia-5f6cdb7bf7-6x7fm   1/1     Running   0          50s
kubia-5f6cdb7bf7-7czjz   1/1     Running   0          50s
kubia-5f6cdb7bf7-sgjsj   1/1     Running   0          49s

$ kubectl get rs
NAME               DESIRED   CURRENT   READY   AGE
kubia-5f6cdb7bf7   3         3         3       4m47s

생성 후 업데이트까지 진행하고 파드를 확인해보았다.

파드 이름 중간에 위치한 해시값은 레플리카셋의 이름에도 포함된, 파드 템플릿 해시 값이다. 

 

create 명령어를 사용할 시 --record 옵션을 포함시켜 개정 이력에 명령어를 기록하기를 권장한다고 들었지만, 

정작 실습을 진행할 때는 deprecated될 예정이라 해서 그 이유를 또 찾아봤다.

Currently --record flag seems like a really bad decision made some time ago, and supporting it makes live harder when trying to modify any parts of code (see this discussion). I'm proposing to deprecate that flag and drop it entirely in future version. @liggitt fyi @kubernetes/sig-cli-feature-requests opinions?

This type of functionality should be addressed using advanced audit feature rather then relying on users to do this. Besides some of the command (apply, for example) record previous state of the resource, iirc.

https://github.com/kubernetes/kubernetes/issues/40422

 

디플로이먼트 업데이트 방식에 대한 이해

기존 kubectl rolling-update와 달리, 디플로이먼트 리소스에 정의된 파드 템플릿만 수정하면 그 정의에 맞게 쿠버네티스가 자동적으로 변경 작업을 수행하는 것이 특징이다. 

 

업데이트 방식은 디플로이먼트 구성 상의 디플로이먼트 전략에 따라 결정된다.

  • RollingUpdate : 롤링 업데이트 전략
  • Recreate : 모든 파드를 삭제한 후 신규 파드 생성 (중간에 완전 중지 상태, 다운타임 발생)

업데이트 프로세스 속도는 minReadySeconds 속성 설정에 따라 조절된다. 

 

 

리소스 수정 방식

  • kubectl patch : 속성 조절을 위해서는 kubectl patch 명령어를 통해 리소스 속성 일부를 수정할 수 있다. 오브젝트 개별 속성을 수정하는 데에 사용한다.
  • kubectl edit : 기본 편집기로 오브젝트 매니페스트를 오픈해 변경하면 오브젝트가 업데이트된다.
  • kubectl apply : 전체 yaml, json 파일 속성 값을 적용해 수정한다. 리소스 전체 정의를 포함한다. 오브젝트 없으면 신규 생성한다.
  • kubectl replace : yaml, json 파일로 오브젝트 전체를 새 것으로 교체한다. apply와 달리 오브젝트가 없으면 오류가 발생한다.
  • kubectl set image : 파드, 레플리케이션 컨트롤러의 정의 상 이미지를 변경한다.

디플로이먼트 리소스는 상기 방법들을 활용해 파드 템플릿을 변경해주면 그것만으로 정책에 따라 업데이트가 진행된다.

정확히는 kubectl 클라이언트가 아니라 컨트롤 플레인 내의 컨트롤러가 업데이트를 수행한다.

(단 파드 템플릿이 컨피그맵을 참조하는 경우, 컨피그맵 수정은 업데이트를 시작하게 하지 않는다. 업데이트에 수정 설정이 필요하다면, 신규 컨피그맵을 만들고 수정한 파드 탬플릿이 이를 참조하도록 수정해야 한다.)

 

업데이트 롤백하기

디플로이먼트를 사용하면, 쿠버네티스 디플로이먼트 마지막 롤아웃을 취소하도록 명령해, 이전 버전으로 롤백이 가능하다.

$ kubectl rollout undo deployment kubia
deployment "kubia" rolled back

이러한 롤아웃의 롤백이 가능한 이유는 개정 이력을 디플로이먼트가 가지고 있기 때문이다.

다만 개정 내역 수의 기본 값이 디플로이먼트 리소스 editionHistoryLimit에 의해 정의되므로, 그 한계치를 초과하는 과거 개정이력은 삭제되고, 그에 해당하는 레플리카셋도 자동 삭제된다. (extensions/v1beta1 = no limit. apps/v1beta2 = limit : 10) 

$ kubectl rollout history deployment kubia
deployments "kubia":
REVISION	CHANGE-CAUSE
2		kubectl set image deployment kubia nodejs=luksa/kubia:v2
3		kubectl set image deployment kubia nodejs=luksa/kubia:v3

--record 옵션이 없으면 개정 이력의 원인 칸이 비어있을 수 있다고 하는데... deprecated될 예정이라 어떻게 변경되는지 궁금하긴 하다..

 

그리고 특정 버전으로도 아예 롤백해버릴 수 있다.

$ kubectl rollout undo deployment kubia --to-revision=1

다만 롤백을 위해서 디플로이먼트 기록이 필요한데,

이는 각 레플리카셋이 보유하고 있는 것이기 때문에 수동 조작으로 함부로 삭제해서는 안된다. 

 

롤아웃 속도 제어는 전략의 maxSurge, maxUnavailable 속성을 통해 진행된다.

  • maxSurge : 디플로이먼트가 의도하는 레플리카 수보다 얼마나 많은 초과치를 허용할 것인가. (기본값=25%, 반올림. 다만 비율 외 절댓값 지정도 가능)
  • maxUnavailable : 업데이트 과정에서 의도하는 레플리카 수를 기준으로 사용할 수 없는 수를 설정. 해당 수치만큼의 파드만 사용 불가능 상태여야 한다는 것. (기본값=25%, 25%의 수는 사용할 수 없고 최소 75%가 사용 가능한 파드 인스턴스 수가 되도록 유지되어야. 비율 외 절댓값 지정도 가능)

롤아웃 프로세스 일시 중지 및 재개

$ kubectl rollout pause deployment kubia
deployment "kubia" paused

$ kubectl rollout resume deployment kubia
deployment "kubia" resumed

 

잘못된 롤아웃 방지

앞서 이야기한 minReadySeconds 속성은, 파드를 사용 가능하다고 간주하기 전에 새로 만든 파드를 준비할 시간을 의미한다.

사용 가능할 때까지 롤아웃 프로세스가 진행되지 않는다.

모든 파드의 레디니스 프로브의 성공 이후 준비된다.

즉, minReadySeconds 경과 이전 레디니스 프로브 실패 시 진행 중인 롤아웃이 차단된다.

 

즉, 해당 속성을 정의해주고, 파드 템플릿에 레디니스 프로브를 추가해줘야 한다.

$ cat kubia-deployment-v3-with-readinesscheck.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: kubia
spec:
  replicas: 3
  minReadySeconds: 10 		// 준비 확인 대기 시간 설정
  strategy:
    rollingUpdate: 		// 롤링업데이트 진행
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
      - image: luksa/kubia:v3
        name: nodejs
        readinessProbe: 	//레디니스 프로브를 설정
          periodSeconds: 1
          httpGet:
            path: /
            port: 8080

 

롤아웃 데드라인 설정

기본적으로 롤아웃이 10분 동안 진행되지 않으면 실패한 것으로 간주한다.

ProgressDeadlineExceeded 조건으로 표시한다.

$ kubectl describe deploy kubia
Name:                   kubia
Namespace:              default
CreationTimestamp:      Thu, 03 Mar 2022 10:22:03 +0900
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
                        kubernetes.io/change-cause: kubectl create --filename=kubia-deployment-v1.yaml --record=true
Selector:               app=kubia
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=kubia
  Containers:
   nodejs:
    Image:        luksa/kubia:v1
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   kubia-5f6cdb7bf7 (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  52m   deployment-controller  Scaled up replica set kubia-5f6cdb7bf7 to 3

원래 Conditions:에 표시되어야 하는데, 실습으로는 확인이 안나왔다.

 

 

배포 전략과 디플로이먼트

앞서 롤링 배포 전략과, 블루-그린 형태의 배포 전략을 적용한 배포 사례를 실습 및 설명을 통해 확인한 바 있다. 

그외에도 책에서 새로운 배포 전략 한 가지를 더 소개한다. 바로 카나리 릴리즈이다.

 

Canary Release

디플로이먼트 리소스를 통해 이미지를 변경해서 롤아웃을 진행하고,

롤아웃 진행을 시작한 즉시 일시 중지를 시켜서, 일부만 업데이트를 진행하는 상태를 통해 카나리 릴리스를 구현하는 것이다.

$ kubectl set image deployment kubia nodejs=luksa/kubia:v4
deployment "kubia" image updated

$ kubectl rollout pause deployment kubia
deployment "kubia" paused

새 파드를 하나 생성했지만, 기존 파드도 모두 계속 실행 중이어서, 적은 수의 이전 파드만 새로운 버전으로 전환하는 효과를 만들어낸다.

이를 통해 소수의 사용자만 초기 신규 버전을 사용하게 하여, 업데이트가 잘못된 버전을 롤아웃할 경우 모든 사용자에게 영향을 주지는 못하도록 원천적으로 차단하는 것이다.

 

자세한 배포 전략의 종류와 디플로이먼트에 대한 부연설명은, 아래의 글들을 참고하자.

[배포 전략의 종류]
가장 대표적인 배포 전략은 아래 네 가지가 되는데요.
아마 실제로 검색해보시면 AWS의 서비스들 중 배포 기능을 가진 것들에서 이들 중 하나는 꼭 들어가 있는 것을 볼 수 있을거에요.
인플레이스 배포 (In-place Deployment)
롤링 배포 (Rolling Update Deployment)
블루/그린 배포 (Blue/Green Deployment)
카나리 배포 (Canary Deployment)

인플레이스 배포 (In-place Deployment)
인플레이스 배포는 사용중인 환경에 새로운 변경사항이 포함된 어플리케이션만 반영하는 방법입니다.

롤링 배포 (Rolling Update Deployment)
여러 개의 가동중인 서버 (인스턴스)를 갖춘 환경에서 한 번에 정해진 수만큼의 서버에 새로운 변경 사항이 포함된 어플리케이션을 배포하는 방법입니다. 구 버전에서 새 버전으로 트래픽을 점진적으로 전환하며, 구 버전의 인스턴스도 점차 삭제됩니다. 그렇기 때문에 서버 수의 제약이 있을 경우에는 유용한 방법이 될 수 있지만 배포 중 인스턴스의 수가 감소 되기 때문에 서버 처리 용량을 미리 고려해야 합니다. 

블루/그린 배포 (Blue/Green Deployment)
새로운 변경사항이 포함된 어플리케이션을 위한 새로운 환경을 구축하고 교체하는 방법입니다. 흔히 블루/그린 배포를 "구 버전의 환경을 새 버전의 환경으로 똑같이 구축해서 한 번에 전환한다" 라고 생각하는데, 사실 이것은 Red/Black 배포의 정의입니다. 그래서 실제로는 "신 버전과 구 버전의 어플리케이션이 한 순간이라도 공존하였다" 라고 하는 것이 더 올바르다고 할 수 있습니다.

카나리 배포 (Canary Deployment)
가동 중인 서버들의 일부에만 새로운 앱을 배포하여, 일부 트래픽을 새 버전의 환경으로 분산하는 방법입니다.

출처 : https://dev.classmethod.jp/articles/ci-cd-deployment-strategies-kr/
[팀 시니어 님의 한마디]
디플로이먼트 리소스는 이전 챕터들의 복습! 묶음 배포에 가깝다.
디플로이먼트는, 설정 내용을 전달하는 기존 방식과의 다른 점은, <배포 시 버저닝이 가능하다는 것>. 버전을 관리하고 어떻게 배포할지 전략을 세울 수 있다

1. 인플레이스 배포 : 예전 방식. 새 버전으로 하나씩 올려주는 방식. 이미지 단위가 아니었던 과거. 요새는 이미지의 불변성을 유지하지만, 예전만 하더라도 한땀한땀 업그레이드..

2. 롤링 배포 : 이미지 단위 하나씩 빼고 넣고 트래픽을 단계적으로 교체. 쿠버네티스의 배포는 기본적으로 롤링 업데이트.

3. 블루그린 : 신규 배포 후 신규 앱을 엔드포인트로 바라본 뒤, 기존 앱을 지우는 방식. 쿠버네티스 미지원.
블루그린 장점은 서비스를 하나하나 힘들게 배포하다 장애가 나면, 장애를 빠르게 복구할 수 있다는 것이 롤링 대비 장점. 쿠버네티스에서는 그 장점이 조금 퇴색된다. 롤링업그레이드 단점은 하나씩 업데이트하다 문제가 발생하면 다시 하나씩 빼면서 재배포해야 하는데, 예전에는 배포하고 롤백하는 시간 소요가 매우 컸다. 직접 패키지를 엔지니어가 인스톨한다거나, 가상머신 재배포가 컨테이너보다 느렸기 때문. 하지만 컨테이너는 이미지가 캐시가 되고, 롤백하기도 빨라서, 블루그린을 하면 로드밸런서의 치밀한 관리를 조금 더 쉽고 간단하게 구현해준다는 것.

4. 카나리 : 기존 서비스 일부 트래픽만 신규 버전으로 보내고 지켜보는 것. 상용환경에서 제대로 동작하지 않을 수 있으므로 일정 비율 트래픽만 보내보고, 모니터링했을 때 트랜잭션 버그가 없는지 확인하고 문제 있으면 롤백하도록.

[추가 공부] ArgoCD >> Spinnaker
ArgoCD로 변경하고자 하는 이유는?

문제 1. 스피내커는 AWS 친화적이기만 하고, 컨테이너 친화적이지 않다.
컨테이너 기반 배포 자동화가 아니다보니, 컨테이너 보편화에 따라 AWS 기반 아키텍처에 컨테이너를 우겨넣어 복잡성이 증가함. 그 위의 Helm 등 구조도 스피내커에 맞춰줘야 하고 관리도 어려워졌다.
문제 2. 스피내커는 깃옵스 기반 자동화도 조금 힘들다.

ArgoCD는, 스피내커처럼 기존 AWS 배포 고려 없이 쿠버네티스 배포에 최적화. 모든 설정 및 배포 파이프라인을 코드로 관리. Helm Native 지원. 

 

 

 

[참고자료]

https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

 

Deployments

A Deployment provides declarative updates for Pods and ReplicaSets. You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new Rep

kubernetes.io