Skip to content

Commit fdbf222

Browse files
claudioloradamjensenbot
authored andcommitted
feat: allow coexistence of podoffloadingpolicy and runtimeclass
1 parent 7e265cb commit fdbf222

File tree

12 files changed

+206
-189
lines changed

12 files changed

+206
-189
lines changed

cmd/webhook/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ func main() {
9090
"Enforce offerer-side that offloaded pods do not exceed offered resources (based on container limits)")
9191
refreshInterval := pflag.Duration("resource-validator-refresh-interval",
9292
5*time.Minute, "The interval at which the resource validator cache is refreshed")
93-
addVirtualNodeTolerationOnOffloadedPods := pflag.Bool("add-virtual-node-toleration-on-offloaded-pods", false,
94-
"Automatically add the virtual node toleration on offloaded pods")
93+
liqoRuntimeClassName := pflag.String("liqo-runtime-class", "liqo",
94+
"Define the Liqo runtime class forcing the pods to be scheduled on virtual nodes")
9595

9696
flagsutils.InitKlogFlags(pflag.CommandLine)
9797
restcfg.InitFlags(pflag.CommandLine)
@@ -192,7 +192,7 @@ func main() {
192192
mgr.GetWebhookServer().Register("/validate/shadowpods", &webhook.Admission{Handler: spv})
193193
mgr.GetWebhookServer().Register("/mutate/shadowpods", shadowpodswh.NewMutator(mgr.GetClient()))
194194
mgr.GetWebhookServer().Register("/validate/namespace-offloading", nsoffwh.New())
195-
mgr.GetWebhookServer().Register("/mutate/pod", podwh.New(mgr.GetClient(), *addVirtualNodeTolerationOnOffloadedPods))
195+
mgr.GetWebhookServer().Register("/mutate/pod", podwh.New(mgr.GetClient(), *liqoRuntimeClassName))
196196
mgr.GetWebhookServer().Register("/mutate/virtualnodes", virtualnodewh.New(
197197
mgr.GetClient(), clusterID, *podcidr, *liqoNamespace, vkOptsDefaultTemplateRef))
198198
mgr.GetWebhookServer().Register("/validate/resourceslices", resourceslicewh.NewValidator(mgr.GetClient()))

deployments/liqo/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@
138138
| offloading.reflection.skip.annotations | list | `["cloud.google.com/neg","cloud.google.com/neg-status","kubernetes.digitalocean.com/load-balancer-id","ingress.kubernetes.io/backends","ingress.kubernetes.io/forwarding-rule","ingress.kubernetes.io/target-proxy","ingress.kubernetes.io/url-map","metallb.universe.tf/address-pool","metallb.universe.tf/ip-allocated-from-pool","metallb.universe.tf/loadBalancerIPs","loadbalancer.openstack.org/load-balancer-id"]` | List of annotations that must not be reflected on remote clusters. |
139139
| offloading.reflection.skip.labels | list | `[]` | List of labels that must not be reflected on remote clusters. |
140140
| offloading.runtimeClass.annotations | object | `{}` | Annotations for the runtime class. |
141-
| offloading.runtimeClass.enabled | bool | `false` | |
142141
| offloading.runtimeClass.handler | string | `"liqo"` | Handler for the runtime class. |
143142
| offloading.runtimeClass.labels | object | `{}` | Labels for the runtime class. |
144143
| offloading.runtimeClass.name | string | `"liqo"` | Name of the runtime class to use for offloading. |

deployments/liqo/templates/liqo-webhook-deployment.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,12 @@ spec:
5353
- --cluster-id=$(CLUSTER_ID)
5454
- --liqo-namespace=$(POD_NAMESPACE)
5555
- --secret-name={{ include "liqo.prefixedName" $webhookConfig }}-certs
56+
- --liqo-runtime-class={{ .Values.offloading.runtimeClass.name }}
5657
- --podcidr={{ .Values.ipam.podCIDR }}
5758
- --vk-options-default-template={{ .Release.Namespace }}/{{ printf "%s-default" $kubeletConfig.name }}
5859
{{- if .Values.controllerManager.config.enableResourceEnforcement }}
5960
- --enable-resource-enforcement
6061
{{- end }}
61-
{{- if not .Values.offloading.runtimeClass.enabled }}
62-
- --add-virtual-node-toleration-on-offloaded-pods
63-
{{- end }}
6462
{{- if .Values.common.extraArgs }}
6563
{{- toYaml .Values.common.extraArgs | nindent 10 }}
6664
{{- end }}

deployments/liqo/templates/runtime-class.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
{{- $runtimeConfig := (merge (dict "name" "runtimeclass" "module" "runtimeclass") .) -}}
22

3-
{{- if .Values.offloading.runtimeClass.enabled }}
4-
53
apiVersion: node.k8s.io/v1
64
kind: RuntimeClass
75
metadata:
@@ -25,5 +23,3 @@ scheduling:
2523
tolerations:
2624
{{- toYaml .Values.offloading.runtimeClass.tolerations.tolerations | nindent 4 }}
2725
{{- end }}
28-
29-
{{- end }}

deployments/liqo/values.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@ offloading:
179179
# by setting the "disableNetworkCheck" field in the resource Spec.
180180
disableNetworkCheck: false
181181
runtimeClass:
182-
enabled: false
183182
# -- Name of the runtime class to use for offloading.
184183
name: liqo
185184
# -- Annotations for the runtime class.

docs/examples/offloading-with-policies.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Offloading with Policies
22

33
This tutorial aims to guide you through a tour to learn how to use the core Liqo features.
4-
You will learn how to tune namespace offloading, and specify the target clusters through the [cluster selector](UsageOffloadingClusterSelector) concept.
4+
You will learn how to tune namespace offloading, and specify the target clusters through the [cluster selector](../usage/namespace-offloading.md#cluster-selector) concept.
55

66
More specifically, you will configure a scenario composed of a *single entry point cluster* leveraged for the deployment of the applications (i.e., the *Venice* cluster, located in *north* Italy) and two *worker clusters* characterized by different geographical regions (i.e., the *Florence* and *Naples* clusters, respectively located in *center* and *south* Italy).
77
Then, you will offload a given namespace (and the applications contained therein) to a subset of the worker clusters (i.e., only to the *Naples* cluster), while allowing pods to be also scheduled on the local cluster (i.e., the *Venice* one).

docs/usage/namespace-offloading.md

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,27 @@ In other words, an empty *cluster selector* matches all virtual clusters.
7979
The remote clusters are backed by a Liqo Virtual Node, which allows the vanilla Kubernetes scheduler to address the remote cluster as target for pod scheduling.
8080
However, by default the Liqo virtual nodes have a [Taint](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) applied to them, which prevents pods from being scheduled on them, unless a *namespace offloading* is enabled in the namespace where the pod is running.
8181

82-
You have two different ways to determine whether a pod can be scheduled on a virtual node (so on a remote cluster) and they are mutually exclusive per Liqo installation:
82+
You have two different ways to determine whether a pod can be scheduled on a virtual node (so on a remote cluster):
8383

8484
* Defining a **pod offloading strategy** for the offloaded namespaces (default), which tells where the pods created on that namespace should be scheduled (whether in the local cluster, the remote clusters, or both letting the vanilla K8s scheduler decide).
8585
* Setting the Liqo **RuntimeClass** in the pod, in this case, the namespace offloading strategy is ignored, and the pod will be scheduled to the virtual nodes.
8686

87+
Note these two methods can be used in conjunction to define how pods should be scheduled in the offloaded namespace.
88+
For example, a user might want to schedule all the pods on physical nodes, and only a subset of them on virtual nodes.
89+
To do so, it is possible to define `Local` as *pod offloading strategy* of the namespace, so that all the pods are scheduled locally and only the ones having the Liqo runtime class will be executed on a virtual node.
90+
8791
### Pod offloading strategy
8892

89-
The *pod offloading strategy* defines high-level constraints about pod scheduling, and can be configured through the `--pod-offloading-strategy` flag.
93+
The *pod offloading strategy* defines high-level constraints about pod scheduling, and can be configured through the `--pod-offloading-strategy` flag to be provided to the `liqoctl offload namespace` command. E.g.
94+
95+
```bash
96+
liqoctl offload namespace NAMESPACE_NAME --pod-offloading-strategy Local
97+
```
98+
9099
The accepted values are:
91100

92-
* **LocalAndRemote** (default): pods deployed in the local namespace can be scheduled **both onto local nodes and onto virtual nodes**, hence possibly offloaded to remote clusters. This will leave the Kubernetes scheduler to decide about the best placement, based on the available resources and the pod requirements. You can still influence the scheduler decision on which pods should be scheduled onto virtual nodes using the [standard Kubernetes mechanisms to assign Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/).
93-
* **Local**: pods deployed in the local namespace are enforced to be scheduled onto **local nodes only**, hence never offloaded to remote clusters.
101+
* **LocalAndRemote** (default): pods deployed in the local namespace can be scheduled **both onto local nodes and onto virtual nodes**, hence possibly offloaded to remote clusters. This will leave the Kubernetes scheduler to decide about the best placement, based on the available resources and the pod requirements. You can still influence the scheduler decision on which pods should be scheduled onto virtual nodes using the [standard Kubernetes mechanisms to assign Pods to Nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/) or via the [Liqo runtime class](#runtimeclass).
102+
* **Local**: pods deployed in the local namespace are enforced to be scheduled onto **local nodes only**, hence never offloaded to remote clusters (unless the pod uses the [Liqo runtime class](#runtimeclass)).
94103
* **Remote**: pods deployed in the local namespace are enforced to be scheduled onto **remote nodes only**, hence always offloaded to remote clusters.
95104

96105
It is worth mentioning that, independently from the selected pod offloading strategy, the services that expose them are propagated to the entire namespace (both locally and in the remote cluster), hence enabling the above pods to be consumed from anywhere in the Liqo domain, as shown in the [service offloading example](../examples/service-offloading.md).
@@ -105,22 +114,22 @@ Due to current limitations of Liqo, the pods violating the *pod offloading strat
105114

106115
### RuntimeClass
107116

108-
At Liqo install or upgrade time, you can specify a flag to enable the creation of a [RuntimeClass](https://kubernetes.io/docs/concepts/containers/runtime-class/) to be used to specify the pods that should be offloaded to the virtual nodes.
117+
By default Liqo creates a [RuntimeClass](https://kubernetes.io/docs/concepts/containers/runtime-class/) with name `liqo`, which can be used to **force pods to be scheduled on virtual nodes (so on the provider clusters) independently from the [pod offloading strategy](#pod-offloading-strategy)** configured on the offloaded namespace.
109118

110-
```bash
111-
liqoctl install [...] --set offloading.runtimeClass.enable=true
112-
```
119+
For example, if the *pod offloading strategy* is `Local` all the pods will be scheduled on the local cluster unless the Liqo runtime class is specified in the manifest of the pod.
113120

114-
or
121+
To use the Liqo runtime class, you will need to specify `runtimeClassName: liqo` in the Pod spec:
115122

116-
```bash
117-
helm install liqo liqo/liqo [...] --set offloading.runtimeClass.enable=true
123+
```yaml
124+
apiVersion: v1
125+
kind: Pod
126+
metadata:
127+
name: mypod
128+
spec:
129+
runtimeClassName: liqo
130+
# ...
118131
```
119132

120-
The RuntimeClass is created with the name `liqo`, and it is configured to add a Toleration to the virtual node taint for pods selecting it and to set a node selector to the virtual node's label.
121-
122-
(UsageOffloadingClusterSelector)=
123-
124133
## Unoffloading a namespace
125134

126135
The offloading of a namespace can be disabled through the dedicated *liqoctl* command, causing in turn the deletion of all resources reflected to remote clusters (including the namespaces themselves), and triggering the rescheduling of all offloaded pods locally:

pkg/webhooks/pod/mutations.go

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,24 @@ func getVirtualNodeToleration() corev1.Toleration {
3838
// or RemotePodOffloadingStrategyType. In case of PodOffloadingStrategyType not recognized, returns an error.
3939
func createTolerationFromNamespaceOffloading(strategy offloadingv1beta1.PodOffloadingStrategyType) (corev1.Toleration, error) {
4040
var toleration corev1.Toleration
41-
switch {
42-
case strategy == offloadingv1beta1.LocalAndRemotePodOffloadingStrategyType, strategy == offloadingv1beta1.RemotePodOffloadingStrategyType:
41+
switch strategy {
42+
case offloadingv1beta1.LocalAndRemotePodOffloadingStrategyType, offloadingv1beta1.RemotePodOffloadingStrategyType:
4343
// The virtual-node toleration must be added.
4444
toleration = getVirtualNodeToleration()
45+
case offloadingv1beta1.LocalPodOffloadingStrategyType:
46+
return toleration, nil
4547
default:
46-
err := fmt.Errorf("PodOffloadingStrategyType '%s' not recognized", strategy)
47-
klog.Error(err)
48-
return corev1.Toleration{}, err
48+
err := fmt.Errorf("unknown PodOffloadingStrategyType %q", strategy)
49+
return toleration, err
4950
}
5051
return toleration, nil
5152
}
5253

5354
// createNodeSelectorFromNamespaceOffloading creates the right NodeSelector according to the PodOffloadingStrategy chosen.
5455
func createNodeSelectorFromNamespaceOffloading(nsoff *offloadingv1beta1.NamespaceOffloading) (*corev1.NodeSelector, error) {
5556
nodeSelector := nsoff.Spec.ClusterSelector
56-
switch {
57-
case nsoff.Spec.PodOffloadingStrategy == offloadingv1beta1.RemotePodOffloadingStrategyType:
57+
switch nsoff.Spec.PodOffloadingStrategy {
58+
case offloadingv1beta1.RemotePodOffloadingStrategyType:
5859
// To ensure that the pod is not scheduled on local nodes is necessary to add to every NodeSelectorTerm a
5960
// new NodeSelectorRequirement. This NodeSelectorRequirement requires explicitly the label
6061
// "liqo.io/type=virtual-node" to exclude local nodes from the scheduler choice.
@@ -71,7 +72,7 @@ func createNodeSelectorFromNamespaceOffloading(nsoff *offloadingv1beta1.Namespac
7172
})
7273
}
7374

74-
case nsoff.Spec.PodOffloadingStrategy == offloadingv1beta1.LocalAndRemotePodOffloadingStrategyType:
75+
case offloadingv1beta1.LocalAndRemotePodOffloadingStrategyType:
7576
// In case the selector is empty, it is not necessary to modify anything, as it already allows pods to be scheduled on all nodes.
7677
if len(nodeSelector.NodeSelectorTerms) == 0 {
7778
return nil, nil
@@ -86,10 +87,10 @@ func createNodeSelectorFromNamespaceOffloading(nsoff *offloadingv1beta1.Namespac
8687
}},
8788
}
8889
nodeSelector.NodeSelectorTerms = append(nodeSelector.NodeSelectorTerms, newNodeSelectorTerm)
89-
90+
case offloadingv1beta1.LocalPodOffloadingStrategyType:
91+
return nil, nil
9092
default:
91-
err := fmt.Errorf("PodOffloadingStrategyType '%s' not recognized", nsoff.Spec.PodOffloadingStrategy)
92-
klog.Error(err)
93+
err := fmt.Errorf("unknown PodOffloadingStrategyType %q", nsoff.Spec.PodOffloadingStrategy)
9394
return nil, err
9495
}
9596
return &nodeSelector, nil
@@ -130,7 +131,8 @@ func fillPodWithTheNewNodeSelector(imposedNodeSelector *corev1.NodeSelector, pod
130131
// chosen in the CR. Two possible modifications:
131132
// - The VirtualNodeToleration is added to the Pod Toleration if necessary.
132133
// - The old Pod NodeSelector is substituted with a new one according to the PodOffloadingStrategyType.
133-
func mutatePod(namespaceOffloading *offloadingv1beta1.NamespaceOffloading, pod *corev1.Pod, addVirtualNodeToleration bool) error {
134+
// No changes are applied to the Pod if the Liqo runtime when the Liqo runtime class is specified.
135+
func mutatePod(namespaceOffloading *offloadingv1beta1.NamespaceOffloading, pod *corev1.Pod, liqoRuntimeClassName string) error {
134136
// The NamespaceOffloading CR contains information about the PodOffloadingStrategy and
135137
// the NodeSelector inserted by the user (ClusterSelector field).
136138
klog.V(5).Infof("Chosen strategy: %s", namespaceOffloading.Spec.PodOffloadingStrategy)
@@ -140,31 +142,36 @@ func mutatePod(namespaceOffloading *offloadingv1beta1.NamespaceOffloading, pod *
140142
return nil
141143
}
142144

143-
if addVirtualNodeToleration {
145+
// Mutate Pod affinity and tolerations only if the Pod has NOT the Liqo runtime class.
146+
hasLiqoRuntimeClass := pod.Spec.RuntimeClassName != nil && *pod.Spec.RuntimeClassName == liqoRuntimeClassName
147+
if !hasLiqoRuntimeClass {
144148
// Create the right Toleration according to the PodOffloadingStrategy case.
145149
toleration, err := createTolerationFromNamespaceOffloading(namespaceOffloading.Spec.PodOffloadingStrategy)
146150
if err != nil {
147-
klog.Errorf("The NamespaceOffloading in namespace '%s' has unknown strategy '%s'",
148-
namespaceOffloading.Namespace, namespaceOffloading.Spec.PodOffloadingStrategy)
149-
return err
151+
wErr := fmt.Errorf("unable to define tolerations for pod %q in namespace %q: %w",
152+
pod.Name, namespaceOffloading.Namespace, err)
153+
klog.Error(wErr)
154+
return wErr
150155
}
151156
klog.V(5).Infof("Generated Toleration: %s", toleration.String())
152157

153158
// It is necessary to add the just created toleration.
154159
pod.Spec.Tolerations = append(pod.Spec.Tolerations, toleration)
155-
}
156160

157-
// Create the right NodeSelector according to the PodOffloadingStrategy case.
158-
imposedNodeSelector, err := createNodeSelectorFromNamespaceOffloading(namespaceOffloading)
159-
if err != nil {
160-
klog.Errorf("The NamespaceOffloading in namespace '%s' has unknown strategy '%s'",
161-
namespaceOffloading.Namespace, namespaceOffloading.Spec.PodOffloadingStrategy)
162-
return err
161+
// Create the right NodeSelector according to the PodOffloadingStrategy case.
162+
imposedNodeSelector, err := createNodeSelectorFromNamespaceOffloading(namespaceOffloading)
163+
if err != nil {
164+
wErr := fmt.Errorf("unable to define node selectors for pod %q in namespace %q: %w",
165+
pod.Name, namespaceOffloading.Namespace, err)
166+
klog.Error(wErr)
167+
return wErr
168+
}
169+
klog.V(5).Infof("ImposedNodeSelector: %s", imposedNodeSelector)
170+
171+
// Enforce the new NodeSelector policy imposed by the NamespaceOffloading creator.
172+
fillPodWithTheNewNodeSelector(imposedNodeSelector, pod)
173+
klog.V(5).Infof("Pod NodeSelector: %s", imposedNodeSelector)
163174
}
164-
klog.V(5).Infof("ImposedNodeSelector: %s", imposedNodeSelector)
165175

166-
// Enforce the new NodeSelector policy imposed by the NamespaceOffloading creator.
167-
fillPodWithTheNewNodeSelector(imposedNodeSelector, pod)
168-
klog.V(5).Infof("Pod NodeSelector: %s", imposedNodeSelector)
169176
return nil
170177
}

pkg/webhooks/pod/pod.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ type podwh struct {
3939
client client.Client
4040
decoder admission.Decoder
4141

42-
addVirtualNodeToleration bool
42+
runtimeClassName string
4343
}
4444

4545
// New returns a new PodWebhook instance.
46-
func New(cl client.Client, addVirtualNodeToleration bool) *webhook.Admission {
46+
func New(cl client.Client, liqoRuntimeClassName string) *webhook.Admission {
4747
return &webhook.Admission{Handler: &podwh{
4848
client: cl,
4949
decoder: admission.NewDecoder(runtime.NewScheme()),
5050

51-
addVirtualNodeToleration: addVirtualNodeToleration,
51+
runtimeClassName: liqoRuntimeClassName,
5252
}}
5353
}
5454

@@ -91,7 +91,7 @@ func (w *podwh) Handle(ctx context.Context, req admission.Request) admission.Res
9191
return admission.Errored(http.StatusInternalServerError, errors.New("failed retrieving NamespaceOffloading"))
9292
}
9393

94-
if err = mutatePod(nsoff, pod, w.addVirtualNodeToleration); err != nil {
94+
if err = mutatePod(nsoff, pod, w.runtimeClassName); err != nil {
9595
return admission.Errored(http.StatusInternalServerError, errors.New("failed constructing pod mutation"))
9696
}
9797

0 commit comments

Comments
 (0)