Merge pull request #31261 from sftim/20220108_improve_api_documentation_objects

Improve documentation about Kubernetes objects & managing them
This commit is contained in:
Kubernetes Prow Robot 2023-08-15 15:52:28 -07:00 committed by GitHub
commit 21c300562f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 449 additions and 186 deletions

View File

@ -1121,6 +1121,22 @@ This annotation is deprecated. You should use the
annotation instead. Kubernetes versions 1.25 and newer ignore this annotation. annotation instead. Kubernetes versions 1.25 and newer ignore this annotation.
{{< /note >}} {{< /note >}}
### kubectl.kubernetes.io/last-applied-configuration
Type: Annotation
Example: _see following snippet_
```yaml
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"example","namespace":"default"},"spec":{"selector":{"matchLabels":{"app.kubernetes.io/name":foo}},"template":{"metadata":{"labels":{"app.kubernetes.io/name":"foo"}},"spec":{"containers":[{"image":"container-registry.example/foo-bar:1.42","name":"foo-bar","ports":[{"containerPort":42}]}]}}}}
```
Used on: all objects
The kubectl command line tool uses this annotation as a legacy mechanism
to track changes. That mechanism has been superseded by
[Server-side apply](/docs/reference/using-api/server-side-apply/).
### endpoints.kubernetes.io/over-capacity ### endpoints.kubernetes.io/over-capacity
Type: Annotation Type: Annotation

View File

