그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그 그

  13 minute read  


Conceptually, all Gardener components are designed to run as a Pod inside a Kubernetes cluster. The Gardener API server extends the Kubernetes API via the user-aggregated API server concepts. However, if you want to develop it, you may want to work locally with the Gardener without building a Docker image and deploying it to a cluster each and every time. That means that the Gardener runs outside a Kubernetes cluster which requires providing a Kubeconfig in your local filesystem and point the Gardener to it when starting it (see below).

Further details can be found in

  1. Principles of Kubernetes, and its components
  2. Kubernetes Development Guide
  3. Architecture of Gardener

This guide is split into three main parts:

Preparing the Setup

[macOS only] Installing homebrew

The copy-paste instructions in this guide are designed for macOS and use the package manager Homebrew.

On macOS run

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Installing git

We use git as VCS which you need to install. On macOS run

brew install git

For other OS, please check the Git installation documentation.

Installing Go

Install the latest version of Go. On macOS run

brew install go

For other OS, please check Go installation documentation.

Installing kubectl

Install kubectl. Please make sure that the version of kubectl is at least v1.20.x. On macOS run

brew install kubernetes-cli

For other OS, please check the kubectl installation documentation.

Installing Docker

You need to have docker installed and running. On macOS run

brew install --cask docker

For other OS please check the docker installation documentation.

Installing iproute2

iproute2 provides a collection of utilities for network administration and configuration. On macOS run

brew install iproute2mac

Installing jq

brew install jq

Installing GNU Parallel

GNU Parallel is a shell tool for executing jobs in parallel, used by the code generation scripts (make generate). On macOS run

brew install parallel

[macOS only] Install GNU Core Utilities

When running on macOS, install the GNU core utilities and friends:

brew install coreutils gnu-sed gnu-tar grep gzip

This will create symbolic links for the GNU utilities with g prefix on your PATH, e.g., gsed or gbase64. To allow using them without the g prefix, add the gnubin directories to the beginning of your PATH environment variable (brew install and brew info will print out instructions for each formula):

export PATH=$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH
export PATH=$(brew --prefix)/opt/gnu-sed/libexec/gnubin:$PATH
export PATH=$(brew --prefix)/opt/gnu-tar/libexec/gnubin:$PATH
export PATH=$(brew --prefix)/opt/grep/libexec/gnubin:$PATH
export PATH=$(brew --prefix)/opt/gzip/bin:$PATH

[Windows Only] WSL2

Apart from Linux distributions and macOS, the local gardener setup can also run on the Windows Subsystem for Linux 2.

While WSL1, plain docker for Windows and various Linux distributions and local Kubernetes environments may be supported, this setup was verified with:

The Gardener repository and all the above-mentioned tools (git, golang, kubectl, …) should be installed in your WSL2 distro, according to the distribution-specific Linux installation instructions.

Start Gardener Locally

Get the Sources

Clone the repository from GitHub into your $GOPATH.

mkdir -p $(go env GOPATH)/src/github.com/gardener
cd $(go env GOPATH)/src/github.com/gardener
git clone git@github.com:gardener/gardener.git
cd gardener

Note: Gardener is using Go modules and cloning the repository into $GOPATH is not a hard requirement. However it is still recommended to clone into $GOPATH because k8s.io/code-generator does not work yet outside of $GOPATH - kubernetes/kubernetes#86753.

Start the Gardener

ℹ️ In the following guide, you have to define the configuration (CloudProfiles, SecretBindings, Seeds, etc.) manually for the infrastructure environment you want to develop against. Additionally, you have to register the respective Gardener extensions manually. If you are rather looking for a quick start guide to develop entirely locally on your machine (no real cloud provider or infrastructure involved), then you should rather follow this guide.

Start a Local Kubernetes Cluster

For the development of Gardener you need a Kubernetes API server on which you can register Gardener’s own Extension API Server as APIService. This cluster doesn’t need any worker nodes to run pods, though, therefore, you can use the “nodeless Garden cluster setup” residing in hack/local-garden. This will start all minimally required components of a Kubernetes cluster (etcd, kube-apiserver, kube-controller-manager) and an etcd Instance for the gardener-apiserver as Docker containers. This is the easiest way to get your Gardener development setup up and running.

Using the nodeless cluster setup

Use the provided Makefile rules to start your local Garden:

make local-garden-up
Starting gardener-dev kube-etcd cluster..!
Starting gardener-dev kube-apiserver..!
Starting gardener-dev kube-controller-manager..!
Starting gardener-dev gardener-etcd cluster..!
namespace/garden created
clusterrole.rbac.authorization.k8s.io/gardener.cloud:admin created
clusterrolebinding.rbac.authorization.k8s.io/front-proxy-client created

