자습서-Kubernetes Canary 배포로 가동 시간 및 복원력 향상

귀하의 조직은 Kubernetes에서 앱을 성공적으로 제공하고 있으며 이제 팀은 백엔드 서비스의 v2를 출시할 준비가 되었습니다. 그러나 트래픽 중단(다운타임이라고도 함)과 v2가 불안정할 가능성에 대한 유효한 우려가 있습니다. Kubernetes 엔지니어는 고객에게 거의 또는 전혀 영향을 미치지 않으면서 v2를 테스트하고 출시할 수 있는 방법을 찾아야 합니다.

새로운 기능 또는 버전의 안정성을 테스트하는 안전하고 민첩한 방법을 제공하기 때문에 트래픽 분할 기술 “카나리(Canary) 배포”를 사용하여 점진적이고 제어된 마이그레이션을 구현하기로 결정했습니다. 사용 사례에는 두 Kubernetes 서비스 간의 트래픽 이동이 포함되므로 NGINX Service Mesh가 쉽고 안정적인 결과를 제공하기 때문에 사용하기로 선택했습니다. 트래픽의 10%를 v2로 보내고 나머지 90%는 여전히 v1으로 라우팅합니다. 안정성이 좋아 보이므로 100%에 도달할 때까지 점점 더 많은 비율의 트래픽을 v2로 전환합니다.

자신의 환경에서 자습서로 수행하려면 다음이 포함된 머신이 필요합니다.

  • CPU 2개 이상
  • 2GB의 여유 메모리
  • 20GB의 디스크 여유 공간
  • 인터넷 연결
  • Docker, HyperKit, Hyper-V, KVM, Parallels, Podman, VirtualBox 또는 VMware Fusion/Workstation과 같은 컨테이너 또는 가상 머신 관리자
  • minikube 설치
  • Helm 설치

이 모범사례에서는 다음 기술을 사용합니다.

이 자습서에는 세 가지 과제가 포함되어 있습니다.

  1. 클러스터 및 NGINX Service Mesh 배포
  2. 두 개의 App 배포(frontend 및 backend)
  3. NGINX Service Mesh를 사용하여 카나리(Canary) 배포 구현

목차

1. 과제 1: 클러스터 및 NGINX Service Mesh 배포
  1-1. NGINX Service Mesh 배포
2. 과제 2: 두 개의 App 배포(frontend 및 backend)
  2-1. Backend-v1 앱 설치
  2-2. frontend 앱 배포
  2-3. 로그(Log) 확인
  2-4. Jaeger로 종속성 그래프 검사
  2-5. Backend-v2 추가
  2-6. 로그 검사(Log Inspect)
  2-7. Jaeger로 Return
3. 과제 3: NGINX Service Mesh를 사용하여 카나리(Canary) 배포 구현
  3-1. 카나리아(Canary) 배포란 무엇입니까?
  3-2. 서비스(Service) 간 카나리아(Canary) 배포
  3-3. 트래픽 분할에 NGINX Service Mesh 사용
  3-4. 카나리(Canary) 배포 생성
  3-5. 로그 관찰(Log Observe)
  3-6. 카나리(Canary) 배포 구현
  3-7. V2로 롤오버(Rollover) 실행

1. 과제 1: 클러스터 및 NGINX Service Mesh 배포

minikube 클러스터를 배포합니다. 몇 초 후 배포가 성공했음을 확인하는 메시지가 표시됩니다.

$ minikube start \ 
--extra-config=apiserver.service-account-signing-key-file=/var/lib/minikube/certs/sa.key \ 
  --extra-config=apiserver.service-account-key-file=/var/lib/minikube/certs/sa.pub \ 
  --extra-config=apiserver.service-account-issuer=kubernetes/serviceaccount \ 
  --extra-config=apiserver.service-account-api-audiences=api 
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default 
  • NGINX Service Mesh에 필요한 항목인 서비스 계정 토큰 볼륨 프로젝션을 활성화하려면 몇 가지 추가 구성이 필요합니다.
  • 이 기능이 활성화되면 kubelet은 서비스 계정 토큰을 각 Pod에 마운트합니다.
  • 이 시점부터 Pod는 클러스터에서 고유하게 식별될 수 있으며 kubelet은 정기적인 간격으로 토큰 회전을 처리합니다.
  • 자세한 내용은 Kubernetes ID를 사용한 마이크로서비스 간 인증을 참조하세요.

