Declarative Management of Kubernetes Objects Using Configuration Files

Kubernetes objects can be created, updated, and deleted by storing multiple object configuration files in a directory and using kubectl apply to recursively create and update those objects as needed. This method retains writes made to live objects without merging the changes back into the object configuration files. kubectl diff also gives you a preview of what changes apply will make.

Before you begin

Install kubectl.

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube, or you can use one of these Kubernetes playgrounds:

To check the version, enter kubectl version.

Trade-offs

The kubectl tool supports three kinds of object management:

  • Imperative commands
  • Imperative object configuration
  • Declarative object configuration

See Kubernetes Object Management for a discussion of the advantages and disadvantage of each kind of object management.

Overview

Declarative object configuration requires a firm understanding of the Kubernetes object definitions and configuration. Read and complete the following documents if you have not already:

Following are definitions for terms used in this document:

  • object configuration file / configuration file: A file that defines the configuration for a Kubernetes object. This topic shows how to pass configuration files to kubectl apply. Configuration files are typically stored in source control, such as Git.
  • live object configuration / live configuration: The live configuration values of an object, as observed by the Kubernetes cluster. These are kept in the Kubernetes cluster storage, typically etcd.
  • declarative configuration writer / declarative writer: A person or software component that makes updates to a live object. The live writers referred to in this topic make changes to object configuration files and run kubectl apply to write the changes.

How to create objects

Use kubectl apply to create all objects, except those that already exist, defined by configuration files in a specified directory:

  1. kubectl apply -f <directory>/

This sets the kubectl.kubernetes.io/last-applied-configuration: '{...}' annotation on each object. The annotation contains the contents of the object configuration file that was used to create the object.

Note: Add the -R flag to recursively process directories.

Here’s an example of an object configuration file:

application/simple_deployment.yaml Declarative Management of Kubernetes Objects Using Configuration Files (EN) - 图1
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. minReadySeconds: 5
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14.2
  18. ports:
  19. - containerPort: 80

Run kubectl diff to print the object that will be created:

  1. kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml

Note:

diff uses server-side dry-run, which needs to be enabled on kube-apiserver.

Since diff performs a server-side apply request in dry-run mode, it requires granting PATCH, CREATE, and UPDATE permissions. See Dry-Run Authorization for details.

Create the object using kubectl apply:

  1. kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

Print the live configuration using kubectl get:

  1. kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

The output shows that the kubectl.kubernetes.io/last-applied-configuration annotation was written to the live configuration, and it matches the configuration file:

  1. kind: Deployment
  2. metadata:
  3. annotations:
  4. # ...
  5. # This is the json representation of simple_deployment.yaml
  6. # It was written by kubectl apply when the object was created
  7. kubectl.kubernetes.io/last-applied-configuration: |
  8. {"apiVersion":"apps/v1","kind":"Deployment",
  9. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  10. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  11. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  12. "ports":[{"containerPort":80}]}]}}}}
  13. # ...
  14. spec:
  15. # ...
  16. minReadySeconds: 5
  17. selector:
  18. matchLabels:
  19. # ...
  20. app: nginx
  21. template:
  22. metadata:
  23. # ...
  24. labels:
  25. app: nginx
  26. spec:
  27. containers:
  28. - image: nginx:1.14.2
  29. # ...
  30. name: nginx
  31. ports:
  32. - containerPort: 80
  33. # ...
  34. # ...
  35. # ...
  36. # ...

How to update objects

You can also use kubectl apply to update all objects defined in a directory, even if those objects already exist. This approach accomplishes the following:

  1. Sets fields that appear in the configuration file in the live configuration.
  2. Clears fields removed from the configuration file in the live configuration.

    1. kubectl diff -f <directory>/
    2. kubectl apply -f <directory>/

Note: Add the -R flag to recursively process directories.

Here’s an example configuration file:

application/simple_deployment.yaml Declarative Management of Kubernetes Objects Using Configuration Files (EN) - 图2
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. minReadySeconds: 5
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14.2
  18. ports:
  19. - containerPort: 80

Create the object using kubectl apply:

  1. kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

Note: For purposes of illustration, the preceding command refers to a single configuration file instead of a directory.

Print the live configuration using kubectl get:

  1. kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