ℹ️ [Optional] If you want to develop the SeedAuthorization feature then you have to run make ACTIVATE_SEEDAUTHORIZER=true local-garden-up. However, please note that this forces you to start the gardener-admission-controller via make start-admission-controller.

To tear down the local Garden cluster and remove the Docker containers, simply run:

make local-garden-down
Alternative: Using a local Kubernetes cluster

Instead of starting a Kubernetes API server and etcd as docker containers, you can also opt for running a local Kubernetes cluster, provided by e.g. minikube, kind or docker desktop.

Note: Gardener requires self-contained kubeconfig files because of a security issue. You can configure your minikube to create self-contained kubeconfig files via:

minikube config set embed-certs true

or when starting the local cluster

minikube start --embed-certs
Alternative: Using a remote Kubernetes cluster

For some testing scenarios, you may want to use a remote cluster instead of a local one as your Garden cluster. To do this, you can use the “remote Garden cluster setup” residing in hack/remote-garden. This will start an etcd instance for the gardener-apiserver as a Docker container, and open tunnels for accessing local gardener components from the remote cluster.

To avoid mistakes, the remote cluster must have a garden namespace labeled with gardener.cloud/purpose=remote-garden. You must create the garden namespace and label it manually before running make remote-garden-up as described below.

Use the provided Makefile rules to bootstrap your remote Garden:

export KUBECONFIG=<path to kubeconfig>
make remote-garden-up
# Start gardener etcd used to store gardener resources (e.g., seeds, shoots)
Starting gardener-dev-remote gardener-etcd cluster!
# Open tunnels for accessing local gardener components from the remote cluster

To close the tunnels and remove the locally-running Docker containers, run:

make remote-garden-down

ℹ️ [Optional] If you want to use the remote Garden cluster setup with the SeedAuthorization feature, you have to adapt the kube-apiserver process of your remote Garden cluster. To do this, perform the following steps after running make remote-garden-up:

  • Create an authorization webhook configuration file using the IP of the garden/quic-server pod running in your remote Garden cluster and port 10444 that tunnels to your locally running gardener-admission-controller process.

    apiVersion: v1
    kind: Config
    current-context: seedauthorizer
    - name: gardener-admission-controller
        insecure-skip-tls-verify: true
        server: https://<quic-server-pod-ip>:10444/webhooks/auth/seed
    - name: kube-apiserver
      user: {}
    - name: seedauthorizer
        cluster: gardener-admission-controller
        user: kube-apiserver
  • Change or add the following command line parameters to your kube-apiserver process:

    • --authorization-mode=<...>,Webhook
    • --authorization-webhook-config-file=<path to config file>
    • --authorization-webhook-cache-authorized-ttl=0
    • --authorization-webhook-cache-unauthorized-ttl=0
  • Delete the cluster role and rolebinding gardener.cloud:system:seeds from your remote Garden cluster.

If your remote Garden cluster is a Gardener shoot, and you can access the seed on which this shoot is scheduled, you can automate the above steps by running the enable-seed-authorizer script and passing the kubeconfig of the seed cluster and the shoot namespace as parameters:

hack/local-development/remote-garden/enable-seed-authorizer <seed kubeconfig> <namespace>

Note: This script is not working anymore, as the ReversedVPN feature can’t be disabled. The annotation alpha.featuregates.shoot.gardener.cloud/reversed-vpn on Shoots is no longer respected.

To prevent Gardener from reconciling the shoot and overwriting your changes, add the annotation shoot.gardener.cloud/ignore: 'true' to the remote Garden shoot. Note that this annotation takes effect only if it is enabled via the constollers.shoot.respectSyncPeriodOverwrite: true option in the gardenlet configuration.

To disable the seed authorizer again, run the same script with -d as a third parameter:

hack/local-development/remote-garden/enable-seed-authorizer <seed kubeconfig> <namespace> -d

If the seed authorizer is enabled, you also have to start the gardener-admission-controller via make start-admission-controller.

⚠️ In the remote garden setup all Gardener components run with administrative permissions, i.e., there is no fine-grained access control via RBAC (as opposed to productive installations of Gardener).

Prepare the Gardener

Now, that you have started your local cluster, we can go ahead and register the Gardener API Server. Just point your KUBECONFIG environment variable to the cluster you created in the previous step and run:

make dev-setup
namespace/garden created
namespace/garden-dev created
deployment.apps/etcd created
service/etcd created
service/gardener-apiserver created
service/gardener-admission-controller created
endpoints/gardener-apiserver created
endpoints/gardener-admission-controller created
apiservice.apiregistration.k8s.io/v1beta1.core.gardener.cloud created
apiservice.apiregistration.k8s.io/v1alpha1.seedmanagement.gardener.cloud created
apiservice.apiregistration.k8s.io/v1alpha1.settings.gardener.cloud created

