Kubernetes Integration¶
MultiFlexi can execute jobs inside Kubernetes pods using the Kubernetes
executor (package multiflexi-executor-k8s).
When a runtemplate uses this executor, the multiflexi-executor daemon:
Optionally deploys the application’s Helm chart (only when
helmchartis set and the release is not already present)Remaps host
MULTIFLEXI_TMP(and env values under it) to/tmpinside the podLaunches a one-shot pod (
--attach --rm, or a hold-and-kubectl cppath when the application declaresartifacts)Captures stdout/stderr into the job record
Copies files matching
app_artifactspath patterns into hostMULTIFLEXI_TMPsoJob::runEnd()can store them in theartifactstable (same path as the Native executor)
This page is the full deployment and configuration guide.
Deployment checklist¶
Do these steps once per MultiFlexi host that should run Kubernetes jobs, and once per cluster for namespace/RBAC.
# |
Step |
Where |
|---|---|---|
1 |
Install |
Executor host |
2 |
Install |
Executor host |
3 |
Create namespace |
Cluster |
4 |
Apply packaged RBAC (ServiceAccount + Role + RoleBinding) |
Cluster |
5 |
Mint a kubeconfig for SA |
Admin workstation → cluster |
6 |
Install kubeconfig as |
Executor host |
7 |
Set |
Executor host |
8 |
Ensure the application has |
MultiFlexi DB / app JSON |
9 |
Set the runtemplate |
MultiFlexi DB / CLI / UI |
10 |
Restart |
Executor host |
Step-by-step host and cluster setup¶
1. Install packages¶
On the machine that runs the daemon (typically as the multiflexi system
user via systemd):
sudo apt-get update
sudo apt-get install -y multiflexi-executor multiflexi-executor-k8s
Confirm the executor class and RBAC manifest are present:
ls -la /usr/share/php/MultiFlexi/Executor/Kubernetes.php
ls -la /usr/share/multiflexi/k8s/multiflexi-executor-rbac.yaml
2. Install kubectl and helm¶
kubectl is mandatory (in
$PATHfor the daemon).helm (v3+) is required only when applications declare a
helmchart. One-shot pods without Helm work with kubectl alone.
On Debian/Ubuntu, kubectl is often available as package kubernetes-client
or via your cluster vendor’s repo. Install helm from your preferred source
if the distro does not ship it.
Verify as root (or any login that can see $PATH):
command -v kubectl
command -v helm # optional unless you use Helm charts
kubectl version --client
3. Create the namespace¶
From a machine that already has cluster-admin (or equivalent) access:
export KUBECONFIG=/path/to/admin.kubeconfig
kubectl create namespace multiflexi
Use a different name only if you will also set MULTIFLEXI_K8S_NAMESPACE to
that same value on every executor host.
4. Apply RBAC¶
The multiflexi-executor-k8s package ships a least-privilege Role for
one-shot pods (create/get/list/watch/delete, logs, attach, exec) plus Helm
resources (deployments, replicasets, configmaps, secrets, serviceaccounts,
services) in namespace multiflexi.
kubectl apply -f /usr/share/multiflexi/k8s/multiflexi-executor-rbac.yaml
This creates:
ServiceAccount
multiflexi-executorRole
multiflexi-executor-roleRoleBinding
multiflexi-executor-binding
Verify:
kubectl get sa,role,rolebinding -n multiflexi
kubectl auth can-i create pods -n multiflexi \
--as=system:serviceaccount:multiflexi:multiflexi-executor
Do not bind the Role to the default ServiceAccount and expect the
daemon to pick it up. The daemon authenticates with whatever identity is in
its kubeconfig file. Use the dedicated multiflexi-executor SA.
5. Create a ServiceAccount kubeconfig¶
Generate a token-based kubeconfig for SA multiflexi-executor (example for
Kubernetes 1.24+, one-year token):
export KUBECONFIG=/path/to/admin.kubeconfig
SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
CA=$(kubectl config view --raw --minify --flatten \
-o jsonpath='{.clusters[0].cluster.certificate-authority-data}')
TOKEN=$(kubectl create token multiflexi-executor -n multiflexi --duration=8760h)
cat > multiflexi-executor.kubeconfig <<EOF
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: ${CA}
server: ${SERVER}
name: multiflexi
contexts:
- context:
cluster: multiflexi
namespace: multiflexi
user: multiflexi-executor
name: multiflexi-executor
current-context: multiflexi-executor
users:
- name: multiflexi-executor
user:
token: ${TOKEN}
EOF
chmod 600 multiflexi-executor.kubeconfig
Smoke-test before copying to the host:
KUBECONFIG=./multiflexi-executor.kubeconfig kubectl get pods -n multiflexi
KUBECONFIG=./multiflexi-executor.kubeconfig helm -n multiflexi list
Note
Prefer this SA kubeconfig over copying a cluster-admin kubeconfig onto the executor host. Renew the token before expiry (or use a longer-lived SA secret if your cluster policy allows it).
6. Install the kubeconfig on the executor host¶
The systemd unit runs as user multiflexi with home
/var/lib/multiflexi/. Place the kubeconfig there:
sudo mkdir -p /var/lib/multiflexi/.kube
sudo install -o multiflexi -g multiflexi -m 0600 \
./multiflexi-executor.kubeconfig \
/var/lib/multiflexi/.kube/config
Verify as the daemon user:
sudo -u multiflexi \
KUBECONFIG=/var/lib/multiflexi/.kube/config \
kubectl get pods -n multiflexi
7. Configure environment variables¶
Edit /etc/multiflexi/multiflexi.env (loaded by
multiflexi-executor.service via EnvironmentFiles=) and add:
KUBECONFIG=/var/lib/multiflexi/.kube/config
MULTIFLEXI_K8S_NAMESPACE=multiflexi
Meaning:
KUBECONFIG – absolute path to the SA kubeconfig. Without it, the executor falls back to
$HOME/.kube/configfor the process user (/var/lib/multiflexi/.kube/configwhen HOME is set correctly).MULTIFLEXI_K8S_NAMESPACE – target namespace for pods and Helm. Overrides the Helm default namespace (
multiflexiwhen a chart is configured). When unset and no Helm chart is used, the cluster default namespace applies.
See also Configuration Environment Variables and Configuration.
8. Restart the daemon¶
sudo systemctl restart multiflexi-executor
systemctl is-active multiflexi-executor
systemctl status multiflexi-executor --no-pager
Application configuration¶
Required and optional application fields¶
usableForApp() only checks that ociimage is non-empty. A missing
helmchart is valid.
Helm chart reference¶
When helmchart is set, it may be:
A local filesystem path readable by the
multiflexiuser (for example/opt/helm-charts/my-app)An OCI registry reference (for example
oci://ghcr.io/org/charts/my-app)A Helm repository chart name (for example
myrepo/my-app)
You can set it via application JSON import or directly in the database:
multiflexi-cli application:import-json --file=multiflexi/myapp.multiflexi.app.json
UPDATE apps SET helmchart='/opt/helm-charts/my-app' WHERE id=23;
Example fragment in *.multiflexi.app.json (imported fields map to DB
columns; runtime currently derives config from helmchart, name, and
artifacts):
{
"ociimage": "docker.io/example/my-app:latest",
"kubernetes": {
"helm": {
"enabled": true,
"chart": "oci://ghcr.io/example/my-app",
"namespace": "multiflexi"
},
"artifacts": {
"enabled": true,
"outputPath": "report.json,output.csv"
}
}
}
Release name is derived as a DNS-1123-safe form of the application name
(max 63 characters), defaulting to mf-app.
Helm chart structure¶
A typical chart for a MultiFlexi application includes ConfigMap, Secret,
Deployment (with envFrom), and optionally a ServiceAccount. See the
multiflexi-probe project’s helm/ directory for a reference.
Configuring a RunTemplate¶
Set the executor on an existing runtemplate:
multiflexi-cli run-template:update --id=158 --executor=Kubernetes
Or create a new one:
multiflexi-cli run-template:create \
--app_id=23 \
--company_id=3 \
--name="Probe via K8s" \
--executor=Kubernetes \
--interv=d \
--cron="0 6 * * *" \
--active=1
Schedule an immediate run:
multiflexi-cli run-template:schedule --id=158 --schedule_time=now
When --executor is omitted on schedule, the executor stored on the
runtemplate is used.
You can also select Kubernetes in the web UI when editing a runtemplate.
Execution flow¶
When the daemon picks up a job with the Kubernetes executor:
Helm status (only if
helmchartis set) —helm status <release>Helm pre-deploy (if needed) —
helm upgrade --installwith--create-namespace,--wait, and the configured timeoutPath remap — host
MULTIFLEXI_TMP(and env values under that directory) are rewritten to/tmpinside the pod; originals are restored after collection soJob::runEnd()sees host pathsPod create — without artifacts:
kubectl run --restart=Never --attach --rm; with artifacts from application.jsonartifacts/app_artifacts: create the pod without attach, run the command, write an exit marker, thensleepso the pod staysRunninglong enough forkubectl cpOutput capture — attach streams stdout/stderr directly; artifact mode uses
kubectl logsafter the exit marker appears. Helper commands (Helm,kubectl cp, delete) use a quiet runner and do not overwrite job outputArtifacts — list pod
/tmp, match everyapp_artifacts.pathpattern (same regex rules asApplication::getResultFiles()),kubectl cpinto hostMULTIFLEXI_TMP;Job::runEnd()then stores them in theartifactstableCleanup — delete the pod unless
keepPodOnFailureis true and the job failed; without artifacts,kubectl run --rmremoves the podPersist — stdout, stderr, exit code, and command line on the job row
Namespace resolution order: MULTIFLEXI_K8S_NAMESPACE → Helm namespace
(default multiflexi when a chart is configured) → cluster default.
Verification¶
Packages and files on the host:
dpkg -l multiflexi-executor multiflexi-executor-k8s
sudo grep -E '^(KUBECONFIG|MULTIFLEXI_K8S_NAMESPACE)=' /etc/multiflexi/multiflexi.env
sudo ls -la /var/lib/multiflexi/.kube/config
systemctl is-active multiflexi-executor
Cluster access as the daemon user:
sudo -u multiflexi \
KUBECONFIG=/var/lib/multiflexi/.kube/config \
kubectl get pods -n multiflexi
sudo -u multiflexi \
KUBECONFIG=/var/lib/multiflexi/.kube/config \
helm -n multiflexi list
After a scheduled job:
multiflexi-cli job:get --id=<JOB_ID> --format=json
Check:
executorisKubernetesexitcodeis0on successstdoutcontains pod outputcommandshows thekubectl runline
Troubleshooting¶
Permission denied / Forbidden¶
Confirm RBAC was applied in the same namespace you use in
MULTIFLEXI_K8S_NAMESPACE.Confirm the kubeconfig user is SA
multiflexi-executor, not an unrelated account.kubectl get namespace multifleximay return Forbidden for the SA (the Role is namespaced and does not grant Namespace get). Listing pods in that namespace is the right smoke test.
Helm pre-deployment fails¶
“path not found” —
helmchartis not readable bymultiflexi.OCI 404/403 — chart missing or needs
helm registry loginasmultiflexi.“cluster unreachable” — missing kubeconfig, wrong mode/owner, or wrong
KUBECONFIGinmultiflexi.env.
ImagePullBackOff¶
Fix the image tag or registry credentials used by the chart / ociimage.
Empty stdout¶
Confirm the app writes to stdout (not only to files).
Inspect job
stderr.Ensure you run a Kubernetes executor build that preserves job stdout across helper
kubectl/helmcalls (jobStdout/jobStderrfields).
Executor not recognized (falls back to Native)¶
multiflexi-cli run-template:get --id=<ID> --format=json—executormust beKubernetes/usr/share/php/MultiFlexi/Executor/Kubernetes.phpmust exist (packagemultiflexi-executor-k8s)sudo systemctl restart multiflexi-executor
Token expired¶
Regenerate the SA token (step 5), reinstall the kubeconfig (step 6), and restart the daemon (step 8).