tech

Wie wir zu Fission migrieren – serverless-v4 Engineering Deep Dive

18. August 2026 serge 7 min read

wie-wir-zu-fission-migrieren-serverless-v4.md
Architektur-Diagramm: v3 OpenWhisk vs v4 Fission Migration

Einleitung

Im August-Update auf evrtng.cloud haben wir erklärt, warum wir Apache OpenWhisk verlassen und zu Fission migrieren [1]. Markt, Strategie und das OpenWhisk-Wind-down-Narrativ stehen dort. Dieser Artikel hier ist das Engineering-Begleitstück: was ändert sich technisch unter der Haube, und was bedeutet die Migration für Entwickler, die Functions auf evrtng betreiben. Wir lassen die Marktanalyse weg und schauen direkt in den Stack.

Warum Fission – drei technische Gründe

Der August-Artikel hat das strategische Warum ausführlich gemacht [1]. Hier nur die Engineering-Perspektive, komprimiert auf drei Punkte. Wer den Pekko-Fork kennt, den wir als Brücke betreiben: der Fork war nie ein Endzustand, sondern die Voraussetzung, um operativ zu bleiben, während der nächste Schritt geplant wurde. Die Series-Kontinuität: Artikel 1 erklärte die OpenWhisk-Entscheidung [4], Artikel 2 den Invocation Flow [5], dieser Artikel die Migration.

Erstens: Fission ist Kubernetes-nativ. State lebt in Custom Resource Definitions und damit in etcd, nicht in einer eigenen CouchDB-Instanz für Control-Flow. Was bei OpenWhisk eine verteilte Eigenentwicklung war (Controller, eigener Message Bus, eigene Datenbank für Subject-State), ist bei Fission eine dünne Schicht aus Controllern über der Kubernetes-API. Backups, RBAC und Observability nutzen die vorhandene Kubernetes-Toolchain direkt, ohne separate Brücken.

Zweitens: Poolmgr statt Spawn-per-Invocation. OpenWhisk hatte Warm Containers nachgerüstet, wir haben das in Artikel 2 beschrieben [5]. Fission macht konsequent, was bei OW ein optionaler Optimierungspfad war: warme Pods bleiben im Pool, der Executor specialisiert sie bei Bedarf. Der Cold-Start-Pfad ist kürzer, weil kein Pod pro Invocation gespawnt und wieder zerstört wird.

Drittens: Dynamic Tenancy in v1.27.0 [2]. Ein Tenant ist ein CR, kein Eintrag in einer Datenbank. Fissions Tenant-Controller generiert HMAC-Keys, RBAC und ServiceAccounts automatisch. Das ersetzt unseren gesamten OW-Subject-Provisioning-Pfad, der bisher aus CouchDB-Dokumenten, manuellen Limits und separaten Auth-Keys bestand.

Architektur-Delta

Die Tabelle zeigt die Zeilen, die sich zwischen v3 und v4 ändern. Alles nicht aufgeführte bleibt identisch.

