Configure SQL Compilers and Compile Workers for Confluent Manager for Apache Flink
Confluent Manager for Apache Flink® (CMF) compiles every Flink SQL statement before it runs. Starting with version 2.5, CMF compiles statements in separate compiler processes, one for each Flink line, so that one CMF installation serves compute pools on different Flink versions. A line is a major and minor version, such as 1.19 or 2.3. There are two kinds of compilers:
- Shared compilers
Containers in the CMF pod, one for each Flink line. They are on by default, with a compiler for Flink 1.19 and one for Flink 2.3. Shared compilers cannot load artifacts.
- Compile workers
A separate pod,
cmf-compile-worker, in the CMF namespace. Compile workers are the only compilers that load artifacts, so statements that use user-defined functions or custom connectors and formats from artifacts need them. The chart adds one compile worker by default whencmf.artifacts.enabledandcmf.sql.environmentCatalog.enabledare bothtrue, and none otherwise.
You configure both with the cmf.statementService Helm values.
Where a statement compiles
Where CMF compiles a statement depends on your Helm values and on whether the statement’s
environment has artifacts. An environment has artifacts when cmf.artifacts.enabled and
cmf.sql.environmentCatalog.enabled are both true and the environment has at least one
artifact of any kind, including an application JAR. All statements of such an environment compile
on the compile workers, including those that use no artifact.
Helm values |
Environments without artifacts |
Environments with artifacts |
|---|---|---|
Shared compilers and compile workers, the default when |
Shared compilers |
Compile workers |
Shared compilers and no compile workers, the default when either feature is off, or with
|
Shared compilers |
Shared compilers. Statements that use an artifact fail. |
|
Compile workers |
Compile workers |
|
The |
The |
If an environment has artifacts but its SQL statements use none, for example because its only
artifacts are application JARs, its statements still compile on the compile workers. Give the
workers what these statements need, as described in Give the workers what catalogs and connectors need, or set
workers.count: 0 to keep them on the shared compilers.
Compile workers
Compile workers compile statements that use your own code, such as user-defined functions and
custom connectors and formats from artifacts, in a pod of their own, so that this code does not run
in the CMF pod. Each worker is one container, worker-<i>, in the cmf-compile-worker pod,
and listens on port 8090 + <i>. All workers share the one pod.
Enable compile workers
Statements can use code from artifacts only when artifact management and the environment catalog are both on, and the chart then adds one compile worker. Without compile workers, those statements fail. The worker runs code from artifacts, so take the steps in Secure the compile workers before you turn on both features in production.
Enable artifact management and the environment catalog. See Enable artifact management and Enable the environment catalog.
Optionally, set the number of workers in your Helm values file. Without
count, you get one worker. A worker compiles one statement at a time, for one environment. If several environments create statements at the same time, raisecount; see Size the compile workers. For example:cmf: artifacts: enabled: true basePath: s3://my-bucket/cmf sql: environmentCatalog: enabled: true statementService: workers: count: 2
The workers compile for the same Flink lines as the shared compilers. With
sharedCompilers: [], also setworkers.compilers. Without it, the chart adds no worker by default and fails with acountabove0. For example:cmf: statementService: sharedCompilers: [] workers: count: 2 compilers: - version: "1.19" - version: "2.3"
Give the workers the environment variables and files that your catalogs and connectors need. Workers do not get the top-level
extraEnv,mountedVolumes, orjvmArgsof CMF. See Give the workers what catalogs and connectors need.If you render the chart with
helm template, or with a GitOps tool that uses it such as Argo CD, see Compile worker certificates.Apply the values with
helm installorhelm upgrade, then check that the worker pod is running:kubectl get deployment cmf-compile-worker --namespace <namespace>
When the chart adds the worker by default, helm install and helm upgrade print a note that
says so. CMF logs Compile workers enabled with the number of workers and their compilers when
it starts.
To run no compile workers, set count: 0. Statements that use artifacts then fail. If artifact
management and the environment catalog are on but there are no workers, helm install and
helm upgrade print a warning, and CMF logs a warning that statements that use artifacts
fail.
Give the workers what catalogs and connectors need
A worker compiles the statements of many environments, so it needs what the catalogs and
connectors of all those statements need, such as Kafka trust stores and key stores, Hadoop
configuration, or cloud credentials. Set them with workers.extraEnv and
workers.mountedVolumes. Set JVM options that catalogs need, such as a trust store, in
workers.compilerJvmArgs, and keep a heap size there, because setting it replaces the default
-Xmx1g.
cmf:
statementService:
workers:
count: 2
extraEnv:
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: s3-credentials
key: access-key
mountedVolumes:
volumes:
- name: kafka-truststore
secret:
secretName: kafka-truststore
volumeMounts:
- name: kafka-truststore
mountPath: /mnt/truststore
readOnly: true
Code that a statement runs on a worker, such as a UDF, can read these variables and files, regardless of the statement’s environment. Give the workers only what catalogs and connectors need, and prefer to put credentials in the options of each environment’s catalogs, which a worker gets only with the statements of that environment.
The chart refuses the following, and helm install or helm upgrade fails:
a volume mount without
readOnly: trueworkers.extraEnvthat setsLD_PRELOAD, anSP_*variable, or a JVM option variable such asJAVA_TOOL_OPTIONS. Set JVM options withworkers.jvmArgsandworkers.compilerJvmArgs.a volume or
secretKeyRefthat uses the compile worker certificates, or a projected service account tokena mount path at, under, or above
/tmp,/dev/shm,/var/cache/artifacts,/etc/cmf/compile-tls, or/etc/passwd
Compile worker settings
Set these values under cmf.statementService.workers:
Property |
Default |
Description |
|---|---|---|
|
|
Number of workers. |
|
The versions of |
The Flink lines each worker compiles for, as a list of |
|
Memory request and limit of |
Container resources of each worker. Setting it replaces the whole default. |
|
|
JVM options of the worker process. Include a heap size. |
|
|
JVM options of the process that compiles a statement. Include a heap size. |
|
|
Size limit of each worker’s |
|
|
Size limit of each worker’s artifact cache, which is in memory. Set it to at least the size of the JAR files that one statement uses. |
|
|
The longest a statement can compile, and the longest it waits for a free worker. |
|
|
With |
|
|
Prefix of the Secrets with your own worker certificates. With |
|
|
Environment variables of every worker container. |
|
|
Volumes of the worker pod, and read-only volume mounts of every worker container. |
CMF also opens an internal port for the workers, cmf.internalPort (default 8081). It
accepts only the workers’ certificates.
The worker pod does not get the top-level nodeSelector, tolerations, affinity,
topologySpreadConstraints, priorityClassName, podLabels, or podAnnotations. Set
them for the worker pod under workerDeploymentOverrides.spec.template.spec, and its labels and
annotations under workerDeploymentOverrides.spec.template.metadata. If your nodes are all
tainted, or admission policies require pod labels, the worker pod stays Pending without them.
See also Prepare the worker nodes.
Size the compile workers
A worker’s JVM heaps and other JVM memory, its /tmp and /dev/shm, and its artifact cache
are all in memory, so all of them count towards resources.limits.memory. A worker that uses
more than this limit is killed and restarts, and the statement it was compiling fails. Set
resources.limits.memory to at least the sum of the following:
Part of a worker’s memory |
Set by |
Default |
|---|---|---|
Heap of the worker process |
|
384 MiB |
Heap of the process that compiles a statement |
|
1 GiB |
Other memory of these two processes |
About 512 MiB |
|
|
|
256 MiB |
|
64 MiB |
|
Artifact cache |
|
512 MiB |
With the defaults these add up to about 2.7 GiB, within the default limit of 3 GiB. The 512 MiB of
other JVM memory is an estimate and grows with large artifacts. When you raise a heap,
tmpSizeLimit, or volumeSizeLimit, raise the memory limit by the same amount. A worker logs
a warning when it starts with a smaller limit or with no limit.
A worker compiles one statement at a time, and serves one environment and one Flink line at a time.
In environments with artifacts this includes statements such as DESCRIBE and
SHOW CREATE TABLE. CMF sends a statement to a free worker that already serves its environment and line. If there is
none, CMF restarts a worker for it, which adds several seconds to that statement. This keeps
environments apart, but when there are fewer workers than active environments, the workers keep
switching and most statements wait for a restart. Choose count as follows:
Set
countto at least the number of pairs of environment and Flink line that create statements at the same time, counting only environments with artifacts. For example, two such environments that each create statements on 1.19 and 2.3 compute pools need 4 workers. WithsharedCompilers: [], count all environments.All workers run in one pod, so one node must have room for
counttimes the workerresources.When all workers are busy, a statement waits up to
compileTimeoutand then fails with HTTP 429 and a message to retry later or raisecmf.statementService.workers.count.Set the read timeout of any ingress or load balancer in front of the CMF REST API to more than (2 ×
compileTimeout) + 45 seconds, which is 285 seconds with the default2m.
Compile worker certificates
CMF and the workers authenticate each other with TLS client certificates. By default, the chart
generates a CA and the certificates, valid for 10 years, in the Secrets cmf-compile-ca,
cmf-compile-tls-cmf, and cmf-compile-tls-worker. Upgrades keep them, and
helm uninstall leaves them in place.
To renew the generated certificates, delete these Secrets and run
helm upgrade. Both pods restart with new certificates. Also delete them before you install the chart again under another release name in the same namespace.If you render the chart with
helm template, or with a GitOps tool that uses it such as Argo CD, the chart generates new certificates on every render. Setworkers.tls.existingSecretPrefixand use your own certificates, as described below. This also applies to the worker that the chart adds by default.
To use your own certificates, set workers.tls.existingSecretPrefix: <prefix> and create two
Secrets in the CMF namespace. Each certificate needs the extended key usages server auth and
client auth. Use a CA that issues no other certificates.
<prefix>-cmfType
kubernetes.io/tls, withtls.crt,tls.key, andca.crtin PEM format. The certificate has the common namecmfand the DNS namescmf-worker-api,cmf-worker-api.<namespace>, andcmf-worker-api.<namespace>.svc.<prefix>-workerType
Opaque, withca.crtand, for each worker<i>from0tocount - 1,worker-<i>.crtandworker-<i>.keyin PEM format. Each certificate has the common namecompile-worker-<i>and the DNS namescmf-compile-worker,cmf-compile-worker.<namespace>, andcmf-compile-worker.<namespace>.svc.
kubectl create secret tls cannot add ca.crt, so create both Secrets with
kubectl create secret generic, for example for one worker:
kubectl create secret generic <prefix>-cmf --namespace <namespace> --type=kubernetes.io/tls \
--from-file=tls.crt=cmf.crt --from-file=tls.key=cmf.key --from-file=ca.crt=ca.crt
kubectl create secret generic <prefix>-worker --namespace <namespace> \
--from-file=ca.crt=ca.crt \
--from-file=worker-0.crt=worker-0.crt --from-file=worker-0.key=worker-0.key
Check the Secrets before you install or upgrade:
If
<prefix>-cmfis missing, has noca.crt, or holds a certificate with another common name, the CMF pod does not start, and the CMF REST API is down.A worker whose pair is missing from
<prefix>-workerdoes not start, and the worker pod is not ready. Add the pairs of the new workers before you raisecount.
CMF and the workers read their certificates only when they start. After you replace the Secrets, restart both Deployments:
kubectl rollout restart deployment/confluent-manager-for-apache-flink --namespace <namespace>
kubectl rollout restart deployment/cmf-compile-worker --namespace <namespace>
If you set nameOverride, use it instead of confluent-manager-for-apache-flink.
Secure the compile workers
Compile workers run code from artifacts while they compile, for the statements of every environment with artifacts. Do the following before you turn on artifact management and the environment catalog in production.
Prepare the worker nodes
On the nodes that run the worker pod:
Set the kernel setting
kernel.yama.ptrace_scopeto1or higher, so that code that a statement runs cannot read the memory of the worker that compiles it. Some Linux distributions set0by default, so check it withsysctl kernel.yama.ptrace_scopeon each node. A worker logs an error when it starts on a node with0or without this setting. To have workers not start on such nodes, setcmf.statementService.workers.requirePtraceProtection: true. The worker pod is then not ready on such a node, and statements that compile on the workers fail until it runs on a node with the setting.Set the kubelet’s
podPidsLimit, so that code in the worker pod cannot start an unbounded number of processes.Keep the worker
resourceslimits set, so that a statement cannot take more memory or CPU from the node.
You can also keep the worker pod on other nodes than the CMF pod. Set nodeSelector,
tolerations, and affinity under workerDeploymentOverrides.spec.template.spec.
Limit network access
The chart creates no NetworkPolicy. If your cluster enforces NetworkPolicies, create one that lets
the worker pod reach only what it needs: DNS, CMF’s internal port, and the services that your
catalogs and connectors use, such as Kafka and Schema Registry. Everything else is then blocked, including the
cloud metadata endpoint 169.254.169.254, the CMF REST API, and the Kubernetes API server.
The following example assumes Kafka and Schema Registry run in the namespace confluent:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cmf-compile-worker
namespace: <namespace>
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: confluent-manager-for-apache-flink-compile-worker
policyTypes:
- Ingress
- Egress
ingress:
# CMF sends statements to the workers on ports 8090 and up, one port for each worker.
# Raise endPort if you run more than 10 workers.
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: confluent-manager-for-apache-flink
ports:
- protocol: TCP
port: 8090
endPort: 8099
egress:
# DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# CMF's internal port (cmf.internalPort), where the workers fetch artifacts
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: confluent-manager-for-apache-flink
ports:
- protocol: TCP
port: 8081
# Kafka and Schema Registry
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: confluent
ports:
- protocol: TCP
port: 9092
- protocol: TCP
port: 8081
Adjust the example to your cluster:
If the DNS pods of your cluster have other labels or ports than
k8s-app: kube-dnsand53, change the DNS rule to match them.If you set
cmf.internalPort, use its value instead of8081in the rule for CMF’s internal port.For services outside the cluster, add
ipBlockrules with their addresses. If you allow a wide range such as0.0.0.0/0, add169.254.169.254/32to itsexceptlist.If you set
nameOverride, the pod labels use that name instead ofconfluent-manager-for-apache-flink.If a NetworkPolicy selects the CMF pod, admit the internal port from the worker pod there too, as in the following rule. If it also limits the egress of the CMF pod, allow egress to the worker pod on ports
8090and up.ingress: - from: - podSelector: matchLabels: app.kubernetes.io/name: confluent-manager-for-apache-flink-compile-worker ports: - protocol: TCP port: 8081
Limit credentials and identities
Keep cloud identities off the worker pod. It runs as the namespace’s ServiceAccount
default, without a Kubernetes API token, and needs no access to artifact storage, because CMF serves the artifacts. Do not annotatedefaultfor IAM roles for service accounts (IRSA) or Workload Identity.If a catalog needs a cloud identity, give the worker pod a ServiceAccount of its own with
workerDeploymentOverrides.spec.template.spec.serviceAccountName, and grant it only what all environments can share. It must not be the CMF ServiceAccount. On Google Kubernetes Engine (GKE), Workload Identity uses the metadata endpoint, so your NetworkPolicy must then allow169.254.169.254.Give the workers only what catalogs and connectors need. See Give the workers what catalogs and connectors need.
Restrict
geton Secrets in the CMF namespace. Any certificate from the workers’ CA with the common namecmfcan send statements to the workers.
Set permissions per environment
Statements of the same environment share compile workers. Grant the permissions to upload artifacts and to create statements in an environment only to users you trust with all statements and artifacts of that environment, and keep teams that must not see each other’s statements in separate environments. See Configure Access Control for Confluent Manager for Apache Flink and Access control.
All compile workers of a CMF installation run in one pod and serve all of its environments. If environments belong to parties that must be fully separated from each other, run a separate CMF installation for each of them.
Keep the CMF pod trusted
Shared compilers run in the CMF pod and get each statement together with the catalog credentials
of its environment. Add containers to the CMF pod, for example with extraContainers, only if
you trust them with this data.
Flink versions and compilers
CMF 2.5 compiles for Flink 2.x compute pools only on a compiler for the 2.3 line, either a shared
compiler or a compile worker. Keep the "2.3" entry in sharedCompilers, and in
workers.compilers if you set it, if you run statements on Flink 2.x compute pools. Compute
pools of each line need a minimum cp-flink-sql image; see
the image requirements of compute pools.
The following need a compute pool on Flink 2.x:
Models:
CREATE MODEL,ALTER MODEL,DROP MODEL,SHOW MODELS,DESCRIBE MODEL, and queries that callML_PREDICTorML_EVALUATEon a model. On a Flink 1.x compute pool, CMF rejects them with a message that models require Flink 2.x.Batch statements, with
execution.runtime-mode=BATCH.Statements that use the tables of a Kafka database with
subjectNameStrategy: AssociatedNameStrategy. CMF accepts such a database only when a compiler for Flink 2.x is configured.
CMF 2.5 runs statements on compute pools with flinkVersion v1_19 or v2_3. It
compiles statements of v1_19 compute pools on the 1.19 compiler and those of v2_3 compute
pools on the 2.3 compiler. If no compiler serves the compute pool’s Flink version, creating the
statement fails.
Statements that do not run on a compute pool, such as those that list catalogs, databases, and tables, use the compiler with the newest version.
Compute pools on Flink 1.x and 2.x in the same environment share its catalogs. A table or view that uses SQL features of Flink 2.x fails when a statement on a Flink 1.x compute pool uses it. Create and change such tables and views from a compute pool on Flink 2.x.
Troubleshoot compilers
Error when you create a statement |
What to do |
|---|---|
|
Check that the artifact exists in the statement’s environment. If it does, CMF has no
compile workers. If your values set |
|
Add a compiler for the compute pool’s Flink line to |
|
The compute pool runs Flink 2.x. Add a |
|
The compiler has not started yet or has stopped. Read the log of the container that the message names, as described in Check the shared compilers, and create the statement again when the compiler runs. |
|
A worker is not running, or CMF and the worker do not accept each other’s certificates.
CMF tries again until |
Connection, TLS, or credential errors from Kafka, Schema Registry, or object storage, only in environments with artifacts |
These statements compile on the compile workers, which do not get the top-level
|
|
Retry later, or raise the compiler’s |
No error, but statements in environments with artifacts take several seconds or more to create |
The workers restart to switch between environments and Flink lines. Raise
|
|
The statement is created in phase |
|
Raise |
To follow a statement on the compile workers, find its metadata.uid in the CMF log line
Dispatching compile to compile worker: requestId='<id>', uuid='<uid>', ..., worker=<i>, then
search the worker’s log for the request ID:
kubectl logs deployment/cmf-compile-worker --namespace <namespace> -c worker-<i> | grep <id>
When you operate the workers:
CMF and the workers must run the same CMF version. While
helm upgradereplaces the CMF pod and the worker pod, statements that compile on the workers fail until both run the new version.The worker pod is not ready while any worker is down, so
helm install --waitandhelm upgrade --waittime out when a worker cannot start, and--atomicthen rolls back the whole release.If the node of the worker pod is lost, the old pod can stay in
Terminating. Delete it withkubectl delete pod <pod> --namespace <namespace> --force --grace-period=0.