Docs Home
Viewing docs for
BYOCNot available for Self-Managed

Pod Scheduling

On this page

You can pin the Apache Flink® JobManager and TaskManager pods that run your deployments to specific Kubernetes nodes in your BYOC cluster. Scheduling is entered as YAML in the console, using the standard Kubernetes nodeSelector, affinity, and tolerations fields.

Use it to run Flink on dedicated node groups in a shared or multi-tenant cluster, and to keep Flink off nodes reserved for other teams. The fields follow the standard Kubernetes schema and semantics, as described in Assigning Pods to Nodes and Taints and Tolerations.

What You Can Schedule

You set scheduling in two places: as a namespace default, and per deployment.

PodsWhere to configureApplies to
Flink JobManager and TaskManager, namespace defaultDeployment defaults, pod scheduling editorDeployments created after the change
Flink JobManager and TaskManager, per deploymentDeployment configuration, pod scheduling editorThat deployment

Per-deployment values take precedence over the namespace default. Because deployment defaults are applied when a deployment is created, existing deployments don't pick up a new default. Set their scheduling per deployment instead.

Session clusters accept the same pods, jobManagerPodTemplate, and taskManagerPodTemplate settings through the session cluster API. The console has no separate scheduling editor for session clusters. A session cluster created in the console copies the scheduling from the namespace's deployment defaults at creation time. To change scheduling on an existing session cluster, use the session cluster API.

In BYOC, each workspace maps to one namespace, so for tenant isolation use one workspace per tenant, each with its own namespace default.

The Flink pod scheduling editor, for both the namespace default and a single deployment, takes three optional blocks. pods applies to both roles, and each pod template applies to one role:

YAML
1pods:                      # JobManager and TaskManager
2  nodeSelector: {}
3  tolerations: []
4  affinity: {}
5jobManagerPodTemplate:     # JobManager only
6  spec:
7    nodeSelector: {}
8    tolerations: []
9    affinity: {}
10taskManagerPodTemplate:    # TaskManager only
11  spec:
12    nodeSelector: {}
13    tolerations: []
14    affinity: {}

When a field is set both in pods and in a role's template, the values combine as follows:

FieldHow pods and the role template combine
nodeSelectorMerged by key. Both sets of labels apply.
tolerationsAdded together. Both sets of tolerations apply.
affinitypods.affinity takes precedence. The role template's affinity is used only when pods.affinity is unset.

BYOC ignores securityContext and serviceAccountName in the pod templates.

To set scheduling for a deployment through the API, update the deployment's kubernetes field with PUT /api/v2/workspaces/{workspace}/namespaces/{namespace}/deployments/{deploymentId}. The request replaces the whole deployment, so retrieve the deployment first, modify the field, and send the full object back. Namespace defaults are configured in the console. See the API reference for the request schema.

When Changes Take Effect

Scheduling changes wait for the next pod creation.

ChangeTakes effectAction needed
Per deploymentWhen the deployment's pods are next createdSuspend and resume, or restart, the deployment
Namespace defaultFor deployments created after the changeSet scheduling on existing deployments individually

Kubernetes never moves a running pod because its scheduling rules changed.

Examples

The examples use illustrative label and taint names. Replace them with the labels and taints on your node groups. Some node labels, such as capacity type, depend on your cloud provider and node provisioner.

A toleration only allows a pod onto a tainted node; it doesn't send the pod there. A dedicated node group needs a taint on the nodes, plus a toleration and a node selector or affinity on the pods.

Give each tenant's node group a label, such as tenant=analytics, and a taint, such as dedicated=analytics:NoSchedule. Then set this as the namespace default in that tenant's workspace:

YAML
1pods:
2  nodeSelector:
3    tenant: analytics
4  tolerations:
5    - key: dedicated
6      operator: Equal
7      value: analytics
8      effect: NoSchedule

The taint keeps other pods off the node group, and the node selector keeps this tenant's Flink pods on it. New deployments pick up the default automatically. Set the same block on existing deployments individually.

JobManager and TaskManagers on Different Nodes

For a large stateful job, split the roles with the pod templates and leave pods.affinity unset:

YAML
1pods:
2  nodeSelector:
3    tenant: analytics
4  tolerations:
5    - key: dedicated
6      operator: Equal
7      value: analytics
8      effect: NoSchedule
9jobManagerPodTemplate:
10  spec:
11    nodeSelector:
12      capacity: on-demand
13taskManagerPodTemplate:
14  spec:
15    affinity:
16      nodeAffinity:
17        requiredDuringSchedulingIgnoredDuringExecution:
18          nodeSelectorTerms:
19            - matchExpressions:
20                - key: node.kubernetes.io/instance-type
21                  operator: In
22                  values: [r6i.2xlarge, r7i.2xlarge]

Because nodeSelector merges by key, the JobManager gets both tenant: analytics and capacity: on-demand. The TaskManagers keep the tenant selector and toleration and add the instance type rule.

Verify and Troubleshoot

After a restart, check where the pods landed and which scheduling rules they received:

BASH
1# Which node each pod runs on
2kubectl get pods -n <flink-namespace> -o wide
3
4# The scheduling rules the pod received
5kubectl get pod <pod> -n <namespace> \
6  -o jsonpath='{.spec.nodeSelector}{"\n"}{.spec.affinity}{"\n"}{.spec.tolerations}{"\n"}'
7
8# Labels and taints on the nodes
9kubectl get nodes --show-labels
10kubectl describe node <node> | grep -A3 Taints

If the pod spec doesn't show your rules, check that:

  • The deployment was restarted after the change.
  • The deployment was created after the namespace default changed. Existing deployments don't pick up a new default.
  • pods.affinity isn't overriding the affinity in a role's pod template.

If the spec shows your rules but the pod runs on the wrong node, check the node labels.

A pod stuck in Pending usually means that no node satisfies every hard rule. A typo in a label value leaves the pod Pending rather than falling back to another node. The Events section of kubectl describe pod <pod> shows the reason:

Event message containsCauseFix
didn't match Pod's node affinity/selectorNo node has the label, or a value is misspelled.Compare the output of kubectl get nodes --show-labels with the YAML.
had untolerated taintThe target nodes are tainted, but the pod has no matching toleration.Add a toleration with the exact key, value, and effect.
Insufficient cpu or Insufficient memoryThe rules match, but the node group is full.Increase the node group's maximum size, or loosen the rule.
didn't match pod anti-affinity rulesA required anti-affinity rule can't be met with the current nodes or zones.Switch to a preferred rule, or add capacity in more zones.

If the cluster autoscaler doesn't add nodes for a Pending Flink pod, check that the node group's launch template or node pool declares the same labels and taints. An autoscaler that scales up from zero nodes only knows the labels and taints that the template declares.

Roll out scheduling in a non-production workspace first, so that a misconfiguration only affects a test job:

  1. Label and taint the target node groups in your cloud provider, and declare the same labels and taints in the launch templates or node pools.
  2. Set the namespace default for Flink pods, create a test deployment, and check its placement with kubectl get pods -o wide.
  3. Set scheduling on existing deployments individually, and restart each one from a savepoint.
  4. Repeat in production. Restart deployments during maintenance windows.

Prefer a node selector plus a taint and toleration for the base rule, and use affinity for exceptions. Keep anti-affinity rules preferred unless a hard guarantee is worth the risk of pods not starting.

Was this helpful?