Deployment: Declarative Application Update

유웅조·2020년 9월 29일
0

kubernetes

목록 보기
4/4

v1 태그가 지정된 파드가 있다고 가정한다.

v2로 업데이트하려면 기존 파드의 이미지를 그냥 변경할 수는 없으므로 다음 방법들이 있을 것이다.

  • 기존 파드를 모두 삭제한 다음 새 파드를 시작한다.
  • 새로운 파드를 시작하고, 기동하면 기존 파드를 삭제한다. 새 파드를 모두 추가한 다음 한꺼번에 기존 파드를 삭제하거나 순차적으로 새 파드를 추가하고 기존 파드를 점진적으로 제거한다.

첫 번째 방법의 경우엔 어플리케이션에 순간 단절이 일어날 수 있다.

두 번재 방법의 경우엔 어플리케이션이 동시에 두 가지 버전을 실행해야 한다. 어플리케이션이 데이터베이스에 데이터를 저장하는 경우 새 버전이 이전 버전을 손상시킬 수 있는 데이터 스키마나 데이터의 수정을 해서는 안 된다.

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

레플리케이션컨트롤러(이하 RC)는 새 인스턴스를 생성할 때 업데이트된 파드 템플릿을 사용한다.

따라서 RC가 있는 경우 파드 템플릿을 수정한 다음 이전 파드 인스턴스를 삭제해 쉽게 교체할 수 있다.

만약 어플리케이션의 순간 단절을 허용할 수 있다면 이것이 가장 간단한 방법이 될 수 있을 것이다.

새 파드 기동 + 이전 파드 삭제

다운타임이 발생하지 않고 한번에 여러 버전의 어플리케이션이 실행하는 것을 지원하는 경우 프로세스를 먼저 전환해 새 파드를 모두 기동한 후 이전 파드를 삭제할 수 있다.

잠시 동안 두 배의 파드가 실행되므로 더 많은 하드웨어 리소스가 필요하다.

한번에 이전 버전에서 새 버전으로 전환

파드의 앞쪽엔 일반적으로 서비스가 배치되어 있다. 새 버전을 실행하는 파드를 불러오는 동안 서비스는 파드의 이전 파드의 이전 버전에 연결된다.

그런 다음 새 파드가 모두 실행되면 서비스의 레이블 셀렉터를 변경하면 서비스를 새 파드로 전환할 수 있다.

이를 Blue-Green Deployment라고 한다. 정상적으로 작동하면 RC를 삭제해 이전 파드를 삭제할 수 있다.

롤링 업데이트 수행

새 파드와 기존 파드를 한꺼번에 교체하는 방법 대신에 순차적으로 업데이트하는 방법도 있다.

이전 RC를 천천히 스케일 다운하고 새 파드를 스케일 업해 이를 수행할 수 있다.

이 경우 서비스의 파드 셀렉터에 이전 파드와 새 파드를 모두 포함하게 해 요청을 두 파드 세트로 보낼 수 있다.

쿠버네티스는 이러한 롤링 업데이트를 손쉽게 수행할 수 있다.

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia-v1
spec:
  replicas: 3
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
        - image: onikss793/kubia:v1
          name: nodejs

---
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  type: NodePort
  selector:
    app: kubia
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30124

위의 yaml 파일로 RC와 서비스를 생성했다고 가정하고, 지속적으로 curl 요청을 보낸다고 가정한다.

이후에 v2로 업데이트한다고 했을 때,

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

kubectl rolling-update {original RC name} {new RC name} --image={new Image}

위와 같은 방식으로 rolling-update 를 사용할 수 있었다.

하지만 더이상 rolling-update는 사용하지 않는다.

왜냐하면 쿠버네티스가 파드의 레이블과 RC의 레이블 셀렉터를 수정하는 것은 다른 개발자들로 하여금 혼란을 야기시킬 수 있다.