The output shows that the kubectl.kubernetes.io/last-applied-configuration annotation was written to the live configuration, and it matches the configuration file:

  1. kind: Deployment
  2. metadata:
  3. annotations:
  4. # ...
  5. # This is the json representation of simple_deployment.yaml
  6. # It was written by kubectl apply when the object was created
  7. kubectl.kubernetes.io/last-applied-configuration: |
  8. {"apiVersion":"apps/v1","kind":"Deployment",
  9. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  10. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  11. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  12. "ports":[{"containerPort":80}]}]}}}}
  13. # ...
  14. spec:
  15. # ...
  16. minReadySeconds: 5
  17. selector:
  18. matchLabels:
  19. # ...
  20. app: nginx
  21. template:
  22. metadata:
  23. # ...
  24. labels:
  25. app: nginx
  26. spec:
  27. containers:
  28. - image: nginx:1.14.2
  29. # ...
  30. name: nginx
  31. ports:
  32. - containerPort: 80
  33. # ...
  34. # ...
  35. # ...
  36. # ...

Directly update the replicas field in the live configuration by using kubectl scale. This does not use kubectl apply:

  1. kubectl scale deployment/nginx-deployment --replicas=2

Print the live configuration using kubectl get:

  1. kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

The output shows that the replicas field has been set to 2, and the last-applied-configuration annotation does not contain a replicas field:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. # note that the annotation does not contain replicas
  7. # because it was not updated through apply
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"apps/v1","kind":"Deployment",
  10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  11. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  12. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  13. "ports":[{"containerPort":80}]}]}}}}
  14. # ...
  15. spec:
  16. replicas: 2 # written by scale
  17. # ...
  18. minReadySeconds: 5
  19. selector:
  20. matchLabels:
  21. # ...
  22. app: nginx
  23. template:
  24. metadata:
  25. # ...
  26. labels:
  27. app: nginx
  28. spec:
  29. containers:
  30. - image: nginx:1.14.2
  31. # ...
  32. name: nginx
  33. ports:
  34. - containerPort: 80
  35. # ...

Update the simple_deployment.yaml configuration file to change the image from nginx:1.14.2 to nginx:1.16.1, and delete the minReadySeconds field:

application/update_deployment.yaml Declarative Management of Kubernetes Objects Using Configuration Files (EN) - 图3
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. template:
  10. metadata:
  11. labels:
  12. app: nginx
  13. spec:
  14. containers:
  15. - name: nginx
  16. image: nginx:1.16.1 # update the image
  17. ports:
  18. - containerPort: 80

Apply the changes made to the configuration file:

  1. kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml
  2. kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml

Print the live configuration using kubectl get:

  1. kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

The output shows the following changes to the live configuration:

  • The replicas field retains the value of 2 set by kubectl scale. This is possible because it is omitted from the configuration file.
  • The image field has been updated to nginx:1.16.1 from nginx:1.14.2.
  • The last-applied-configuration annotation has been updated with the new image.
  • The minReadySeconds field has been cleared.
  • The last-applied-configuration annotation no longer contains the minReadySeconds field.

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. annotations:
    5. # ...
    6. # The annotation contains the updated image to nginx 1.11.9,
    7. # but does not contain the updated replicas to 2
    8. kubectl.kubernetes.io/last-applied-configuration: |
    9. {"apiVersion":"apps/v1","kind":"Deployment",
    10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
    11. "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
    12. "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
    13. "ports":[{"containerPort":80}]}]}}}}
    14. # ...
    15. spec:
    16. replicas: 2 # Set by `kubectl scale`. Ignored by `kubectl apply`.
    17. # minReadySeconds cleared by `kubectl apply`
    18. # ...
    19. selector:
    20. matchLabels:
    21. # ...
    22. app: nginx
    23. template:
    24. metadata:
    25. # ...
    26. labels:
    27. app: nginx
    28. spec:
    29. containers:
    30. - image: nginx:1.16.1 # Set by `kubectl apply`
    31. # ...
    32. name: nginx
    33. ports:
    34. - containerPort: 80
    35. # ...
    36. # ...
    37. # ...
    38. # ...

Warning: Mixing kubectl apply with the imperative object configuration commands create and replace is not supported. This is because create and replace do not retain the kubectl.kubernetes.io/last-applied-configuration that kubectl apply uses to compute updates.

How to delete objects

There are two approaches to delete objects managed by kubectl apply.

Recommended: kubectl delete -f

