mirror of
https://github.com/kubernetes/client-go.git
synced 2025-07-03 02:07:55 +00:00
client-go: use retry util in CRUD example
This updates the create-update-delete-deployment example with the following: Make use of client-go retry util in Update() steps instead of simple for loops. Using RetryOnConflict is generally better practice as it won't become stuck in a retry loop and uses exponential backoff to prevent exhausting the apiserver. Instead of changing annotations to demonstrate Updates/Rollbacks, change the container image as it is less confusing for readers and a better real-world example. Improve comments and README to reflect above changes. Signed-off-by: John Kelly <jekohk@gmail.com> Kubernetes-commit: 94f5bcf6f77d5b35074dfab47b5de37096d8ee00
This commit is contained in:
parent
4e80b27156
commit
ddf6c35ca5
@ -19,10 +19,10 @@ go_library(
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/retry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -29,18 +29,19 @@ Now, run this application on your workstation with your local kubeconfig file:
|
||||
|
||||
Running this command will execute the following operations on your cluster:
|
||||
|
||||
1. **Create Deployment:** This will create a 2 replica Deployment with
|
||||
annotation `fizz=buzz`. Verify with `kubectl get pods`.
|
||||
1. **Create Deployment:** This will create a 2 replica Deployment. Verify with
|
||||
`kubectl get pods`.
|
||||
2. **Update Deployment:** This will update the Deployment resource created in
|
||||
previous step to set the replica count to 1 and update annotations. You are
|
||||
encouraged to inspect the retry loop that handles conflicts. Verify the new
|
||||
replica count and `foo=bar` annotation with `kubectl describe deployment
|
||||
demo`.
|
||||
previous step by setting the replica count to 1 and changing the container
|
||||
image to `nginx:1.13`. You are encouraged to inspect the retry loop that
|
||||
handles conflicts. Verify the new replica count and container image with
|
||||
`kubectl describe deployment demo`.
|
||||
3. **Rollback Deployment:** This will rollback the Deployment to the last
|
||||
revision, in this case the revision created in Step 1. Use `kubectl describe`
|
||||
to verify the original annotation `fizz=buzz`. Also note the replica count
|
||||
is still 1; this is because a Deployment revision is created if and only
|
||||
if the Deployment's pod template (`.spec.template`) is changed.
|
||||
revision. In this case, it's the revision that was created in Step 1.
|
||||
Use `kubectl describe` to verify the container image is now `nginx:1.12`.
|
||||
Also note that the Deployment's replica count is still 1; this is because a
|
||||
Deployment revision is created if and only if the Deployment's pod template
|
||||
(`.spec.template`) is changed.
|
||||
4. **List Deployments:** This will retrieve Deployments in the `default`
|
||||
namespace and print their names and replica counts.
|
||||
5. **Delete Deployment:** This will delete the Deployment object and its
|
||||
|
@ -26,10 +26,10 @@ import (
|
||||
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/retry"
|
||||
// Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters).
|
||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
)
|
||||
@ -65,15 +65,12 @@ func main() {
|
||||
Labels: map[string]string{
|
||||
"app": "demo",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fizz": "buzz",
|
||||
},
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
{
|
||||
Name: "web",
|
||||
Image: "nginx:1.13",
|
||||
Image: "nginx:1.12",
|
||||
Ports: []apiv1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
@ -104,66 +101,52 @@ func main() {
|
||||
// 1. Modify the "deployment" variable and call: Update(deployment).
|
||||
// This works like the "kubectl replace" command and it overwrites/loses changes
|
||||
// made by other clients between you Create() and Update() the object.
|
||||
// 2. Modify the "result" returned by Create()/Get() and retry Update(result) until
|
||||
// 2. Modify the "result" returned by Get() and retry Update(result) until
|
||||
// you no longer get a conflict error. This way, you can preserve changes made
|
||||
// by other clients between Create() and Update(). This is implemented below:
|
||||
// by other clients between Create() and Update(). This is implemented below
|
||||
// using the retry utility package included with client-go. (RECOMMENDED)
|
||||
//
|
||||
// See the API Conventions:
|
||||
// More Info:
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency
|
||||
|
||||
for {
|
||||
// Retrieve latest version of Deployment before modifying and updating
|
||||
err = retry.RetryOnConflict(retry.DefaultRetry, func() (err error) {
|
||||
// Retrieve the latest version of Deployment before attempting update
|
||||
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
|
||||
result, err = deploymentsClient.Get("demo-deployment", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Get failed: %+v", err))
|
||||
}
|
||||
result.Spec.Replicas = int32Ptr(1) // reduce replica count
|
||||
result.Spec.Template.Annotations = map[string]string{ // add annotations
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
if _, err = deploymentsClient.Update(result); err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
// Deployment was modified since last retrieved, need to retry
|
||||
fmt.Println("Conflicting resource versions, retrying")
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Updated deployment...")
|
||||
break
|
||||
}
|
||||
|
||||
// TODO: You should sleep here with an exponential backoff to avoid
|
||||
// exhausting the apiserver, and add a limit/timeout on the retries to
|
||||
// avoid getting stuck in this loop indefintiely.
|
||||
result.Spec.Replicas = int32Ptr(1) // reduce replica count
|
||||
result.Spec.Template.Spec.Containers[0].Image = "nginx:1.13" // change nginx version
|
||||
result, err = deploymentsClient.Update(result)
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Update failed: %+v", err))
|
||||
}
|
||||
fmt.Println("Updated deployment...")
|
||||
|
||||
// Rollback Deployment
|
||||
prompt()
|
||||
fmt.Println("Rolling back deployment...")
|
||||
|
||||
// Use same method as above to avoid version conflicts
|
||||
for {
|
||||
// Once again use RetryOnConflict to avoid update conflicts
|
||||
err = retry.RetryOnConflict(retry.DefaultRetry, func() (err error) {
|
||||
result, err = deploymentsClient.Get("demo-deployment", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Get failed: %+v", err))
|
||||
}
|
||||
result.Spec.RollbackTo = &appsv1beta1.RollbackConfig{
|
||||
Revision: 0, // Can be specific revision number or 0 for last revision
|
||||
}
|
||||
|
||||
if _, err = deploymentsClient.Update(result); err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
fmt.Println("Conflicting resource versions, retrying")
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Rolled back deployment...")
|
||||
break
|
||||
result.Spec.RollbackTo = &appsv1beta1.RollbackConfig{
|
||||
Revision: 0, // can be specific revision number, or 0 for last revision
|
||||
}
|
||||
result, err = deploymentsClient.Update(result)
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Rollback failed: %+v", err))
|
||||
}
|
||||
fmt.Println("Rolled back deployment...")
|
||||
|
||||
// List Deployments
|
||||
prompt()
|
||||
|
Loading…
Reference in New Issue
Block a user