From 8555b53308c9e88b9b1dab7aa7619459809ff241 Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Tue, 29 Nov 2022 17:58:51 -0800 Subject: [PATCH 01/10] Add blog post for StatefulSet Migration using StatefulSetStartOrdinal --- .../2022-12-16-statefulset-migration.md | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 content/en/blog/_posts/2022-12-16-statefulset-migration.md diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md new file mode 100644 index 0000000000000..c191306b3e7d1 --- /dev/null +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -0,0 +1,180 @@ +--- +layout: blog +title: "Kubernetes 1.26: StatefulSet Migration" +date: 2022-12-16 +slug: statefulset-migration +--- + +**Author**: Peter Schuurman (Google) + +Kubernetes v1.26 introduces a new, alpha-level feature for +[StatefulSets](/docs/concepts/workloads/controllers/statefulset/) that controls +the ordinal numbering of Pod replicas. Ordinals can start from arbitrary +non-negative numbers. This blog post will discuss how this feature can be +used. + +## Background + +StatefulSets ordinals provide sequential identities for pod replicas. When using +[OrderedReady Pod Management](/docs/tutorials/stateful-application/basic-stateful-set/#orderedready-pod-management), +Pods are created from ordinal index `0` up to `N-1`. + +With Kubernetes today, orchestrating a StatefulSet migration across clusters is +challenging. Backup and restore solutions exist, but these require the +application to be scaled down to zero replicas prior to migration. In today's +fully connected world, planned downtime and unavailability may not allow you to +meet your business goals. You could use +[Cascading Delete](/docs/tutorials/stateful-application/basic-stateful-set/#cascading-delete) +or +[On Delete](/docs/tutorials/stateful-application/basic-stateful-set/#on-delete) +to migrate individual pods, however this is error prone and tedious to manage. +You lose the self-healing benefit of the StatefulSet controller when your Pods +fail or are evicted. + +This feature enables a StatefulSet to be responsible for a range of ordinals +within a logical range of `[0, N)`. With it, you can scale down a range +(`[0, k)`) in a source cluster, and scale up the complementary range (`[k, N)`) +in a destination cluster, while maintaining application availability. This +enables you to retain *at most one* semantics and +[Rolling Update](/docs/tutorials/stateful-application/basic-stateful-set/#rolling-update) +behavior when orchestrating a migration across clusters. + +### Why would I want to use this feature? + +Say you're running your StatefulSet in one cluster, and need to migrate it out +to a different cluster. There are many reasons why you would need to do this: + * **Scalability**: Your StatefulSet has scaled too large for your cluster, and + has started to disrupt the quality of service for other workloads in your + cluster. + * **Isolation**: You're running a StatefulSet in a cluster that is accessed + by multiple users, and namespace isolation isn't sufficient. + * **Cluster Configuration**: You want to move your StatefulSet to a different + cluster to use some environment that is not available on your current + cluster. + * **Control Plane Upgrades**: You want to move your StatefulSet to a cluster + running an upgraded control plane, and can't handle the risk or downtime of + in-place control plane upgrades. + +### How do I use it? + +Enable the `StatefulSetStartOrdinal` feature gate on a cluster, and create a +StatefulSet with a customized `.spec.ordinals.start`. + +### Try it for yourself + +In this demo, you'll use the `StatefulSetStartOrdinal` feature to migrate a +StatefulSet from one cluster to another. For this demo, the +[redis-cluster](https://github.com/bitnami/charts/tree/main/bitnami/redis-cluster) +Bitnami Helm chart is used to install Redis. + +Tools Required: + * [yq](https://github.com/mikefarah/yq) + * [helm](https://helm.sh/docs/helm/helm_install/) + +Pre-requisites: Two clusters named `source` and `destination`. + * `StatefulSetStartOrdinal` feature gate is enabled on both clusters + * [MultiClusterServices](https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api) +support is enabled + * The same default `StorageClass` is installed on both clusters. This + `StorageClass` should provision underlying storage that is accessible from + both clusters + +1. Create a demo namespace on both clusters. + +``` +kubectl create ns kep-3335 +``` + +2. Deploy a `ServiceExport` on both clusters. + +``` +kind: ServiceExport +apiVersion: multicluster.x-k8s.io/v1alpha1 +metadata: + namespace: kep-3335 + name: redis-redis-cluster-headless +``` + +3. Deploy a Redis cluster on `source`. + +``` +helm repo add bitnami https://charts.bitnami.com/bitnami +helm install redis --namespace kep-3335 \ + bitnami/redis-cluster \ + --set persistence.size=1Gi +``` + +4. On `source`, check the replication status. + +``` +kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ + "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" +``` + +``` +2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 myself,master - 0 1669764411000 3 connected 10923-16383 +7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669764410000 3 connected +961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669764411000 1 connected +7136e37d8864db983f334b85d2b094be47c830e5 10.104.0.15:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669764412595 2 connected +a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669764411592 1 connected 0-5460 +2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669764410000 2 connected 5461-10922 +``` + +5. On `destination`, deploy Redis with zero replicas. + +``` +helm install redis --namespace kep-3335 \ + bitnami/redis-cluster \ + --set persistence.size=1Gi \ + --set cluster.nodes=0 \ + --set redis.extraEnvVars\[0\].name=REDIS_NODES,redis.extraEnvVars\[0\].value="redis-redis-cluster-headless.kep-3335.svc.cluster.local" \ + --set existingSecret=redis-redis-cluster +``` + +6. Scale down replica `redis-redis-cluster-5` in the source cluster. + +``` +kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}' +``` + +7. Migrate dependencies from `source` to `destination`. + +Source Cluster + +``` +kubectl get pvc redis-data-redis-redis-cluster-5 -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .status)' > /tmp/pvc-redis-data-redis-redis-cluster-5.yaml +kubectl get pv $(yq '.spec.volumeName' /tmp/pvc-redis-data-redis-redis-cluster-5.yaml) -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .spec.claimRef, .status)' > /tmp/pv-redis-data-redis-redis-cluster-5.yaml +kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion)' > /tmp/secret-redis-redis-cluster.yaml +``` + +Destination Cluster + +``` +kubectl create -f /tmp/pv-redis-data-redis-redis-cluster-5.yaml +kubectl create -f /tmp/pvc-redis-data-redis-redis-cluster-5.yaml +kubectl create -f /tmp/secret-redis-redis-cluster.yaml +``` + +8. Scale up replica `redis-redis-cluster-5` in the destination cluster. + +``` +kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}' +``` + +9. On the source cluster, check the replication status. + +``` +kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ + "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" +``` + +You should see that the new replica's address has joined the Redis cluster. + +``` +2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 myself,master - 0 1669766684000 2 connected 5461-10922 +7136e37d8864db983f334b85d2b094be47c830e5 10.108.0.22:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669766685609 2 connected +2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 master - 0 1669766684000 3 connected 10923-16383 +961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669766683600 1 connected +a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669766685000 1 connected 0-5460 +7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669766686613 3 connected +``` From bd45ab5474c7a9170742b0cc2fc9573d0c3b34c8 Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Tue, 29 Nov 2022 18:33:24 -0800 Subject: [PATCH 02/10] Update blog post headings and add What's Next section --- .../2022-12-16-statefulset-migration.md | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index c191306b3e7d1..be6f591adc1d8 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -39,7 +39,7 @@ enables you to retain *at most one* semantics and [Rolling Update](/docs/tutorials/stateful-application/basic-stateful-set/#rolling-update) behavior when orchestrating a migration across clusters. -### Why would I want to use this feature? +## Why would I want to use this feature? Say you're running your StatefulSet in one cluster, and need to migrate it out to a different cluster. There are many reasons why you would need to do this: @@ -55,12 +55,12 @@ to a different cluster. There are many reasons why you would need to do this: running an upgraded control plane, and can't handle the risk or downtime of in-place control plane upgrades. -### How do I use it? +## How do I use it? Enable the `StatefulSetStartOrdinal` feature gate on a cluster, and create a StatefulSet with a customized `.spec.ordinals.start`. -### Try it for yourself +## Try it for yourself In this demo, you'll use the `StatefulSetStartOrdinal` feature to migrate a StatefulSet from one cluster to another. For this demo, the @@ -139,6 +139,17 @@ kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}' 7. Migrate dependencies from `source` to `destination`. +The following commands copy resources from `source` to `destionation`. Details +that are not relevant in `destination` cluster are removed (eg: `uid`, +`resourceVersion`, `status`). + +Note: For the PV/PVC, this procedure only works if the underlying storage system + that your `StorageClass` uses can support copying existing PVs into a + new cluster. Storage that is associated with a specific node or topology + may not be supported. Additionally, some storage systems may store + addtional metadata about volumes outside of a PV object, and may require + a more specialized sequence to import a volume. + Source Cluster ``` @@ -178,3 +189,18 @@ You should see that the new replica's address has joined the Redis cluster. a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669766685000 1 connected 0-5460 7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669766686613 3 connected ``` + +## What's Next? + +This feature provides a building block for a StatefulSet to be split up across +clusters, but does not prescribe the mechanism as to how the StatefulSet should +be migrated. Migration requires coordination of StatefulSet replicas, along with +orchestration of the storage and network layer. This is dependent on the storage +and connectivity requirements of the application installed by the StatefulSet. +Additionally, many StatefulSets are controlled by Operators, which adds another +layer of complexity to migration. + +If you're interested in building blocks to make these processes easier, get +involved with +[SIG Multicluster](https://github.com/kubernetes/community/blob/master/sig-multicluster) +to contribute! From 0043f1967f69c0dc5afec5fa9981907a8394a922 Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Thu, 1 Dec 2022 08:52:40 -0800 Subject: [PATCH 03/10] Add a note about copying PV/PVC from source to destination cluster --- .../2022-12-16-statefulset-migration.md | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index be6f591adc1d8..a279b5f8cc833 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -143,15 +143,14 @@ The following commands copy resources from `source` to `destionation`. Details that are not relevant in `destination` cluster are removed (eg: `uid`, `resourceVersion`, `status`). -Note: For the PV/PVC, this procedure only works if the underlying storage system - that your `StorageClass` uses can support copying existing PVs into a - new cluster. Storage that is associated with a specific node or topology - may not be supported. Additionally, some storage systems may store - addtional metadata about volumes outside of a PV object, and may require - a more specialized sequence to import a volume. - Source Cluster +Note: If using a `StorageClass` with `reclaimPolicy: Delete` configured, you + should patch the PVs in `source` with `reclaimPolicy: Retain` prior to + deletion to retain the underlying storage used in `destination`. See + [Change the Reclaim Policy of a PersistentVolume](/docs/tasks/administer-cluster/change-pv-reclaim-policy/) + for more details. + ``` kubectl get pvc redis-data-redis-redis-cluster-5 -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .status)' > /tmp/pvc-redis-data-redis-redis-cluster-5.yaml kubectl get pv $(yq '.spec.volumeName' /tmp/pvc-redis-data-redis-redis-cluster-5.yaml) -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .spec.claimRef, .status)' > /tmp/pv-redis-data-redis-redis-cluster-5.yaml @@ -160,6 +159,13 @@ kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadat Destination Cluster +Note: For the PV/PVC, this procedure only works if the underlying storage system + that your PVs use can support being copied into `destination`. Storage + that is associated with a specific node or topology may not be supported. + Additionally, some storage systems may store addtional metadata about + volumes outside of a PV object, and may require a more specialized + sequence to import a volume. + ``` kubectl create -f /tmp/pv-redis-data-redis-redis-cluster-5.yaml kubectl create -f /tmp/pvc-redis-data-redis-redis-cluster-5.yaml From bd610ae7d3a9786a1b7d9b3af4070eb1ff4cae0b Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Sat, 17 Dec 2022 04:21:04 -0800 Subject: [PATCH 04/10] Review updates for StatefulSet StartOrdinal blog post --- .../2022-12-16-statefulset-migration.md | 186 +++++++++--------- 1 file changed, 94 insertions(+), 92 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index a279b5f8cc833..d7b80aef07111 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -1,7 +1,7 @@ --- layout: blog -title: "Kubernetes 1.26: StatefulSet Migration" -date: 2022-12-16 +title: "Kubernetes 1.26: StatefulSet Start Ordinal Simplifies Migration" +date: 2023-01-03 slug: statefulset-migration --- @@ -16,13 +16,13 @@ used. ## Background StatefulSets ordinals provide sequential identities for pod replicas. When using -[OrderedReady Pod Management](/docs/tutorials/stateful-application/basic-stateful-set/#orderedready-pod-management), +[`OrderedReady` Pod management](/docs/tutorials/stateful-application/basic-stateful-set/#orderedready-pod-management), Pods are created from ordinal index `0` up to `N-1`. With Kubernetes today, orchestrating a StatefulSet migration across clusters is challenging. Backup and restore solutions exist, but these require the application to be scaled down to zero replicas prior to migration. In today's -fully connected world, planned downtime and unavailability may not allow you to +fully connected world, even planned application downtime may not allow you to meet your business goals. You could use [Cascading Delete](/docs/tutorials/stateful-application/basic-stateful-set/#cascading-delete) or @@ -31,8 +31,9 @@ to migrate individual pods, however this is error prone and tedious to manage. You lose the self-healing benefit of the StatefulSet controller when your Pods fail or are evicted. -This feature enables a StatefulSet to be responsible for a range of ordinals -within a logical range of `[0, N)`. With it, you can scale down a range +Kubernetes v1.26 enables a StatefulSet to be responsible for a range of ordinals +within a half-open interval `[0, N)` (the ordinals 0, 1, ... N-1). +With it, you can scale down a range (`[0, k)`) in a source cluster, and scale up the complementary range (`[k, N)`) in a destination cluster, while maintaining application availability. This enables you to retain *at most one* semantics and @@ -63,7 +64,7 @@ StatefulSet with a customized `.spec.ordinals.start`. ## Try it for yourself In this demo, you'll use the `StatefulSetStartOrdinal` feature to migrate a -StatefulSet from one cluster to another. For this demo, the +StatefulSet from one Kubernetes cluster to another. For this demo, the [redis-cluster](https://github.com/bitnami/charts/tree/main/bitnami/redis-cluster) Bitnami Helm chart is used to install Redis. @@ -77,124 +78,125 @@ Pre-requisites: Two clusters named `source` and `destination`. support is enabled * The same default `StorageClass` is installed on both clusters. This `StorageClass` should provision underlying storage that is accessible from - both clusters + both clusters. 1. Create a demo namespace on both clusters. -``` -kubectl create ns kep-3335 -``` + ``` + kubectl create ns kep-3335 + ``` 2. Deploy a `ServiceExport` on both clusters. -``` -kind: ServiceExport -apiVersion: multicluster.x-k8s.io/v1alpha1 -metadata: - namespace: kep-3335 - name: redis-redis-cluster-headless -``` + ``` + kind: ServiceExport + apiVersion: multicluster.x-k8s.io/v1alpha1 + metadata: + namespace: kep-3335 + name: redis-redis-cluster-headless + ``` 3. Deploy a Redis cluster on `source`. -``` -helm repo add bitnami https://charts.bitnami.com/bitnami -helm install redis --namespace kep-3335 \ - bitnami/redis-cluster \ - --set persistence.size=1Gi -``` + ``` + helm repo add bitnami https://charts.bitnami.com/bitnami + helm install redis --namespace kep-3335 \ + bitnami/redis-cluster \ + --set persistence.size=1Gi + ``` 4. On `source`, check the replication status. -``` -kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ - "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" -``` + ``` + kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ + "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" + ``` -``` -2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 myself,master - 0 1669764411000 3 connected 10923-16383 -7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669764410000 3 connected -961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669764411000 1 connected -7136e37d8864db983f334b85d2b094be47c830e5 10.104.0.15:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669764412595 2 connected -a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669764411592 1 connected 0-5460 -2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669764410000 2 connected 5461-10922 -``` + ``` + 2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 myself,master - 0 1669764411000 3 connected 10923-16383 + 7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669764410000 3 connected + 961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669764411000 1 connected + 7136e37d8864db983f334b85d2b094be47c830e5 10.104.0.15:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669764412595 2 connected + a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669764411592 1 connected 0-5460 + 2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669764410000 2 connected 5461-10922 + ``` 5. On `destination`, deploy Redis with zero replicas. -``` -helm install redis --namespace kep-3335 \ - bitnami/redis-cluster \ - --set persistence.size=1Gi \ - --set cluster.nodes=0 \ - --set redis.extraEnvVars\[0\].name=REDIS_NODES,redis.extraEnvVars\[0\].value="redis-redis-cluster-headless.kep-3335.svc.cluster.local" \ - --set existingSecret=redis-redis-cluster -``` + ``` + helm install redis --namespace kep-3335 \ + bitnami/redis-cluster \ + --set persistence.size=1Gi \ + --set cluster.nodes=0 \ + --set redis.extraEnvVars\[0\].name=REDIS_NODES,redis.extraEnvVars\[0\].value="redis-redis-cluster-headless.kep-3335.svc.cluster.local" \ + --set existingSecret=redis-redis-cluster + ``` 6. Scale down replica `redis-redis-cluster-5` in the source cluster. -``` -kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}' -``` + ``` + kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}' + ``` 7. Migrate dependencies from `source` to `destination`. -The following commands copy resources from `source` to `destionation`. Details -that are not relevant in `destination` cluster are removed (eg: `uid`, -`resourceVersion`, `status`). + The following commands copy resources from `source` to `destionation`. Details + that are not relevant in `destination` cluster are removed (eg: `uid`, + `resourceVersion`, `status`). -Source Cluster + #### Source Cluster -Note: If using a `StorageClass` with `reclaimPolicy: Delete` configured, you - should patch the PVs in `source` with `reclaimPolicy: Retain` prior to - deletion to retain the underlying storage used in `destination`. See - [Change the Reclaim Policy of a PersistentVolume](/docs/tasks/administer-cluster/change-pv-reclaim-policy/) - for more details. + Note: If using a `StorageClass` with `reclaimPolicy: Delete` configured, you + should patch the PVs in `source` with `reclaimPolicy: Retain` prior to + deletion to retain the underlying storage used in `destination`. See + [Change the Reclaim Policy of a PersistentVolume](/docs/tasks/administer-cluster/change-pv-reclaim-policy/) + for more details. -``` -kubectl get pvc redis-data-redis-redis-cluster-5 -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .status)' > /tmp/pvc-redis-data-redis-redis-cluster-5.yaml -kubectl get pv $(yq '.spec.volumeName' /tmp/pvc-redis-data-redis-redis-cluster-5.yaml) -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .spec.claimRef, .status)' > /tmp/pv-redis-data-redis-redis-cluster-5.yaml -kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion)' > /tmp/secret-redis-redis-cluster.yaml -``` + ``` + kubectl get pvc redis-data-redis-redis-cluster-5 -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .status)' > /tmp/pvc-redis-data-redis-redis-cluster-5.yaml + kubectl get pv $(yq '.spec.volumeName' /tmp/pvc-redis-data-redis-redis-cluster-5.yaml) -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .spec.claimRef, .status)' > /tmp/pv-redis-data-redis-redis-cluster-5.yaml + kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion)' > /tmp/secret-redis-redis-cluster.yaml + ``` -Destination Cluster + #### Destination Cluster -Note: For the PV/PVC, this procedure only works if the underlying storage system - that your PVs use can support being copied into `destination`. Storage - that is associated with a specific node or topology may not be supported. - Additionally, some storage systems may store addtional metadata about - volumes outside of a PV object, and may require a more specialized - sequence to import a volume. + Note: For the PV/PVC, this procedure only works if the underlying storage system + that your PVs use can support being copied into `destination`. Storage + that is associated with a specific node or topology may not be supported. + Additionally, some storage systems may store addtional metadata about + volumes outside of a PV object, and may require a more specialized + sequence to import a volume. -``` -kubectl create -f /tmp/pv-redis-data-redis-redis-cluster-5.yaml -kubectl create -f /tmp/pvc-redis-data-redis-redis-cluster-5.yaml -kubectl create -f /tmp/secret-redis-redis-cluster.yaml -``` + ``` + kubectl create -f /tmp/pv-redis-data-redis-redis-cluster-5.yaml + kubectl create -f /tmp/pvc-redis-data-redis-redis-cluster-5.yaml + kubectl create -f /tmp/secret-redis-redis-cluster.yaml + ``` -8. Scale up replica `redis-redis-cluster-5` in the destination cluster. +8. Scale up replica `redis-redis-cluster-5` in the destination cluster, with a + start ordinal of 5: -``` -kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}' -``` + ``` + kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}' + ``` 9. On the source cluster, check the replication status. -``` -kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ - "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" -``` - -You should see that the new replica's address has joined the Redis cluster. - -``` -2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 myself,master - 0 1669766684000 2 connected 5461-10922 -7136e37d8864db983f334b85d2b094be47c830e5 10.108.0.22:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669766685609 2 connected -2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 master - 0 1669766684000 3 connected 10923-16383 -961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669766683600 1 connected -a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669766685000 1 connected 0-5460 -7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669766686613 3 connected -``` + ``` + kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ + "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" + ``` + + You should see that the new replica's address has joined the Redis cluster. + + ``` + 2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 myself,master - 0 1669766684000 2 connected 5461-10922 + 7136e37d8864db983f334b85d2b094be47c830e5 10.108.0.22:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669766685609 2 connected + 2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 master - 0 1669766684000 3 connected 10923-16383 + 961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669766683600 1 connected + a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669766685000 1 connected 0-5460 + 7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669766686613 3 connected + ``` ## What's Next? From 76dae7885750c692255e439afa7d2f631856cd1f Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Sat, 17 Dec 2022 04:29:51 -0800 Subject: [PATCH 05/10] Remove MCS references from StatefulSet start ordinal blog post --- .../2022-12-16-statefulset-migration.md | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index d7b80aef07111..c9e5226cc80da 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -72,13 +72,14 @@ Tools Required: * [yq](https://github.com/mikefarah/yq) * [helm](https://helm.sh/docs/helm/helm_install/) -Pre-requisites: Two clusters named `source` and `destination`. +Pre-requisites: Two Kubernetes clusters named `source` and `destination`. * `StatefulSetStartOrdinal` feature gate is enabled on both clusters - * [MultiClusterServices](https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api) -support is enabled * The same default `StorageClass` is installed on both clusters. This `StorageClass` should provision underlying storage that is accessible from both clusters. + * A flat network topology that allows for pods to be accessible across both + Kubernetes clusters. If creating clusters on a cloud provider, this + configuration may be called private cloud or private network. 1. Create a demo namespace on both clusters. @@ -86,17 +87,7 @@ support is enabled kubectl create ns kep-3335 ``` -2. Deploy a `ServiceExport` on both clusters. - - ``` - kind: ServiceExport - apiVersion: multicluster.x-k8s.io/v1alpha1 - metadata: - namespace: kep-3335 - name: redis-redis-cluster-headless - ``` - -3. Deploy a Redis cluster on `source`. +2. Deploy a Redis cluster on `source`. ``` helm repo add bitnami https://charts.bitnami.com/bitnami @@ -105,7 +96,7 @@ support is enabled --set persistence.size=1Gi ``` -4. On `source`, check the replication status. +3. On `source`, check the replication status. ``` kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ @@ -121,7 +112,7 @@ support is enabled 2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669764410000 2 connected 5461-10922 ``` -5. On `destination`, deploy Redis with zero replicas. +4. On `destination`, deploy Redis with zero replicas. ``` helm install redis --namespace kep-3335 \ @@ -132,13 +123,13 @@ support is enabled --set existingSecret=redis-redis-cluster ``` -6. Scale down replica `redis-redis-cluster-5` in the source cluster. +5. Scale down replica `redis-redis-cluster-5` in the source cluster. ``` kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}' ``` -7. Migrate dependencies from `source` to `destination`. +6. Migrate dependencies from `source` to `destination`. The following commands copy resources from `source` to `destionation`. Details that are not relevant in `destination` cluster are removed (eg: `uid`, @@ -173,14 +164,14 @@ support is enabled kubectl create -f /tmp/secret-redis-redis-cluster.yaml ``` -8. Scale up replica `redis-redis-cluster-5` in the destination cluster, with a +7. Scale up replica `redis-redis-cluster-5` in the destination cluster, with a start ordinal of 5: ``` kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}' ``` -9. On the source cluster, check the replication status. +8. On the source cluster, check the replication status. ``` kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ From 8ca5a5d775de6f308ee3ed61a8e759d3915372c1 Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Sat, 17 Dec 2022 04:39:16 -0800 Subject: [PATCH 06/10] Minor edits to StatefulSet start ordinal blog post --- content/en/blog/_posts/2022-12-16-statefulset-migration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index c9e5226cc80da..e6d7f5617194c 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -16,7 +16,7 @@ used. ## Background StatefulSets ordinals provide sequential identities for pod replicas. When using -[`OrderedReady` Pod management](/docs/tutorials/stateful-application/basic-stateful-set/#orderedready-pod-management), +[`OrderedReady` Pod management](/docs/tutorials/stateful-application/basic-stateful-set/#orderedready-pod-management) Pods are created from ordinal index `0` up to `N-1`. With Kubernetes today, orchestrating a StatefulSet migration across clusters is @@ -135,7 +135,7 @@ Pre-requisites: Two Kubernetes clusters named `source` and `destination`. that are not relevant in `destination` cluster are removed (eg: `uid`, `resourceVersion`, `status`). - #### Source Cluster + #### Source cluster Note: If using a `StorageClass` with `reclaimPolicy: Delete` configured, you should patch the PVs in `source` with `reclaimPolicy: Retain` prior to @@ -149,7 +149,7 @@ Pre-requisites: Two Kubernetes clusters named `source` and `destination`. kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion)' > /tmp/secret-redis-redis-cluster.yaml ``` - #### Destination Cluster + #### Destination cluster Note: For the PV/PVC, this procedure only works if the underlying storage system that your PVs use can support being copied into `destination`. Storage From 13f1c8ab0526b65bc5af4691863c5c94d7249424 Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Wed, 11 Jan 2023 21:39:11 -0800 Subject: [PATCH 07/10] Update formatting and wording for StatefulSet Migration Redis demo --- .../2022-12-16-statefulset-migration.md | 87 +++++++++++-------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index e6d7f5617194c..9b1f1297f2788 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -2,7 +2,7 @@ layout: blog title: "Kubernetes 1.26: StatefulSet Start Ordinal Simplifies Migration" date: 2023-01-03 -slug: statefulset-migration +slug: statefulset-start-ordinal --- **Author**: Peter Schuurman (Google) @@ -32,11 +32,12 @@ You lose the self-healing benefit of the StatefulSet controller when your Pods fail or are evicted. Kubernetes v1.26 enables a StatefulSet to be responsible for a range of ordinals -within a half-open interval `[0, N)` (the ordinals 0, 1, ... N-1). +within a range {0..N-1} (the ordinals 0, 1, ... up to N-1). With it, you can scale down a range -(`[0, k)`) in a source cluster, and scale up the complementary range (`[k, N)`) +{0..k-1} in a source cluster, and scale up the complementary range {k..N-1} in a destination cluster, while maintaining application availability. This -enables you to retain *at most one* semantics and +enables you to retain *at most one* semantics (meaning there is at most one Pod +with a given identity running in a StatefulSet) and [Rolling Update](/docs/tutorials/stateful-application/basic-stateful-set/#rolling-update) behavior when orchestrating a migration across clusters. @@ -61,42 +62,50 @@ to a different cluster. There are many reasons why you would need to do this: Enable the `StatefulSetStartOrdinal` feature gate on a cluster, and create a StatefulSet with a customized `.spec.ordinals.start`. -## Try it for yourself +## Try it out -In this demo, you'll use the `StatefulSetStartOrdinal` feature to migrate a -StatefulSet from one Kubernetes cluster to another. For this demo, the +In this demo, I'll use the new mechanism to migrate a +StatefulSet from one Kubernetes cluster to another. The [redis-cluster](https://github.com/bitnami/charts/tree/main/bitnami/redis-cluster) -Bitnami Helm chart is used to install Redis. +Bitnami Helm chart will be used to install Redis. Tools Required: * [yq](https://github.com/mikefarah/yq) * [helm](https://helm.sh/docs/helm/helm_install/) -Pre-requisites: Two Kubernetes clusters named `source` and `destination`. - * `StatefulSetStartOrdinal` feature gate is enabled on both clusters - * The same default `StorageClass` is installed on both clusters. This - `StorageClass` should provision underlying storage that is accessible from - both clusters. - * A flat network topology that allows for pods to be accessible across both - Kubernetes clusters. If creating clusters on a cloud provider, this - configuration may be called private cloud or private network. +### Pre-requisites {#demo-pre-requisites} -1. Create a demo namespace on both clusters. +To do this, I need two Kubernetes clusters that can both access common +networking and storage; I've named my clusters `source` and `destination`. +Specifically, I need: + +* The `StatefulSetStartOrdinal` feature gate enabled on both clusters. +* Client configuration for `kubectl` that lets me access both clusters as an + administrator. +* The same `StorageClass` installed on both clusters, and set as the default + StorageClass for both clusters. This `StorageClass` should provision + underlying storage that is accessible from either or both clusters. +* A flat network topology that allows for pods to send and receive packets to + and from Pods in either clusters. If you are creating clusters on a cloud + provider, this configuration may be called private cloud or private network. + +1. Create a demo namespace on both clusters: ``` kubectl create ns kep-3335 ``` -2. Deploy a Redis cluster on `source`. +2. Deploy a Redis cluster with six replicas in the source cluster: ``` helm repo add bitnami https://charts.bitnami.com/bitnami helm install redis --namespace kep-3335 \ bitnami/redis-cluster \ - --set persistence.size=1Gi + --set persistence.size=1Gi \ + --set cluster.nodes=6 ``` -3. On `source`, check the replication status. +3. Check the replication status in the source cluster: ``` kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ @@ -112,7 +121,7 @@ Pre-requisites: Two Kubernetes clusters named `source` and `destination`. 2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669764410000 2 connected 5461-10922 ``` -4. On `destination`, deploy Redis with zero replicas. +4. Deploy a Redis cluster with zero replicas in the destination cluster: ``` helm install redis --namespace kep-3335 \ @@ -123,19 +132,20 @@ Pre-requisites: Two Kubernetes clusters named `source` and `destination`. --set existingSecret=redis-redis-cluster ``` -5. Scale down replica `redis-redis-cluster-5` in the source cluster. +5. Scale down the `redis-redis-cluster` StatefulSet in the source cluster by 1, + to remove the replica `redis-redis-cluster-5`: ``` kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}' ``` -6. Migrate dependencies from `source` to `destination`. +6. Migrate dependencies from the source cluster to the destination cluster: The following commands copy resources from `source` to `destionation`. Details that are not relevant in `destination` cluster are removed (eg: `uid`, `resourceVersion`, `status`). - #### Source cluster + **Steps for the source cluster** Note: If using a `StorageClass` with `reclaimPolicy: Delete` configured, you should patch the PVs in `source` with `reclaimPolicy: Retain` prior to @@ -149,7 +159,7 @@ Pre-requisites: Two Kubernetes clusters named `source` and `destination`. kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion)' > /tmp/secret-redis-redis-cluster.yaml ``` - #### Destination cluster + **Steps for the destination cluster** Note: For the PV/PVC, this procedure only works if the underlying storage system that your PVs use can support being copied into `destination`. Storage @@ -164,31 +174,37 @@ Pre-requisites: Two Kubernetes clusters named `source` and `destination`. kubectl create -f /tmp/secret-redis-redis-cluster.yaml ``` -7. Scale up replica `redis-redis-cluster-5` in the destination cluster, with a - start ordinal of 5: +7. Scale up the `redis-redis-cluster` StatefulSet in the destination cluster by + 1, with a start ordinal of 5: ``` kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}' ``` -8. On the source cluster, check the replication status. +8. Check the replication status in the destination cluster: ``` - kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \ + kubectl exec -it redis-redis-cluster-5 -- /bin/bash -c \ "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;" ``` - You should see that the new replica's address has joined the Redis cluster. + I should see that the new replica (labeled `myself`) has joined the Redis + cluster (the IP address belongs to a different CIDR block than the + replicas in the source cluster). ``` - 2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 myself,master - 0 1669766684000 2 connected 5461-10922 - 7136e37d8864db983f334b85d2b094be47c830e5 10.108.0.22:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669766685609 2 connected + 2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669766684000 2 connected 5461-10922 + 7136e37d8864db983f334b85d2b094be47c830e5 10.108.0.22:6379@16379 myself,slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669766685609 2 connected 2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 master - 0 1669766684000 3 connected 10923-16383 961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669766683600 1 connected a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669766685000 1 connected 0-5460 7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669766686613 3 connected ``` +9. Repeat steps #5 to #7 for the remainder of the replicas, until the + Redis StatefulSet in the source cluster is scaled to 0, and the Redis + StatefulSet in the destination cluster is healthy with 6 total replicas. + ## What's Next? This feature provides a building block for a StatefulSet to be split up across @@ -196,10 +212,11 @@ clusters, but does not prescribe the mechanism as to how the StatefulSet should be migrated. Migration requires coordination of StatefulSet replicas, along with orchestration of the storage and network layer. This is dependent on the storage and connectivity requirements of the application installed by the StatefulSet. -Additionally, many StatefulSets are controlled by Operators, which adds another +Additionally, many StatefulSets are managed by +[operators](/docs/concepts/extend-kubernetes/operator/), which adds another layer of complexity to migration. -If you're interested in building blocks to make these processes easier, get -involved with +If you're interested in building enhancements to make these processes easier, +get involved with [SIG Multicluster](https://github.com/kubernetes/community/blob/master/sig-multicluster) to contribute! From 22101af31513194986d6406b52a94568114a01f4 Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Mon, 13 Mar 2023 12:15:51 -0700 Subject: [PATCH 08/10] Update StatefulSetStartOrdinal blog post for beta v1.27 --- content/en/blog/_posts/2022-12-16-statefulset-migration.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2022-12-16-statefulset-migration.md index 9b1f1297f2788..83fa30d1cb81d 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2022-12-16-statefulset-migration.md @@ -1,15 +1,16 @@ --- layout: blog title: "Kubernetes 1.26: StatefulSet Start Ordinal Simplifies Migration" -date: 2023-01-03 +date: 2023-04-19 slug: statefulset-start-ordinal --- **Author**: Peter Schuurman (Google) -Kubernetes v1.26 introduces a new, alpha-level feature for +Kubernetes v1.26 introduced a new, alpha-level feature for [StatefulSets](/docs/concepts/workloads/controllers/statefulset/) that controls -the ordinal numbering of Pod replicas. Ordinals can start from arbitrary +the ordinal numbering of Pod replicas. As of Kubernetes v1.27, this feature is +now beta. Ordinals can start from arbitrary non-negative numbers. This blog post will discuss how this feature can be used. From 4f223e0d3b3081edcafa7d74d95f19aa8d8bbc0d Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Thu, 13 Apr 2023 20:36:44 -0700 Subject: [PATCH 09/10] Add publish date for StatefulSet Migration blog --- ...efulset-migration.md => 2023-04-28-statefulset-migration.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename content/en/blog/_posts/{2022-12-16-statefulset-migration.md => 2023-04-28-statefulset-migration.md} (99%) diff --git a/content/en/blog/_posts/2022-12-16-statefulset-migration.md b/content/en/blog/_posts/2023-04-28-statefulset-migration.md similarity index 99% rename from content/en/blog/_posts/2022-12-16-statefulset-migration.md rename to content/en/blog/_posts/2023-04-28-statefulset-migration.md index 83fa30d1cb81d..b01e75a0565c1 100644 --- a/content/en/blog/_posts/2022-12-16-statefulset-migration.md +++ b/content/en/blog/_posts/2023-04-28-statefulset-migration.md @@ -1,7 +1,7 @@ --- layout: blog title: "Kubernetes 1.26: StatefulSet Start Ordinal Simplifies Migration" -date: 2023-04-19 +date: 2023-04-28 slug: statefulset-start-ordinal --- From f71a86263d07cc87932ee86aef1fbc8ac4e53a7b Mon Sep 17 00:00:00 2001 From: Peter Schuurman Date: Tue, 25 Apr 2023 10:38:02 -0700 Subject: [PATCH 10/10] Update title to reflect k8s 1.27 --- content/en/blog/_posts/2023-04-28-statefulset-migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/en/blog/_posts/2023-04-28-statefulset-migration.md b/content/en/blog/_posts/2023-04-28-statefulset-migration.md index b01e75a0565c1..6f52bf2eab72a 100644 --- a/content/en/blog/_posts/2023-04-28-statefulset-migration.md +++ b/content/en/blog/_posts/2023-04-28-statefulset-migration.md @@ -1,6 +1,6 @@ --- layout: blog -title: "Kubernetes 1.26: StatefulSet Start Ordinal Simplifies Migration" +title: "Kubernetes 1.27: StatefulSet Start Ordinal Simplifies Migration" date: 2023-04-28 slug: statefulset-start-ordinal ---