@ -76,6 +76,7 @@ For PUT requests, Kubernetes internally classifies these as either **create** or
based on the state of the existing object. An **update** is different from a **patch**; the based on the state of the existing object. An **update** is different from a **patch**; the
HTTP verb for a **patch** is PATCH. HTTP verb for a **patch** is PATCH.
## Resource URIs ## Resource URIs
All resource types are either scoped by the cluster (`/apis/GROUP/VERSION/*`) or to a All resource types are either scoped by the cluster (`/apis/GROUP/VERSION/*`) or to a
@ -162,7 +163,7 @@ For example:
``` ```
2. Starting from resource version 10245, receive notifications of any API operations 2. Starting from resource version 10245, receive notifications of any API operations
(such as **create**, **delete**, **apply** or **update**) that affect Pods in the (such as **create**, **delete**, **patch** or **update**) that affect Pods in the
_test_ namespace. Each change notification is a JSON document. The HTTP response body _test_ namespace. Each change notification is a JSON document. The HTTP response body
(served as `application/json`) consists a series of JSON documents. (served as `application/json`) consists a series of JSON documents.
@ -750,7 +751,7 @@ Once the last finalizer is removed, the resource is actually removed from etcd.
## Single resource API ## Single resource API
The Kubernetes API verbs **get**, **create**, **apply**, **update**, **patch**, The Kubernetes API verbs **get**, **create**, **update**, **patch**,
**delete** and **proxy** support single resources only. **delete** and **proxy** support single resources only.
These verbs with single resource support have no support for submitting multiple These verbs with single resource support have no support for submitting multiple
resources together in an ordered or unordered list or transaction. resources together in an ordered or unordered list or transaction.
@ -786,9 +787,9 @@ These situations are:
fields via `x-kubernetes-preserve-unknown-fields`). fields via `x-kubernetes-preserve-unknown-fields`).
2. The field is duplicated in the object. 2. The field is duplicated in the object.
### Validation for unrecognized or duplicate fields (#setting-the-field-validation-level) ### Validation for unrecognized or duplicate fields {#setting-the-field-validation-level}
{{< feature-state for_k8s_version="v1.27" state="stable" >}} {{< feature-state for_k8s_version="v1.27" state="stable" >}}
From 1.25 onward, unrecognized or duplicate fields in an object are detected via From 1.25 onward, unrecognized or duplicate fields in an object are detected via
validation on the server when you use HTTP verbs that can submit data (`POST`, `PUT`, and `PATCH`). Possible levels of validation on the server when you use HTTP verbs that can submit data (`POST`, `PUT`, and `PATCH`). Possible levels of
@ -937,17 +938,143 @@ rules:
See [Authorization Overview](/docs/reference/access-authn-authz/authorization/). See [Authorization Overview](/docs/reference/access-authn-authz/authorization/).
## Server Side Apply ## Updates to existing resources {#patch-and-apply}
Kubernetes provides several ways to update existing objects.
You can read [choosing an update mechanism](#update-mechanism-choose) to
learn about which approach might be best for your use case.
You can overwrite (**update**) an existing resource - for example, a ConfigMap -
using an HTTP PUT. For a PUT request, it is the client's responsibility to specify
the `resourceVersion` (taking this from the object being updated). Kubernetes uses
that `resourceVersion` information so that the API server can detect lost updates
and reject requests made by a client that is out of date with the cluster.
In the event that the resource has changed (the `resourceVersion` the client
provided is stale), the API server returns a `409 Conflict` error response.
Instead of sending a PUT request, the client can send an instruction to the API
server to **patch** an existing resource. A **patch** is typically appropriate
if the change that the client wants to make isn't conditional on the existing data. Clients that need effective detection of lost updates should consider
making their request conditional on the existing `resourceVersion` (either HTTP PUT or HTTP PATCH),
and then handle any retries that are needed in case there is a conflict.
The Kubernetes API supports four different PATCH operations, determined by their
corresponding HTTP `Content-Type` header:
`application/apply-patch+yaml`
: Server Side Apply YAML (a Kubernetes-specific extension, based on YAML).
All JSON documents are valid YAML, so you can also submit JSON using this
media type. See [Server Side Apply serialization](/docs/reference/using-api/server-side-apply/#serialization)
for more details.
To Kubernetes, this is a **create** operation if the object does not exist,
or a **patch** operation if the object already exists.
`application/json-patch+json`
: JSON Patch, as defined in [RFC6902](https://tools.ietf.org/html/rfc6902).
A JSON patch is a sequence of operations that are executed on the resource;
for example `{"op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ]}`.
To Kubernetes, this is a **patch** operation.
A **patch** using `application/json-patch+json` can include conditions to
validate consistency, allowing the operation to fail if those conditions
are not met (for example, to avoid a lost update).
`application/merge-patch+json`
: JSON Merge Patch, as defined in [RFC7386](https://tools.ietf.org/html/rfc7386).
A JSON Merge Patch is essentially a partial representation of the resource.
The submitted JSON is combined with the current resource to create a new one,
then the new one is saved.
To Kubernetes, this is a **patch** operation.
`application/strategic-merge-patch+json`
: Strategic Merge Patch (a Kubernetes-specific extension based on JSON).
Strategic Merge Patch is a custom implementation of JSON Merge Patch.
You can only use Strategic Merge Patch with built-in APIs, or with aggregated
API servers that have special support for it. You cannot use
`application/strategic-merge-patch+json` with any API
defined using a {{< glossary_tooltip term_id="CustomResourceDefinition" text="CustomResourceDefinition" >}}.
{{< note >}}
The Kubernetes _server side apply_ mechanism has superseded Strategic Merge
Patch.
{{< /note >}}
Kubernetes' [Server Side Apply](/docs/reference/using-api/server-side-apply/) Kubernetes' [Server Side Apply](/docs/reference/using-api/server-side-apply/)
feature allows the control plane to track managed fields for newly created objects. feature allows the control plane to track managed fields for newly created objects.
Server Side Apply provides a clear pattern for managing field conflicts, Server Side Apply provides a clear pattern for managing field conflicts,
offers server-side `Apply` and `Update` operations, and replaces the offers server-side **apply** and **update** operations, and replaces the
client-side functionality of `kubectl apply`. client-side functionality of `kubectl apply`.
The API verb for Server-Side Apply is **apply**. For Server-Side Apply, Kubernetes treats the request as a **create** if the object
does not yet exist, and a **patch** otherwise. For other requests that use PATCH
at the HTTP level, the logical Kubernetes operation is always **patch**.
See [Server Side Apply](/docs/reference/using-api/server-side-apply/) for more details. See [Server Side Apply](/docs/reference/using-api/server-side-apply/) for more details.
### Choosing an update mechanism {#update-mechanism-choose}
#### HTTP PUT to replace existing resource {#update-mechanism-update}
The **update** (HTTP `PUT`) operation is simple to implement and flexible,
but has drawbacks:
* You need to handle conflicts where the `resourceVersion` of the object changes
between your client reading it and trying to write it back. Kubernetes always
detects the conflict, but you as the client author need to implement retries.
* You might accidentally drop fields if you decode an object locally (for example,
using client-go, you could receive fields that your client does not know how to
handle - and then drop them as part of your update.
* If there's a lot of contention on the object (even on a field, or set of fields,
that you're not trying to edit), you might have trouble sending the update.
The problem is worse for larger objects and for objects with many fields.
#### HTTP PATCH using JSON Patch {#update-mechanism-json-patch}
A **patch** update is helpful, because:
* As you're only sending differences, you have less data to send in the `PATCH`
request.
* You can make changes that rely on existing values, such as copying the
value of a particular field into an annotation.
* Unlike with an **update** (HTTP `PUT`), making your change can happen right away
even if there are frequent changes to unrelated fields): you usually would
not need to retry.
* You might still need to specify the `resourceVersion` (to match an existing object)
if you want to be extra careful to avoid lost updates
* It's still good practice to write in some retry logic in case of errors.
* You can use test conditions to careful craft specific update conditions.
For example, you can increment a counter without reading it if the existing
value matches what you expect. You can do this with no lost update risk,
even if the object has changed in other ways since you last wrote to it.
(If the test condition fails, you can fall back to reading the current value
and then write back the changed number).
However:
* you need more local (client) logic to build the patch; it helps a lot if you have
a library implementation of JSON Patch, or even for making a JSON Patch specifically against Kubernetes
* as the author of client software, you need to be careful when building the patch
(the HTTP request body) not to drop fields (the order of operations matters)
#### HTTP PATCH using Server-Side Apply {#update-mechanism-server-side-apply}
Server-Side Apply has some clear benefits:
* A single round trip: it rarely requires making a `GET` request first.
* and you can still detect conflicts for unexpected changes
* you have the option to force override a conflict, if appropriate
* Client implementations are easy to make
* You get an atomic create-or-update operation without extra effort
(similar to `UPSERT` in some SQL dialects)
However:
* Server-Side Apply does not work at all for field changes that depend on a current value of the object
* You can only apply updates to objects. Some resources in the Kubernetes HTTP API are
not objects (they do not have a `.metadata` field), and Server-Side Apply
is only relevant for Kubernetes objects.
## Resource versions ## Resource versions
Resource versions are strings that identify the server's internal version of an Resource versions are strings that identify the server's internal version of an

View File

@ -7,75 +7,97 @@ reviewers:
- liggitt - liggitt
content_type: concept content_type: concept
weight: 25 weight: 25
min-kubernetes-server-version: 1.16
--- ---
<!-- overview --> <!-- overview -->
{{< feature-state for_k8s_version="v1.22" state="stable" >}} {{< feature-state for_k8s_version="v1.22" state="stable" >}}
## Introduction Kubernetes supports multiple appliers collaborating to manage the fields
of a single [object](/docs/concepts/overview/working-with-objects/).
Server-Side Apply helps users and controllers manage their resources through Server-Side Apply provides an optional mechanism for your cluster's control plane to track
declarative configurations. Clients can create and modify their changes to an object's fields. At the level of a specific resource, Server-Side
Apply records and tracks information about control over the fields of that object.
Server-Side Apply helps users and {{< glossary_tooltip text="controllers" term_id="controller" >}}
manage their resources through declarative configuration. Clients can create and modify
{{< glossary_tooltip text="objects" term_id="object" >}} {{< glossary_tooltip text="objects" term_id="object" >}}
declaratively by sending their fully specified intent. declaratively by submitting their _fully specified intent_.
A fully specified intent is a partial object that only includes the fields and A fully specified intent is a partial object that only includes the fields and
values for which the user has an opinion. That intent either creates a new values for which the user has an opinion. That intent either creates a new
object or is [combined](#merge-strategy), by the server, with the existing object. object (using default values for unspecified fields), or is
[combined](#merge-strategy), by the API server, with the existing object.
The system supports multiple appliers collaborating on a single object. [Comparison with Client-Side Apply](#comparison-with-client-side-apply) explains
how Server-Side Apply differs from the original, client-side `kubectl apply`
implementation.
Changes to an object's fields are tracked through a "[field management](#field-management)" <!-- body -->
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 that have a different value and are owned by another manager will
result in a [conflict](#conflicts). This is done in order to signal that the
operation might undo another collaborator's changes. Conflicts can be forced,
in which case the value will be overridden, and the ownership will be
transferred.
If you remove a field from a configuration and apply the configuration, ## Field management
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 The Kubernetes API server tracks _managed fields_ for all newly created objects.
apply` and as a simpler mechanism for controllers to enact their changes.
If you have Server-Side Apply enabled, the control plane tracks managed fields When trying to apply an object, fields that have a different value and are owned by
for all newly created objects. another [manager](#managers) will result in a [conflict](#conflicts). This is done
in order to signal that the operation might undo another collaborator's changes.
Writes to objects with managed fields can be forced, in which case the value of any
conflicted field will be overridden, and the ownership will be transferred.
## Field Management Whenever a field's value does change, ownership moves from its current manager to the
manager making the change.
Compared to the `last-applied` annotation managed by `kubectl`, Server-Side Apply checks if there are any other field managers that also own the
Apply uses a more declarative approach, which tracks a user's field management, field. If the field is not owned by any other field managers, that field is
rather than a user's last applied state. This means that as a side effect of set to its default value (if there is one), or otherwise is deleted from the
using Server-Side Apply, information about which field manager manages each object.
field in an object also becomes available. The same rule applies to fields that are lists, associative lists, or maps.
For a user to manage a field, in the Server-Side Apply sense, means that the For a user to manage a field, in the Server-Side Apply sense, means that the
user relies on and expects the value of the field not to change. The user who user relies on and expects the value of the field not to change. The user who
last made an assertion about the value of a field will be recorded as the last made an assertion about the value of a field will be recorded as the
current field manager. This can be done either by changing the value with current field manager. This can be done by changing the field manager
`POST`, `PUT`, or non-apply `PATCH`, or by including the field in a config sent details explicitly using HTTP `POST` (**create**), `PUT` (**update**), or non-apply
to the Server-Side Apply endpoint. When using Server-Side Apply, trying to `PATCH` (**patch**). You can also declare and record a field manager
change a field which is managed by someone else will result in a rejected by including a value for that field in a Server-Side Apply operation.
request (if not forced, see [Conflicts](#conflicts)).
A Server-Side Apply **patch** request requires the client to provide its identity
as a [field manager](#managers). When using Server-Side Apply, trying to change a
field that is controlled by a different manager results in a rejected
request unless the client forces an override.
For details of overrides, see [Conflicts](#conflicts).
When two or more appliers set a field to the same value, they share ownership of 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 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 the appliers, results in a conflict. Shared field owners may give up ownership
of a field by removing it from their configuration. of a field by making a Server-Side Apply **patch** request that doesn't include
that field.
Field management is stored in a`managedFields` field that is part of an object's Field management details are stored in a `managedFields` field that is part of an
[`metadata`](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#objectmeta-v1-meta). object's [`metadata`](/docs/reference/kubernetes-api/common-definitions/object-meta/).
A simple example of an object created by Server-Side Apply could look like this: If you remove a field from a manifest and apply that manifest, 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.
Compared to the (legacy)
[`kubectl.kubernetes.io/last-applied-configuration`](/docs/reference/labels-annotations-taints/#kubectl-kubernetes-io-last-applied-configuration)
annotation managed by `kubectl`, Server-Side Apply uses a more declarative
approach, that tracks a user's (or client's) field management, rather than
a user's last applied state. As a side effect of using Server-Side Apply,
information about which field manager manages each field in an object also
becomes available.
### Example {#ssa-example-configmap}
A simple example of an object created using Server-Side Apply could look like this:
```yaml ```yaml
---
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
@ -85,7 +107,7 @@ metadata:
test-label: test test-label: test
managedFields: managedFields:
- manager: kubectl - manager: kubectl
operation: Apply operation: Apply # note capitalization: "Apply" (or "Update")
apiVersion: v1 apiVersion: v1
time: "2010-10-10T0:00:00Z" time: "2010-10-10T0:00:00Z"
fieldsType: FieldsV1 fieldsType: FieldsV1
@ -99,63 +121,76 @@ data:
key: some value key: some value
``` ```
The above object contains a single manager in `metadata.managedFields`. The That example ConfigMap object contains a single field management record in
manager consists of basic information about the managing entity itself, like `.metadata.managedFields`. The field management record consists of basic information
operation type, API version, and the fields managed by it. about the managing entity itself, plus details about the fields being managed and
the relevant operation (`Apply` or `Update`). If the request that last changed that
field was a Server-Side Apply **patch** then the value of `operation` is `Apply`;
otherwise, it is `Update`.
{{< note >}} There is another possible outcome. A client could submit an invalid request
This field is managed by the API server and should not be changed by body. If the fully specified intent does not produce a valid object, the
the user. request fails.
{{< /note >}}
Nevertheless it is possible to change `metadata.managedFields` through an It is however possible to change `.metadata.managedFields` through an
`Update` operation. Doing so is highly discouraged, but might be a reasonable **update**, or through a **patch** operation that does not use Server-Side Apply.
option to try if, for example, the `managedFields` get into an inconsistent Doing so is highly discouraged, but might be a reasonable option to try if,
state (which clearly should not happen). for example, the `.metatadata.managedFields` get into an inconsistent state
(which should not happen in normal operations).
The format of the `managedFields` is described in the The format of `managedFields` is [described](/docs/reference/kubernetes-api/common-definitions/object-meta/#System)
[API](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#fieldsv1-v1-meta). in the Kubernetes API reference.
## Conflicts {{< caution >}}
The `.metadata.managedFields` field is managed by the API server.
You should avoid updating it manually.
{{< /caution >}}
A conflict is a special status error that occurs when an `Apply` operation tries ### Conflicts
to change a field, which another user also claims to manage. This prevents an
A _conflict_ is a special status error that occurs when an `Apply` operation tries
to change a field that another manager also claims to manage. This prevents an
applier from unintentionally overwriting the value set by another user. When applier from unintentionally overwriting the value set by another user. When
this occurs, the applier has 3 options to resolve the conflicts: this occurs, the applier has 3 options to resolve the conflicts:
* **Overwrite value, become sole manager:** If overwriting the value was * **Overwrite value, become sole manager:** If overwriting the value was
intentional (or if the applier is an automated process like a controller) the intentional (or if the applier is an automated process like a controller) the
applier should set the `force` query parameter to true (in kubectl, it can be done by applier should set the `force` query parameter to true (for `kubectl apply`,
using the `--force-conflicts` flag with the apply command) and make the request you use the `--force-conflicts` command line parameter), and make the request
again. This forces the operation to succeed, changes the value of the field, again. This forces the operation to succeed, changes the value of the field,
and removes the field from all other managers' entries in managedFields. and removes the field from all other managers' entries in `managedFields`.
* **Don't overwrite value, give up management claim:** If the applier doesn't * **Don't overwrite value, give up management claim:** If the applier doesn't
care about the value of the field anymore, they can remove it from their care about the value of the field any more, the applier can remove it from their
config and make the request again. This leaves the value unchanged, and causes local model of the resource, and make a new request with that particular field
the field to be removed from the applier's entry in managedFields. omitted. This leaves the value unchanged, and causes the field to be removed
from the applier's entry in `managedFields`.
* **Don't overwrite value, become shared manager:** If the applier still cares * **Don't overwrite value, become shared manager:** If the applier still cares
about the value of the field, but doesn't want to overwrite it, they can about the value of a field, but doesn't want to overwrite it, they can
change the value of the field in their config to match the value of the object change the value of that field in their local model of the resource so as to
on the server, and make the request again. This leaves the value unchanged, match the value of the object on the server, and then make a new request that
and causes the field's management to be shared by the applier and all other takes into account that local update. Doing so leaves the value unchanged,
field managers that already claimed to manage it. and causes that field's management to be shared by the applier along with all
other field managers that already claimed to manage it.
## Managers ### Field managers {#managers}
Managers identify distinct workflows that are modifying the object (especially Managers identify distinct workflows that are modifying the object (especially
useful on conflicts!), and can be specified through the `fieldManager` query useful on conflicts!), and can be specified through the
parameter as part of a modifying request. It is required for the apply endpoint, [`fieldManager`](/docs/reference/kubernetes-api/common-parameters/common-parameters/#fieldManager)
though kubectl will default it to `kubectl`. For other updates, its default is query parameter as part of a modifying request. When you Apply to a resource,
computed from the user-agent. the `fieldManager` parameter is required
For other updates, the API server infers a field manager identity from the
"User-Agent:" HTTP header (if present).
## Apply and Update When you use the `kubectl` tool to perform a Server-Side Apply operation, `kubectl`
sets the manager identity to `"kubectl"` by default.
The two operation types considered by this feature are `Apply` (`PATCH` with ## Serialization
content type `application/apply-patch+yaml`) and `Update` (all other operations
which modify the object). Both operations update the `managedFields`, but behave At the protocol level, Kubernetes represents Server-Side Apply message bodies
a little differently. as [YAML](https://yaml.org/), with the media type `application/apply-patch+yaml`.
{{< note >}} {{< note >}}
Whether you are submitting JSON data or YAML data, use Whether you are submitting JSON data or YAML data, use
@ -164,15 +199,55 @@ Whether you are submitting JSON data or YAML data, use
All JSON documents are valid YAML. All JSON documents are valid YAML.
{{< /note >}} {{< /note >}}
For instance, only the apply operation fails on conflicts while update does The serialization is the same as for Kubernetes objects, with the exception that
not. Also, apply operations are required to identify themselves by providing a clients are not required to send a complete object.
`fieldManager` query parameter, while the query parameter is optional for update
operations. Finally, when using the apply operation you cannot have Here's an example of a Server-Side Apply message body (fully specified intent):
`managedFields` in the object that is being applied. ```yaml
{
"apiVersion": "v1",
"kind": "ConfigMap"
}
```
(this would make a no-change update, provided that it was sent as the body
of a **patch** request to a valid `v1/configmaps` resource, and with the
appropriate request `Content-Type`).
## Server-Side Apply for custom resources {#custom-resources}
By default, Server-Side Apply treats
{{< glossary_tooltip term_id="CustomResourceDefinition" text="custom resources" >}}
as unstructured data. All keys are treated the same as if they were struct fields for
a built-in API, and all lists are considered atomic.
If the CustomResourceDefinition defines a
[schema](/docs/reference/kubernetes-api/extend-resources/custom-resource-definition-v1/#JSONSchemaProps)
that contains annotations as defined in [Merge strategy](#merge-strategy),
then these annotations will be used when merging objects of this type.
## Operations in scope for field management {#apply-and-update}
The Kubernetes API operations where field management is considered are:
1. Server-Side Apply (HTTP `PATCH`, with content type `application/apply-patch+yaml`)
2. Replacing an existing object (**update** to Kubernetes; `PUT` at the HTTP level)
Both operations update `.metadata.managedFields`, but behave a little differently.
Unless you specify a forced override, an apply operation that encounters field-level
conflicts always fails; by contrast, if you make a change using **update** that would
affect a managed field, a conflict never provokes failure of the operation.
All Server-Side Apply **patch** requests are required to identify themselves by providing a
`fieldManager` query parameter, while the query parameter is optional for **update**
operations. Finally, when using the `Apply` operation you cannot define `managedFields` in
the body of the request that you submit.
An example object with multiple managers could look like this: An example object with multiple managers could look like this:
```yaml ```yaml
---
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
@ -199,11 +274,11 @@ data:
key: new value key: new value
``` ```
In this example, a second operation was run as an `Update` by the manager called In this example, a second operation was run as an **update** by the manager called
`kube-controller-manager`. The update changed a value in the data field which `kube-controller-manager`. The update request succeeded and changed a value in the data
caused the field's management to change to the `kube-controller-manager`. field, which caused that field's management to change to the `kube-controller-manager`.
If this update would have been an `Apply` operation, the operation If this update has instead been attempted using Server-Side Apply, the request
would have failed due to conflicting ownership. would have failed due to conflicting ownership.
## Merge strategy ## Merge strategy
@ -213,27 +288,27 @@ more stable object lifecycle. Server-Side Apply tries to merge fields based on
the actor who manages them instead of overruling based on values. This way the actor who manages them instead of overruling based on values. This way
multiple actors can update the same object without causing unexpected interference. multiple actors can update the same object without causing unexpected interference.
When a user sends a "fully-specified intent" object to the Server-Side Apply When a user sends a _fully-specified intent_ object to the Server-Side Apply
endpoint, the server merges it with the live object favoring the value in the endpoint, the server merges it with the live object favoring the value from the
applied config if it is specified in both places. If the set of items present in request body if it is specified in both places. If the set of items present in
the applied config is not a superset of the items applied by the same user last the applied config is not a superset of the items applied by the same user last
time, each missing item not managed by any other appliers is removed. For time, each missing item not managed by any other appliers is removed. For
more information about how an object's schema is used to make decisions when more information about how an object's schema is used to make decisions when
merging, see merging, see
[sigs.k8s.io/structured-merge-diff](https://sigs.k8s.io/structured-merge-diff). [sigs.k8s.io/structured-merge-diff](https://sigs.k8s.io/structured-merge-diff).
A number of markers were added in Kubernetes 1.16 and 1.17, to allow API The Kubernetes API (and the Go code that implements that API for Kubernetes) allows
developers to describe the merge strategy supported by lists, maps, and defining _merge strategy markers_. These markers describe the merge strategy supported
structs. These markers can be applied to objects of the respective type, for fields within Kubernetes objects.
in Go files or in the OpenAPI schema definition of the For a {{< glossary_tooltip term_id="CustomResourceDefinition" text="CustomResourceDefinition" >}},
[CRD](/docs/reference/generated/kubernetes-api/{{< param "version" >}}#jsonschemaprops-v1-apiextensions-k8s-io): you can set these markers when you define the custom resource.
| Golang marker | OpenAPI extension | Accepted values | Description | Introduced in | | Golang marker | OpenAPI extension | Possible values | Description |
|---|---|---|---|---| |---|---|---|---|---|
| `//+listType` | `x-kubernetes-list-type` | `atomic`/`set`/`map` | Applicable to lists. `set` applies to lists that include only scalar elements. These elements must be unique. `map` applies to lists of nested types only. The key values (see `listMapKey`) must be unique in the list. `atomic` can apply to any list. If configured as `atomic`, the entire list is replaced during merge. At any point in time, a single manager owns the list. If `set` or `map`, different managers can manage entries separately. | 1.16 | | `//+listType` | `x-kubernetes-list-type` | `atomic`/`set`/`map` | Applicable to lists. `set` applies to lists that include only scalar elements. These elements must be unique. `map` applies to lists of nested types only. The key values (see `listMapKey`) must be unique in the list. `atomic` can apply to any list. If configured as `atomic`, the entire list is replaced during merge. At any point in time, a single manager owns the list. If `set` or `map`, different managers can manage entries separately. |
| `//+listMapKey` | `x-kubernetes-list-map-keys` | List of field names, e.g. `["port", "protocol"]` | Only applicable when `+listType=map`. A list of field names whose values uniquely identify entries in the list. While there can be multiple keys, `listMapKey` is singular because keys need to be specified individually in the Go type. The key fields must be scalars. | 1.16 | | `//+listMapKey` | `x-kubernetes-list-map-keys` | List of field names, e.g. `["port", "protocol"]` | Only applicable when `+listType=map`. A list of field names whose values uniquely identify entries in the list. While there can be multiple keys, `listMapKey` is singular because keys need to be specified individually in the Go type. The key fields must be scalars. |
| `//+mapType` | `x-kubernetes-map-type` | `atomic`/`granular` | Applicable to maps. `atomic` means that the map can only be entirely replaced by a single manager. `granular` means that the map supports separate managers updating individual fields. | 1.17 | | `//+mapType` | `x-kubernetes-map-type` | `atomic`/`granular` | Applicable to maps. `atomic` means that the map can only be entirely replaced by a single manager. `granular` means that the map supports separate managers updating individual fields. |
| `//+structType` | `x-kubernetes-map-type` | `atomic`/`granular` | Applicable to structs; otherwise same usage and OpenAPI annotation as `//+mapType`.| 1.17 | | `//+structType` | `x-kubernetes-map-type` | `atomic`/`granular` | Applicable to structs; otherwise same usage and OpenAPI annotation as `//+mapType`.|
If `listType` is missing, the API server interprets a If `listType` is missing, the API server interprets a
`patchMergeStrategy=merge` marker as a `listType=map` and the `patchMergeStrategy=merge` marker as a `listType=map` and the
@ -241,17 +316,29 @@ corresponding `patchMergeKey` marker as a `listMapKey`.
The `atomic` list type is recursive. The `atomic` list type is recursive.
These markers are specified as comments and don't have to be repeated as (In the [Go](https://go.dev/) code for Kubernetes, these markers are specified as
field tags. comments and code authors need not repeat them as field tags).
## Custom resources and Server-Side Apply
By default, Server-Side Apply treats custom resources as unstructured data. All
keys are treated the same as struct fields, and all lists are considered atomic.
If the CustomResourceDefinition defines a
[schema](/docs/reference/generated/kubernetes-api/{{< param "version" >}}#jsonschemaprops-v1-apiextensions-k8s-io)
that contains annotations as defined in the previous [Merge Strategy](#merge-strategy)
section, these annotations will be used when merging objects of this
type.
### Compatibility across topology changes ### Compatibility across topology changes
On rare occurrences, a CRD or built-in type author may want to change the On rare occurrences, the author for a CustomResourceDefinition (CRD) or built-in
specific topology of a field in their resource without incrementing its may want to change the specific topology of a field in their resource,
version. Changing the topology of types, by upgrading the cluster or without incrementing its API version. Changing the topology of types,
updating the CRD, has different consequences when updating existing by upgrading the cluster or updating the CRD, has different consequences when
objects. There are two categories of changes: when a field goes from updating existing objects. There are two categories of changes: when a field goes from
`map`/`set`/`granular` to `atomic` and the other way around. `map`/`set`/`granular` to `atomic`, and the other way around.
When the `listType`, `mapType`, or `structType` changes from When the `listType`, `mapType`, or `structType` changes from
`map`/`set`/`granular` to `atomic`, the whole list, map, or struct of `map`/`set`/`granular` to `atomic`, the whole list, map, or struct of
@ -259,8 +346,8 @@ existing objects will end-up being owned by actors who owned an element
of these types. This means that any further change to these objects of these types. This means that any further change to these objects
would cause a conflict. would cause a conflict.
When a list, map, or struct changes from `atomic` to When a `listType`, `mapType`, or `structType` changes from `atomic` to
`map`/`set`/`granular`, the API server won't be able to infer the new `map`/`set`/`granular`, the API server is unable to infer the new
ownership of these fields. Because of that, no conflict will be produced ownership of these fields. Because of that, no conflict will be produced
when objects have these fields updated. For that reason, it is not when objects have these fields updated. For that reason, it is not
recommended to change a type from `atomic` to `map`/`set`/`granular`. recommended to change a type from `atomic` to `map`/`set`/`granular`.
@ -268,12 +355,13 @@ recommended to change a type from `atomic` to `map`/`set`/`granular`.
Take for example, the custom resource: Take for example, the custom resource:
```yaml ```yaml
---
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Foo kind: Foo
metadata: metadata:
name: foo-sample name: foo-sample
managedFields: managedFields:
- manager: manager-one - manager: "manager-one"
operation: Apply operation: Apply
apiVersion: example.com/v1 apiVersion: example.com/v1
fields: fields:
@ -293,33 +381,22 @@ Before `spec.data` gets changed from `atomic` to `granular`,
without a conflict), but it no longer owns `key1` and `key2`, so another without a conflict), but it no longer owns `key1` and `key2`, so another
manager can then modify or delete those fields without conflict. manager can then modify or delete those fields without conflict.
## Custom Resources
By default, Server-Side Apply treats custom resources as unstructured data. All
keys are treated the same as struct fields, and all lists are considered atomic.
If the Custom Resource Definition defines a
[schema](/docs/reference/generated/kubernetes-api/{{< param "version" >}}#jsonschemaprops-v1-apiextensions-k8s-io)
that contains annotations as defined in the previous "Merge Strategy"
section, these annotations will be used when merging objects of this
type.
## Using Server-Side Apply in a controller ## Using Server-Side Apply in a controller
As a developer of a controller, you can use server-side apply as a way to As a developer of a controller, you can use Server-Side Apply as a way to
simplify the update logic of your controller. The main differences with a simplify the update logic of your controller. The main differences with a
read-modify-write and/or patch are the following: read-modify-write and/or patch are the following:
* the applied object must contain all the fields that the controller cares about. * the applied object must contain all the fields that the controller cares about.
* there is no way to remove fields that haven't been applied by the controller * there is no way to remove fields that haven't been applied by the controller
before (controller can still send a PATCH/UPDATE for these use-cases). before (controller can still send a **patch** or **update** for these use-cases).
* the object doesn't have to be read beforehand, `resourceVersion` doesn't have * the object doesn't have to be read beforehand; `resourceVersion` doesn't have
to be specified. to be specified.
It is strongly recommended for controllers to always "force" conflicts, since they It is strongly recommended for controllers to always force conflicts on objects that
might not be able to resolve or act on these conflicts. the own and manage, since they might not be able to resolve or act on these conflicts.
## Transferring Ownership ## Transferring ownership
In addition to the concurrency controls provided by [conflict resolution](#conflicts), In addition to the concurrency controls provided by [conflict resolution](#conflicts),
Server-Side Apply provides ways to perform coordinated Server-Side Apply provides ways to perform coordinated
@ -330,41 +407,43 @@ ownership of the `replicas` field from a user to a controller while enabling
automatic horizontal scaling for a Deployment, using the HorizontalPodAutoscaler automatic horizontal scaling for a Deployment, using the HorizontalPodAutoscaler
resource and its accompanying controller. resource and its accompanying controller.
Say a user has defined deployment with `replicas` set to the desired value: Say a user has defined Deployment with `replicas` set to the desired value:
{{% code file="application/ssa/nginx-deployment.yaml" %}} {{% code file="application/ssa/nginx-deployment.yaml" %}}
And the user has created the deployment using Server-Side Apply like so: And the user has created the Deployment using Server-Side Apply, like so:
```shell ```shell
kubectl apply -f https://k8s.io/examples/application/ssa/nginx-deployment.yaml --server-side kubectl apply -f https://k8s.io/examples/application/ssa/nginx-deployment.yaml --server-side
``` ```
Then later, HPA is enabled for the deployment, e.g.: Then later, automatic scaling is enabled for the Deployment; for example:
```shell ```shell
kubectl autoscale deployment nginx-deployment --cpu-percent=50 --min=1 --max=10 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 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 don't accidentally fight with the HorizontalPodAutoscaler (HPA) and its controller.
might take some time before HPA feels the need to adjust `replicas`, and if However, there is a race: it might take some time before the HPA feels the need
the user removes `replicas` before the HPA writes to the field and becomes to adjust `.spec.replicas`; if the user removes `.spec.replicas` before the HPA writes
its owner, then apiserver will set `replicas` to 1, its default value. This to the field and becomes its owner, then the API server would set `.spec.replicas` to
is not what the user wants to happen, even temporarily. 1 (the default replica count for Deployment).
This is not what the user wants to happen, even temporarily - it might well degrade
a running workload.
There are two solutions: There are two solutions:
- (basic) Leave `replicas` in the configuration; when HPA eventually writes to that - (basic) Leave `replicas` in the configuration; when the HPA eventually writes to that
field, the system gives the user a conflict over it. At that point, it is safe field, the system gives the user a conflict over it. At that point, it is safe
to remove from the configuration. to remove from the configuration.
- (more advanced) If, however, the user doesn't want to wait, for example - (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 because they want to keep the cluster legible to their colleagues, then they
the following steps to make it safe to remove `replicas` from their can take the following steps to make it safe to remove `replicas` from their
configuration: configuration:
First, the user defines a new configuration containing only the `replicas` field: First, the user defines a new manifest containing only the `replicas` field:
```yaml ```yaml
# Save this file as 'nginx-deployment-replicas-only.yaml'. # Save this file as 'nginx-deployment-replicas-only.yaml'.
@ -382,7 +461,8 @@ You are not supposed to provide a fully compliant Deployment manifest if you onl
want to modify the `spec.replicas` field using SSA. want to modify the `spec.replicas` field using SSA.
{{< /note >}} {{< /note >}}
The user applies that configuration using the field manager name `handover-to-hpa`: The user applies that manifest using a private field manager name. In this example,
the user picked `handover-to-hpa`:
```shell ```shell
kubectl apply -f nginx-deployment-replicas-only.yaml \ kubectl apply -f nginx-deployment-replicas-only.yaml \
@ -394,23 +474,34 @@ If the apply results in a conflict with the HPA controller, then do nothing. The
conflict indicates the controller has claimed the field earlier in the conflict indicates the controller has claimed the field earlier in the
process than it sometimes does. process than it sometimes does.
At this point the user may remove the `replicas` field from their configuration. At this point the user may remove the `replicas` field from their manifest:
{{% code file="application/ssa/nginx-deployment-no-replicas.yaml" %}} {{% code file="application/ssa/nginx-deployment-no-replicas.yaml" %}}
Note that whenever the HPA controller sets the `replicas` field to a new value, 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 the temporary field manager will no longer own any fields and will be
automatically deleted. No clean up is required. automatically deleted. No further clean up is required.
### Transferring Ownership Between Users ### Transferring ownership between managers
Users can transfer ownership of a field between each other by setting the field Field managers 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 to the same value in both of their applied configurations, causing them to share
ownership of the field. Once the users share ownership of the field, one of them ownership of the field. Once the managers share ownership of the field, one of them
can remove the field from their applied configuration to give up ownership and can remove the field from their applied configuration to give up ownership and
complete the transfer to the other user. complete the transfer to the other field manager.
## Comparison with Client Side Apply ## Comparison with Client-Side Apply
Server-Side Apply is meant both as a replacement for the original client-side
implementation of the `kubectl apply` subcommand, and as simple and effective
mechanism for {{< glossary_tooltip term_id="controller" text="controllers" >}}
to enact their changes.
Compared to the `last-applied` annotation managed by `kubectl`, Server-Side
Apply uses a more declarative approach, which tracks an object's field management,
rather than a user's last applied state. This means that as a side effect of
using Server-Side Apply, information about which field manager manages each
field in an object also becomes available.
A consequence of the conflict detection and resolution implemented by Server-Side 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 Apply is that an applier always has up to date field values in their local
@ -418,17 +509,19 @@ 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 configuration being an three options to resolve conflicts results in the applied configuration being an
up to date subset of the object on the server's fields. 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 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 overwritten by other users are left in an applier's local config. These values
only become accurate when the user updates that specific field, if ever, and an only become accurate when the user updates that specific field, if ever, and an
applier has no way of knowing whether their next apply will overwrite other applier has no way of knowing whether their next apply will overwrite other
users' changes. users' changes.
Another difference is that an applier using Client Side Apply is unable to Another difference is that an applier using Client-Side Apply is unable to
change the API version they are using, but Server-Side Apply supports this use change the API version they are using, but Server-Side Apply supports this use
case. case.
## Upgrading from client-side apply to server-side apply ## Migration between client-side and server-side apply
### Upgrading from client-side apply to server-side apply
Client-side apply users who manage a resource with `kubectl apply` can start Client-side apply users who manage a resource with `kubectl apply` can start
using server-side apply with the following flag. using server-side apply with the following flag.
@ -438,11 +531,11 @@ kubectl apply --server-side [--dry-run=server]
``` ```
By default, field management of the object transfers from client-side apply to By default, field management of the object transfers from client-side apply to
kubectl server-side apply without encountering conflicts. kubectl server-side apply, without encountering conflicts.
{{< caution >}} {{< caution >}}
Keep the `last-applied-configuration` annotation up to date. Keep the `last-applied-configuration` annotation up to date.
The annotation infers client-side apply's managed fields. The annotation infers client-side applies managed fields.
Any fields not managed by client-side apply raise conflicts. Any fields not managed by client-side apply raise conflicts.
For example, if you used `kubectl scale` to update the replicas field after For example, if you used `kubectl scale` to update the replicas field after
@ -459,16 +552,16 @@ manager for kubectl server-side apply is `kubectl`.
kubectl apply --server-side --field-manager=my-manager [--dry-run=server] kubectl apply --server-side --field-manager=my-manager [--dry-run=server]
``` ```
## Downgrading from server-side apply to client-side apply ### Downgrading from server-side apply to client-side apply
If you manage a resource with `kubectl apply --server-side`, If you manage a resource with `kubectl apply --server-side`,
you can downgrade to client-side apply directly with `kubectl apply`. you can downgrade to client-side apply directly with `kubectl apply`.
Downgrading works because kubectl server-side apply keeps the Downgrading works because kubectl Server-Side Apply keeps the
`last-applied-configuration` annotation up-to-date if you use `last-applied-configuration` annotation up-to-date if you use
`kubectl apply`. `kubectl apply`.
This behavior applies to server-side apply with the `kubectl` field manager. This behavior applies to Server-Side Apply with the `kubectl` field manager.
As an exception, you can opt-out of this behavior by specifying a different, As an exception, you can opt-out of this behavior by specifying a different,
non-default field manager, as seen in the following example. The default field non-default field manager, as seen in the following example. The default field
manager for kubectl server-side apply is `kubectl`. manager for kubectl server-side apply is `kubectl`.
@ -477,48 +570,75 @@ manager for kubectl server-side apply is `kubectl`.
kubectl apply --server-side --field-manager=my-manager [--dry-run=server] kubectl apply --server-side --field-manager=my-manager [--dry-run=server]
``` ```
## API Endpoint ## API implementation
With the Server-Side Apply feature enabled, the `PATCH` endpoint accepts the The `PATCH` verb for a resource that supports Server-Side Apply can accepts the
additional `application/apply-patch+yaml` content type. Users of Server-Side unofficial `application/apply-patch+yaml` content type. Users of Server-Side
Apply can send partially specified objects as YAML to this endpoint. When Apply can send partially specified objects as YAML as the body of a `PATCH` request
applying a configuration, one should always include all the fields that they to the URI of a resource. When applying a configuration, you should always include all the
have an opinion about. fields that are important to the outcome (such as a desired state) that you want to define.
### RBAC and permissions All JSON messages are valid YAML. Some clients specify Server-Side Apply requests using YAML
request bodies that are also valid JSON.
Since Server-Side Apply is a type of `PATCH`, a role will require the ### Access control and permissions {#rbac-and-permissions}
`PATCH` permission to edit resources, but will also need the `CREATE`
verb permission in order to create resources with Server-Side Apply.
## Clearing ManagedFields Since Server-Side Apply is a type of `PATCH`, a principal (such as a Role for Kubernetes
{{< glossary_tooltip text="RBAC" term_id="rbac" >}}) requires the **patch** permission to
edit existing resources, and also needs the **create** verb permission in order to create
new resources with Server-Side Apply.
It is possible to strip all managedFields from an object by overwriting them ## Clearing `managedFields`
using `MergePatch`, `StrategicMergePatch`, `JSONPatch`, or `Update`, so every
non-apply operation. This can be done by overwriting the managedFields field It is possible to strip all `managedFields` from an object by overwriting them
using a **patch** (JSON Merge Patch, Strategic Merge Patch, JSON Patch), or
through an **update** (HTTP `PUT`); in other words, through every write operation
other than **apply**. This can be done by overwriting the `managedFields` field
with an empty entry. Two examples are: with an empty entry. Two examples are:
```console ```console
PATCH /api/v1/namespaces/default/configmaps/example-cm PATCH /api/v1/namespaces/default/configmaps/example-cm
Content-Type: application/merge-patch+json
Accept: application/json Accept: application/json
Data: {"metadata":{"managedFields": [{}]}} Content-Type: application/merge-patch+json
{
"metadata": {
"managedFields": [
{}
]
}
}
``` ```
```console ```console
PATCH /api/v1/namespaces/default/configmaps/example-cm PATCH /api/v1/namespaces/default/configmaps/example-cm
Content-Type: application/json-patch+json
Accept: application/json Accept: application/json
Data: [{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}] Content-Type: application/json-patch+json
If-Match: 1234567890123456789
[{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]
``` ```
This will overwrite the managedFields with a list containing a single empty This will overwrite the `managedFields` with a list containing a single empty
entry that then results in the managedFields being stripped entirely from the entry that then results in the `managedFields` being stripped entirely from the
object. Note that setting the managedFields to an empty list will not object. Note that setting the `managedFields` to an empty list will not
reset the field. This is on purpose, so managedFields never get stripped by reset the field. This is on purpose, so `managedFields` never get stripped by
clients not aware of the field. clients not aware of the field.
In cases where the reset operation is combined with changes to other fields In cases where the reset operation is combined with changes to other fields
than the managedFields, this will result in the managedFields being reset than the `managedFields`, this will result in the `managedFields` being reset
first and the other changes being processed afterwards. As a result the first and the other changes being processed afterwards. As a result the
applier takes ownership of any fields updated in the same request. applier takes ownership of any fields updated in the same request.
{{< note >}}
Server-Side Apply does not correctly track ownership on
sub-resources that don't receive the resource object type. If you are
using Server-Side Apply with such a sub-resource, the changed fields
may not be tracked.
{{< /note >}}
## {{% heading "whatsnext" %}}
You can read about `managedFields` within the Kubernetes API reference for the
[`metadata`](/docs/reference/kubernetes-api/common-definitions/object-meta/)
top level field.