Kubernetes in Action : Chapter9. Deployment

    들어가기 전

    이 글은 쿠버네티스 인 액션 9장을 공부하며 작성한 글입니다. 


    9. Deployment : 선언적 어플리케이션 업데이트

    쿠버네티스의 Deployment 리소스는 배포와 어플리케이션의 업데이트를 좀 더 쉽게 할 목적으로 만들어진 녀석이다. 이번 장에서는 Deployment를 사용하지 않을 때 / 사용했을 때의 어플리케이션 업데이트에 대해서 공부할 것이다. 

    • 파드를 최신 버전으로 교체
    • 관리되는 파드 업데이트
    • Deployment 리소스로 파드의 선언적 업데이트
    • 롤링 업데이트 수행
    • 잘못된 버전의 롤아웃 자동 차단
    • 롤아웃 속도 제어
    • 이전 버전으로 파드 되돌리기 

    9.1 파드에서 실행 중인 어플리케이션 업데이트

    일반적으로 클라이언트는 k8s의 Pod에 접근할 때, 서비스를 통해서 접근하게 된다. 그리고 Pod는 ReplicaSet에 의해서 관리되고 있다. 만약 어플리케이션이 업데이트 되어서 파드를 업데이트 해야한다면, 어떻게 해야할까? 

    파드를 업데이트 하는 방법에는 세 가지가 있을 수 있다. 그리고 각 방법의 특징은 다음과 같다.

    • 기존 파드를 삭제하고 새로운 파드를 생성. 
      • 일시적인 서비스 중지 타임이 있음. 
    • 새로운 파드를 시작하고, 기존 파드를 삭제한다. 
      • 순차 처리 / 일괄 처리
      • 서비스 중지 타임은 없으나, 리소스가 더 필요함. 

    9.1.1 오래된 파드를 삭제하고 새 파드로 교체

    ReplicaSet 컨트롤러는 다음 특징을 가진다.

    • 매칭되는 Lable을 가진 파드의 개수가 부족하면, 가지고 있는 Template으로 파드를 새로 생성한다.
    • 기존의 파드의 내용과 ReplicaSet이 가지고 있는 Template의 내용이 다른 것은 신경쓰지 않는다.

    ReplicaSet의 이 성질을 이용하면 '오래된 파드를 삭제하고 새 파드로 교체'하는 방식의 업데이트는 아주 쉽다. 단지 이전 버전의 파드를 삭제해주기만 하면 된다. ReplicaSet은 파드 개수가 desired status를 만족하지 못하기 때문에 새로운 파드를 새로운 Template을 이용해 만들어준다. 정리하면 다음과 같다.

    1. 이전 버전의 파드를 하나 삭제함. 
    2. ReplicaSet은 파드가 삭제되면, 트리거 되어서 ReplicaSet이 가지고 있는 Template을 읽어서 새로운 파드를 생성함.

    9.1.2 새 파드 기동과 이전 파드 삭제

    만약 다음 요구 사항을 지키면서 업데이트 해야한다면 어떻게 해야할까?

    • 서비스 중지 타임이 발생하면 안됨. 
    • 한번에 여러 버전의 어플리케이션이 동작하면 안됨. 

    필요한 새 파드들을 먼저 생성한 다음, 기존 파드를 삭제해야한다. 이렇게 하려면 잠시동안 두 배의 컴퓨팅 파워가 필요하다는 단점이 존재한다. 


    한 번에 이전 버전에서 새 버전으로 전환 (Blue - Green Deployment)

    다음 순서대로 진행하면, 한 번에 V1 → V2 버전으로 모든 파드들을 업데이트 할 수 있다. 

    1. ReplicaSet V2를 생성한다.
    2. Service의 Selector Lable을 V2 버전 Pod로 변경해준다. 

    Blue - Green Deploy


    롤링 업데이트 

    롤링 업데이트는 서비스의 엔드포인트가 가리키는 파드의 버전을 하나씩 서서히 변경해가는 것이다. 수동으로 롤링 업데이트를 하는 것은 추천하지 않지만, 수동으로 하는 방법은 다음과 같다. 

    1. Replicaset V2를 만들고, Replicas = 1로 한다. 
    2. 서비스가 ReplicaSet V2의 파드를 바라보게 한다.
    3. ReplicaSet V1의 Replicas를 하나 줄인다. 

    이 방법을 반복해서 V1의 Replicas = 0이 되는 순간까지 하면 롤링 업데이트가 완료된다. 하지만 롤링 업데이트는 복잡한 오퍼레이션이고, 각 명령을 내리는 것이 쿠버네티스의 철학과는 맞지 않아서 수동 롤링 업데이트는 추천하는 방법이 아니다. 

    Rolling Update


    9.2 ReplicaSet 자동 롤링 업데이트 수행

    책에는 이 방법에 대한 설명이 나와있다. 하지만 해당 내용을 수행하기 위해 아래 명령어를 사용한다. 그렇지만 rolling-update는 쿠버네티스 1.15 버전부터 제거되었다고 한다. 따라서 해당 내용을 수행할 수 없으므로 스킵한다. 

    $ kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2

    다만,  기대하는 동작의 전반적인 그림을 살펴보면 다음과 같다.

    1. ReplicaSet을 생성한다. ReplicaSet은 Pod Selector를 통해서 관리하는 파드를 구별한다. 이 때, Dep 이라는 라벨을 추가해서 각 ReplicaSet이 관리하는 파드를 구분한다.
    2. Service는 Lable Selector를 통해서 Endpoints를 관리한다. 이 때, Lable Selector는 App: A를 가리키고 있기 때문에 ReplicaSet V1 / V2가 생성한 각 Pod를 모두 Endpoints롤 참조한다. 
    3. 이 상태에서 ReplicaSet V1 / V2의 Replicas를 하나는 줄이고, 하나는 늘리는 방식으로 Rolling Update를 처리한다. 

     


    9.2.3 rolling-update 명령어가 더 이상 쓰이지 않는 이유 (사라진 이유) 

    • rolling-update를 이용해서 업데이트를 수행하는 것이 나쁜 이유는 '실제 명령'을 나타내기 때문이다. 이것은 쿠버네티스의 철학과 반대되는 일이다. 
    • 쿠버네티스는 필요로 하는 상태를 선언하고, 쿠버네티스가 그 상태를 달성하기 가장 좋은 방법을 찾아내서 그 상태를 스스로 달성하도록 하는 것이다. 하지만, 이 방법은 사용자가 원하는 방식대로 '실제 명령'을 처리했기 때문에 쿠버네티스 철학에 맞지 않는 방법이다. 
    • 쿠버네티스의 파드를 줄이거나, 삭제하도록 동작시키면 안된다. 사용자는 원하는 이미지를 설정하고, replicas를 선언만 한다. 그리고 나머지는 쿠버네티스에게 맡겨야 한다. 

    9.3 어플리케이션을 선언적으로 업데이트 하기 위한 Deployment 사용하기 

    Deployement는 어플리케이션을 선언적으로 업데이트하기 위해 추가된 쿠버네티스의 오브젝트다. Deployment는 Replicaset을 이용해서 선언적 상태를 달성한다. 즉, Deployment는 하이레벨 리소스, ReplicaSet은 로우레벨 리소스다.

    파드가 지속적으로 선언 상태를 유지하기 위해서는 ReplicaSet만으로 충분한데, 굳이 Deployment 리소스를 추가한 것은 파드 업데이트를 손쉽게 하기 위함이다. Deployment가 없을 때는 ReplicaSet을 2개 만들고 라벨링을 한 다음, Replicas를 각각 조작해서 롤링 업데이트를 달성했다. 하지만 그 방법은 좋은 방법이 아니기 때문에 개선이 필요했고, 쿠버네티스는 Deployment라는 리소스를 도입해서 해결했다. 


    9.3.1 Deployment 생성

    Deployment는 다음 매니페스트 파일을 통해서 손쉽게 생성할 수 있다. Deployment를 생성하면 다음 두 가지 자원이 생성된다.

    • Deployment
    • ReplicaSet 

    이 때, ReplicaSet은 Deployment의 이름에 해시값을 붙인 녀석으로 만들어진다. Deployment를 이용해서 롤링 업데이트를 할 때 새로운 ReplicaSet을 만드는데, 이 때 ReplicaSet을 구별하기 위해 해시값을 붙인 ReplicaSet을 만든다. Deployment가 가진 Pod Template의 내용을 해시 값으로 바꾼 것이다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: c9-7-deploy
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: kubia
      template:
        metadata:
          name: kubia
          labels:
            app: kubia
        spec:
          containers:
            - name: c9-7-deploy
              image: luksa/kubia:v1

    디플로이먼트 롤아웃 상태 출력

    Deployment는 롤링 업데이트를 편리하기 위해서 만들어진 하이 레벨 오브젝트다. 따라서 기본적으로 rollout 명령어의 대상이 된다. 아래 명령어를 이용해서 배포된 Deployment의 Roll Out 상태를 확인해보자. 현재 해당 Deployment(위의 매니페스트 파일로 만든 녀석)은 정상적으로 배포된 것을 확인할 수 있다. 

    $ kubectl rollout status deploy c9-7-deploy
    >>>
    deployment "c9-7-deploy" successfully rolled out

    Deployment가 ReplicaSet을 생성하는 방법과 ReplicaSet이 Pod를 생성하는 방식 이해

    • Deployment가 생성되면, Deployment는 ReplicaSet을 만든다. Deployment에 의해서 생성된 ReplicaSet은 해시값을 가진다. 해시값이 붙은 ReplicaSet은 Deployment가 직접 관리한다는 것을 의미한다. 
    • Deployment는 버전별로 ReplicaSet을 만들고, 해시 값으로 구분한다. 
    NAME                        DESIRED   CURRENT   READY   AGE
    c9-7-deploy-56dbc996c5      3         3         3       8m14s
    kubia-dep                   5         5         5       7d20h

    9.3.2 Deployment 업데이트

    Deployment를 이용해 Application을 업데이트 하는 방법은 다음과 같다. 

    • Deployment에 정의된 파드 Template을 수정하기만 하면, 쿠버네티스가 정의된 상태를 만드는데 필요한 모든 단계를 수행함.
      • 파드 Template이 정의되면, 새로운 Pod Template Hash가 생긴다. 이 Hash 값을 가진 ReplicaSet이 만들어지면서 각 파드의 상태가 업데이트 된다. 
      • 업데이트 방식은 업데이트 Policy에 따라 다르다. (Rolling Update / Recreate)

    정리하면 Deployment의 업데이트 시작은 Pod Template이 수정되는 경우에만 발생한다. 


    사용 가능한 Deployment 전략

    Deployment의 파드 템플릿을 업데이트 하면, '새로운 원하는 상태'가 선정된다. 이에 따라 Pod Template의 해시값이 바뀌게 되고, 그렇기 때문에 이 상태를 만족하기 위해서 배포된 파드를 쿠버네티스는 업데이트하기 시작한다. Deployment가 지원하는 파드 업데이트 방법은 다음이 존재한다. 

    • Recreate : 기존 파드 전체 삭제 → 새로운 파드 생성. 여러 버전 병렬 지원하지 않고, 서비스 다운 타임을 허용할 때 가능함. 
    • RollingUpdate : 이전 버전 파드 하나 제거 + 새 파드 추가하는 방식으로 업데이트 함. 서비스 다운 타임이 없으나, 서로 다른 버전이 동작해도 괜찮은 경우에만 사용해야 함. 

    데모 목적으로 롤링 업데이트 속도 느리게 하기 

    Deployment의 Spec에는 minReadySeconds 라는 속성을 추가할 수 있다. 이 속성을 설정하면 다음을 가능하게 한다. 

    • 모든 파드가 Ready 상태가 된 후에 대기하는 시간. 이 시간이 지나야 다음 Pod를 업데이트 함.
    • 단, 새로 생성된 파드의 Readness Probe가 성공하는 경우 바로 다음 Pod를 업데이트 함. 

    참고로 minReadySeconds는 다른 용도로 사용되어야만 한다. minReadySeconds는 롤링 업데이트 속도를 느리게 하기 위해 사용하는 것이 아니라, 롤링 업데이트를 할 때 잘못된 Rollout이 방지되도록 Readness Probe를 통해 괜찮은 파드인지 확인하는 시간으로 사용된다. 아래에 자세한 내용이 적혀있다.

     

    롤링 업데이트 시작

    롤링 업데이트를 실행하면서, 실제 과정을 확인해보자. 먼저 아래 리소스를 배포한다

    • Deployment : curl 요청 시, v1 is running이라는 응답을 반환함. 
    • Service : Deployment를 묶어주는 포인트로 사용함. 
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: c9-8-deploy
    spec:
      replicas: 3
      minReadySeconds: 10
      selector:
        matchLabels:
          app: c9-8-deploy-kubia
      template:
        metadata:
          name: c9-8-deploy-kubia
          labels:
            app: c9-8-deploy-kubia
        spec:
          containers:
            - name: c9-7-deploy
              image: luksa/kubia:v1
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: c9-8-service
    spec:
      type: LoadBalancer
      selector:
        app: c9-8-deploy-kubia
      ports:
        - port: 80
          targetPort: 8080

    롤링 업데이트를 확인하기 위해서 우선 다음 명령어를 실행하도록 한다. 다음 명령어를 실행하면, Pod에게 요청을 보내게 되는데 Pod는 현재 v1 Running 중이라는 내용을 반환한다. 

    $ while true; do curl http://192.168.100.103 && sleep 1; done
    >>>
    This is v1 running in pod c9-8-deploy-5dfc478f8d-xscnx
    This is v1 running in pod c9-8-deploy-5dfc478f8d-dsb49
    This is v1 running in pod c9-8-deploy-5dfc478f8d-tnpc5
    This is v1 running in pod c9-8-deploy-5dfc478f8d-xscnx
    This is v1 running in pod c9-8-deploy-5dfc478f8d-dsb49
    This is v1 running in pod c9-8-deploy-5dfc478f8d-tnpc5
    This is v1 running in pod c9-8-deploy-5dfc478f8d-xscnx
    This is v1 running in pod c9-8-deploy-5dfc478f8d-dsb49
    This is v1 running in pod c9-8-deploy-5dfc478f8d-tnpc5
    This is v1 running in pod c9-8-deploy-5dfc478f8d-xscnx
    ...

    이 때 Deployment의 파드 이미지를 다음과 같이 변경하자. 이미지를 수정하면 Pod Template의 해시가 바뀌기 때문에 Deployment의 업데이트가 트리거 된다. 

    image: luksa/kubia:v1 → luksa/kubia:v2

    Deployment의 업데이트가 시작되면, 아래 내용을 이용해서 업데이트가 실제로 이루어지고 있는지 확인할 수 있다. 

    $ kubectl rollout status deploy c9-8-deploy
    >>>
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 old replicas are pending termination...
    
    $ while true; do curl http://192.168.100.103 && sleep 1; done
    >>>
    This is v1 running in pod c9-8-deploy-5dfc478f8d-xscnx
    This is v1 running in pod c9-8-deploy-5dfc478f8d-dsb49
    This is v1 running in pod c9-8-deploy-5dfc478f8d-tnpc5
    This is v2 running in pod c9-8-deploy-5dfc478f8d-xscnx
    This is v1 running in pod c9-8-deploy-5dfc478f8d-dsb49
    This is v1 running in pod c9-8-deploy-5dfc478f8d-tnpc5
    This is v1 running in pod c9-8-deploy-5dfc478f8d-xscnx
    This is v2 running in pod c9-8-deploy-5dfc478f8d-dsb49
    This is v1 running in pod c9-8-deploy-5dfc478f8d-tnpc5
    This is v2 running in pod c9-8-deploy-5dfc478f8d-xscnx
    ...

    Deployment가 업데이트 될 때 , 새로운 해시 값을 가지는 ReplicaSet 역시 새로 생성되게 된다. 

    NAME                        DESIRED   CURRENT   READY   AGE
    c9-8-deploy-5dfc478f8d      3         3         3       19m
    c9-8-deploy-6d55cc7467      0         0         0       26m

    9.3.3 Deployment Rollback

    만약 새로운 어플리케이션을 배포했는데, 해당 어플리케이션이 문제가 되는 경우라면 어떻게 해야할까? 이전 버전으로 롤백하는 것이 마땅하다. 쿠버네티스는 Deployment를 통해서 RollBack까지 지원한다. 해당 내용을 확인하기 위해서 다음 실습을 해보자. 

     

    Step1. Deployment로 업데이트 하기

    luksa/kuiba:v2 → luksa/kubia:v3 이미지로 업데이트한다. v3 이미지는 다섯번째 요청부터는 항상 500 에러를 응답한다. 즉, '잘못된 어플리케이션 배포'를 가정한 이미지다.

    // deploy 매니페스트 파일 수정 
    image: luksa/kubia:v2 → luksa/kubia:v3
    
    // Rollout 상태 확인 
    $ kubectl rollout status deploy c9-8-deploy
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "c9-8-deploy" rollout to finish: 1 old replicas are pending termination...
    deployment "c9-8-deploy" successfully rolled out

    해당 매니페스트 파일을 수정하고, Roll Out 상태를 하면 V2 → V3 버전으로 성공적으로 마이그레이션 된 것을 알 수 있다. 이제 아래 명령어를 이용해서 파드에 요청을 보내보자. 파드는 4번째 요청까지는 정상적으로 처리하지만, 5번째 요청부터는 오류를 발생시킨다. 아래 로그에서 확인할 수 있다. 이제 문제가 생겼기 때문에 이전 버전의 파드로 롤백해야 한다. 

    $ while true; do curl http://192.168.100.103 && sleep 1; done
    This is v3 running in pod c9-8-deploy-b8b5d6897-zbjfn
    This is v3 running in pod c9-8-deploy-b8b5d6897-zbjfn
    This is v3 running in pod c9-8-deploy-b8b5d6897-zbjfn
    This is v3 running in pod c9-8-deploy-b8b5d6897-l4srp
    This is v3 running in pod c9-8-deploy-b8b5d6897-562l7
    Some internal error has occurred! This is pod c9-8-deploy-b8b5d6897-zbjfn
    Some internal error has occurred! This is pod c9-8-deploy-b8b5d6897-l4srp
    Some internal error has occurred! This is pod c9-8-deploy-b8b5d6897-562l7
    Some internal error has occurred! This is pod c9-8-deploy-b8b5d6897-zbjfn
    Some internal error has occurred! This is pod c9-8-deploy-b8b5d6897-l4srp
    Some internal error has occurred! This is pod c9-8-deploy-b8b5d6897-562l7

     

    Step2. 롤백하기

    문제가 발생했기 때문에 롤백을 해야한다. Deployment의 Pod Template이 업데이트 되었고, 새로운 Pod Template에 대응되는 ReplicaSet을 만드는 것을 확인했다. 아래에서 확인이 가능한데, 쿠버네티스가 굳이 이전 버전의 ReplicaSet을 유지하는 것은 이처럼 롤백에 대응하기 위함이다. 아래에서 볼 수 있듯이, 롤백 선택지는 3개가 있다. 

    NAME                        DESIRED   CURRENT   READY   AGE
    c9-8-deploy-5dfc478f8d      0         0         0       34m
    c9-8-deploy-6d55cc7467      0         0         0       40m
    c9-8-deploy-b8b5d6897       3         3         3       5m41s

    쿠버네티스는 롤백에 대해서 직전 버전 / 지정한 버전으로 롤백하는 기능을 지원한다.

    • 'rollout history' 명령어를 이용해서 롤백 가능한 Revision을 확인할 수 있다. 
    • 'rollout undo' 명령어를 이용해서 이전 Revision으로 롤백할 수 있다. 
    // 롤백 가능한 버전 살펴보기
    $ kubectl rollout history deploy c9-8-deploy
    deployment.apps/c9-8-deploy
    REVISION  CHANGE-CAUSE
    1         <none>
    2         <none>
    3         <none>
    
    // 롤백 하기 (특정 버전)
    $ kubectl rollout undo deploy c9-8-deploy --to-revision=1
    
    // 롤백 하기 (이전 버전)
    $ kubectl rollout undo deploy c9-8-deploy

    특정 Deployment Revision으로 롤백 

    Deployment는 ReplicaSet을 실제로는 다음과 같이 관리하고 있다.

    • Deployment의 History를 살펴보면 각각의 ReplicaSet의 Revision을 가지고 있다.
    • Deployment에 대해서 undo를 하면서 --to-revision 옵션을 통해서 특정 Revision의 ReplicaSet으로 롤백할 수 있다. 


    9.3.4 롤아웃 속도 제어

    Deployment의 업데이트 전략이 RollingUpdate인 경우라면, 롤아웃 속도를 제어하는 여러가지 옵션을 제공한다. 이 절에서는 해당 내용을 공부해보고자 한다. 


    롤링 업데이트 전략의 maxSurge / maxUnavailable 속성 소개

    쿠버네티스는 롤링 업데이트를 할 때 한번에 생성되고 삭제되는 파드 갯수를 통해 롤링 업데이트의 속도를 조절해주기도 한다. 이 값은 Deployment의 spec.strategy.rollingUpdate 하위에서 설정할 수 있다.

    ...
    spec:
      strategy:
        rollingUpdate:
          maxSurge: 50%
          maxUnavailable: 50%
      ...

    자세한 내용은 아래에서 볼 수 있다. 

    속성 설명
    maxSurge Desired Replicas를 초과해서 있을 수 있는 최대 파드 개수. 
    절대값(10), 퍼센트(10%)로 설정 가능
    maxUnavailable Desired Replicas에서 서비스 불가능한 상태로 있을 수 있는 최대 파드 개수.
    절대값(10), 퍼센트(10%)로 설정 가능

    예를 들어 maxSurge를 30%로 설정하면, 롤링업데이트 시작 시 새로운 ReplicaSet의 크기를 즉시 조정해서 기존 + 새로운 파드 갯수가 recplias의 130%를 넘지 않도록 한다. 또한 maxUnavailable를 30%로 설정하면 롤링 업데이트 시작 시, 기존 파드를 70% 수준으로 Scale Down할 수 있다. 

    참고: 쿠버네티스가 availableReplicas 수를 계산할 때 종료 중인(terminating) 파드는 포함하지 않으며, 이 수는 replicas - maxUnavailable  replicas + maxSurge 사이에 존재한다. 그 결과, 롤아웃 중에는 파드의 수가 예상보다 많을 수 있으며, 종료 중인 파드의 terminationGracePeriodSeconds가 만료될 때까지는 디플로이먼트가 소비하는 총 리소스가 replicas + maxSurge 이상일 수 있다.

     

    maxUnavailable / maxSurge 관련 설명

    maxUnavailable / maxSurge는 나는 다음과 같이 이해했다. 이 두 녀석의 기준이 되는 것은 Desired State, 즉 replicas다. maxUnavailable은 Replicas를 기준으로 최대 사용 불가능한 녀석을 계산한다고 볼 수 있다. 예를 들어 다음 경우를 생각해보자.

    • replicas = 10
    • maxUnavailable = 2
    • maxSurge = 2 

    만약 이렇게 설정할 경우, 가용 불가능한 파드의 갯수는 총 6개가 될 수 있다. 왜냐하면 설정값에 따르면 다음과 같이 동작할 수 있기 때문이다.

    • maxUnavailable = 2이기 때문에 한번에 2개의 기존 파드를 삭제할 수 있음. → 8개의 파드만 동작함. 2개는 Terminating 중.
    • maxSurge = 2이며, 이미 2개의 파드가 삭제되었기 때문에 한번에 4개의 기존 파드를 생성할 수 있음.4개의 파드는 생성 중이므로 서비스 불가능한 상태임. 

    따라서 현재 생성중인 파드는 총 12개인데, 사용할 수 없는 녀석은  4개다. 하지만 Replicas와 maxUnavailable을 기준으로 따져보면 가용 가능한 파드는 8대(10-2)여야한다. 이 업데이트는 해당 조건을 잘 만족시킨다. 

    마찬가지로 maxSurge 역시 Replicas를 기준으로 최대 초과 생성될 수 있는 파드를 가리킨다. 한번에 4개의 파드를 생성했지만, replicas + maxSurge = 12이므로 쿠버네티스는 해당 설정값을 잘 지키면서 파드를 생성했다. 


    9.3.5 롤아웃 프로세스 일시 중지 (Canary Release)

    앞서서 v2 이미지를 v3 이미지로 rollout을 통해 한번에 다 바꾸면서 대규모 서비스 장애가 있었다. 한번에 rollout을 하는 대신 Canary Release를 사용할 수 있다. Canary Release는 다음 같은 개념이다. 

    • V4 이미지를 가진 파드를 하나만 투입하고, 서비스가 트래픽을 넣도록 한다.
    • 시간을 가지고 모니터링을 하면서 V4 이미지 파드가 문제가 없는 경우, 나머지를 모두 배포한다. 

    Canary Release는 보다 신중하게 어플리케이션을 업데이트 하는데, 쿠버네티스는 Deployment 오브젝트를 통해서 제공해준다. 실행 명령어는 다음과 같다. 실행 명령어는 rollout을 '일시정지 + 재개'하는 기능을 나타낸다. rollout을 수동으로 일시정지 / 재개하면서 우리는 카나리 배포를 달성할 수 있다.

    # rollout 일시중지
    $ kubectl rollout pause deployment kubia
    
    # rollout 재개
    $ kubectl rollout resume deployment kubia

    9.3.6 잘못된 버전의 롤아웃 방지 

    앞선 예시에서 잘못된 버전을 롤아웃 했을 때, 대규모의 서비스 문제가 발생할 수 있음을 확인했다. Canary Release를 통해서 이 부분을 개선할 수도 있지만, minReadySeconds 설정값을 통해서도 잘못된 버전의 롤아웃을 방지할 수 있다. 

    minReadySeconds의 설정값은 다음 동작을 의미한다.

    1. 모든 파드가 Ready가 된 후, minReadySeconds만큼의 시간이 흐른 후 다음 RollOut을 진행함. 
    2. 모든 파드의 Readness Probe 응답이 정상일 경우, minReadySeconds 시간만큼 기다리지 않고 다음 RollOut을 진행함. 
    3. 만약 파드가 Ready가 되지 않거나, Readness Probe에 실패하면 다음 RollOut이 진행되지 않음. 

    minReadySeconds는 모든 파드가 안정적으로 동작할 상태까지 기다린 후에 괜찮으면 다음 RollOut을 진행하도록 하기 때문에 잘못된 어플리케이션이 서비스에 포함되어서 배포되는 것을 방지해준다. 


    Readness Probe가 잘못된 버전으로 롤아웃 되는 것을 방지하는 법

    Readness Probe를 이용하면 효과적으로 잘못된 버전의 롤아웃을 방지할 수 있다.

    1. RollOut을 시작한다. 이 때, 새로운 파드가 생성되었다.
    2. 새로운 파드는 Readness Probe 체크를 받는다. 이 때, Readness Probe에서 500을 응답한다. 즉, 잘못된 어플리케이션이라고 가정한다.
    3. 모든 파드의 Readness Probe가 성공하지 못했기 때문에 RollOut은 중지된다. 

    Readness Probe를 이용하면, 이런 형태로 Deployment가 잘못된 버전으로 RollOut하는 것을 방지한다. 

    Readness Probe를 사용할 때, minReadySeconds는 적절한 시간이 설정되어야한다. 왜냐하면 아래 같은 경우에는 문제가 발생할 수 있기 때문이다.

    1. 파드가 시작하자마자 Readness Probe를 받았는데 성공함 → Ready로 판단함.
    2. 모든 파드의 Readness Probe가 성공했기 때문에 다음으로 RollOut 진행함.
    3. 다음으로 RollOut 진행했는데, 이전에 만들어진 새로운 버전의 파드의 Readness Probe가 실패함(잘못된 어플리케이션)

    이처럼 Readness Probe의 실패 기간을 정확히 고려하지 못하고 minReadySeconds를 짧게 설정하면, 잘못된 버전의 어플리케이션이 일부 Roll Out이 계속 진행될 수 있다. 이를 해결하기 위해서 Readness Probe가 머지않아 실패할 것을 가정해서, minReadySeconds를 조금 더 올려주는 것이 좋다. 

    롤아웃 데드라인 설정

    안되는 롤아웃을 무한정 기다리고 있을 수는 없다. 롤아웃이 실패하는 마지노선을 설정할 수 있다. Deployment의 spec.progressDeadlineSeconds로 해당 값을 설정할 수 있다. 이 값동안 Deploy가 Rollout이 완료되지 않는 경우, RollOut이 실패한 것으로 판단한다. 

    apiVersion: apps/v1
    kind: Deployment
    ....
    spec:
      ...
      progressDeadlineSeconds: 10
      ...

    만약 제한시간동안 RollOut이 완료되지 않았다면, 아래와 같이 Progressing Fail이 기록되며, 더 이상 RollOut이 되지 않도록 설정된다. 

    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    False   ProgressDeadlineExceeded

    잘못된 롤아웃 중지

    잘못된 롤아웃은 롤아웃이 계속 되면 안된다. 따라서 지금 롤아웃의 취소가 필요하다. 롤아웃의 취소는 undo 명령어를 이용하면 된다. 

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

    요약

    • ReplicaSet은 다음 특징을 가짐.
      • 매칭되는 라벨의 파드 갯수만 확인함. 달라질 경우, 새로운 파드 생성이 트리거 됨.
      • 파드의 Template은 단순히 파드를 생성하는데만 사용됨. Template이 바뀐다고 새로운 파드의 생성이 요청되지는 않음. 
    • 업데이트 전략
      • Recreate : 한번에 모든 파드를 삭제 → 새로운 파드 생성. 일시적인 서비스 중단 발생. 여러 버전 어플리케이션 지원 안될 때 사용함.
      • RollingUpdate : 순차적으로 파드 삭제 + 생성 진행함. → 여러 버전 어플리케이션 지원 가능하며, 서비스 중단 타임이 없어야 하는 경우 사용함. 
      • 카나리 : deployment가 지원하는 기능은 아님. rollout pause + rollout resume 기능을 이용해서 카나리 배포 처리.
    • Deployment가 하나 생성되면 다음이 생성됨
      • Deployment + ReplicaSet
    • 쿠버네티스는 Deployment가 업데이트 될 때 마다 새로운 ReplicaSet을 생성함. 그리고 이전 ReplicaSet은 삭제하지 않고 유지하고 있음. 단 replicas = 0인 상태임.
    • Deployment는 Pod Template을 해시 값으로 바꿔서, 해당 값이 바뀌면 업데이트가 트리거 됨. 
    • Deployment는 Pod Template이 바껴서 업데이트가 트리거 될 때 마다, 새로운 ReplicaSet을 만듦.
    • Deployment의 Pod Template은 'Template'이라는 것 아래에 있는 어떠한 값이라도 바뀌면 Pod Template이 바뀌게 된다.
    • Deployment의 업데이트 상황을 확인하려면 kubectl rollout status 명령어를 이용할 수 있음. 
    • rollout은 다음 명령어를 지원해줌.
      • status : 현재 rollout 상태 보여줌
      • history : 현재 rollout과 관련된 revision을 보여줌.
      • undo : 롤백할 수 있음. --to-revision=<revision>을 이용해서 특정 revision으로도 롤백 가능함.
      • pause / resume :  롤백 일시정지 + 재개 가능
    • maxUnavailable : replicas를 기준으로 최대 서비스 불가능한 파드 갯수 설정. 
    • maxSurge:  replicas를 기준으로 최대 생성되어 있을 수 있는 파드 갯수 설정.
    • maxReadySeconds:
      • Roll Out시, 모든 파드가 Ready가 된 후 기다리는 최대 시간. 모두 Ready면 이 시간이 지난 후 자동으로 Rollout 한다.
      • 모든 파드의 Readness 프로브가 성공하면 해당 시간만큼 기다리지 않고 Rollout 한다.
      • Readness Probe와 함께 사용할 때, maxReadySeconds가 충분한 시간이 설정되지 않았다면 잘못된 어플리케이션의 RollOut이 발생할 수 있음. 
      • maxReadySeconds + Readness Probe를 이용해서 잘못된 버전의 어플리케이션 Roll Out을 방지할 수 있음. 

     

     

     

    댓글

    Designed by JB FACTORY