Area v3 (OpenWhisk) v4 (Fission)
FaaS engine OpenWhisk (Pekko-Fork) Fission v1.27.0
Function pods wskN-M-<runtime>-<action> (Invoker spawn) fission-function-<name> (Poolmgr warm pool)
API path /api/v1/* -> nginx -> controller /fission-function/* -> fission-router
Billing input Kafka events topic APISIX http-logger HTTP POST
Tenancy OW subjects in CouchDB FissionTenant CR + CouchDB mirror
Quota enforcement OW concurrentInvocations=0 Fission Function.concurrency=0
Isolation Kata+Firecracker (kata-fc) gleich (RuntimeClass erhalten)
Activation records CouchDB test_activations Fission Prometheus metrics
Vanity URLs nginx regex HTTPTrigger routeConfig.hostnames

Neun Zeilen ändern sich, der Rest des Clusters nicht. Das ist der Kernpunkt der Migration: Herz getauscht, nicht Körper.

Konstanten

Was bleibt: Traefik als Edge-Router mit Let’s-Encrypt, APISIX als Gateway mit Rate-Limiting und Billing-Logs, CouchDB für Billing- und Subject-Daten, Redis, Strimzi-Kafka (nur noch für MQ-Trigger via KEDA), Kata+Firecracker als RuntimeClass, Prometheus und Grafana mit acht Custom Dashboards, K8up fürs Backup auf S3. Der openwhisk-Namespace bleibt bestehen für diese Stateful-Services und unsere Custom-Workloads (billing, provisioning, quotas). Fission bekommt einen eigenen Namespace. Das minimiert Migrationsrisiko – CouchDB- und Kafka-PVCs lassen sich aus dem Backup in dieselben Namespace-Namen restaurieren. Die Isolationsstufe ist über beide Stack-Generationen hinweg dieselbe – Kata-Firecracker bleibt die konstante Isolationsstufe, wie der August-Artikel festhält [1].

Invocation Flow v4

Der Flow aus Artikel 2 [5] verkürzt sich. Vier Schritte statt neun.

  1. Traefik TLS -> APISIX: Am Edge terminiert Traefik TLS und leitet an APISIX weiter. APISIX setzt Rate-Limits und pusht Invocation-Logs via http-logger an billing-consumer.
  2. APISIX -> fission-router: Hinter APISIX steht der fission-router als ClusterIP-Service, zwei Replicas. HA läuft über endpointSliceCache und incrementalRoutes, nicht über Leader-Election – der Router ist stateless. Leader-Election haben executor, buildermgr und tenantController. Er ist das, was bei OW Controller plus Nginx war.
  3. Router -> warmer Pool-Pod: Der Router leitet an einen gepoolten Function-Pod weiter, nicht an einen frisch gespawnten. Der Pod läuft mit runtimeClassName: kata-fc auf dem evr-invoke-Node-Pool, poolsize 3 pro Environment.
  4. Kein Kafka-Hop mehr: Für synchrone Aufrufe entfällt der Kafka-Umweg über den Invoker. Kafka läuft nur noch für MQ-Trigger, angetrieben durch KEDA.

API und CLI – was sich für Entwickler ändert

Das ist der Teil, den der August-Artikel nicht abdeckt. Entwickler wollen wissen: was ändert sich an meinem Code, an meiner CLI, an meinen URLs.

CLI-Mapping:

OpenWhisk Fission
wsk action create hello hello.js fission function create --name hello --env nodejs --code hello.js
wsk action invoke hello fission function test --name hello
wsk api create /v1/hello GET hello HTTPTrigger CR (prefix /v1/hello, methods [GET])
wsk CLI + ~/.wskprops fission CLI + FISSION_URL / FISSION_AUTH=""

Function-Signatur in Node.js – das ist ein Breaking Change für Nutzer:

// v3 - OpenWhisk
async function main(params) {
  return { statusCode: 200, body: { ok: true } };
}

// v4 - Fission
module.exports = async function(ctx) {
  return { status: 200, body: { ok: true } };
};

Drei Deltas: main(params) wird zu module.exports = async function(ctx), das Argument ist ein Kontext-Objekt statt nur Params, und statusCode wird zu status. Wer OW-Functions migriert, muss den Einstiegspunkt anpassen. Aktuell passiert das manuell, wir bieten keinen Shim an. Eine Funktion anzupassen dauert Minuten, ein Shim wäre eine Dauerbelastung geworden – er müsste beide Signatur-Welten am Leben halten, inklusive der Semantikunterschiede im Kontext-Objekt.

API-Pfad: /api/v1/* wird zu /fission-function/* als öffentlicher Pfad. /v1/* bleibt für Kompatibilität geroutet, sodass alte Clients nicht sofort brechen. Activation Records entfallen als Konzept: OpenWhisk hat jede Ausführung in CouchDB test_activations gespeichert, abrufbar unter /api/v1/namespaces/_/activations/<id>. Fission hat keine Activation-Datenbank. Metriken liegen in Prometheus, eine direkte äquivalente API gibt es nicht. Wer Activation-IDs brauchte, muss auf Logs und Metriken umsteigen. Das ist der sichtbarste Bruch für Nutzer, die Activation-Historie in eigenen Tools ausgewertet haben.

Code-Beispiele aus dem Repo

Vier Snippets, die zeigen, was sich intern ändert.

FissionTenant CR – ein Tenant ist jetzt ein Kubernetes-Objekt, kein Datenbankeintrag:

apiVersion: fission.io/v1
kind: FissionTenant
metadata: { name: acme-corp }
spec: { namespace: acme-corp }
# Fission generiert HMAC-Key, RBAC, SA automatisch

runtimePodSpec – die Isolationsstufe wird global am Environment verankert, nicht pro Function:

runtimePodSpec:
  enabled: true
  podSpec:
    runtimeClassName: kata-fc
    nodeSelector: { worker.gardener.cloud/pool: evr-invoke }
    tolerations: [{ key: sandbox, operator: Exists }]

Function-Signatur-Delta: siehe Section oben.

Billing-HTTP-Input – aus einem Kafka-Consumer wurde ein Flask-Endpoint:

@app.route("/ingest", methods=["POST"])
def ingest():
    logs = request.json  # APISIX http-logger POSTs
    # selbe Billing-Logik wie v3 (pricing, CouchDB write)

Die Pricing-Logik, GBS-Tiers und CouchDB-Writes sind identisch mit v3. Nur der Eingangskanal hat sich geändert: HTTP statt Kafka.

Isolation vertieft – Kata und Firecracker

In Artikel 2 [5] haben wir Prozess-Isolation nur kurz erwähnt. Hier der Nachtrag. Jeder Function-Pod in v4 läuft in einer Firecracker-MicroVM [3]. Die runtimeClassName: kata-fc im runtimePodSpec sorgt dafür, dass Kubernetes den Pod über den kata-fc-Shim startet statt über einen normalen Container-Runtime. Die fünf Nodes im evr-invoke-Pool tragen den Taint sandbox=true:NoSchedule, nur Pods mit der entsprechenden Toleration landen dort. Function-Workloads verschiedener Kunden teilen sich Hardware, aber keinen Kernel.

AWS Lambda hat Firecracker im Hyperscale-Massstab pioneered [1]. EVRTNG liefert es via Standard-Kubernetes-RuntimeClass, ohne Custom-Integration in den FaaS-Engine. Das ist der Grund, warum die Isolationsstufe zwischen v3 und v4 unverändert bleibt – sie hängt nicht am FaaS, sondern am Kubernetes.

Lessons Learned

Drei konkrete Erkenntnisse, nicht glorifiziert.

Erstens: Die Kata devmapper-Saga. Fissions runtimePodSpec erlaubt runtimeClassName, aber Kata mit Firecracker braucht einen Snapshotter für containerd. Wir sind mit dem devmapper-Snapshotter gestartet, LVM auf Loopback. Das war fragil – das Repo enthält 15 kata-* Dateien (kata-devmapper-diag, kata-isolation-test, kata-overlay-install und weitere), entstanden über Tage Debugging, bis die erste Function in der MicroVM lief. Fallback auf overlayfs hat das Problem behoben. Wer Kata+Firecracker auf Kubernetes betreibt, sollte overlayfs prüfen, bevor er devmapper versucht. Devmapper ist der dokumentierte Default für Firecracker, aber der dokumentierte Default geht von echter Block-Storage-Backing aus, nicht von einer Loopback-Datei.

Zweitens: Fission v1.27.0 [2] benötigt Kubernetes >=1.32. Unser Gardener-Shoot lief auf 1.31. Zwei Optionen: Shoot-Upgrade auf 1.32 oder Pin auf Fission v1.24.x (die letzte Linie, die 1.28 unterstützt). Entscheidung: Shoot-Upgrade. Ein FaaS-Engine-Pin auf eine ältere Version wäre eine Dauerbelastung geworden, ein Kubernetes-Versionsupgrade ist ein Tagesprojekt. Wir nehmen das Upgrade und akzeptieren, dass andere Komponenten bei einem 1.32-Sprung mitgeprüft werden müssen.

Drittens: Billing-Pipeline neu denken. OpenWhisk emittierte Kafka-Events – das war bequem, der Consumer musste nur konsumieren. Fission emittiert nichts. APISIX http-logger als Ersatz ist kürzer und direkter, aber der Consumer musste von einem Kafka-Consumer auf einen Flask-HTTP-Endpoint umgeschrieben werden. Activation Records entfallen als Billing-Quelle, stattdessen liefern APISIX-Logs alle Felder, die wir brauchen: URI, Status, Latenz, Namespace. Die Pricing-Logik blieb identisch, der Eingangskanal wurde neu geschrieben. Wer ein ähnliches Setup fährt, sollte planen, dass Fission keine Event-Quelle ist – man muss eine eigene vorschalten, in unserem Fall APISIX.

Ausblick

Die Migration läuft. Dieser Artikel deckt das Engineering-Delta ab, der August-Artikel die Strategie [1]. Im nächsten Beitrag der Serie geht es um Multi-Tenancy mit Fission dynamic tenancy in Produktion: FissionTenant CR, HMAC-Flow, Namespace-Provisioning vertieft. Wer wissen will, wie ein Tenant technisch provisioniert wird und wie die HMAC-Keys durch den Stack fliessen, findet das dort.


  1. August-Strategie-Artikel: https://evrtng.cloud/2026/08/15/serverless-computing-2026-update/
  2. Fission v1.27.0 Release: https://github.com/fission/fission/releases/tag/v1.27.0
  3. Kata Containers: https://github.com/kata-containers/kata-containers
  4. Artikel 1 (OpenWhisk-Architektur): https://functions.evrtng.cloud/2026/03/30/warum-wir-apache-openwhisk-gewaehlt-haben-2/
  5. Artikel 2 (Invocation Flow): https://functions.evrtng.cloud/2026/07/17/so-funktioniert-ein-function-aufruf-bei-evrtng/
$ cd ~/blog