Control Plane Expose Strategy
Expose Strategies
This chapter describes the control plane expose strategies in the Kubermatic Kubernetes Platform (KKP).
The expose strategy defines how the control plane components (e.g. Kubernetes API server) are exposed
outside the seed cluster - to the worker nodes and the actual cluster users.
The expose strategies rely on a component called nodeport-proxy. It is a L4 service proxy based on Envoy,
capable of routing the traffic based on TCP destination port, SNI or HTTP/2 tunnels, depending on which expose strategy is used.
There are 3 different expose strategies in KKP: NodePort
, LoadBalancer
and Tunneling
.
Different user clusters in KKP may be using different expose strategies at the same time.
NodePort
NodePort is the default expose strategy in KKP. With this strategy a k8s service of type NodePort is created for each
exposed component (e.g. Kubernetes API Server) of each user cluster. This implies, that each apiserver will be
exposed on a randomly assigned TCP port from the nodePort range configured for the seed cluster.
Each cluster normally consumes 2 NodePorts (one for the apiserver + one for the worker-nodes to control-plane “tunnel”),
which limits the number of total clusters per Seed based on the NodePort range configured in the Seed cluster.
By default, the nodeport-proxy is enabled for this expose strategy. If services of type LoadBalancer are
available in the Seed, all the services of all user clusters will be made available through a single LoadBalancer
service in front of the nodeport-proxy. This approach however has a limitation on some cloud platforms, where the
number of listeners per load balancer is limited, which can limit the total number of user clusters per Seed even more.
The nodeport-proxy can be disabled
if your platform doesn’t support LoadBalancer services or if the front LoadBalancer service is not required.
LoadBalancer
In the LoadBalancer expose strategy, a dedicated service of type LoadBalancer will be created for each user cluster.
This strategy requires services of type LoadBalancer to be available on the Seed cluster and usually results into higher cost of cloud resources.
However, this expose strategy supports more user clusters per Seed, and provides better ingress traffic isolation per user cluster.
The port number on which the apiserver is exposed via the LoadBalancer service is still randomly allocated from the
NodePort range configured in the Seed cluster.
Tunneling
The Tunneling expose strategy addresses both the scaling issues of the NodePort strategy and cost issues of the LoadBalancer strategy.
With this strategy, the traffic is routed to the based on a combination of SNI and HTTP/2 tunnels by the nodeport-proxy.
Another benefit of this expose strategy is that control plane components of each user cluster are always exposed
on fixed port numbers: 6443
for the apiserver and 8088
for the worker-nodes to control-plane “tunnel”.
This allows for more strict firewall configuration than exposing the whole nodePort range.
Note that only the port 6443
needs to be allowed for external apiserver access, the port 8088
needs to be allowed
only between the worker-nodes network and the seed cluster network.
The following limitations apply to the Tunneling expose strategy:
- It is not supported in set-ups where the worker nodes should pass from a
corporate proxy (HTTPS proxy) to reach the control plane.
- An agent is deployed on each worker node to provide access to the apiserver from
within the cluster via the
kubernetes
service. The agent binds the IP used as the
kubernetes
service endpoint. This address can not collide with any other address in the cluster / datacenter.
The default value of tunneling agent IP is 100.64.30.10
. The default value can be changed via the cluster API (spec.clusterNetwork.tunnelingAgentIP
).
Configuring the Expose Strategy
The expose strategy can be configured at 3 levels:
- globally,
- on Seed level,
- on User Cluster level.
Seed level configuration overrides the global one, and user cluster level configuration overrides both.
Global Configuration
The expose strategy can be configured globally with the KubermaticConfiguration
as follows:
apiVersion: kubermatic.k8c.io/v1
kind: KubermaticConfiguration
metadata:
name: kubermatic
namespace: kubermatic
spec:
exposeStrategy: NodePort
The valid values for the exposeStrategy
are NodePort
/ LoadBalancer
/ Tunneling
.
NOTE: If the exposeStrategy
is not specified in the KubermaticConfiguration
, it would default to NodePort
.
Seed Level Configuration
The expose strategy can be overridden at Seed level in the Seed
CR, e.g.:
apiVersion: kubermatic.k8c.io/v1
kind: Seed
metadata:
name: kubermatic
namespace: kubermatic
spec:
# Override the global expose strategy with 'LoadBalancer'
exposeStrategy: LoadBalancer
User Cluster Level Configuration
The expose strategy can be also overridden on the user cluster level. To do that, configure the
desired expose strategy in cluster’s spec.exposeStrategy
in the cluster API, for example:
apiVersion: kubermatic.k8c.io/v1
kind: Cluster
metadata:
name: clustername
spec:
# Override the expose strategy for this cluster only
exposeStrategy: Tunneling
Disabling Nodeport-Proxy for the NodePort Expose Strategy
By default, the nodeport-proxy is enabled when using the NodePort
expose strategy.
If services of type LoadBalancer are available in the Seed, all the services of all user clusters will be made
available through a single LoadBalancer service in front of the nodeport-proxy.
The nodeport-proxy can be disabled if your platform doesn’t support LoadBalancer services or if the front LoadBalancer service is not required.
If nodeport-proxy is disabled, the DNS entries for the Seed cluster need to point directly to the Seed cluster’s node IPs.
Note that this approach has limitations in case of Seed cluster node failures and DNS entries need
to be manually re-configured upon Seed cluster node rotation.
The nodeport-proxy can be disabled at the Seed level, as shown on the following example:
apiVersion: kubermatic.k8c.io/v1
kind: Seed
metadata:
name: kubermatic
namespace: kubermatic
spec:
# Configure the expose strategy to NodePort and disable the nodeport-proxy
exposeStrategy: NodePort
nodeportProxy:
disable: true
Migrating the Expose Strategy for Existing Clusters
The expose strategy of a user cluster normally cannot be changed after the cluster creation.
However, for experienced KKP administrators, it is still possible to migrate a user cluster from one expose strategy to another using some manual steps.
This procedure will cause temporary outage in the user cluster, so it should be performed during a maintenance window. It is also recommended trying this procedure first on a testing cluster with the same setup (same Seed, same cloud provider, same worker node OS images, etc.) before performing it on a production cluster.
Step 1:
In order to allow the expose strategy migration, the cluster first needs to be labeled with the unsafe-expose-strategy-migration
label (e.g. unsafe-expose-strategy-migration: "true"
).
By putting this label on your cluster you acknowledge that this type of upgrade is not supported by Kubermatic and you are fully responsible for the consequences it may have.
It is recommended to suspend/terminate all workloads (e.g. by draining all active nodes via kubectl drain
) as all nodes will
lose connectivity to the cluster control plane upon updating the expose strategy. Therefore, updating the expose strategy
in the next step will stop the ability to coordinate and properly terminate workloads on existing nodes. A planned shutdown
might be desired to prevent potential data loss in your application stack.
Step 2:
At this point, you are able to change the expose strategy of the cluster in the Cluster API.
Change the Cluster spec.exposeStrategy
to the desired version:
either using KKP API endpoint /api/v2/projects/{project_id}/clusters/{cluster_id}
,
or by editing the cluster CR in the Seed Cluster (kubectl edit cluster <cluster-name>
).
When migrating from the Tunneling expose strategy (to any other), it is also necessary to delete the clusterNetwork.tunnelingAgentIP
in the cluster spec.
Now wait until control-plane components in the seed cluster redeploy.
Step 3:
At this point, all existing kubeconfig files used to access the cluster are invalid and not working anymore.
To access the cluster, download a new kubeconfig file.
Step 4:
Perform a rolling restart of all machine deployments in the user cluster. All nodes need to be rotated so that
kubelet running on the nodes starts using the new API server endpoint, and all workloads in the cluster do the
same as well. This can be done from KKP UI, or using kubectl, e.g.:
forceRestartAnnotations="{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"forceRestart\":\"$(date +%s)\"}}}}}"
for md in $(kubectl get machinedeployments -n kube-system --no-headers | awk '{print $1}'); do
kubectl patch machinedeployment -n kube-system $md --type=merge -p $forceRestartAnnotations
done
Afterwards, all Node
objects that show up as NotReady
should be deleted. No quick shell script is provided because
nodes might still be joining the cluster and temporarily show up as NotReady
. Those should of course not be deleted.