ℹ️ [Optional] If you want to enable logging, in the gardenlet configuration add:

  enabled: true

The Gardener exposes the API servers of Shoot clusters via Kubernetes services of type LoadBalancer. In order to establish stable endpoints (robust against changes of the load balancer address), it creates DNS records pointing to these load balancer addresses. They are used internally and by all cluster components to communicate. You need to have control over a domain (or subdomain) for which these records will be created. Please provide an internal domain secret (see this for an example) which contains credentials with the proper privileges. Further information can be found in Gardener Configuration and Usage.

kubectl apply -f example/10-secret-internal-domain-unmanaged.yaml
secret/internal-domain-unmanaged created

Run the Gardener

Next, run the Gardener API Server, the Gardener Controller Manager (optionally), the Gardener Scheduler (optionally), and the gardenlet in different terminal windows/panes using rules in the Makefile.

make start-apiserver
I0306 15:23:51.044421   74536 plugins.go:84] Registered admission plugin "ResourceReferenceManager"
I0306 15:23:51.044523   74536 plugins.go:84] Registered admission plugin "DeletionConfirmation"
I0306 15:23:51.626836   74536 secure_serving.go:116] Serving securely on [::]:8443

(Optional) Now you are ready to launch the Gardener Controller Manager.

make start-controller-manager
time="2019-03-06T15:24:17+02:00" level=info msg="Starting Gardener controller manager..."
time="2019-03-06T15:24:17+02:00" level=info msg="Feature Gates: "
time="2019-03-06T15:24:17+02:00" level=info msg="Starting HTTP server on"
time="2019-03-06T15:24:17+02:00" level=info msg="Acquired leadership, starting controllers."
time="2019-03-06T15:24:18+02:00" level=info msg="Starting HTTPS server on"
time="2019-03-06T15:24:18+02:00" level=info msg="Found internal domain secret internal-domain-unmanaged for domain nip.io."
time="2019-03-06T15:24:18+02:00" level=info msg="Successfully bootstrapped the Garden cluster."
time="2019-03-06T15:24:18+02:00" level=info msg="Gardener controller manager (version 1.0.0-dev) initialized."
time="2019-03-06T15:24:18+02:00" level=info msg="ControllerRegistration controller initialized."
time="2019-03-06T15:24:18+02:00" level=info msg="SecretBinding controller initialized."
time="2019-03-06T15:24:18+02:00" level=info msg="Project controller initialized."
time="2019-03-06T15:24:18+02:00" level=info msg="Quota controller initialized."
time="2019-03-06T15:24:18+02:00" level=info msg="CloudProfile controller initialized."

(Optional) Now you are ready to launch the Gardener Scheduler.

make start-scheduler
time="2019-05-02T16:31:50+02:00" level=info msg="Starting Gardener scheduler ..."
time="2019-05-02T16:31:50+02:00" level=info msg="Starting HTTP server on"
time="2019-05-02T16:31:50+02:00" level=info msg="Acquired leadership, starting scheduler."
time="2019-05-02T16:31:50+02:00" level=info msg="Gardener scheduler initialized (with Strategy: SameRegion)"
time="2019-05-02T16:31:50+02:00" level=info msg="Scheduler controller initialized."

The Gardener should now be ready to operate on Shoot resources. You can use

kubectl get shoots
No resources found.

to operate against your local running Gardener API Server.

Note: It may take several seconds until the Gardener API server has been started and is available. No resources found is the expected result of our initial development setup.

Create a Shoot

The steps below describe the general process of creating a Shoot. Have in mind that the steps do not provide full example manifests. The reader needs to check the provider documentation and adapt the manifests accordingly.

1. Copy the Example Manifests

The next steps require modifications of the example manifests. These modifications are part of local setup and should not be git push-ed. To do not interfere with git, let’s copy the example manifests to dev/ which is ignored by git.