1-1. NGINX Service Mesh 배포

NGINX Service Mesh는 F5 NGINX에서 유지 관리하며 NGINX Plus를 사이드카(Sidecar)로 사용합니다. NGINX Plus는 상용 제품이지만 NGINX Service Mesh의 일부로 무료로 사용할 수 있습니다.

설치에는 두 가지 옵션이 있습니다.

Helm은 가장 간단하고 빠른 방법이므로 이 모범사례에서 사용합니다.

1. NGINX Service Mesh 다운로드 및 설치

helm install nms ./nginx-service-mesh  --namespace nginx-mesh --create-namespace 

2. STATUS 열에 Running 값으로 표시된 대로 NGINX Service Mesh Pod가 배포되었는지 확인합니다.

kubectl get pods --namespace nginx-mesh 
NAME                                  READY   STATUS 
grafana-7c6c88b959-62r72              1/1     Running 
jaeger-86b56bf686-gdjd8               1/1     Running 
nats-server-6d7b6779fb-j8qbw          2/2     Running 
nginx-mesh-api-7864df964-669s2        1/1     Running 
nginx-mesh-metrics-559b6b7869-pr4pz   1/1     Running 
prometheus-8d5fb5879-8xlnf            1/1     Running 
spire-agent-9m95d                     1/1     Running 
spire-server-0                        2/2     Running 

모든 Pod를 배포하는 데 1.5분이 소요될 수 있습니다. NGINX Service Mesh Pod 외에도 Grafana, Jaeger, NATS, Prometheus 및 Spire용 Pod가 있습니다.

2. 과제 2: 두 개의 App 배포(frontend 및 backend)

앱 배포는 두 개의 마이크로서비스로 구성됩니다.

  • frontend: 방문자에게 UI를 제공하는 웹 앱. 복잡한 요청을 분해하고 수많은 backend 앱에 호출을 보냅니다.
  • backend-v1: Kubernetes API를 통해 frontend에 데이터를 제공하는 비즈니스 논리 앱입니다.

2-1. Backend-v1 앱 설치

1. 선택한 텍스트 편집기를 사용하여 다음 내용으로 1-backend-v1.yaml이라는 YAML 파일을 만듭니다.

apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: backend-v1 
data: 
  nginx.conf: |- 
    events {} 
    http { 
        server { 
            listen 80; 
            location / { 
                return 200 '{"name":"backend","version":"1"}'; 
            } 
        } 
    } 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: backend-v1 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: backend 
      version: "1" 
  template: 
    metadata: 
      labels: 
        app: backend 
        version: "1" 
      annotations: 
    spec: 
      containers: 
        - name: backend-v1 
          image: "nginx" 
          ports: 
            - containerPort: 80 
          volumeMounts: 
            - mountPath: /etc/nginx 
              name: nginx-config 
      volumes: 
        - name: nginx-config 
          configMap: 
            name: backend-v1 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: backend-svc 
  labels: 
    app: backend 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
  selector: 
    app: backend 

2. backend-v1 배포

$ kubectl apply -f 1-backend-v1.yaml 
configmap/backend-v1 created 
deployment.apps/backend-v1 created 
service/backend-svc created 

3. STATUS 열에 Running 값으로 표시된 대로 backend-v1 Pod 및 Service가 배포되었는지 확인합니다.

$ kubectl get pods,services 
NAME                              READY   STATUS 
pod/backend-v1-745597b6f9-hvqht   2/2     Running 

NAME                  TYPE        CLUSTER-IP       PORT(S) 
service/backend-svc   ClusterIP   10.102.173.77    80/TCP 
service/kubernetes    ClusterIP   10.96.0.1        443/TCP 

