10. 쿠버네티스 핵심 개념 (5)
이 문서는 인프런 강의 "데브옵스를 위한 쿠버네티스 마스터"을 듣고 작성되었습니다. 최대한 요약해서 강의 내용을 최소로 하는데 목표를 두고 있어서, 더 친절하고 정확한 내용을 원하신다면 강의를 구매하시는 것을 추천드립니다. => 강의 링크
네트워크 볼륨 (NFS)
Kubernetes
는 스토리지로 네트워크 파일 시스템(NFS
, GlusterFS
)을 이용할 수 있다. 이 절에서는 NFS
를 볼륨으로 이용하는 것에 대해서 다룬다. 이 절은 "VM"에서 진행한다. 먼저 모든 노드에 NFS
를 설치한다.
$ sudo -i
# apt-get install nfs-common nfs-kernel-server portmap -y
그리고 노드 1개를 선택해서 다음 작업을 진행한다. 나는 "슬레이브2"에서 진행했다.
## nfs용 디렉토리 생성
# mkdir /home/nfs
## 권한 부여
# chmod 777 /home/nfs
## 모든 노드의 ip nfs 디렉토리 접근할 수 있도록 권한 부여
# tee /etc/exports <<EOF
/home/nfs 10.0.2.15(rw,sync,no_root_squash) 10.0.2.4(rw,sync,no_root_squash) 10.0.2.5(rw,sync,no_root_squash)
EOF
## nfs 서버 재시작
# systemctl restart nfs-server
## 현재 마운트된 디렉토리 접근 가능 호스트 목록 확인
# showmount -e 127.0.0.1
Export list for 127.0.0.1:
/home/nfs 10.0.2.5,10.0.2.4,10.0.2.15
## /mnt 를 nfs 디렉토리에 마운트, 다음 형식을 따름
## mount -t nfs <자신의 노드>:<NFS 디렉토리> <마운트 디렉토리>
# mount -t nfs 10.0.2.5:/home/nfs /mnt
## 테스트 파일 생성
# echo test >> /home/nfs/test.txt
## /mnt에 공유되는지 확인
# cat /mnt/test.txt
test
그 후 다음과 같이 volume-nfs.yaml
파일을 만든다.
apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
containers:
- image: mongo
name: mongodb
volumeMounts:
- mountPath: /data/db
name: mongodb
ports:
- containerPort: 27017
volumes:
- name: mongodb
nfs:
server: 10.0.2.5
path: /home/nfs
이제 리소스를 생성한다.
$ kubectl create -f volume-nfs.yaml
생성이 끝나면 mongodb
포드에 접속해서 mongo
명령어를 실행한다.
$ kubectl exec -it mongodb mongo
...
>
이제 다음과 같이 mongodb
명령어를 사용해보자. 데이터베이스를 생성하고, 객체를 하나 저장하고 쿼리하는 내용이다.
# 데이터베이스 생성 및 선택
> use mydb
switched to db mydb
# 데이터 추가
> db.foo.insert({name: "test", value: 1234})
WriteResult({ "nInserted" : 1 })
# 데이터 쿼리
> db.foo.find()
{ "_id" : ObjectId("611280e2a9c5bd71d0b71690"), "name" : "test", "value" : 1234 }
# 종료
> exit
이제 포드를 제거해보자.
$ kubectl delete -f volume-nfs.yaml
그 후 다시 생성한다.
$ kubectl create -f volume-nfs.yaml
정상적으로 영구 디스크를 볼륨으로 사용하고 있었다면, 포드를 삭제했더라도 데이터가 남아 있을 것이다. mongodb
에 접속해서 데이터베이스 선택, 데이터 쿼리를 진행해보자.
$ kubectl exec -it mongodb mongo
...
# 데이터베이스 선택
> use mydb
switched to db mydb
# 데이터 쿼리
db.foo.find()
{ "_id" : ObjectId("611280e2a9c5bd71d0b71690"), "name" : "test", "value" : 1234 }
# 종료
> exit
잘 수행이 된다 굳!
클라우드 네트워크 볼륨 (gcePersistentDisk)
이 절은 "GCP"에서 진행한다. 이전 절에서 했던 작업을 GCP
같은 클라우드엣거는 미리 제공을 한다. 여기서는 NFS
역할을 하는 영구 디스크인 gcePersistentDisk
를 볼륨으로 사용하는 것을 다룬다. 먼저 영구 디스크인 gcePersistentDisk
를 생성한다.
$ gcloud compute disks create --size=10GB --zone=asia-northeast3-a mongodb
그 후 다음과 같이 volume-nfs-gce.yaml
파일을 만든다.
src/ch10/k8s/volume-nfs-gce.yaml
apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
containers:
- image: mongo
name: mongodb
volumeMounts:
- mountPath: /data/db
name: mongodb
ports:
- containerPort: 27017
volumes:
- name: mongodb
gcePersistentDisk:
pdName: mongodb
fsType: ext4
이제 리소스를 생성한다.
$ kubectl create -f volume-nfs-gce.yaml
생성이 끝나면 mongodb
포드에 접속해서 mongo
명령어를 실행한다.
$ kubectl exec -it mongodb mongo
...
>
이제 다음과 같이 mongodb
명령어를 사용해보자. 데이터베이스를 생성하고, 객체를 하나 저장하고 쿼리하는 내용이다.
# 데이터베이스 생성 및 선택
> use mydb
switched to db mydb
# 데이터 추가
> db.foo.insert({name: "test", value: 1234})
WriteResult({ "nInserted" : 1 })
# 데이터 쿼리
> db.foo.find()
{ "_id" : ObjectId("611280e2a9c5bd71d0b71690"), "name" : "test", "value" : 1234 }
# 종료
> exit
이제 포드를 제거해보자.
$ kubectl delete -f volume-nfs-gce.yaml
그 후 다시 생성한다.
$ kubectl create -f volume-nfs-gce.yaml
정상적으로 영구 디스크를 볼륨으로 사용하고 있었다면, 포드를 삭제했더라도 데이터가 남아 있을 것이다. mongodb
에 접속해서 데이터베이스 선택, 데이터 쿼리를 진행해보자.
$ kubectl exec -it mongodb mongo
...
# 데이터베이스 선택
> use mydb
switched to db mydb
# 데이터 쿼리
db.foo.find()
{ "_id" : ObjectId("611280e2a9c5bd71d0b71690"), "name" : "test", "value" : 1234 }
# 종료
> exit
잘 수행이 된다 굳!
PV와 PVC (1) 정적 프로비저닝
이 절은 "GCP"에서 진행된다. 그리고 이전 절 "클라우드 네트워크 볼륨 (gcePersistentDisk)"을 진행하고 오길 바란다.
PersistentVolume(이하 pv)
는 Kubernetes
에서 유일하게 운영자가 관리하는 리소스이다. 실제적으로 스토리지 연결을 이 리소스로 한다고 생각하면 된다. PersistentVolumeClaim(이하 pvc)
는 추상 계층으로써 개발자가 Kubernetes
에서 스토리지 작업을 하지 않고도 작업된 스토리지 볼륨을 사용하는데 쓰이는 리소스이다. 대충 이런 느낌이랄까?
이제 다음과 같이 volume-pv.yaml
파일을 만든다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongo-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
gcePersistentDisk:
pdName: mongodb
fsType: ext4
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: ""
---
apiVersion: v1
kind: Pod
metadata:
name: volume-pv
spec:
containers:
- image: mongo
name: mongodb
volumeMounts:
- mountPath: /data/db
name: mongodb
ports:
- containerPort: 27017
volumes:
- name: mongodb
persistentVolumeClaim:
claimName: mongo-pvc
pod
에서 볼륨은 이제 pvc
로 지정하는 것을 볼 수 있다. 이 때 claimName
은 pvc
의 이름이어야 한다. 또한 pvc
에 작성된 spec
을 토대로 알맞는 pv
가 있다면 그것을 사용하게 된다. 그리고 pv
는 이전 절과 같이 gcePersistentDisk
를 사용한다. 이제 리소스를 생성한다.
$ kubectl create -f volume-pv.yaml
생성이 끝나면 다음 명령어들을 확인해보자.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongo-pv 10Gi RWO,ROX Retain Bound default/mongo-pvc 25s
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongo-pvc Bound mongo-pv 10Gi RWO,ROX 28s
이때 pv
에서 CLAIM
에서 pvc
가 보이는지 STATUS
는 "Bound"인지 확인한다. 마찬가지로 pvc
에서는 STATUS
가 "Bound"인지 VOLUME
이 pv
의 이름이 설정됐는지 확인하면 된다. 이제 mongodb
포드에 접속해서 mongo
명령어를 실행한다.
$ kubectl exec -it mongodb mongo
...
>
이제 다음과 같이 mongodb
명령어를 사용해보자. 이전 절에서 진행했다면 데이터베이스를 선택하고 바로 저장한 객체를 다음과 같이 불러올 수 있을 것이다.
# 데이터베이스 선택
> use mydb
switched to db mydb
# 데이터 쿼리
db.foo.find()
{ "_id" : ObjectId("611280e2a9c5bd71d0b71690"), "name" : "test", "value" : 1234 }
# 종료
> exit
PV와 PVC (2) 동적 프로비저닝 StorageClass
이 절은 "GCP"에서 진행한다. pv
는 몇 가지 고려 사항이 몇 가지 있다.
- 개발 팀에 스토리지를 관리할 수 있는 자원이 있는가
- 디스크의 양이 충분한가
결국 pv
는 스토리지 영역을 컨트롤 할 수 있는 운영자가 절대적으로 필요하다. 만약 이런 부분이 미흡하다면 어떻게 해야 할까? StorageClass
를 이용하면 이 문제를 손쉽게 해결할 수 있다. StorageClass
는 pv
를 만들지 않고도 동적으로 pvc
가 요청한 크기만큼 스토리지를 볼륨으로 프로비저닝할 수가 있다. 다만 클라우드 프로바이더 GCP
, AWS
, Azure
등의 환경에서 사용하는 것이 권장된다.
다음과 같이 코드를 작성해보자. (이 때 pv
, pvc
는 모두 삭제해두어야 한다.)
src/ch10/k8s/volume-storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mongo-stroage-class
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: mongo-stroage-class
---
apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
containers:
- image: mongo
name: mongodb
volumeMounts:
- mountPath: /data/db
name: mongodb
ports:
- containerPort: 27017
volumes:
- name: mongodb
persistentVolumeClaim:
claimName: mongo-pvc
이전 절에서 진행했던 코드에서 다음이 변경되었다.
- pv는 storageClass로 대체한다.
- pvc의 storageClassName 필드에 storageClass의 이름을 할당한다.
이제 리소스를 생성한 후 다음 명령어들을 확인해보자.
# 리소스 생성
$ kubectl create -f volume-storage-class.yaml
# pv 확인
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongo-pv 10Gi RWO,ROX Retain Released default/mongo-pvc 34m
pvc-9a48f495-1af2-4174-824b-20c28d177615 1Gi RWO Delete Bound default/mongo-pvc mongo-stroage-class 4m18s
# pvc 확인
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongo-pvc Bound pvc-9a48f495-1af2-4174-824b-20c28d177615 1Gi RWO mongo-stroage-class 4m39s
pvc
에서 요청했던대로 1Gi 만큼의 볼륨이 생긴 것을 확인할 수 있다. 디스크를 확인해보면 ssd 형 디스크가 하나 생성한 것을 확인할 수 있다.
StatefulSet
StatefulSet
은 애플리케이션의 상태를 저장하고 관리하는데 사용되는 리소스이다. Deployment
와 상당히 유사한데, 차이점이라면 Deployment
는 포드가 무작위로 생성되고 배치되는 반면, StatefulSet
은 포드의 순서와 배치를 결정할 수 있다.
StatefulSet
은 다음과 같은 경우에 사용할 수 있다.
- 안정적이고 고유한 네티워크 식별자가 필요한 경우
- 안정적이고 지속적인 스토리지를 사용해야 하는 경우
- 질서 정연한 포드의 배치와 확장을 원하는 경우
- 포드의 자동 롤링 업데이트를 사용하기 원하는 경우
각 포드의 상태를 유지할 수 있는 장점이 생기는 반면 다음과 같은 단점도 생긴다.
- StatefulSet 관련된 볼륨이 자동으로 삭제되지 않는다.
- Pod의 Storage는 PV 혹은 StorageClass로 프로비저닝을 해주어야 한다.
- 롤링 업데이트 수행 시 수동으로 복구해야 하는 경우가 생긴다.
- Pod 네트워크 ID를 유지하기 위해서 Headless Service가 필요하게 된다.
즉 상태를 유지할 수 있는 장점과 동시에 수동으로 관리의 필요성이 생긴다는 단점이 생긴다. 한 번 StatefulSet
을 만들어보자. 이 절은 "GCP"에서 진행한다. (로컬도 상관은 없다.) 다음과 같이 파일을 작성한다.
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None # Headless
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "stateful-set-storage-class"
resources:
requests:
storage: 1Gi
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: stateful-set-storage-class
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
작성 요령은 Deployment
와 거의 동일하다. 가장 먼저 차이점은 Service
작성 요령에 있다.
apiVersion: v1
kind: Service
# ...
spec:
# ...
clusterIP: None # Headless
spec.clusterIP
의 값이 "None"이다. 이렇게 작성된 리소스를 Headless Service
라고 한다. StatefulSet
이 관리하는 Pod
를 Service
로 연결해주려면 반드시 이렇게 만들어주어야 한다.
# ...
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
# ...
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
# ...
StatefulSet
관련해서 Deployment
와 가장 큰 차이점은 Pod
에 대해 작성할 때 다음 데이터가 반드시 필요하다.
- spec.terminationGracePeriodSeconds
- spec.containers[].ports.ports[].name
terminationGracePeriodSeconds
는 리소스를 종료할 때 대기해주는 시간을 의미한다. 순서대로 배치시키기 때문에 이 값은 필수적으로 들어간다. 또한, ports
관련 작성 시 이름이 필수적으로 들어가게 된다.
# ...
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
# ...
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "stateful-set-storage-class"
resources:
requests:
storage: 1Gi
또한 volumeClaimTemplates
에서 pvc
와 연결해주어야 한다. 작성 요령은 Pod
의 volumes
와 유사하다. 이제 터미널에 다음을 입력하여 리소스를 생성한다.
$ kubectl create -f statefulset.yaml
service/nginx created
statefulset.apps/web created
storageclass.storage.k8s.io/stateful-set-stroage-class created
리소스가 다 생성된다면 다음 명령어로 만들어진 리소스들을 확인해보자.
$ kubectl get pod -w
# 순서대로 web-n (0, 1, 2...) 로 만들어지는 것을 확인할 수 있다.
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 26s
web-0 1/1 Running 0 32s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 7s
web-1 0/1 ContainerCreating 0 7s
web-1 1/1 Running 0 22s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 7s
web-2 0/1 ContainerCreating 0 7s
web-2 1/1 Running 0 17s
$ kubectl get pvc
# 역시 www-web-n (0, 1, 2..) 형식으로 출력된다.
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-fc161b94-5c7d-4367-b15d-c148eab6cdea 1Gi RWO stateful-set-storage-class 2m16s
www-web-1 Bound pvc-a49ba9cf-2b6d-48e7-b8c8-d78ec259d554 1Gi RWO stateful-set-storage-class 104s
www-web-2 Bound pvc-7e940025-e320-4da5-a3ef-500b4e5134a3 1Gi RWO stateful-set-storage-class 82s
$ kubectl get statefulset
NAME READY AGE
web 3/3 2m55s
이제 StatefulSet
을 스케일 아웃해보자. 3개에서 5개로 Pod
를 늘린다.
$ kubectl scale statefulset web --replicas=5
statefulset.apps/web scaled
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 12m
web-1 1/1 Running 0 11m
web-2 1/1 Running 0 11m
# 여기서부터 확인 가능하다. 역시 오름차순 형태로 pod가 생성된다.
web-3 0/1 Pending 0 5s
web-3 0/1 Pending 0 7s
web-3 0/1 ContainerCreating 0 7s
web-3 1/1 Running 0 17s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 7s
web-4 0/1 ContainerCreating 0 7s
web-4 1/1 Running 0 17s
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-fc161b94-5c7d-4367-b15d-c148eab6cdea 1Gi RWO stateful-set-storage-class 13m
www-web-1 Bound pvc-a49ba9cf-2b6d-48e7-b8c8-d78ec259d554 1Gi RWO stateful-set-storage-class 13m
www-web-2 Bound pvc-7e940025-e320-4da5-a3ef-500b4e5134a3 1Gi RWO stateful-set-storage-class 12m
www-web-3 Bound pvc-7565e8f0-7c9a-4f9f-b38f-e6f582e33187 1Gi RWO stateful-set-storage-class 86s
www-web-4 Bound pvc-0ddd0bae-7aa9-4543-9dd0-0e176970ba09 1Gi RWO stateful-set-storage-class 69s
이제는 스케일 인해보자. 5개에서 1개로 줄인다.
$ kubectl scale statefulset web --replicas=1
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 14m
web-1 1/1 Running 0 13m
web-2 1/1 Running 0 13m
# web-4는 순식간에 삭제되었다.. 역순으로 삭제된다.
web-3 0/1 Terminating 0 118s
web-3 0/1 Terminating 0 2m3s
web-3 0/1 Terminating 0 2m3s
web-2 1/1 Terminating 0 13m
web-2 0/1 Terminating 0 13m
web-2 0/1 Terminating 0 13m
web-2 0/1 Terminating 0 13m
web-1 1/1 Terminating 0 14m
web-1 0/1 Terminating 0 14m
web-1 0/1 Terminating 0 14m
web-1 0/1 Terminating 0 14m
$ kubectl get pvc
www-web-0 Bound pvc-fc161b94-5c7d-4367-b15d-c148eab6cdea 1Gi RWO stateful-set-storage-class 15m
www-web-1 Bound pvc-a49ba9cf-2b6d-48e7-b8c8-d78ec259d554 1Gi RWO stateful-set-storage-class 14m
www-web-2 Bound pvc-7e940025-e320-4da5-a3ef-500b4e5134a3 1Gi RWO stateful-set-storage-class 14m
www-web-3 Bound pvc-7565e8f0-7c9a-4f9f-b38f-e6f582e33187 1Gi RWO stateful-set-storage-class 3m5s
www-web-4 Bound pvc-0ddd0bae-7aa9-4543-9dd0-0e176970ba09 1Gi RWO stateful-set-storage-class 2m48s
리소스가 오름차순으로 순차적으로 늘어나는것과 반대로 내림차순으로 삭제되는 것을 확인할 수 있다. 한 가지 이상한 점이 눈에 띈다. pvc
는 삭제되지 않았다는 것이다. 스토리지 볼륨의 경우 삭제하고 싶다면 수동으로 삭제를 진행해야 한다. 이번엔 롤링 업데이트를 해보자. StatefulSet
의 업데이트 전략은 2가지가 있다.
- OnDelete : 모든 Pod를 수동으로 삭제 후 새로운 Pod가 업데이트 된다.
- RollingUpdate : Pod가 내림차순으로 삭제 후
$ kubectl edit statefulset web
다음 처럼 수정한다. (replicas = 1 -> 3, image = 0.8 -> 0.9)
그 후 리소스 변화를 관찰해보자.
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 20m
web-1 0/1 ContainerCreating 0 3s
web-1 1/1 Running 0 24s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 17s
web-0 1/1 Terminating 0 21m
web-0 0/1 Terminating 0 21m
web-0 0/1 Terminating 0 21m
web-0 0/1 Terminating 0 21m
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 10s
먼저 web-0
가 실행되는 상황에서 Pod
가 2개 더 필요하다. 그럼 web-1
, web-2
는 최신 버전인 "0.9"로 컨테이너가 실행된다. 그 후 web-0
가 삭제 후 다시 만들어진다. 다시 버전을 0.8로 복귀시켜보자.
$ kubectl edit statefulset web
statefulset.apps/web edited
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m28s
web-1 1/1 Running 0 3m11s
web-2 0/1 Terminating 0 2m47s
web-2 0/1 Terminating 0 2m50s
web-2 0/1 Terminating 0 2m50s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 10s
web-1 1/1 Terminating 0 3m24s
web-1 0/1 Terminating 0 3m25s
web-1 0/1 Terminating 0 3m34s
web-1 0/1 Terminating 0 3m34s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 10s
web-0 1/1 Terminating 0 3m1s
web-0 0/1 Terminating 0 3m2s
web-0 0/1 Terminating 0 3m11s
web-0 0/1 Terminating 0 3m11s
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 10s
역순으로 업데이트되는 것을 확인할 수 있다. 즉 레플리카 수에서 부족한 번호부터 채우되 기본적으로는 역순으로 업데이트하는 것을 확인할 수 있다.