Config Sync provides two ways to manage drift:
- Use Config Sync's built-in self-healing (Recommended, no configuration needed): Config Sync automatically detects and reverts drift. You can't disable Config Sync's self-healing. Self-healing has a minimal impact on performance.
- Enable the drift prevention feature (this page): drift prevention uses an admission webhook to block conflicting changes. This feature can cause high memory usage and out-of-memory (OOM) errors, especially in clusters with many CustomResourceDefinitions (CRDs).
Why drift prevention is not recommended
The admission webhook requires Config Sync to load the Kubernetes OpenAPI schema to validate requests. In clusters with many resources or CRDs, this schema processing can exceed memory limits, leading to component failures. For most use cases, Config Sync's built-in self-healing provides drift protection without the stability risks of the webhook.
When enabled, drift prevention protects RootSync objects by default. You can
configure the feature to protect RepoSync objects. To use drift prevention, you must enable the
RootSync and RepoSync APIs.
Before you begin
If you previously installed the Google Cloud CLI, get the latest version by
running the gcloud components update command.
Enable drift prevention
You can enable drift prevention by using gcloud CLI. You can't enable drift prevention in the Google Cloud console.
To enable drift prevention, complete the following steps:
Update your apply spec manifest to set the
spec.configSync.preventDriftfield totrue:applySpecVersion: 1 spec: configSync: enabled: true ... existing content ... preventDrift: trueApply the updated manifest:
gcloud beta container fleet config-management apply \ --membership=MEMBERSHIP_NAME \ --config=MANIFEST_NAME \ --project=PROJECT_IDReplace the following:
MEMBERSHIP_NAME: the fleet membership name that you chose when you registered your cluster. Get the name with thegcloud container fleet memberships listcommand.MANIFEST_NAME: the name of your apply spec manifest, usuallyapply-spec.yaml.PROJECT_ID: your project ID.
Wait until the Config Sync
ValidateWebhookConfigurationobject is created by the ConfigManagement Operator:kubectl get validatingwebhookconfiguration admission-webhook.configsync.gke.ioYou should see output similar to the following example:
NAME WEBHOOKS AGE admission-webhook.configsync.gke.io 0 2m15sCommit a new change to the source of truth to be synced so that the
root-reconcilerDeployment can add webhooks into the Config Sync ValidatingWebhookConfiguration object. An alternative is to delete theroot-reconcilierDeployment to trigger a reconciliation. The newroot-reconcilerDeployment would update the Config Sync ValidatingWebhookConfiguration object.Wait until the webhook server is ready. The Config Sync admission webhook Deployment log should include
serving webhook server. This can take several minutes.kubectl logs -n config-management-system -l app=admission-webhook --tail=-1 | grep "serving webhook server"You should see output similar to the following example:
I1201 18:05:41.805531 1 deleg.go:130] controller-runtime/webhook "level"=0 "msg"="serving webhook server" "host"="" "port"=10250 I1201 18:07:04.626199 1 deleg.go:130] controller-runtime/webhook "level"=0 "msg"="serving webhook server" "host"="" "port"=10250
Disable drift prevention
When you disable drift prevention, Config Sync deletes all the Config Sync admission webhook resources.
Since the Config Sync ValidatingWebhookConfiguration object no longer exists,
the Config Sync reconcilers no longer generate the webhook configs for
managed resources.
To disable drift prevention, complete the following steps:
Update your apply spec manifest to set the
spec.configSync.preventDriftfield tofalse:applySpecVersion: 1 spec: configSync: enabled: false ... existing content ... preventDrift: falseApply the updated manifest:
gcloud beta container fleet config-management apply \ --membership=MEMBERSHIP_NAME \ --config=MANIFEST_NAME \ --project=PROJECT_IDReplace the following:
MEMBERSHIP_NAME: the fleet membership name that you chose when you registered your cluster. Get the name with thegcloud container fleet memberships listcommand.MANIFEST_NAME: the name of your apply spec manifest, usuallyapply-spec.yaml.PROJECT_ID: your project ID.
Enable the admission webhook in namespace-scoped sources
Namespace-scoped sources of truth are not fully protected by the webhook. The
Config Sync reconciler for each namespace source does not have permission to
read or update the ValidatingWebhookConfiguration objects at the cluster level.
This lack of permission results in an error for the namespace reconcilers logs similar to the following example:
Failed to update admission webhook: KNV2013: applying changes to
admission webhook: Insufficient permission. To fix, make sure the reconciler has
sufficient permissions.:
validatingwebhookconfigurations.admissionregistration.k8s.io "admission-
webhook.configsync.gke.io" is forbidden: User "system:serviceaccount:config-
management-system:ns-reconciler-NAMESPACE" cannot update resource
"validatingwebhookconfigurations" in API group "admissionregistration.k8s.io" at
the cluster scope
You can ignore this error if you don't want to use the webhook protection for your
namespace-scoped source of truth. However, if you want to use the webhook, grant
permission to the reconciler for each namespace-scoped source of truth after you have
configured syncing from more than one source of truth.
You might not need to perform these steps if a RoleBinding for the
ns-reconciler-NAMESPACE already exists with ClusterRole cluster-admin permissions.
In the root source of truth, declare a new ClusterRole configuration that grants permission to the Config Sync admission webhook. This ClusterRole only needs to be defined once per cluster:
# ROOT_SOURCE/cluster-roles/webhook-role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: admission-webhook-role rules: - apiGroups: ["admissionregistration.k8s.io"] resources: ["validatingwebhookconfigurations"] resourceNames: ["admission-webhook.configsync.gke.io"] verbs: ["get", "update"]For each namespace-scoped source where the admission webhook permission needs to be granted, declare a ClusterRoleBinding configuration to grant access to the admission webhook:
# ROOT_SOURCE/NAMESPACE/sync-webhook-rolebinding.yaml kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: syncs-webhook subjects: - kind: ServiceAccount name: ns-reconciler-NAMESPACE namespace: config-management-system roleRef: kind: ClusterRole name: admission-webhook-role apiGroup: rbac.authorization.k8s.ioReplace
NAMESPACEwith the namespace that you created your namespace-scoped source in.Commit the changes to the root source of truth, for example, if syncing from a Git repository:
git add . git commit -m 'Providing namespace repository the permission to update the admission webhook.' git pushTo verify, use
kubectl getto make sure the ClusterRole and ClusterRoleBinding have been created:kubectl get clusterrole admission-webhook-role kubectl get clusterrolebindings syncs-webhook
What's next
- Learn how to troubleshoot the webhook.