“backend-v1에 대해 두 개의 Pod가 실행되는 이유는 무엇입니까?”

NGINX Service Mesh는 사이드카 프록시(Sidecar Proxy)를 Pod에 삽입합니다.
이 사이드카(Sidecar) 컨테이너는 Pod로 들어오는 모든 트래픽과 나가는 트래픽을 가로챕니다.
수집된 모든 데이터는 메트릭에 사용되지만 이 프록시를 사용하여 트래픽이 어디로 가야 하는지 결정할 수도 있습니다.

2-2. frontend 앱 배포

1. 다음 내용으로 2-frontend.yaml이라는 YAML 파일을 만듭니다. Pod는 cURL을 사용하여 1초마다 백엔드 서비스(backend-svc)에 요청을 발행합니다.

apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: frontend 
spec: 
  selector: 
    matchLabels: 
      app: frontend 
  template: 
    metadata: 
      labels: 
        app: frontend 
    spec: 
      containers: 
      - name: frontend 
        image: curlimages/curl:7.72.0 
        command: [ "/bin/sh", "-c", "--" ] 
        args: [ "sleep 10; while true; do curl -s http://backend-svc/; sleep 1 && echo ' '; done" ] 

2. frontend 배포

$ kubectl apply -f 2-frontend.yaml 
deployment.apps/frontend created 

3. STATUS 열에 Running 값으로 표시된 대로 frontend Pod가 배포되었는지 확인합니다. 다시 말하지만, NGINX Service Mesh의 일부이기 때문에 각 앱에 대해 두 개의 Pod가 있습니다.

$ kubectl get pods 
NAME                         READY   STATUS    RESTARTS 
backend-v1-5cdbf9586-s47kx   2/2     Running   0 
frontend-6c64d7446-mmgpv     2/2     Running   0 

2-3. 로그(Log) 확인

다음으로, 트래픽이 frontend에서 backend-v1로 흐르고 있는지 확인하기 위해 로그를 검사합니다. 로그를 검색하는 명령을 사용하려면 다음 형식을 사용하여 통합해야 합니다.

kubectl logs -c frontend <insert the full pod id displayed in your Terminal>

TIP: 이 명령을 만든 후에는 이 모범사례에서 반복적으로 사용되므로 검색하기 쉬운 위치에 저장하십시오.

전체 Pod ID는 이전 단계(frontend-6c64d7446-mmgpv)에서 사용할 수 있으며 배포에 고유합니다. 명령을 제출할 때 로그는 모든 트래픽이 backend-v1로 라우팅되고 있다고 보고해야 합니다. 이는 유일한 backend이기 때문에 예상되는 것입니다.

$ kubectl logs -c frontend frontend-6c64d7446-mmgpv 

