Kubernetes 이야기

Nginx Ingress를 통해 Canary 배포 설정 본문

Kubernetes/devops

Nginx Ingress를 통해 Canary 배포 설정

kmaster 2022. 9. 4. 08:21
반응형

Canary 배포는 새 버전이 모든 사용자에게 릴리스되기 전에 초기 테스트로 일부 사용자에게 점진적으로 롤아웃하는 방법이다. 

 

자세한 내용은 다음을 참고한다.

https://kmaster.tistory.com/9

 

Kubernetes의 다양한 배포방식 (3) Canary 배포

Canary 배포 Canary 라는 용어는 옛날 탄광에서 나오는 유독 가스에 죽거나 다치는 일을 피하고자 광부들이 유독 가스에 민감한 카나리아를 데리고 갱도로 내려간 일에서 나온 용어로 다가온 위험

kmaster.tistory.com

 

Nginx에서는 Canary 배포를 위한 다양한 Annotation 을 지원한다. Nginx Ingress Controller 에서 제공하는 Canary 배포 annotation 은 다음과 같다.

  • nginx.ingress.kubernetes.io/canary : canary 활성화 여부, "true" or "false"
  • nginx.ingress.kubernetes.io/canary-by-header : Canary Ingress에 지정된 서비스로 요청을 라우팅하도록 Ingress에 알리는 데 사용할 헤더
  • nginx.ingress.kubernetes.io/canary-by-header-value : Ingress에 요청을 Canary Ingress에 지정된 서비스로 라우팅하도록 알리기 위해 일치시킬 헤더 값
  • nginx.ingress.kubernetes.io/canary-by-header-pattern : CIngress에 요청을 Canary Ingress에 지정된 서비스로 라우팅하도록 알리는 데 사용할 정규식
  • nginx.ingress.kubernetes.io/canary-by-cookie : Ingress에 요청을 Canary Ingress에 지정된 서비스로 라우팅하도록 알리는 데 사용할 쿠키 값. 쿠키 값이 always로 설정되면 canary로 라우팅된다.
  • nginx.ingress.kubernetes.io/canary-weight : Canary Ingress에 지정된 서비스로 라우팅되어야 하는 임의 요청의 백분율
  • nginx.ingress.kubernetes.io/canary-weight-total : 트래픽의 총 중량. 지정하지 않으면 기본값은 100
카나리아 규칙의 우선 순위는 다음과 같다. 
canary-by-header -> canary-by-cookie -> canary-weight

다음과 같은 앱을 배포해 보자.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: production
  labels:
    app: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: production
  template:
    metadata:
      labels:
        app: production
    spec:
      containers:
      - name: production
        image: mirrorgooglecontainers/echoserver:1.10
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP

---

apiVersion: v1
kind: Service
metadata:
  name: production
  labels:
    app: production
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: production

이제 Ingress에 다음과 같이 설정한다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: test.10.10.0.110.nip.io
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: production
            port:
              number: 80

서비스를 한 번 호출해 보자.

 

배포 사용 예

다음과 같이 새로 만들 앱 (name : canary)을 배포해 보자.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary
  labels:
    app: canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: canary
  template:
    metadata:
      labels:
        app: canary
    spec:
      containers:
      - name: canary
        image: mirrorgooglecontainers/echoserver:1.10
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP

---

apiVersion: v1
kind: Service
metadata:
  name: canary
  labels:
    app: canary
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: canary

현재 배포된 앱은 다음과 같다.

# k get svc,pod -n test
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/canary       ClusterIP   10.100.51.112   <none>        80/TCP    26s
service/production   ClusterIP   10.97.182.169   <none>        80/TCP    15m

NAME                              READY   STATUS    RESTARTS   AGE
pod/canary-58dc8b498f-bbrss       1/1     Running   0          26s
pod/production-846769889f-g2dck   1/1     Running   0          15m

 

Header 기반 설정

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "x-region"
    nginx.ingress.kubernetes.io/canary-by-header-value: "ap-seoul"
spec:
  rules:
  - host: test.10.10.0.110.nip.io
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: canary
            port:
              number: 80

header값을 canary 의 haeder값으로 설정하여 호출해 보자. ( hostname이 canary-xx 로 호출되는 것을 볼 수 있다. )

# curl -H "Host: test.10.10.0.110.nip.io" -H "x-region: ap-seoul" http://test.10.10.0.110.nip.io


Hostname: canary-58dc8b498f-bbrss