cp example/*.yaml dev/

2. Create a Project

Every Shoot is associated with a Project. Check the corresponding example manifests dev/00-namespace-garden-dev.yaml and dev/05-project-dev.yaml. Adapt them and create them.

kubectl apply -f dev/00-namespace-garden-dev.yaml
kubectl apply -f dev/05-project-dev.yaml

Make sure that the Project is successfully reconciled:

$ kubectl get project dev
NAME   NAMESPACE    STATUS   OWNER                  CREATOR            AGE
dev    garden-dev   Ready    john.doe@example.com   kubernetes-admin   6s

3. Create a CloudProfile

The CloudProfile resource is provider specific and describes the underlying cloud provider (available machine types, regions, machine images, etc.). Check the corresponding example manifest dev/30-cloudprofile.yaml. Check also the documentation and example manifests of the provider extension. Adapt dev/30-cloudprofile.yaml and apply it.

kubectl apply -f dev/30-cloudprofile.yaml

4. Install Necessary Gardener Extensions

The Known Extension Implementations section contains a list of available extension implementations. You need to create a ControllerRegistration and ControllerDeployment for:

  • at least one infrastructure provider
  • a DNS provider (if the DNS for the Seed is not disabled)
  • at least one operating system extension
  • at least one network plugin extension

As a convention, the example ControllerRegistration manifest (containing also the necessary ControllerDeployment) for an extension is located under example/controller-registration.yaml in the corresponding repository (for example for AWS the ControllerRegistration can be found here). An example creation for provider-aws (make sure to replace <version> with the newest released version tag):

kubectl apply -f https://raw.githubusercontent.com/gardener/gardener-extension-provider-aws/<version>/example/controller-registration.yaml

Instead of updating extensions manually you can use Gardener Extensions Manager to install and update extension controllers. This is especially useful if you want to keep and maintain your development setup for a longer time. Also, please refer to Registering Extension Controllers for further information about how extensions are registered in case you want to use other versions than the latest releases.

5. Register a Seed

Shoot controlplanes run in seed clusters, so we need to create our first Seed now.

Check the corresponding example manifest dev/40-secret-seed.yaml and dev/50-seed.yaml. Update dev/40-secret-seed.yaml with base64 encoded kubeconfig of the cluster that will be used as Seed (the scope of the permissions should be identical to the kubeconfig that the gardenlet creates during bootstrapping - for now, cluster-admin privileges are recommended).

kubectl apply -f dev/40-secret-seed.yaml

Adapt dev/50-seed.yaml - adjust .spec.secretRef to refer the newly created Secret, adjust .spec.provider with the Seed cluster provider and revise the other fields.

kubectl apply -f dev/50-seed.yaml

6. Start the gardenlet

Once the Seed is created, start the gardenlet to reconcile it. The make start-gardenlet command will automatically configure the local gardenlet process to use the Seed and its kubeconfig. If you have multiple Seeds, you have to specify which to use by setting the SEED_NAME environment variable like in make start-gardenlet SEED_NAME=my-first-seed.

make start-gardenlet
time="2019-11-06T15:24:17+02:00" level=info msg="Starting Gardenlet..."
time="2019-11-06T15:24:17+02:00" level=info msg="Feature Gates: HVPA=true, Logging=true"
time="2019-11-06T15:24:17+02:00" level=info msg="Acquired leadership, starting controllers."
time="2019-11-06T15:24:18+02:00" level=info msg="Found internal domain secret internal-domain-unmanaged for domain nip.io."
time="2019-11-06T15:24:18+02:00" level=info msg="Gardenlet (version 1.0.0-dev) initialized."
time="2019-11-06T15:24:18+02:00" level=info msg="ControllerInstallation controller initialized."
time="2019-11-06T15:24:18+02:00" level=info msg="Shoot controller initialized."
time="2019-11-06T15:24:18+02:00" level=info msg="Seed controller initialized."

The gardenlet will now reconcile the Seed. Check the progess from time to time until it’s Ready:

kubectl get seed
seed-aws   Ready     aws         eu-west-1   4m     v1.61.0-dev   v1.24.8

7. Create a Shoot

A Shoot requires a SecretBinding. The SecretBinding refers to a Secret that contains the cloud provider credentials. The Secret data keys are provider specific and you need to check the documentation of the provider to find out which data keys are expected (for example for AWS the related documentation can be found at Provider Secret Data). Adapt dev/70-secret-provider.yaml and dev/80-secretbinding.yaml and apply them.

kubectl apply -f dev/70-secret-provider.yaml
kubectl apply -f dev/80-secretbinding.yaml

After the SecretBinding creation, you are ready to proceed with the Shoot creation. You need to check the documentation of the provider to find out the expected configuration (for example for AWS the related documentation and example Shoot manifest can be found at Using the AWS provider extension with Gardener as end-user). Adapt dev/90-shoot.yaml and apply it.

To make sure that a specific Seed cluster will be chosen or to skip the scheduling (the sheduling requires Gardener Scheduler to be running), specify the .spec.seedName field (see here).

kubectl apply -f dev/90-shoot.yaml

Watch the progress of the operation and make sure that the Shoot will be successfully created.

watch kubectl get shoot --all-namespaces