This tutorial walks you through an example to manage a group of pods spread across multiple domains.
Let's say there are three nodes, each of which has a unique label to identify its domain. For example,
$ kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
node-a Ready <none> 107d v1.12.2 beta.kubernetes.io/arch=amd64,...,az=zone-a
node-b Ready <none> 107d v1.12.2 beta.kubernetes.io/arch=amd64,...,az=zone-b
node-c Ready <none> 107d v1.12.2 beta.kubernetes.io/arch=amd64,...,az=zone-c
Run below command to create a UnitedDeployment:
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/docs/tutorial/v1/uniteddeployment.yaml
Then check the UnitedDeployment:
$ kubectl get ud
NAME DESIRED CURRENT UPDATED READY AGE
demo-guestbook-kruise 10 10 10 10 3m
$ kubectl get sts
NAME DESIRED CURRENT AGE
demo-guestbook-kruise-subset-a-c9x8n 2 2 3m25s
demo-guestbook-kruise-subset-b-9c8dg 5 5 3m25s
demo-guestbook-kruise-subset-c-bxpd9 3 3 3m25s
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE NODE
demo-guestbook-kruise-subset-a-c9x8n-0 1/1 Running 0 3m40s node-a
demo-guestbook-kruise-subset-a-c9x8n-1 1/1 Running 0 3m33s node-a
demo-guestbook-kruise-subset-b-9c8dg-0 1/1 Running 0 3m40s node-b
demo-guestbook-kruise-subset-b-9c8dg-1 1/1 Running 0 3m34s node-b
demo-guestbook-kruise-subset-b-9c8dg-2 1/1 Running 0 3m27s node-b
demo-guestbook-kruise-subset-b-9c8dg-3 1/1 Running 0 3m21s node-b
demo-guestbook-kruise-subset-b-9c8dg-4 1/1 Running 0 3m15s node-b
demo-guestbook-kruise-subset-c-bxpd9-0 1/1 Running 0 3m40s node-c
demo-guestbook-kruise-subset-c-bxpd9-1 1/1 Running 0 3m35s node-c
demo-guestbook-kruise-subset-c-bxpd9-2 1/1 Running 0 3m29s node-c
There should be 3 StatefulSets and 10 pods created. The pods are spread in three nodes following
the node affinity specified in the subset
. According to the subset name prefix format <UnitedDeployment-name>-<subset-name>-
,
it is easy to identify which subset a StatefulSet represents.
For example, the StatefulSet demo-guestbook-kruise-subset-a-c9x8n
represents a subset named subset-a
under the UnitedDeployment demo-guestbook-kruise
.
The desired replicas of each StatefulSet should match the values specified in spec.topology
.
......
topology:
subsets:
- name: subset-a
replicas: 2
nodeSelectorTerm:
matchExpressions:
- key: az
operator: In
values:
- zone-a
- name: subset-b
replicas: 50%
nodeSelectorTerm:
matchExpressions:
- key: az
operator: In
values:
- zone-b
- name: subset-c
nodeSelectorTerm:
matchExpressions:
- key: az
operator: In
values:
- zone-c
As shown above, subset-a
should provision 2 pods, subset-b
should provision half of spec.replicas
pods, which is 5,
and the remaining pods, which is 3, should belong to subset-c
.
From the UnitedDeployment status, we can clearly see the provision results.
...
status:
conditions:
- lastTransitionTime: "2019-12-12T16:19:06Z"
status: "True"
type: SubsetProvisioned
- lastTransitionTime: "2019-12-12T16:19:06Z"
status: "True"
type: SubsetUpdated
currentRevision: demo-guestbook-kruise-55f9dbcb4b
observedGeneration: 1
readyReplicas: 10
replicas: 10
subsetReplicas:
subset-a: 2
subset-b: 5
subset-c: 3
updateStatus:
currentPartitions:
subset-a: 0
subset-b: 0
subset-c: 0
updatedRevision: demo-guestbook-kruise-55f9dbcb4b
updatedReadyReplicas: 10
updatedReplicas: 10
...
It is possible to re-distribute the pods by editing each subset's replicas in spec.topology
.
If we change the topology
to the following, the controller will trigger a pod re-distribution.
...
topology:
subsets:
- name: subset-a
nodeSelectorTerm:
matchExpressions:
- key: az
operator: In
values:
- zone-a
replicas: 1
- name: subset-b
nodeSelectorTerm:
matchExpressions:
- key: az
operator: In
values:
- zone-b
replicas: 40%
- name: subset-c
nodeSelectorTerm:
matchExpressions:
- key: az
operator: In
values:
- zone-c
After a minute, we can find that controller has adjusted the pod distribution to match the above topology
.
$ kubectl get sts
NAME DESIRED CURRENT AGE
demo-guestbook-kruise-subset-a-c9x8n 1 1 4m40s
demo-guestbook-kruise-subset-b-9c8dg 4 5 4m40s
demo-guestbook-kruise-subset-c-bxpd9 5 5 4m40s
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
demo-guestbook-kruise-subset-a-c9x8n-0 1/1 Running 0 4m58s
demo-guestbook-kruise-subset-b-9c8dg-0 1/1 Running 0 4m58s
demo-guestbook-kruise-subset-b-9c8dg-1 1/1 Running 0 4m52s
demo-guestbook-kruise-subset-b-9c8dg-2 1/1 Running 0 4m45s
demo-guestbook-kruise-subset-b-9c8dg-3 1/1 Running 0 4m39s
demo-guestbook-kruise-subset-c-bxpd9-0 1/1 Running 0 4m58s
demo-guestbook-kruise-subset-c-bxpd9-1 1/1 Running 0 4m53s
demo-guestbook-kruise-subset-c-bxpd9-2 1/1 Running 0 4m47s
demo-guestbook-kruise-subset-c-bxpd9-3 1/1 Running 0 31s
demo-guestbook-kruise-subset-c-bxpd9-4 1/1 Running 0 23s
By default, if we don't set subset replicas at all, pods will be distributed evenly.
$ kubectl -n kruise get pod
NAME READY STATUS RESTARTS AGE
demo-guestbook-kruise-subset-a-c9x8n-0 1/1 Running 0 6m1s
demo-guestbook-kruise-subset-a-c9x8n-1 1/1 Running 0 35s
demo-guestbook-kruise-subset-a-c9x8n-2 1/1 Running 0 27s
demo-guestbook-kruise-subset-b-9c8dg-0 1/1 Running 0 6m1s
demo-guestbook-kruise-subset-b-9c8dg-1 1/1 Running 0 5m55s
demo-guestbook-kruise-subset-b-9c8dg-2 1/1 Running 0 5m48s
demo-guestbook-kruise-subset-c-bxpd9-0 1/1 Running 0 6m1s
demo-guestbook-kruise-subset-c-bxpd9-1 1/1 Running 0 5m56s
demo-guestbook-kruise-subset-c-bxpd9-2 1/1 Running 0 5m50s
demo-guestbook-kruise-subset-c-bxpd9-3 1/1 Running 0 94s
UnitedDeployment provides Manual
update strategy to control the subsets update progress
by setting the partition of each subset in updateStrategy.manualUpdate.partitions
.
Let's say we want to update pod's environment variable in a UnitedDeployment and we provide the following partitions.
...
+ updateStrategy:
+ type: Manual
+ manualUpdate:
+ partitions:
+ subset-a: 1
+ subset-b: 2
template:
statefulSetTemplate:
metadata:
labels:
app.kubernetes.io/instance: demo
app.kubernetes.io/name: guestbook-kruise
spec:
template:
metadata:
labels:
app.kubernetes.io/instance: demo
app.kubernetes.io/name: guestbook-kruise
spec:
containers:
- image: openkruise/guestbook:v1
imagePullPolicy: Always
name: guestbook-kruise
+ env:
+ - name: version
+ value: v2
ports:
- containerPort: 3000
name: http-server
...
After a minute, part of pods should be updated.
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
demo-guestbook-kruise-subset-a-c9x8n-0 1/1 Running 0 5m50s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-55f9dbcb4b,...
demo-guestbook-kruise-subset-a-c9x8n-1 1/1 Running 0 2m50s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
demo-guestbook-kruise-subset-a-c9x8n-2 1/1 Running 0 3m ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
demo-guestbook-kruise-subset-b-9c8dg-0 1/1 Running 0 5m42s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-55f9dbcb4b,...
demo-guestbook-kruise-subset-b-9c8dg-1 1/1 Running 0 5m55s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-55f9dbcb4b,...
demo-guestbook-kruise-subset-b-9c8dg-2 1/1 Running 0 3m2s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
demo-guestbook-kruise-subset-c-bxpd9-0 1/1 Running 0 2m5s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
demo-guestbook-kruise-subset-c-bxpd9-1 1/1 Running 0 2m25s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
demo-guestbook-kruise-subset-c-bxpd9-2 1/1 Running 0 2m45s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
demo-guestbook-kruise-subset-c-bxpd9-3 1/1 Running 0 3m3s ...,apps.kruise.io/controller-revision-hash=demo-guestbook-kruise-64494c46ff,...
With --show-labels
appended, the revision of every pod is shown with the
label key apps.kruise.io/controller-revision-hash
.
From the UnitedDeployment status below, we can see that the UnitedDeployment is
undergoing an update and the target revision is demo-guestbook-kruise-64494c46ff
.
Furthermore, there are 2, 1, 4 pods in each subset that have been updated.
An empty partition of a subset means partition
is 0, all pods of subset-c
will be updated.
...
status:
...
+ currentRevision: demo-guestbook-kruise-55f9dbcb4b
...
subsetReplicas:
subset-a: 3
subset-b: 3
subset-c: 4
updateStatus:
currentPartitions:
subset-a: 1
subset-b: 2
subset-c: 0
+ updatedRevision: demo-guestbook-kruise-64494c46ff
updatedReadyReplicas: 7
updatedReplicas: 7
UnitedDeployment now supports three types of subset which are StatefulSet
、AdvancedStatefulSet
、CloneSet
.
It is allowed to change subset type from one to another at runtime. Take the above UnitedDeployment as an example.
Create a new UnitedDeployment with the same definition.
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/docs/tutorial/v1/uniteddeployment.yaml
We get the three subsets of StatefulSet as expected.
$ kubectl get sts
NAME DESIRED CURRENT AGE
demo-guestbook-kruise-subset-a-jtz9t 2 2 82s
demo-guestbook-kruise-subset-b-bhjm6 5 5 82s
demo-guestbook-kruise-subset-c-bj7f2 3 3 82s
Then we update the subset type from StatefulSet to AdvancedStatefulSet
by using the advancedStatefulSetTemplate
to describe the subset template.
By the way, let's enable the in-place update of the subset pod.
(and use cloneSetTemplate
if you need CloneSet)
...
spec:
replicas: 10
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/instance: demo
app.kubernetes.io/name: guestbook-kruise
template:
- statefulSetTemplate:
+ advancedStatefulSetTemplate:
metadata:
labels:
app.kubernetes.io/instance: demo
app.kubernetes.io/name: guestbook-kruise
spec:
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ podUpdatePolicy: InPlaceIfPossible
+ selector:
+ matchLabels:
+ app.kubernetes.io/instance: demo
+ app.kubernetes.io/name: guestbook-kruise
template:
metadata:
labels:
app.kubernetes.io/instance: demo
app.kubernetes.io/name: guestbook-kruise
spec:
containers:
- image: openkruise/guestbook:v1
imagePullPolicy: Always
name: guestbook-kruise
ports:
- containerPort: 3000
name: http-server
...
Then three StatefulSets are all deleted. Instead, three AdvancedStatefulSets are created with in-place update feature enabled.
$ kubectl get sts
No resources found.
$ kubectl get statefulset.apps.kruise.io
NAME DESIRED CURRENT UPDATED READY AGE
demo-guestbook-kruise-subset-a-2r8dm 2 2 2 2 1m
demo-guestbook-kruise-subset-b-tj2n6 5 5 5 5 1m
demo-guestbook-kruise-subset-c-qflc9 3 3 3 3 1m
$ kubectl get statefulset.apps.kruise.io -o custom-columns=NAME:.metadata.name,POD-UPDATE-POLICY:.spec.updateStrategy.rollingUpdate.podUpdatePolicy
NAME POD-UPDATE-POLICY
demo-guestbook-kruise-subset-a-2r8dm InPlaceIfPossible
demo-guestbook-kruise-subset-b-tj2n6 InPlaceIfPossible
demo-guestbook-kruise-subset-c-qflc9 InPlaceIfPossible
Now it is possible to in-place update each pod of these subsets by updating the UnitedDeployment. More details of AdvancedStatefulSet are provided in this tutorial.
Please note that the process of switching subset type is not graceful, so the service may be unavailable during it.
UnitedDeployment might support multiple kinds of workload as its subset. Currently, only StatefulSet is supported. To better support StatefulSet, UnitedDeployment controller makes a few enhancements:
To make sure a RollingUpdate
progress will not be blocked by this issue,
UnitedDeployment controller helps to delete the stuck pods.
OnDelete
update strategy is allowed in template.statefulSetTemplate
.
However, the pods need to be deleted manually to keep consistent with the behavior of StatefulSet controller.