더 나아가 rolling-update를 수행하는 것이 kubectl Client이다. 이는 해당 명령어를 수행할 때 로그를 확인해보면 알 수 있는데, kubectl은 쿠버네티스 API 서버로 HTTP PUT 요청을 보내게 된다.

만약 업데이트 프로세스를 진행하는 도중 네트워크에 장애가 생긴다면 프로세스는 중간에 중단되고 파드와 RC는 그 상태에서 중단된 상태로 머물 것이다.

따라서 쿠버네티스에 파드를 추가하거나 제거하라는 지시는 가급적 피하는 것이 좋다. 그저 선언적으로 몇개의 파드를 가지라고 명령하는 편이 훨씬 낫다.

Deployment

RS(Replica Set)을 이용해서 파드를 관리했다면, Deployment를 이용해서 RS를 관리하면 된다.

파드 인스턴스의 관리 차원에선 RS가 충분하지만 앞서 보았듯이 업데이트 등에서는 복수의 RS를 잘 사용하는 것이 중요하다. 그리고 이것을 수행하기 위해 Deployment를 사용하는 것이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    matchLabels:
      app: kubia
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
      - image: onikss793/kubia:v1
        name: nodejs

가장 기본적인 yaml 파일이다.

kubectl get po를 해보면

NAME                     READY   STATUS    RESTARTS   AGE
kubia-6c8d4fddfb-98pdh   1/1     Running   0          69s
kubia-6c8d4fddfb-dpsn7   1/1     Running   0          69s
kubia-6c8d4fddfb-xr79m   1/1     Running   0          69s

이런 모습이 나오는데, 기존의 파드의 이름과 비교해보면 가운데 해쉬값이 추가적으로 들어있음을 알 수 있다.

이 가운데 해쉬값은 해당 파드들을 관리하는 RS의 이름이다.

kubectl get rs를 해보면,

NAME               DESIRED   CURRENT   READY   AGE
kubia-6c8d4fddfb   3         3         3       2m4s

이와 같이 동일한 이름의 RS를 확인할 수 있다.

Deployment Update

디플로이먼트 업데이트는 디플로이먼트 리소스에 정의된 파드 템플릿을 수정하기만 하면 쿠버네티스가 실제 시스템 상태를 리소스에 정의된 상태로 만드는 데 필요한 모든 단계를 수행한다.

디플로이먼트에 구성된 디플로이먼트 전략에 의해 새로 정의된 상태로 변화되는 방법을 설정할 수 있다.

기본적으로는 Rolling Update, 롤링 업데이트 전략이다. 대안으로는 Recreate 전략이 있는데 한번에 모든 기존 파드를 삭제한 뒤 새로운 파드를 만드는 방법이다.

이 방법은 앞서 RC 에서 살펴봤던 방법과도 비슷한 방법으로 짧은 다운 타임이 발생하게 된다.

반면 Rolling Update는 이전 파드를 하나씩 제거하고 동시에 새 파드를 추가해 전체 프로세스에서 어플리케이션을 지속적으로 사용할 수 있다.

이 방법은 어플리케이션에서 구버전과 새버전을 동시에 실행할 수 있는 경우에만 사용해야 한다.

kube set image deployment kubia nodejs=onikss793/kubia:v2 이 명령어로 간단하게 파드 템플릿을 변경해보았다.

그렇게 되면 차츰 v1 에서 v2 로 파드가 변경되는 것을 볼 수 있다.

파드 템플릿이 컨피그맵을 참조하는 경우, 컨피그맵을 수정하는 것으로는 업데이트가 진행되지 않는다. 새로운 컨피그맵을 만들고 파드 템플릿이 새 컨피그맵을 참조하도록 수정해야 한다.

kube get rs

NAME               DESIRED   CURRENT   READY   AGE
kubia-6c8d4fddfb   0         0         0       22m
kubia-c56f95b7c    3         3         3       4m5s

