The gardener-controller-manager
(often refered to as "GCM") is a component that runs next to the Gardener API server, similar to the Kubernetes Controller Manager.
It runs several controllers that do not require talking to any seed or shoot cluster.
Also, as of today, it exposes an HTTP server that is serving several health check endpoints and metrics.
This document explains the various functionalities of the gardener-controller-manager
and their purpose.
Bastion
resources have a limited lifetime which can be extended up to a certain amount by performing a heartbeat on them.
The Bastion
controller is responsible for deleting expired or rotten Bastion
s.
- "expired" means a
Bastion
has exceeded itsstatus.expirationTimestamp
. - "rotten" means a
Bastion
is older than the configuredmaxLifetime
.
The maxLifetime
defaults to 24 hours and is an option in the BastionControllerConfiguration
which is part of gardener-controller-manager
s ControllerManagerControllerConfiguration
, see the example config file for details.
The controller also deletes Bastion
s in case the referenced Shoot
:
- no longer exists
- is marked for deletion (i.e., have a non-
nil
.metadata.deletionTimestamp
) - was migrated to another seed (i.e.,
Shoot.spec.seedName
is different thanBastion.spec.seedName
).
The deletion of Bastion
s triggers the gardenlet
to perform the necessary cleanups in the Seed cluster, so some time can pass between deletion and the Bastion
actually disappearing.
Clients like gardenctl
are advised to not re-use Bastion
s whose deletion timestamp has been set already.
Refer to GEP-15 for more information on the lifecycle of
Bastion
resources.
After the gardenlet gets deployed on the Seed cluster, it needs to establish itself as a trusted party to communicate with the Gardener API server. It runs through a bootstrap flow similar to the kubelet bootstrap process.
On startup, the gardenlet uses a kubeconfig
with a bootstrap token which authenticates it as being part of the system:bootstrappers
group. This kubeconfig is used to create a CertificateSigningRequest
(CSR) against the Gardener API server.
The controller in gardener-controller-manager
checks whether the CertificateSigningRequest
has the expected organisation, common name and usages which the gardenlet would request.
It only auto-approves the CSR if the client making the request is allowed to "create" the
certificatesigningrequests/seedclient
subresource. Clients with the system:bootstrappers
group are bound to the gardener.cloud:system:seed-bootstrapper
ClusterRole
, hence, they have such privileges. As the bootstrap kubeconfig for the gardenlet contains a bootstrap token which is authenticated as being part of the systems:bootstrappers
group, its created CSR gets auto-approved.
CloudProfile
s are essential when it comes to reconciling Shoot
s since they contain constraints (like valid machine types, Kubernetes versions, or machine images) and sometimes also some global configuration for the respective environment (typically via provider-specific configuration in .spec.providerConfig
).
Consequently, to ensure that CloudProfile
s in-use are always present in the system until the last referring Shoot
gets deleted, the controller adds a finalizer which is only released when there is no Shoot
referencing the CloudProfile
anymore.
Extensions are registered in the garden cluster via ControllerRegistration
and deployment of respective extensions are specified via ControllerDeployment
. For more info refer to Registering Extension Controllers.
This controller ensures that ControllerDeployment
in-use always exists until the last ControllerRegistration
referencing them gets deleted. The controller adds a finalizer which is only released when there is no ControllerRegistration
referencing the ControllerDeployment
anymore.
The ControllerRegistration
controller makes sure that the required Gardener Extensions specified by the ControllerRegistration
resources are present in the seed clusters.
It also takes care of the creation and deletion of ControllerInstallation
objects for a given seed cluster.
The controller has three reconciliation loops.
This reconciliation loop watches the Seed
objects and determines which ControllerRegistration
s are required for them and reconciles the corresponding ControllerInstallation
resources to reach the determined state.
To begin with, it computes the kind/type combinations of extensions required for the seed.
For this, the controller examines a live list of ControllerRegistration
s, ControllerInstallation
s, BackupBucket
s, BackupEntry
s, Shoot
s, and Secret
s from the garden cluster.
For example, it examines the shoots running on the seed and deducts the kind/type, like Infrastructure/gcp
.
The seed (seed.spec.provider.type
) and DNS (seed.spec.dns.provider.type
) provider types are considered when calculating the list of required ControllerRegistration
s, as well.
It also decides whether they should always be deployed based on the .spec.deployment.policy
.
For the configuration options, please see this section.
Based on these required combinations, each of them are mapped to ControllerRegistration
objects and then to their corresponding ControllerInstallation
objects (if existing).
The controller then creates or updates the required ControllerInstallation
objects for the given seed.
It also deletes every existing ControllerInstallation
whose referenced ControllerRegistration
is not part of the required list.
For example, if the shoots in the seed are no longer using the DNS provider aws-route53
, then the controller proceeds to delete the respective ControllerInstallation
object.
This reconciliation loop watches the ControllerRegistration
resource and adds finalizers to it when they are created.
In case a deletion request comes in for the resource, i.e., if a .metadata.deletionTimestamp
is set, it actively scans for a ControllerInstallation
resource using this ControllerRegistration
, and decides whether the deletion can be allowed.
In case no related ControllerInstallation
is present, it removes the finalizer and marks it for deletion.
This loop also watches the Seed
object and adds finalizers to it at creation.
If a .metadata.deletionTimestamp
is set for the seed, then the controller checks for existing ControllerInstallation
objects which reference this seed.
If no such objects exist, then it removes the finalizer and allows the deletion.
With the Gardener Event Controller, you can prolong the lifespan of events related to Shoot clusters. This is an optional controller which will become active once you provide the below mentioned configuration.
All events in K8s are deleted after a configurable time-to-live (controlled via a kube-apiserver argument called --event-ttl
(defaulting to 1 hour)).
The need to prolong the time-to-live for Shoot cluster events frequently arises when debugging customer issues on live systems.
This controller leaves events involving Shoots untouched, while deleting all other events after a configured time.
In order to activate it, provide the following configuration:
concurrentSyncs
: The amount of goroutines scheduled for reconciling events.ttlNonShootEvents
: When an event reaches this time-to-live it gets deleted unless it is a Shoot-related event (defaults to1h
, equivalent to theevent-ttl
default).
⚠️ In addition, you should also configure the--event-ttl
for the kube-apiserver to define an upper-limit of how long Shoot-related events should be stored. The--event-ttl
should be larger than thettlNonShootEvents
or this controller will have no effect.
ExposureClass
abstracts the ability to expose a Shoot clusters control plane in certain network environments (e.g. corporate networks, DMZ, internet) on all Seeds or a subset of the Seeds. For more information, see ExposureClasses.
Consequently, to ensure that ExposureClass
es in-use are always present in the system until the last referring Shoot
gets deleted, the controller adds a finalizer which is only released when there is no Shoot
referencing the ExposureClass
anymore.
ManagedSeedSet
objects maintain a stable set of replicas of ManagedSeed
s, i.e. they guarantee the availability of a specified number of identical ManagedSeed
s on an equal number of identical Shoot
s.
The ManagedSeedSet
controller creates and deletes ManagedSeed
s and Shoot
s in response to changes to the replicas and selector fields. For more information, refer to the ManagedSeedSet
proposal document.
- The reconciler first gets all the replicas of the given
ManagedSeedSet
in theManagedSeedSet
's namespace and with the matching selector. Each replica is a struct that contains aManagedSeed
, its correspondingSeed
andShoot
objects. - Then the pending replica is retrieved, if it exists.
- Next it determines the ready, postponed, and deletable replicas.
- A replica is considered
ready
when aSeed
owned by aManagedSeed
has been registered either directly or by deployinggardenlet
into aShoot
, theSeed
isReady
and theShoot
's status isHealthy
. - If a replica is not ready and it is not pending, i.e. it is not specified in the
ManagedSeed
'sstatus.pendingReplica
field, then it is added to thepostponed
replicas. - A replica is deletable if it has no scheduled
Shoot
s and the replica'sShoot
andManagedSeed
do not have theseedmanagement.gardener.cloud/protect-from-deletion
annotation.
- A replica is considered
- Finally, it checks the actual and target replica counts. If the actual count is less than the target count, the controller scales up the replicas by creating new replicas to match the desired target count. If the actual count is more than the target, the controller deletes replicas to match the desired count. Before scale-out or scale-in, the controller first reconciles the pending replica (there can always only be one) and makes sure the replica is ready before moving on to the next one.
Scale-out
(actual count < target count)- During the scale-out phase, the controller first creates the
Shoot
object from theManagedSeedSet
'sspec.shootTemplate
field and adds the replica to thestatus.pendingReplica
of theManagedSeedSet
. - For the subsequent reconciliation steps, the controller makes sure that the pending replica is ready before proceeding to the next replica. Once the
Shoot
is created successfully, theManagedSeed
object is created from theManagedSeedSet
'sspec.template
. TheManagedSeed
object is reconciled by theManagedSeed
controller and aSeed
object is created for the replica. Once the replica'sSeed
becomes ready and theShoot
becomes healthy, the replica also becomes ready.
- During the scale-out phase, the controller first creates the
Scale-in
(actual count > target count)- During the scale-in phase, the controller first determines the replica that can be deleted. From the deletable replicas, it chooses the one with the lowest priority and deletes it. Priority is determined in the following order:
- First, compare replica statuses. Replicas with "less advanced" status are considered lower priority. For example, a replica with
StatusShootReconciling
status has a lower value than a replica withStatusShootReconciled
status. Hence, in this case, a replica with aStatusShootReconciling
status will have lower priority and will be considered for deletion. - Then, the replicas are compared with the readiness of their
Seed
s. Replicas with non-readySeed
s are considered lower priority. - Then, the replicas are compared with the health statuses of their
Shoot
s. Replicas with "worse" statuses are considered lower priority. - Finally, the replica ordinals are compared. Replicas with lower ordinals are considered lower priority.
- First, compare replica statuses. Replicas with "less advanced" status are considered lower priority. For example, a replica with
- During the scale-in phase, the controller first determines the replica that can be deleted. From the deletable replicas, it chooses the one with the lowest priority and deletes it. Priority is determined in the following order:
Quota
object limits the resources consumed by shoot clusters either per provider secret or per project/namespace.
Consequently, to ensure that Quota
s in-use are always present in the system until the last SecretBinding
that references them gets deleted, the controller adds a finalizer which is only released when there is no SecretBinding
referencing the Quota
anymore.
There are multiple controllers responsible for different aspects of Project
objects.
Please also refer to the Project
documentation.
This reconciler manages a dedicated Namespace
for each Project
.
The namespace name can either be specified explicitly in .spec.namespace
(must be prefixed with garden-
) or it will be determined by the controller.
If .spec.namespace
is set, it tries to create it. If it already exists, it tries to adopt it.
This will only succeed if the Namespace
was previously labeled with gardener.cloud/role=project
and project.gardener.cloud/name=<project-name>
.
This is to prevent end-users from being able to adopt arbitrary namespaces and escalate their privileges, e.g. the kube-system
namespace.
After the namespace was created/adopted, the controller creates several ClusterRole
s and ClusterRoleBinding
s that allow the project members to access related resources based on their roles.
These RBAC resources are prefixed with gardener.cloud:system:project{-member,-viewer}:<project-name>
.
Gardener administrators and extension developers can define their own roles. For more information, see Extending Project Roles for more information.
In addition, operators can configure the Project controller to maintain a default ResourceQuota for project namespaces.
Quotas can especially limit the creation of user facing resources, e.g. Shoots
, SecretBindings
, Secrets
and thus protect the garden cluster from massive resource exhaustion but also enable operators to align quotas with respective enterprise policies.
⚠️ Gardener itself is not exempted from configured quotas. For example, Gardener createsSecrets
for every shoot cluster in the project namespace and at the same time increases the available quota count. Please mind this additional resource consumption.
The controller configuration provides a template section controllers.project.quotas
where such a ResourceQuota (see the example below) can be deposited.
controllers:
project:
quotas:
- config:
apiVersion: v1
kind: ResourceQuota
spec:
hard:
count/shoots.core.gardener.cloud: "100"
count/secretbindings.core.gardener.cloud: "10"
count/secrets: "800"
projectSelector: {}
The Project controller takes the specified config
and creates a ResourceQuota
with the name gardener
in the project namespace.
If a ResourceQuota
resource with the name gardener
already exists, the controller will only update fields in spec.hard
which are unavailable at that time.
This is done to configure a default Quota
in all projects but to allow manual quota increases as the projects' demands increase.
spec.hard
fields in the ResourceQuota
object that are not present in the configuration are removed from the object.
Labels and annotations on the ResourceQuota
config
get merged with the respective fields on existing ResourceQuota
s.
An optional projectSelector
narrows down the amount of projects that are equipped with the given config
.
If multiple configs match for a project, then only the first match in the list is applied to the project namespace.
The .status.phase
of the Project
resources is set to Ready
or Failed
by the reconciler to indicate whether the reconciliation loop was performed successfully.
Also, it generates Event
s to provide further information about its operations.
When a Project
is marked for deletion, the controller ensures that there are no Shoots
left in the project namespace.
Once all Shoots
are gone, the Namespace
and Project
are released.
As Gardener is a large-scale Kubernetes as a Service, it is designed for being used by a large amount of end-users.
Over time, it is likely to happen that some of the hundreds or thousands of Project
resources are no longer actively used.
Gardener offers the "stale projects" reconciler which will take care of identifying such stale projects, marking them with a "warning", and eventually deleting them after a certain time period. This reconciler is enabled by default and works as follows:
- Projects are considered as "stale"/not actively used when all of the following conditions apply: The namespace associated with the
Project
does not have any...Shoot
resources.BackupEntry
resources.Secret
resources that are referenced by aSecretBinding
that is in use by aShoot
(not necessarily in the same namespace).Quota
resources that are referenced by aSecretBinding
that is in use by aShoot
(not necessarily in the same namespace).- The time period when the project was used for the last time (
status.lastActivityTimestamp
) is longer than the configuredminimumLifetimeDays
If a project is considered "stale", then its .status.staleSinceTimestamp
will be set to the time when it was first detected to be stale.
If it gets actively used again, this timestamp will be removed.
After some time, the .status.staleAutoDeleteTimestamp
will be set to a timestamp after which Gardener will auto-delete the Project
resource if it still is not actively used.
The component configuration of the gardener-controller-manager
offers to configure the following options:
minimumLifetimeDays
: Don't consider newly createdProject
s as "stale" too early to give people/end-users some time to onboard and get familiar with the system. The "stale project" reconciler won't set any timestamp forProject
s younger thanminimumLifetimeDays
. When you change this value, then projects marked as "stale" may be no longer marked as "stale" in case they are young enough, or vice versa.staleGracePeriodDays
: Don't compute auto-delete timestamps for staleProject
s that are unused for less thanstaleGracePeriodDays
. This is to not unnecessarily make people/end-users nervous "just because" they haven't actively used theirProject
for a given amount of time. When you change this value, then already assigned auto-delete timestamps may be removed if the new grace period is not yet exceeded.staleExpirationTimeDays
: Expiration time after which staleProject
s are finally auto-deleted (after.status.staleSinceTimestamp
). If this value is changed and an auto-delete timestamp got already assigned to the projects, then the new value will only take effect if it's increased. Hence, decreasing thestaleExpirationTimeDays
will not decrease already assigned auto-delete timestamps.
Gardener administrators/operators can exclude specific
Project
s from the stale check by annotating the relatedNamespace
resource withproject.gardener.cloud/skip-stale-check=true
.
Since the other two reconcilers are unable to actively monitor the relevant objects that are used in a Project
(Shoot
, Secret
, etc.), there could be a situation where the user creates and deletes objects in a short period of time. In that case, the Stale Project Reconciler
could not see that there was any activity on that project and it will still mark it as a Stale
, even though it is actively used.
The Project Activity Reconciler
is implemented to take care of such cases. An event handler will notify the reconciler for any acitivity and then it will update the status.lastActivityTimestamp
. This update will also trigger the Stale Project Reconciler
.
SecretBinding
s reference Secret
s and Quota
s and are themselves referenced by Shoot
s.
The controller adds finalizers to the referenced objects to ensure they don't get deleted while still being referenced.
Similarly, to ensure that SecretBinding
s in-use are always present in the system until the last referring Shoot
gets deleted, the controller adds a finalizer which is only released when there is no Shoot
referencing the SecretBinding
anymore.
Referenced Secret
s will also be labeled with provider.shoot.gardener.cloud/<type>=true
, where <type>
is the value of the .provider.type
of the SecretBinding
.
Also, all referenced Secret
s, as well as Quota
s, will be labeled with reference.gardener.cloud/secretbinding=true
to allow for easily filtering for objects referenced by SecretBinding
s.
The Seed controller in the gardener-controller-manager
reconciles Seed
objects with the help of the following reconcilers.
This reconciliation loop takes care of seed related operations in the garden cluster. When a new Seed
object is created,
the reconciler creates a new Namespace
in the garden cluster seed-<seed-name>
. Namespaces
dedicated to single
seed clusters allow us to segregate access permissions i.e., a gardenlet
must not have permissions to access objects in
all Namespaces
in the garden cluster.
There are objects in a Garden environment which are created once by the operator e.g., default domain secret,
alerting credentials, and are required for operations happening in the gardenlet
. Therefore, we not only need a seed specific
Namespace
but also a copy of these "shared" objects.
The "main" reconciler takes care about this replication:
Kind | Namespace | Label Selector |
---|---|---|
Secret | garden | gardener.cloud/role |
Every time a BackupBucket
object is created or updated, the referenced Seed
object is enqueued for reconciliation.
It's the reconciler's task to check the status
subresource of all existing BackupBucket
s that reference this Seed
.
If at least one BackupBucket
has .status.lastError != nil
, the BackupBucketsReady
condition on the Seed
will be set to False
, and consequently the Seed
is considered as NotReady
.
If the SeedBackupBucketsCheckControllerConfiguration
(which is part of gardener-controller-manager
s component configuration) contains a conditionThreshold
for the BackupBucketsReady
, the condition will instead first be set to Progressing
and eventually to False
once the conditionThreshold
expires. See the example config file for details.
Once the BackupBucket
is healthy again, the seed will be re-queued and the condition will turn true
.
This reconciler reconciles Seed
objects and checks whether all ControllerInstallation
s referencing them are in a healthy state.
Concretely, all three conditions Valid
, Installed
, and Healthy
must have status True
and the Progressing
condition must have status False
.
Based on this check, it maintains the ExtensionsReady
condition in the respective Seed
's .status.conditions
list.
The "Lifecycle" reconciler processes Seed
objects which are enqueued every 10 seconds in order to check if the responsible
gardenlet
is still responding and operable. Therefore, it checks renewals via Lease
objects of the seed in the garden cluster
which are renewed regularly by the gardenlet
.
In case a Lease
is not renewed for the configured amount in config.controllers.seed.monitorPeriod.duration
:
- The reconciler assumes that the
gardenlet
stopped operating and updates theGardenletReady
condition toUnknown
. - Additionally, the conditions and constraints of all
Shoot
resources scheduled on the affected seed are set toUnknown
as well, because a strikinggardenlet
won't be able to maintain these conditions any more. - If the gardenlet's client certificate has expired (identified based on the
.status.clientCertificateExpirationTimestamp
field in theSeed
resource) and if it is managed by aManagedSeed
, then this will be triggered for a reconciliation. This will trigger the bootstrapping process again and allows gardenlets to obtain a fresh client certificate.
In case the reconciled Shoot
is registered via a ManagedSeed
as a seed cluster, this reconciler merges the conditions in the respective Seed
's .status.conditions
into the .status.conditions
of the Shoot
.
This is to provide a holistic view on the status of the registered seed cluster by just looking at the Shoot
resource.
This reconciler is responsible for hibernating or awakening shoot clusters based on the schedules defined in their .spec.hibernation.schedules
.
It ignores failed Shoot
s and those marked for deletion.
This reconciler is responsible for maintaining shoot clusters based on the time window defined in their .spec.maintenance.timeWindow
.
It might auto-update the Kubernetes version or the operating system versions specified in the worker pools (.spec.provider.workers
).
It could also add some operation or task annotations. For more information, see Shoot Maintenance.
This reconciler might auto-delete shoot clusters in case their referenced SecretBinding
is itself referencing a Quota
with .spec.clusterLifetimeDays != nil
.
If the shoot cluster is older than the configured lifetime, then it gets deleted.
It maintains the expiration time of the Shoot
in the value of the shoot.gardener.cloud/expiration-timestamp
annotation.
This annotation might be overridden, however only by at most twice the value of the .spec.clusterLifetimeDays
.
Shoot objects may specify references to other objects in the garden cluster which are required for certain features.
For example, users can configure various DNS providers via .spec.dns.providers
and usually need to refer to a corresponding Secret
with valid DNS provider credentials inside.
Such objects need a special protection against deletion requests as long as they are still being referenced by one or multiple shoots.
Therefore, this reconciler checks Shoot
s for referenced objects and adds the finalizer gardener.cloud/reference-protection
to their .metadata.finalizers
list.
The reconciled Shoot
also gets this finalizer to enable a proper garbage collection in case the gardener-controller-manager
is offline at the moment of an incoming deletion request.
When an object is not actively referenced anymore because the Shoot
specification has changed or all related shoots were deleted (are in deletion), the controller will remove the added finalizer again so that the object can safely be deleted or garbage collected.
This reconciler inspects the following references:
- DNS provider secrets (
.spec.dns.provider
) - Audit policy configmaps (
.spec.kubernetes.kubeAPIServer.auditConfig.auditPolicy.configMapRef
)
Further checks might be added in the future.
This reconciler is responsible for retrying certain failed Shoot
s.
Currently, the reconciler retries only failed Shoot
s with an error code ERR_INFRA_RATE_LIMITS_EXCEEDED
. See Shoot Status for more details.
This reconciler is responsible for maintaining the shoot.gardener.cloud/status
label on Shoot
s. See Shoot Status for more details.