Manually deleting objects using the imperative command is the recommended approach, as it is more explicit about what is being deleted, and less likely to result in the user deleting something unintentionally:

  1. kubectl delete -f <filename>

Alternative: kubectl apply -f —prune -l your=label

Only use this if you know what you are doing.

Warning: kubectl apply --prune is in alpha, and backwards incompatible changes might be introduced in subsequent releases.

Warning: You must be careful when using this command, so that you do not delete objects unintentionally.

As an alternative to kubectl delete, you can use kubectl apply to identify objects to be deleted after their configuration files have been removed from the directory. Apply with --prune queries the API server for all objects matching a set of labels, and attempts to match the returned live object configurations against the object configuration files. If an object matches the query, and it does not have a configuration file in the directory, and it has a last-applied-configuration annotation, it is deleted.

  1. kubectl apply -f <directory/> --prune -l <labels>

Warning: Apply with prune should only be run against the root directory containing the object configuration files. Running against sub-directories can cause objects to be unintentionally deleted if they are returned by the label selector query specified with -l <labels> and do not appear in the subdirectory.

How to view an object

You can use kubectl get with -o yaml to view the configuration of a live object:

  1. kubectl get -f <filename|url> -o yaml

How apply calculates differences and merges changes

Caution: A patch is an update operation that is scoped to specific fields of an object instead of the entire object. This enables updating only a specific set of fields on an object without reading the object first.

When kubectl apply updates the live configuration for an object, it does so by sending a patch request to the API server. The patch defines updates scoped to specific fields of the live object configuration. The kubectl apply command calculates this patch request using the configuration file, the live configuration, and the last-applied-configuration annotation stored in the live configuration.

Merge patch calculation

The kubectl apply command writes the contents of the configuration file to the kubectl.kubernetes.io/last-applied-configuration annotation. This is used to identify fields that have been removed from the configuration file and need to be cleared from the live configuration. Here are the steps used to calculate which fields should be deleted or set:

  1. Calculate the fields to delete. These are the fields present in last-applied-configuration and missing from the configuration file.
  2. Calculate the fields to add or set. These are the fields present in the configuration file whose values don’t match the live configuration.

Here’s an example. Suppose this is the configuration file for a Deployment object:

application/update_deployment.yaml Declarative Management of Kubernetes Objects Using Configuration Files (EN) - 图4
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. template:
  10. metadata:
  11. labels:
  12. app: nginx
  13. spec:
  14. containers:
  15. - name: nginx
  16. image: nginx:1.16.1 # update the image
  17. ports:
  18. - containerPort: 80

Also, suppose this is the live configuration for the same Deployment object:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. # note that the annotation does not contain replicas
  7. # because it was not updated through apply
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"apps/v1","kind":"Deployment",
  10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  11. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  12. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  13. "ports":[{"containerPort":80}]}]}}}}
  14. # ...
  15. spec:
  16. replicas: 2 # written by scale
  17. # ...
  18. minReadySeconds: 5
  19. selector:
  20. matchLabels:
  21. # ...
  22. app: nginx
  23. template:
  24. metadata:
  25. # ...
  26. labels:
  27. app: nginx
  28. spec:
  29. containers:
  30. - image: nginx:1.14.2
  31. # ...
  32. name: nginx
  33. ports:
  34. - containerPort: 80
  35. # ...

Here are the merge calculations that would be performed by kubectl apply:

  1. Calculate the fields to delete by reading values from last-applied-configuration and comparing them to values in the configuration file. Clear fields explicitly set to null in the local object configuration file regardless of whether they appear in the last-applied-configuration. In this example, minReadySeconds appears in the last-applied-configuration annotation, but does not appear in the configuration file. Action: Clear minReadySeconds from the live configuration.
  2. Calculate the fields to set by reading values from the configuration file and comparing them to values in the live configuration. In this example, the value of image in the configuration file does not match the value in the live configuration. Action: Set the value of image in the live configuration.
  3. Set the last-applied-configuration annotation to match the value of the configuration file.
  4. Merge the results from 1, 2, 3 into a single patch request to the API server.