{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 

2-4. Jaeger로 종속성 그래프 검사

더 흥미로운 점은 두 앱과 함께 배포된 NGINX Service Mesh 사이드카(Sidecar)가 트래픽 흐름에 따라 메트릭을 수집한다는 것입니다. 이 데이터를 사용하여 Jaeger로 아키텍처의 종속성 그래프를 파생할 수 있습니다.

  • minikube service jaeger를 사용하여 브라우저에서 Jaeger 대시보드를 엽니다.
  • 매우 간단한 아키텍처가 표시되어야 하는 “시스템 아키텍처” 탭을 클릭합니다(레이블을 보려면 그래프 위로 커서를 이동). DAG 탭은 확대 보기를 제공합니다. frontend에서 수십 또는 수백 개의 backend 서비스에 액세스한다고 상상해 보세요. 이것은 매우 흥미로운 그래프가 될 것입니다!

2-5. Backend-v2 추가

이제 frontend도 제공할 두 번째 backend 앱인 backend-v2를 배포합니다. 버전(version) 번호에서 알 수 있듯이 backend-v2는 backend-v1의 새 버전입니다.

1. 다음 내용으로 3-backend-v2.yaml이라는 YAML 파일을 만들고 확인합니다.

  • 배포에서 앱을 공유하는 방법: backend 레이블을 이전 배포와 공유합니다.
  • service selector는 app: backend이므로 backend-v1과 backend-v2 간에 트래픽이 고르게 분산될 것으로 예상해야 합니다.
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: backend-v2 
data: 
  nginx.conf: |- 
    events {} 
    http { 
        server { 
            listen 80; 
            location / { 
                return 200 '{"name":"backend","version":"2"}'; 
            } 
        } 
    } 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: backend-v2 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: backend 
      version: "2" 
  template: 
    metadata: 
      labels: 
        app: backend 
        version: "2" 
      annotations: 
    spec: 
      containers: 
        - name: backend-v2 
          image: "nginx" 
          ports: 
            - containerPort: 80 
          volumeMounts: 
            - mountPath: /etc/nginx 
              name: nginx-config 
      volumes: 
        - name: nginx-config 
          configMap: 
            name: backend-v2 

2. backend-v2 배포

$ kubectl apply -f 3-backend-v2.yaml 
configmap/backend-v2 created 
deployment.apps/backend-v2 created

3. STATUS 열에 Running 값으로 표시된 대로 backend-v2 Pod 및 Service가 배포되었는지 확인합니다.

2-6. 로그 검사(Log Inspect)

이전과 동일한 명령을 사용하여 로그(Log)를 검사(Inspect)합니다. 이제 두 backend 버전에서 고르게 분포된 응답을 볼 수 있습니다.

$ kubectl logs -c frontend frontend-6c64d7446-mmgpv 

{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 

2-7. Jaeger로 Return

Jaeger 탭으로 돌아가 NGINX Service Mesh가 두 backend 버전을 모두 올바르게 매핑할 수 있는지 확인합니다.

  • Jaeger가 두 번째 backend가 추가되었음을 인식하는 데 몇 분 정도 걸릴 수 있습니다.
  • 1분 후에도 표시되지 않으면 다음 챌린지를 계속 진행하고 잠시 후 종속성 차트를 다시 확인하세요.

3. 과제 3: NGINX Service Mesh를 사용하여 카나리(Canary) 배포 구현

지금까지 이 모범사례에서는 두 가지 버전의 백엔드(v1 및 v2)를 배포했습니다. 모든 트래픽을 즉시 v2로 이동할 수 있지만 프로덕션 트래픽을 새 버전에 맡기기 전에 안정성을 테스트하는 것이 가장 좋습니다. 카나리(Canary) 배포는 이 사용 사례에 대한 완벽한 기술입니다.

3-1. 카나리아(Canary) 배포란 무엇입니까?

고급 트래픽 관리를 사용하여 Kubernetes의 복원력을 개선하는 방법 모범사례에서 논의한 바와 같이 카나리(Canary) 배포는 새로운 기능 또는 버전의 안정성을 테스트하는 안전하고 민첩한 방법을 제공하는 일종의 트래픽 분할입니다. 일반적인 카나리(Canary) 배포는 안정적인 버전에서 사용자의 높은 점유율(예: 99%)로 시작하여 작은 그룹(나머지 1%)을 새 버전으로 이동합니다. 새 버전이 실패하는 경우(예: 충돌 또는 클라이언트에 오류 반환) 테스트 그룹을 즉시 안정적인 버전으로 다시 이동할 수 있습니다. 성공하면 사용자를 안정적인 버전에서 새 버전으로 한 번에 전환하거나(일반적인 경우처럼) 점진적이고 제어된 마이그레이션으로 전환할 수 있습니다.

이 다이어그램은 트래픽을 분할하기 위해 Ingress Controller를 사용하는 카나리(Canary) 배포를 보여줍니다.

3-2. 서비스(Service) 간 카나리아(Canary) 배포

이 모범사례에서는 트래픽의 90%가 backend-v1로 이동하고 10%가 backend-v2로 이동하도록 트래픽 분할을 설정합니다.

Ingress Controller는 클라이언트에서 Kubernetes 서비스로 이동할 때 트래픽을 분할하는 데 사용되지만 서비스 간에 트래픽을 분할하는 데 사용할 수는 없습니다. 이 유형의 카나리(Canary) 배포를 구현하기 위한 두 가지 옵션이 있습니다.

Option 1: 어려운 방법(The Hard Way)

10개의 요청 중 9개를 backend-v1에 보내도록 frontend Pod의 프록시(Proxy) 지시할 수 있습니다. 그러나 수십 개의 frontend 복제본이 있다고 상상해 보십시오. 모든 프록시를 수동으로 업데이트하시겠습니까? 아니요! 오류가 발생하기 쉽고 시간이 많이 소요됩니다.

Option 2: 더 나은 방법(The Better Way)

관찰 가능성과 제어는 서비스 메시(Service Mesh)를 사용하는 훌륭한 이유입니다. 그리고 훨씬 더 많은 일을 할 수 있습니다. 서비스 메시는 메시가 제공하는 모든 frontend 복제본에 단일 정책을 적용할 수 있기 때문에 서비스 간 트래픽 분할을 구현하는 데 이상적인 도구이기도 합니다.

3-3. 트래픽 분할에 NGINX Service Mesh 사용

NGINX Service Mesh는 TrafficSplit, TrafficTarget 및 HTTPRouteGroup과 같은 유형이 지정된 리소스를 사용하여 Kubernetes의 Service Mesh에 대한 표준 인터페이스를 정의하는 사양인 SMI(Service Mesh Interface)를 구현합니다. 이러한 표준 Kubernetes 구성을 통해 NGINX Service Mesh 및 NGINX SMI 확장은 카나리(Canary) 배포와 같은 트래픽 분할 정책을 프로덕션 트래픽에 대한 중단을 최소화하면서 배포를 간단하게 만듭니다.

모든 메시와 마찬가지로 NGINX Service Mesh의 아키텍처에는 데이터 플레인과 컨트롤 플레인이 있습니다. NGINX Service Mesh는 데이터 플레인에 대해 NGINX Plus를 활용하기 때문에 고급 배포 시나리오를 수행할 수 있습니다.

  • Data plane: 사이드카(Sidecar)라고 하는 컨테이너화된 NGINX Plus 프록시로 구성되어 메시 내의 모든 앱에 필요한 기능을 offloading하고 카나리(Canary) 배포와 같은 배포 패턴을 구현합니다.
  • Control plane: 데이터 평면을 관리합니다. 사이드카(Sidecar)가 애플리케이션 트래픽을 라우팅하고 다른 데이터 플레인 서비스를 제공하는 동안 컨트롤 플레인은 사이드카(Sidecar)를 Pod에 삽입하고 mTLS 인증서를 갱신하고 적절한 사이드카로 푸시하는 등의 관리 작업을 수행합니다.

3-4. 카나리(Canary) 배포 생성

NGINX Service Mesh 컨트롤 플레인은 Kubernetes 사용자 지정 리소스 정의(CRD)로 제어할 수 있습니다. Kubernetes 서비스를 사용하여 Pod IP 주소 및 포트 목록을 검색합니다. 그런 다음 CRD의 지침을 결합하고 트래픽을 Pod로 직접 라우팅하도록 사이드카(Sidecar)에 알립니다.

1. 선택한 텍스트 편집기를 사용하여 TrafficSplit CRD를 사용하여 트래픽 분할을 정의하는 5-split.yaml이라는 YAML 파일을 만듭니다.

apiVersion: split.smi-spec.io/v1alpha3 
kind: TrafficSplit 
metadata: 
  name: backend-ts 
spec: 
  service: backend-svc 
  backends: 
  - service: backend-v1 
    weight: 90 
  - service: backend-v2 
    weight: 10 

CRD에 세 가지 서비스가 정의되어 있음을 확인하세요(지금까지는 하나만 만들었습니다).

  • backend-svc는 모든 Pod를 대상으로 하는 서비스입니다.
  • backend-v1backend-v1 배포에서 Pod를 선택하는 서비스입니다.
  • backend-v2backend-v2 배포에서 Pod를 선택하는 서비스입니다.

2. 트래픽 분할을 구현하기 전에 누락된 서비스를 생성해야 합니다. 다음 내용으로 4-services.yaml이라는 YAML 파일을 만듭니다.

apiVersion: v1 
kind: Service 
metadata: 
  name: backend-v1 
  labels: 
    app: backend 
    version: "1" 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
  selector: 
    app: backend 
    version: "1" 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: backend-v2 
  labels: 
    app: backend 
    version: "2" 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
  selector: 
    app: backend 
    version: "2" 

3. 서비스 추가

$ kubectl apply -f 4-services.yaml 
service/backend-v1 created 
service/backend-v2 created 

3-5. 로그 관찰(Log Observe)

트래픽 분할을 구현하기 전에 로그(Log)를 확인하여 TrafficSplit CRD가 없는 상태에서 트래픽이 어떻게 흐르는지 확인하십시오. 트래픽이 v1과 v2 간에 균등하게 분할되고 있음을 확인해야 합니다.

$ kubectl logs -c frontend frontend-6c64d7446-mmgpv 

{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 

3-6. 카나리(Canary) 배포 구현

1. TrafficSplit CRD를 적용합니다.

$ kubectl apply -f 5-split.yaml 
trafficsplit.split.smi-spec.io/backend-ts created 

2. 로그를 다시 관찰하십시오. 이제 5-split.yaml에 정의된 대로 트래픽의 90%가 v1으로 전달되는 것을 볼 수 있습니다.

$ kubectl logs -c frontend frontend-6c64d7446-mmgpv 

{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"1"} 

3-7. V2로 롤오버(Rollover) 실행

90/10 트래픽 분할을 수행한 다음 즉시 모든 트래픽을 새 버전으로 이동하려는 경우는 드뭅니다. 대신 트래픽을 점진적으로 이동하는 것이 가장 좋습니다. 예: 0%, 5%, 10%, 25%, 50% 및 100%. 증분 롤오버(Rollover)를 구현하는 것이 얼마나 쉬운지 설명하기 위해 가중치를 20/80으로 변경한 다음 0/100으로 변경합니다.

1. backend-v1이 트래픽의 20%를 가져오고 backend-v2가 나머지 80%를 가져오도록 5-split.yaml을 편집합니다.

apiVersion: split.smi-spec.io/v1alpha3 
kind: TrafficSplit 
metadata: 
  name: backend-ts 
spec: 
  service: backend-svc 
  backends: 
  - service: backend-v1 
    weight: 20 
  - service: backend-v2 
    weight: 80 

2. 변경사항 적용

$ kubectl apply -f 5-split.yaml 
trafficsplit.split.smi-spec.io/backend-ts configured 

3. 변경 사항을 보려면 로그를 관찰하십시오.

$ kubectl logs -c frontend frontend-6c64d7446-mmgpv 

{"name":"backend","version":"2"} 
{"name":"backend","version":"1"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"2"} 

4. 롤오버(rollover)를 완료하려면 backend-v1이 트래픽의 0%를 가져오고 backend-v2가 100%가 되도록 5-split.yaml을 편집합니다.

apiVersion: split.smi-spec.io/v1alpha3 
kind: TrafficSplit 
metadata: 
  name: backend-ts 
spec: 
  service: backend-svc 
  backends: 
  - service: backend-v1 
    weight: 0 
  - service: backend-v2 
    weight: 100 

5. 변경사항 적용

$ kubectl apply -f 5-split.yaml 
trafficsplit.split.smi-spec.io/backend-ts configured 

6. 로그를 관찰하여 변경 사항을 확인하십시오. 모든 응답이 backend-v2로 이동되었으며 이는 롤오버(rollover)가 완료되었음을 의미합니다!

$ kubectl logs -c frontend frontend-6c64d7446-mmgpv 

{"name":"backend","version":"2"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"2"} 
{"name":"backend","version":"2"}