ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쿠버네티스 라벨을 이용한 카나리(canary) 배포
    Kubernetes 2018. 7. 31. 12:35
    라벨은 다양하게 활용할 수 있습니다. 그 중에서도 배포에 활용하는 방법을 알아보도록 하겠습니다. 일반적으로 배포를 이야기 할 때는 롤링업데이트, 블루/그린, 카나리등 여러가지 방법이 있습니다. 롤링업데이트는 전체 배포 되어 있는 포드들을 한꺼번에 교체하는 것이 아니라 일정 개수씩 바꿔치기하면서 배포하는 방법으로 디플로이먼트의 기본 배포 방법입니다. 블루/그린은 기존에 띄워져 있는 포드 개수와 동일한 개수 만큼의 신규포드를 모두 띄운 다음에 신규 포드가 이상없이 정상적으로 떴는지 확인한 다음에 들어오는 트래픽을 한번에 신규포드쪽으로 옮기는 방법입니다. 이것역시 디플로이먼트를 이용하면 가능합니다. 카나리 배포는 옛날 광부들이 광산에 유독가스가 있는지 확인하기 위해 가스에 민감한 카나리아를 광산에 가지고 들어간 거에서 아이디어를 얻은 배포 방법입니다. 신규 버전을 배포할 때 한꺼번에 앱의 전체를 교체하는게 아니라 기존 버전을 유지한 채로 일부 버전만 신규 버전으로 올려서 신규 버전에 버그나 이상은 없는지를 사용자 반응은 어떤지 확인하는데 유용하게 사용하는 방법입니다. 쿠버네티스의 기본 디플로이먼트로는 디플로이먼트에 속한 포드들을 하나씩이던 한꺼번에든 모두 교체하는 방식이기 때문에 이런 카나리 배포를 하기에는 어려움이 있습니다. 하지만 라벨을 이용하면 쿠버네티스에서도 카나리 배포를 할 수 있습니다.

    카나리 배포를 위해서 우선 2개의 디플로이먼트를 만듭니다.
    deployment-v1.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: k8s-testapp
      labels:
        app: myapp
        version: stable
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: myapp
          version: stable
      template:
        metadata:
          labels:
            app: myapp
            version: stable
        spec:
          containers:
          - name: testapp
            image: arisu1000/simple-container-app:v0.1
            ports:
            - containerPort: 8080

    deployment-v2.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: k8s-testapp-canary
      labels:
        app: myapp
        version: canary
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: myapp
          version: canary
      template:
        metadata:
          labels:
            app: myapp
            version: canary
        spec:
          containers:
          - name: testapp
            image: arisu1000/simple-container-app:v0.2
            ports:
            - containerPort: 8080
    앞의 yaml파일을 이용해서 버전이 다른 2개의 포드를 실행합니다.
    kubectl apply -f deployment-v1.yaml
    kubectl apply -f deployment-v2.yaml

    v1과 v2에서 실행되는 컨테이너 이미지는 접속한 내역을 보여주는 간단한 앱입니다. 앞부분에 app version을 표시하는데 v1에서는 v0.1을 보여주고 v2에서는 v0.2를 보여줍니다. deployment파일의 내용중 deployment-v1.yaml은 app=myapp, version=stable 라는 라벨을 가지고 있고, deployment-v2.yaml은 app=myapp, version=canary라는 라벨을 가지고 있습니다. 같은 앱이지만 라벨을 이용해서 stable버전과 canary버전을 구분하기 위한 용도입니다. 그리고 v1은 포드를 2개 실행했고, v2는 canary용이기 때문에 1개만 실행했습니다.
    이제 이 2개의 디플로이먼트에 접근하는 하나의 서비스를 실행해 보겠습니다.

    myapp-svc.yaml
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: myapp
      name: myapp-svc
      namespace: default
    spec:
      ports:
      - nodePort: 30880
        port: 8080
        protocol: TCP
        targetPort: 8080
      selector:
        app: myapp
      type: NodePort
    kubectl apply -f myapp-svc.yaml로 실행하면 30880의 포트를 가진 노드포트로 서비스를 실행합니다. 
    curl localhost:30880 로 여러번 접속해 보면 결과를 확인할 수 있습니다. app version이 v0.1과 v0.2를 번갈아 가면서 출력되는걸 확인할 수 있습니다. 여러개의 디플로이먼트를 하나의 접속 주소로 묶어서 서비스하고 있다는걸 확인할 수 있습니다.


    보다 자세한 내용을 확인하려면 다음 명령으로 현재 pod정보와 svc가 어떻게 연결되어 있는지 확인할 수 있습니다.

    [wcjung@jeong-woncheon-ui-MacBook-Pro ~]$ kubectl get pods -o wide
    NAME                                  READY     STATUS    RESTARTS   AGE       IP           NODE
    k8s-testapp-86b5494dd4-2glc6          1/1       Running   0          27m       10.1.0.123   docker-for-desktop
    k8s-testapp-86b5494dd4-crm4p          1/1       Running   0          27m       10.1.0.124   docker-for-desktop
    k8s-testapp-canary-54fcdcfdbd-drh59   1/1       Running   0          27m       10.1.0.125   docker-for-desktop
    [wcjung@jeong-woncheon-ui-MacBook-Pro ~]$ kubectl describe svc myapp-svc
    Name:                     myapp-svc
    Namespace:                default
    Labels:                   app=myapp
    Annotations:              kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"myapp"},"name":"myapp-svc","namespace":"default"},"spec":{"ports":[{"...
    Selector:                 app=myapp
    Type:                     NodePort
    IP:                       10.107.123.233
    LoadBalancer Ingress:     localhost
    Port:                     <unset>  8080/TCP
    TargetPort:               8080/TCP
    NodePort:                 <unset>  30880/TCP
    Endpoints:                10.1.0.123:8080,10.1.0.124:8080,10.1.0.125:8080
    Session Affinity:         None
    External Traffic Policy:  Cluster
    Events:                   <none>
    각 pod정보의 IP들이 service의 Endpoints부분에 보면 나와있는걸 확인할 수 있습니다. 이렇게 stable버전의 포드와 canary버전의 포드를 동시에 서비스할 수 있습니다. 이 상태에서 canary버전이 정상작동하는지 확인하고 만약 제대로 작동하지 않는다면 디플로이먼트를 지우거나 디플로이먼트의 replicas를 0으로 조정해서 서비스에서 제외할 수 있습니다. 좀 더 많은 사용자에게 테스트를 해보고 싶다면 canary버전의 replicas 개수를 늘려서 보다 많은 요청을 canary버전에서 받게할 수도 있습니다. 그리고 canary버전이 정상작동한다면 stable에 있는 컨테이너의 이미지를 업데이트해서 전체 서비스에 적용할 수 있습니다.


    댓글

Designed by Tistory.