Here is the live configuration that is the result of the merge:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. # The annotation contains the updated image to nginx 1.11.9,
  7. # but does not contain the updated replicas to 2
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"apps/v1","kind":"Deployment",
  10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  11. "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  12. "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
  13. "ports":[{"containerPort":80}]}]}}}}
  14. # ...
  15. spec:
  16. selector:
  17. matchLabels:
  18. # ...
  19. app: nginx
  20. replicas: 2 # Set by `kubectl scale`. Ignored by `kubectl apply`.
  21. # minReadySeconds cleared by `kubectl apply`
  22. # ...
  23. template:
  24. metadata:
  25. # ...
  26. labels:
  27. app: nginx
  28. spec:
  29. containers:
  30. - image: nginx:1.16.1 # Set by `kubectl apply`
  31. # ...
  32. name: nginx
  33. ports:
  34. - containerPort: 80
  35. # ...
  36. # ...
  37. # ...
  38. # ...

How different types of fields are merged

How a particular field in a configuration file is merged with the live configuration depends on the type of the field. There are several types of fields:

  • primitive: A field of type string, integer, or boolean. For example, image and replicas are primitive fields. Action: Replace.

  • map, also called object: A field of type map or a complex type that contains subfields. For example, labels, annotations,spec and metadata are all maps. Action: Merge elements or subfields.

  • list: A field containing a list of items that can be either primitive types or maps. For example, containers, ports, and args are lists. Action: Varies.

When kubectl apply updates a map or list field, it typically does not replace the entire field, but instead updates the individual subelements. For instance, when merging the spec on a Deployment, the entire spec is not replaced. Instead the subfields of spec, such as replicas, are compared and merged.

Merging changes to primitive fields

Primitive fields are replaced or cleared.

Note: - is used for “not applicable” because the value is not used.

Field in object configuration fileField in live object configurationField in last-applied-configurationAction
YesYes-Set live to configuration file value.
YesNo-Set live to local configuration.
No-YesClear from live configuration.
No-NoDo nothing. Keep live value.

Merging changes to map fields

Fields that represent maps are merged by comparing each of the subfields or elements of the map:

Note: - is used for “not applicable” because the value is not used.

Key in object configuration fileKey in live object configurationField in last-applied-configurationAction
YesYes-Compare sub fields values.
YesNo-Set live to local configuration.
No-YesDelete from live configuration.
No-NoDo nothing. Keep live value.

Merging changes for fields of type list

Merging changes to a list uses one of three strategies:

  • Replace the list if all its elements are primitives.
  • Merge individual elements in a list of complex elements.
  • Merge a list of primitive elements.

The choice of strategy is made on a per-field basis.

Replace the list if all its elements are primitives

Treat the list the same as a primitive field. Replace or delete the entire list. This preserves ordering.

Example: Use kubectl apply to update the args field of a Container in a Pod. This sets the value of args in the live configuration to the value in the configuration file. Any args elements that had previously been added to the live configuration are lost. The order of the args elements defined in the configuration file is retained in the live configuration.

  1. # last-applied-configuration value
  2. args: ["a", "b"]
  3. # configuration file value
  4. args: ["a", "c"]
  5. # live configuration
  6. args: ["a", "b", "d"]
  7. # result after merge
  8. args: ["a", "c"]

Explanation: The merge used the configuration file value as the new list value.

Merge individual elements of a list of complex elements:

Treat the list as a map, and treat a specific field of each element as a key. Add, delete, or update individual elements. This does not preserve ordering.

This merge strategy uses a special tag on each field called a patchMergeKey. The patchMergeKey is defined for each field in the Kubernetes source code: types.go When merging a list of maps, the field specified as the patchMergeKey for a given element is used like a map key for that element.

Example: Use kubectl apply to update the containers field of a PodSpec. This merges the list as though it was a map where each element is keyed by name.

  1. # last-applied-configuration value
  2. containers:
  3. - name: nginx
  4. image: nginx:1.16
  5. - name: nginx-helper-a # key: nginx-helper-a; will be deleted in result
  6. image: helper:1.3
  7. - name: nginx-helper-b # key: nginx-helper-b; will be retained
  8. image: helper:1.3
  9. # configuration file value
  10. containers:
  11. - name: nginx
  12. image: nginx:1.16
  13. - name: nginx-helper-b
  14. image: helper:1.3
  15. - name: nginx-helper-c # key: nginx-helper-c; will be added in result
  16. image: helper:1.3
  17. # live configuration
  18. containers:
  19. - name: nginx
  20. image: nginx:1.16
  21. - name: nginx-helper-a
  22. image: helper:1.3
  23. - name: nginx-helper-b
  24. image: helper:1.3
  25. args: ["run"] # Field will be retained
  26. - name: nginx-helper-d # key: nginx-helper-d; will be retained
  27. image: helper:1.3
  28. # result after merge
  29. containers:
  30. - name: nginx
  31. image: nginx:1.16
  32. # Element nginx-helper-a was deleted
  33. - name: nginx-helper-b
  34. image: helper:1.3
  35. args: ["run"] # Field was retained
  36. - name: nginx-helper-c # Element was added
  37. image: helper:1.3
  38. - name: nginx-helper-d # Element was ignored
  39. image: helper:1.3