Pod Information:
        node name:      node2
        pod name:       canary-58dc8b498f-bbrss
        pod namespace:  test
        pod IP: 192.168.104.17

Server values:
        server_version=nginx: 1.13.3 - lua: 10008

Request Information:
        client_address=192.168.166.129
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://test.10.10.0.110.nip.io:8080/

Request Headers:
        accept=*/*
        host=test.10.10.0.110.nip.io
        user-agent=curl/7.29.0
        x-forwarded-for=10.10.0.110
        x-forwarded-host=test.10.10.0.110.nip.io
        x-forwarded-port=80
        x-forwarded-proto=http
        x-forwarded-scheme=http
        x-real-ip=10.10.0.110
        x-region=ap-seoul
        x-request-id=8deb3ad9419a786ebf7148db99b513c4
        x-scheme=http

Request Body:
        -no body in request-

header 값이 틀린 경우는 기존 production 이 호출된다.

# curl -H "Host: test.10.10.0.110.nip.io" -H "x-region: ap-busan" http://test.10.10.0.110.nip.io


Hostname: production-846769889f-g2dck

Pod Information:
        node name:      node1
        pod name:       production-846769889f-g2dck
        pod namespace:  test
        pod IP: 192.168.166.136

Server values:
        server_version=nginx: 1.13.3 - lua: 10008

Request Information:
        client_address=10.140.0.112
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://test.10.10.0.110.nip.io:8080/

Request Headers:
        accept=*/*
        host=test.10.10.0.110.nip.io
        user-agent=curl/7.29.0
        x-forwarded-for=10.10.0.110
        x-forwarded-host=test.10.10.0.110.nip.io
        x-forwarded-port=80
        x-forwarded-proto=http
        x-forwarded-scheme=http
        x-real-ip=10.10.0.110
        x-region=ap-busan
        x-request-id=b7de598c0fc2ed55111e2fdb96b837e6
        x-scheme=http

Request Body:
        -no body in request-

 

Cookie 기반 정책

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-cookie: "canary-session"
spec:
  rules:
  - host: test.10.10.0.110.nip.io
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: canary
            port:
              number: 80

Cookie값을 "canary-session" 값으로 설정하여 호출해 보자. 

# curl -H "Host: test.10.10.0.110.nip.io" --cookie "canary-session=always" http://test.10.10.0.110.nip.io


Hostname: canary-58dc8b498f-bbrss

Pod Information:
        node name:      node2
        pod name:       canary-58dc8b498f-bbrss
        pod namespace:  test
        pod IP: 192.168.104.17

Server values:
        server_version=nginx: 1.13.3 - lua: 10008

Request Information:
        client_address=192.168.166.129
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://test.10.10.0.110.nip.io:8080/

Request Headers:
        accept=*/*
        cookie=canary-session=always
        host=test.10.10.0.110.nip.io
        user-agent=curl/7.29.0
        x-forwarded-for=10.10.0.110
        x-forwarded-host=test.10.10.0.110.nip.io
        x-forwarded-port=80
        x-forwarded-proto=http
        x-forwarded-scheme=http
        x-real-ip=10.10.0.110
        x-request-id=8fdff585937ec03b60688f3ca5fc9f21
        x-scheme=http

Request Body:
        -no body in request-

 

가중치(weight) 기반 정책

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
  rules:
  - host: test.10.10.0.110.nip.io
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: canary
            port:
              number: 80

다음과 같이 호출해 보자.

# for i in {1..10}; do curl -ss -H "Host: test.10.10.0.110.nip.io"  http://test.10.10.0.110.nip.io; done  | grep Hostname
Hostname: production-846769889f-g2dck
Hostname: production-846769889f-g2dck
Hostname: production-846769889f-g2dck
Hostname: production-846769889f-g2dck
Hostname: production-846769889f-g2dck
Hostname: canary-58dc8b498f-bbrss
Hostname: canary-58dc8b498f-bbrss
Hostname: production-846769889f-g2dck
Hostname: production-846769889f-g2dck
Hostname: production-846769889f-g2dck

 

가중치를 100%로 하게되면 Blue/Green 배포와 유사한 형태가 된다.

 

반응형

'Kubernetes > devops' 카테고리의 다른 글

Jenkins와 Keycloak을 이용한 OIDC 연동  (0) 2022.09.10
jenkins 를 활용하여 Kubernetes에 이미지 배포 및 모니터링  (0) 2022.09.08
Keptn (활용)  (0) 2022.08.04
Ketpn (설치)  (0) 2022.07.28
argo CD Image Updater  (1) 2022.05.20
Comments