기존의 RS는 더이상 사용되지 않고 새로운 RS가 생긴 것을 알 수 있다. 이는 직접적으로 RS를 관리하는 것보다 단일 디플로이먼트를 관리하는 것이 여러모로 효율적이라는 것을 보여준다.

만약 새로 반영된 업데이트 버전에 치명적인 에러가 있어 되돌려야 한다면 롤아웃을 되돌려야 한다.

Undo Roll Out

우선 수동으로 처리할 수 있는 방법이 있다.

kubectl rollout undo deployment kubia

RS를 기준으로 Rollout History를 추적하는 것이 가능하다. 기본적으로 10개까지 기록할 수 있으며 이를 이용하여 특정 배포 시점으로 돌아가는 것도 가능하다.

제한 갯수 이전의 레플리카셋은 자동으로 삭제된다.

Rollout Speed Control

새 파드를 만들고 기존 파드를 삭제하는 방법을 구성하는 방법에 두 가지 추가 속성을 추가해 더욱 풍성하게 사용하는 것이 가능하다.

이 속성들을 활용하면 롤링 업데이트 중에 한번에 몇 개의 파드를 교체할지를 결정할 수 있다.

spec:
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailabe: 0
    type: RollingUpdate
  1. maxSurge: 디플로이먼트가 의도하는 레플리카 수보다 얼마나 많은 파드 인스턴스 수를 허용할 수 있는지 결정한다. 기본적으로 25%로 설정되고 의도한 개수보다 최대 25% 더 많은 파드 인스턴스가 있을 수 있다. 의도하는 레플리카 수가 4로 설정된 경우 업데이트 중에 동시에 5개 이상의 파드 인스턴스가 실행되지 않는다. 백분율을 절대 숫자로 변환하면 숫자가 반올림된다.
  2. maxUnavailable: 업데이트 중에 의도하는 레플리카 수를 기준으로 사용할 수 없는 파드 인스턴스 수를 결정한다. 기본적으로 25%로 설정되고 75% 이하로 떨어지지 않아야 한다.

Rollout Pause

새로운 버전을 완전히 배포하기 앞서서 새로운 파드를 읿부 사용하여 정상 작동하는지 확인한 뒤, 완전한 롤아웃을 진행하고 싶을 수도 있을 것이다.

이를 위해 디플로이먼트 자체에서 다른 옵션을 사용하면 된다. 롤아웃 프로세스 중에 일시 중지하는 것이다. 이렇게 하면 나머지 롤아웃을 진행하기 이전에 새 버전으로 모든 것이 정상인지 확인할 수 있다.

kubectl rollout pause deployment kubia

rollout pause 명령어를 사용하여 일시중시하는 것이 가능하다.

잘못된 버전의 롤아웃 방지

디플로이먼트에서 설정한 minReadySeconds 속성은 롤아웃 속도를 늦춰 롤링 업데이트 과정을 직접 볼 수 있도록 해주고 모든 파드를 한번에 교체하지 않는다.

이 기능의 핵심은 단순히 배포 속도를 늦추는 것이 아니라 오작동 버전의 배포를 방지하는 것이다.

minReadySeconds 속성은 파드를 사용 가능한 것으로 취급하기 이전에 새로 만든 파드를 준비할 시간을 지정한다.

모든 파드의 레디니스 프로브가 성공하면 파드가 준비된다. 만약 minReadySeconds가 지나기 전에 새 파드의 레디니스 프로브가 실패한다면 새 버전의 롤아웃은 차단된다.

적절하게 minReadySecondsreadinessProbe를 사용한다면 설사 프로덕션 버전에 버그가 흘러들어간다 하더라도 효과적으로 차단할 수 있게 된다.

롤아웃 설정

롤아웃 프로세스에서 readinessProbe가 실패한 파드를 감지하면 더이상 롤아웃을 진행하지 않는다.

그렇다면 배포가 중단된 ㅅ아태에서 어떻게 해야할까.

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

이러한 설정은 progressDeadlineSeconds로 설정할 수 있다. %

0개의 댓글