Explanation:

  • The container named “nginx-helper-a” was deleted because no container named “nginx-helper-a” appeared in the configuration file.
  • The container named “nginx-helper-b” retained the changes to args in the live configuration. kubectl apply was able to identify that “nginx-helper-b” in the live configuration was the same “nginx-helper-b” as in the configuration file, even though their fields had different values (no args in the configuration file). This is because the patchMergeKey field value (name) was identical in both.
  • The container named “nginx-helper-c” was added because no container with that name appeared in the live configuration, but one with that name appeared in the configuration file.
  • The container named “nginx-helper-d” was retained because no element with that name appeared in the last-applied-configuration.

Merge a list of primitive elements

As of Kubernetes 1.5, merging lists of primitive elements is not supported.

Note: Which of the above strategies is chosen for a given field is controlled by the patchStrategy tag in types.go If no patchStrategy is specified for a field of type list, then the list is replaced.

Default field values

The API server sets certain fields to default values in the live configuration if they are not specified when the object is created.

Here’s a configuration file for a Deployment. The file does not specify strategy:

application/simple_deployment.yaml Declarative Management of Kubernetes Objects Using Configuration Files (EN) - 图5
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. minReadySeconds: 5
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14.2
  18. ports:
  19. - containerPort: 80

Create the object using kubectl apply:

  1. kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

Print the live configuration using kubectl get:

  1. kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

The output shows that the API server set several fields to default values in the live configuration. These fields were not specified in the configuration file.

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. # ...
  4. spec:
  5. selector:
  6. matchLabels:
  7. app: nginx
  8. minReadySeconds: 5
  9. replicas: 1 # defaulted by apiserver
  10. strategy:
  11. rollingUpdate: # defaulted by apiserver - derived from strategy.type
  12. maxSurge: 1
  13. maxUnavailable: 1
  14. type: RollingUpdate # defaulted by apiserver
  15. template:
  16. metadata:
  17. creationTimestamp: null
  18. labels:
  19. app: nginx
  20. spec:
  21. containers:
  22. - image: nginx:1.14.2
  23. imagePullPolicy: IfNotPresent # defaulted by apiserver
  24. name: nginx
  25. ports:
  26. - containerPort: 80
  27. protocol: TCP # defaulted by apiserver
  28. resources: {} # defaulted by apiserver
  29. terminationMessagePath: /dev/termination-log # defaulted by apiserver
  30. dnsPolicy: ClusterFirst # defaulted by apiserver
  31. restartPolicy: Always # defaulted by apiserver
  32. securityContext: {} # defaulted by apiserver
  33. terminationGracePeriodSeconds: 30 # defaulted by apiserver
  34. # ...

In a patch request, defaulted fields are not re-defaulted unless they are explicitly cleared as part of a patch request. This can cause unexpected behavior for fields that are defaulted based on the values of other fields. When the other fields are later changed, the values defaulted from them will not be updated unless they are explicitly cleared.

For this reason, it is recommended that certain fields defaulted by the server are explicitly defined in the configuration file, even if the desired values match the server defaults. This makes it easier to recognize conflicting values that will not be re-defaulted by the server.

