From 648508c05155eec3af11f5f49b88077cb25afefe Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Tue, 7 Jul 2020 09:30:59 -0700 Subject: [PATCH] Add transferring ownership section to api concepts --- .../docs/reference/using-api/api-concepts.md | 103 ++++++++++++++++-- .../ssa/nginx-deployment-no-replicas.yaml | 18 +++ .../ssa/nginx-deployment-replicas-only.yaml | 6 + .../application/ssa/nginx-deployment.yaml | 19 ++++ 4 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 content/en/examples/application/ssa/nginx-deployment-no-replicas.yaml create mode 100644 content/en/examples/application/ssa/nginx-deployment-replicas-only.yaml create mode 100644 content/en/examples/application/ssa/nginx-deployment.yaml diff --git a/content/en/docs/reference/using-api/api-concepts.md b/content/en/docs/reference/using-api/api-concepts.md index 5dd2202830..66c5944e2f 100644 --- a/content/en/docs/reference/using-api/api-concepts.md +++ b/content/en/docs/reference/using-api/api-concepts.md @@ -414,12 +414,6 @@ object or is [combined](#merge-strategy), by the server, with the existing objec The system supports multiple appliers collaborating on a single object. -This model of specifying intent makes it difficult to remove existing fields. -When a field is removed from one's config and applied, the value will be kept -(the system assumes that you don't care about that value anymore). If an item is -removed from a list or a map, it will be removed if no other appliers care about -its presence. - Changes to an object's fields are tracked through a "[field management](#field-management)" mechanism. When a field's value changes, ownership moves from its current manager to the manager making the change. When trying to apply an object, fields @@ -428,8 +422,14 @@ that have a different value and are owned by another manager will result in a collaborator's changes. Conflicts can be forced, in which case the value will be overridden, and the ownership will be transferred. -It is meant both as a replacement for the original `kubectl apply` and as a -simpler mechanism to write controllers. +If you remove a field from a configuration and apply the configuration, server side apply checks +if there are any other field managers that also own the field. If the field is +not owned by any other field managers, it is either deleted from the live +object or reset to its default value, if it has one. The same rule applies to associative list or +map items. + +Server side apply is meant both as a replacement for the original `kubectl +apply` and as a simpler mechanism for controllers to enact their changes. ### Field Management @@ -448,8 +448,12 @@ to the Server Side Apply endpoint. When using Server-Side Apply, trying to change a field which is managed by someone else will result in a rejected request (if not forced, see [Conflicts](#conflicts)). -Field management is stored in a newly introduced `managedFields` field that is -part of an object's +When two or more appliers set a field to the same value, they share ownership of +that field. Any subsequent attempt to change the value of the shared field, by any of +the appliers, results in a conflict. Shared field owners may give up ownership +of a field by removing it from their configuration. + +Field management is stored in a`managedFields` field that is part of an object's [`metadata`](/docs/reference/generated/kubernetes-api/{{< latest-version >}}/#objectmeta-v1-meta). A simple example of an object created by Server Side Apply could look like this: @@ -627,13 +631,88 @@ read-modify-write and/or patch are the following: It is strongly recommended for controllers to always "force" conflicts, since they might not be able to resolve or act on these conflicts. +### Transferring Ownership + +In addition to the concurrency controls provided by [conflict +resolution](#conflicts), Server Side Apply provides ways to perform coordinated +field ownership transfers from users to controllers. + +This is best explained by example. Let's look at how to safely transfer +ownership of the `replicas` field from a user to a controller while enabling +automatic horizontal scaling for a Deployment, using the HorizontalPodAutoscaler +resource and its accompanying controller. + +Say a user has defined deployment with `replicas` set to the desired value: + +{{< codenew file="application/ssa/nginx-deployment.yaml" >}} + +And the user has created the deployment using server side apply like so: + +```shell +kubectl apply -f application/ssa/nginx-deployment.yaml --server-side +``` + +Then later, HPA is enabled for the deployment, e.g.: + +``` +kubectl autoscale deployment nginx-deployment --cpu-percent=50 --min=1 --max=10 +``` + +Now, the user would like to remove `replicas` from their configuration, so they +don't accidentally fight with the HPA controller. However, there is a race: it +might take some time before HPA feels the need to adjust `replicas`, and if +the user removes `replicas` before the HPA writes to the field and becomes +its owner, then apiserver will set `replicas` to 1, its default value. This +is not what the user wants to happen, even temporarily. + +There are two solutions: + +- (easy) Leave `replicas` in the configuration; when HPA eventually writes to that + field, the system gives the user a conflict over it. At that point, it is safe + to remove from the configuration. + +- (more advanced) If, however, the user doesn't want to wait, for example + because they want to keep the cluster legible to coworkers, then they can take + the following steps to make it safe to remove `replicas` from their + configuration: + +First, the user defines a new configuration containing only the `replicas` field: + +{{< codenew file="application/ssa/nginx-deployment-replicas-only.yaml" >}} + +The user applies that configuration using the field manager name `handover-to-hpa`: + +```shell +kubectl apply -f application/ssa/nginx-deployment-replicas-only.yaml --server-side --field-manager=handover-to-hpa --validate=false +``` + +If the apply results in a conflict with the HPA controller, then do nothing. The +conflict just indicates the controller has claimed the field earlier in the +process that it sometimes does. + +At this point the user may remove the `replicas` field from their configuration. + +{{< codenew file="application/ssa/nginx-deployment-no-replicas.yaml" >}} + +Note that whenever the HPA controller sets the `replicas` field to a new value, +the temporary field manager will no longer own any fields and will be +automatically deleted. No clean up is required. + +### Transferring Ownership Between Users + +Users can transfer ownership of a field between each other by setting the field +to the same value in both of their applied configs, causing them to share +ownership of the field. Once the users share ownership of the field, one of them +can remove the field from their applied configuration to give up ownership and +complete the transfer to the other user. + ### Comparison with Client Side Apply A consequence of the conflict detection and resolution implemented by Server Side Apply is that an applier always has up to date field values in their local state. If they don't, they get a conflict the next time they apply. Any of the -three options to resolve conflicts results in the applied config being an up to -date subset of the object on the server's fields. +three options to resolve conflicts results in the applied configuration being an +up to date subset of the object on the server's fields. This is different from Client Side Apply, where outdated values which have been overwritten by other users are left in an applier's local config. These values diff --git a/content/en/examples/application/ssa/nginx-deployment-no-replicas.yaml b/content/en/examples/application/ssa/nginx-deployment-no-replicas.yaml new file mode 100644 index 0000000000..c18bdd35c0 --- /dev/null +++ b/content/en/examples/application/ssa/nginx-deployment-no-replicas.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 diff --git a/content/en/examples/application/ssa/nginx-deployment-replicas-only.yaml b/content/en/examples/application/ssa/nginx-deployment-replicas-only.yaml new file mode 100644 index 0000000000..0848ba0e21 --- /dev/null +++ b/content/en/examples/application/ssa/nginx-deployment-replicas-only.yaml @@ -0,0 +1,6 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment +spec: + replicas: 3 diff --git a/content/en/examples/application/ssa/nginx-deployment.yaml b/content/en/examples/application/ssa/nginx-deployment.yaml new file mode 100644 index 0000000000..7797b13d9a --- /dev/null +++ b/content/en/examples/application/ssa/nginx-deployment.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2