Skip to content
This repository was archived by the owner on Dec 15, 2021. It is now read-only.

Commit 3eff84c

Browse files
authored
Add soft pod anti affinity (#1115)
1 parent ac91c1e commit 3eff84c

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

docs/advanced-function-deployment.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ The fields that a Function specification can contain are:
3535

3636
Apart from the basic parameters, it is possible to add the specification of a `Deployment`, a `Service` or an `Horizontal Pod Autoscaler` that Kubeless will use to generate them.
3737

38+
## Pod Anti Affinity
39+
40+
By default, a kubless generated `Deployment` will include a soft pod anti-affinity rule that will signal to kubernetes that it should try to deploy pods to different nodes. This behaviour can be overridden using a deployment template.
41+
3842
## Deploying large functions
3943

4044
As any Kubernetes object, function objects have a maximum size of 1.5MiB (due to the [maximum size](https://github.com/etcd-io/etcd/blob/master/Documentation/dev-guide/limit.md#request-size-limit) of an etcd entry). Because of that, it's not possible to specify in the `function` field of the YAML content that surpasses that size. To workaround this issue it's possible to specify an URL in the `function` field. This file will be downloaded at build time (extracted if necessary) and the checksum will be checked. Doing this we avoid any limitation regarding the file size. It's also possible to include the function dependencies in this file and skip the dependency installation step. Note that since the file will be downloaded in a pod the URL should be accessible from within the cluster:
@@ -91,7 +95,7 @@ spec:
9195
```
9296

9397
Would create a function with the environment variable `FOO`, using CPU and memory limits and mounting the secret `my-secret` as a volume. Note that you can also specify a default template for a Deployment spec in the [controller configuration](/docs/function-controller-configuration).
94-
The resource configuration in `initContainers` will be applied to all of the initial containers in the target deployment (like `provision`, `compile` etc.)
98+
The resource configuration in `initContainers` will be applied to all of the initial containers in the target deployment (like `provision`, `compile` etc.)
9599

96100

97101
## Custom Service

pkg/utils/kubelessutil.go

+22
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,28 @@ func EnsureFuncDeployment(client kubernetes.Interface, funcObj *kubelessApi.Func
684684
}
685685
}
686686

687+
// Add soft pod anti affinity
688+
if dpm.Spec.Template.Spec.Affinity == nil {
689+
dpm.Spec.Template.Spec.Affinity = &v1.Affinity{
690+
PodAntiAffinity: &v1.PodAntiAffinity{
691+
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
692+
{
693+
Weight: 100,
694+
PodAffinityTerm: v1.PodAffinityTerm{
695+
LabelSelector: &metav1.LabelSelector{
696+
MatchLabels: map[string]string{
697+
"created-by": "kubeless",
698+
"function": funcObj.ObjectMeta.Name,
699+
},
700+
},
701+
TopologyKey: "kubernetes.io/hostname",
702+
},
703+
},
704+
},
705+
},
706+
}
707+
}
708+
687709
_, err = client.AppsV1().Deployments(funcObj.ObjectMeta.Namespace).Create(dpm)
688710
if err != nil && k8sErrors.IsAlreadyExists(err) {
689711
// In case the Deployment already exists we should update

pkg/utils/kubelessutil_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -636,10 +636,34 @@ func TestEnsureDeployment(t *testing.T) {
636636
},
637637
},
638638
}
639+
639640
if !reflect.DeepEqual(dpm.Spec.Template.Spec.Containers[0], expectedContainer) {
640641
t.Errorf("Unexpected container definition. Received:\n %+v\nExpecting:\n %+v", dpm.Spec.Template.Spec.Containers[0], expectedContainer)
641642
}
642643

644+
expectedAffinity := &v1.Affinity{
645+
PodAntiAffinity: &v1.PodAntiAffinity{
646+
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
647+
{
648+
Weight: 100,
649+
PodAffinityTerm: v1.PodAffinityTerm{
650+
LabelSelector: &metav1.LabelSelector{
651+
MatchLabels: map[string]string{
652+
"created-by": "kubeless",
653+
"function": f1Name,
654+
},
655+
},
656+
TopologyKey: "kubernetes.io/hostname",
657+
},
658+
},
659+
},
660+
},
661+
}
662+
663+
if !reflect.DeepEqual(dpm.Spec.Template.Spec.Affinity, expectedAffinity) {
664+
t.Errorf("Unexpected pod affinity definition. Received:\n %+v\nExpecting:\n %+v", dpm.Spec.Template.Spec.Affinity, expectedAffinity)
665+
}
666+
643667
secrets := dpm.Spec.Template.Spec.ImagePullSecrets
644668
if secrets[0].Name != "creds" && secrets[1].Name != "p1" && secrets[2].Name != "p2" {
645669
t.Errorf("Expected first secret to be 'p1' but found %v and second secret to be 'p2' and found %v", secrets[0], secrets[1])
@@ -847,6 +871,30 @@ func TestDeploymentWithVolumes(t *testing.T) {
847871
}
848872
}
849873

874+
func TestEnsureDeploymentWithAffinityOverridden(t *testing.T) {
875+
funcName := "func"
876+
clientset, or, ns, lr := prepareDeploymentTest(funcName)
877+
// If the Image has been already provided it should not resolve it
878+
f3 := getDefaultFunc(funcName, ns)
879+
f3.Spec.Deployment.Spec.Template.Spec.Affinity = &v1.Affinity{}
880+
err := EnsureFuncDeployment(clientset, f3, or, lr, "", "unzip", []v1.LocalObjectReference{})
881+
if err != nil {
882+
t.Errorf("Unexpected error: %s", err)
883+
}
884+
dpm, err := clientset.AppsV1().Deployments(ns).Get(funcName, metav1.GetOptions{})
885+
if err != nil {
886+
t.Errorf("Unexpected error: %s", err)
887+
}
888+
expectedAffinity := &v1.Affinity{NodeAffinity: nil, PodAffinity: nil, PodAntiAffinity: nil}
889+
if *dpm.Spec.Template.Spec.Affinity != *expectedAffinity {
890+
t.Errorf(
891+
"Unexpected Affinity Definition:\nExpecting: %+v\nReceived: %+v",
892+
expectedAffinity,
893+
dpm.Spec.Template.Spec.Affinity,
894+
)
895+
}
896+
}
897+
850898
func doesNotContain(envs []v1.EnvVar, env v1.EnvVar) bool {
851899
for _, e := range envs {
852900
if e == env {

0 commit comments

Comments
 (0)