Example:

  1. # last-applied-configuration
  2. spec:
  3. template:
  4. metadata:
  5. labels:
  6. app: nginx
  7. spec:
  8. containers:
  9. - name: nginx
  10. image: nginx:1.14.2
  11. ports:
  12. - containerPort: 80
  13. # configuration file
  14. spec:
  15. strategy:
  16. type: Recreate # updated value
  17. template:
  18. metadata:
  19. labels:
  20. app: nginx
  21. spec:
  22. containers:
  23. - name: nginx
  24. image: nginx:1.14.2
  25. ports:
  26. - containerPort: 80
  27. # live configuration
  28. spec:
  29. strategy:
  30. type: RollingUpdate # defaulted value
  31. rollingUpdate: # defaulted value derived from type
  32. maxSurge : 1
  33. maxUnavailable: 1
  34. template:
  35. metadata:
  36. labels:
  37. app: nginx
  38. spec:
  39. containers:
  40. - name: nginx
  41. image: nginx:1.14.2
  42. ports:
  43. - containerPort: 80
  44. # result after merge - ERROR!
  45. spec:
  46. strategy:
  47. type: Recreate # updated value: incompatible with rollingUpdate
  48. rollingUpdate: # defaulted value: incompatible with "type: Recreate"
  49. maxSurge : 1
  50. maxUnavailable: 1
  51. template:
  52. metadata:
  53. labels:
  54. app: nginx
  55. spec:
  56. containers:
  57. - name: nginx
  58. image: nginx:1.14.2
  59. ports:
  60. - containerPort: 80

Explanation:

  1. The user creates a Deployment without defining strategy.type.
  2. The server defaults strategy.type to RollingUpdate and defaults the strategy.rollingUpdate values.
  3. The user changes strategy.type to Recreate. The strategy.rollingUpdate values remain at their defaulted values, though the server expects them to be cleared. If the strategy.rollingUpdate values had been defined initially in the configuration file, it would have been more clear that they needed to be deleted.
  4. Apply fails because strategy.rollingUpdate is not cleared. The strategy.rollingupdate field cannot be defined with a strategy.type of Recreate.

Recommendation: These fields should be explicitly defined in the object configuration file:

  • Selectors and PodTemplate labels on workloads, such as Deployment, StatefulSet, Job, DaemonSet, ReplicaSet, and ReplicationController
  • Deployment rollout strategy

How to clear server-defaulted fields or fields set by other writers

Fields that do not appear in the configuration file can be cleared by setting their values to null and then applying the configuration file. For fields defaulted by the server, this triggers re-defaulting the values.

How to change ownership of a field between the configuration file and direct imperative writers

These are the only methods you should use to change an individual object field:

  • Use kubectl apply.
  • Write directly to the live configuration without modifying the configuration file: for example, use kubectl scale.

Changing the owner from a direct imperative writer to a configuration file

Add the field to the configuration file. For the field, discontinue direct updates to the live configuration that do not go through kubectl apply.

Changing the owner from a configuration file to a direct imperative writer

As of Kubernetes 1.5, changing ownership of a field from a configuration file to an imperative writer requires manual steps:

  • Remove the field from the configuration file.
  • Remove the field from the kubectl.kubernetes.io/last-applied-configuration annotation on the live object.

Changing management methods

Kubernetes objects should be managed using only one method at a time. Switching from one method to another is possible, but is a manual process.

Note: It is OK to use imperative deletion with declarative management.

Migrating from imperative command management to declarative object configuration

Migrating from imperative command management to declarative object configuration involves several manual steps:

  1. Export the live object to a local configuration file:

    1. kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
  2. Manually remove the status field from the configuration file.

    Note: This step is optional, as kubectl apply does not update the status field even if it is present in the configuration file.

  3. Set the kubectl.kubernetes.io/last-applied-configuration annotation on the object:

    1. kubectl replace --save-config -f <kind>_<name>.yaml
  4. Change processes to use kubectl apply for managing the object exclusively.

Migrating from imperative object configuration to declarative object configuration

  1. Set the kubectl.kubernetes.io/last-applied-configuration annotation on the object:

    1. kubectl replace --save-config -f <kind>_<name>.yaml
  2. Change processes to use kubectl apply for managing the object exclusively.

Defining controller selectors and PodTemplate labels

Warning: Updating selectors on controllers is strongly discouraged.

The recommended approach is to define a single, immutable PodTemplate label used only by the controller selector with no other semantic meaning.

Example:

  1. selector:
  2. matchLabels:
  3. controller-selector: "apps/v1/deployment/nginx"
  4. template:
  5. metadata:
  6. labels:
  7. controller-selector: "apps/v1/deployment/nginx"

What’s next

Feedback

Was this page helpful?

Thanks for the feedback. If you have a specific, answerable question about how to use Kubernetes, ask it on Stack Overflow. Open an issue in the GitHub repo if you want to report a problem or suggest an improvement.