ANSI C in OpenWhisk

Führen Sie nativen C-Code als Serverless-Funktion mit einer benutzerdefinierten Docker-Runtime auf evrtng functions aus. Maximale Performance, null Server zu verwalten.

ANSI C / GCC Docker Runtime Apache OpenWhisk

Warum ANSI C?

OpenWhisk liefert von Haus aus keine ANSI C-Runtime — aber die Docker-Action-Unterstützung bedeutet, dass Sie jedes kompilierte C-Binary als Serverless-Funktion ausführen können. Dieser Ansatz bietet Ihnen:

Nahezu native Performance Kompiliertes C wird direkt ausgeführt — kein Interpreter-Overhead.
Winziges Container-Image Ein statisch gelinktes C-Binary kann ein Container-Image unter 10 MB erzeugen.
Vollständige C-Standardbibliothek GCC, libc und jede Bibliothek, die Sie brauchen — volle Kontrolle via Dockerfile.
Native OpenWhisk-Integration Ihre C-Funktion empfängt JSON-Parameter, gibt JSON zurück — wie jede andere Action.

// Voraussetzungen

wsk CLI OpenWhisk CLI installiert und konfiguriert Docs →
Docker Docker Desktop oder Docker Engine lokal ausgeführt Docs →
GCC GCC-Compiler für lokale Tests (optional)
Registry Docker Hub-Konto oder private Container-Registry Docs →
Auth Key evrtng functions API-Key aus Ihrem Konto Docs →
check prerequisites
$ wsk --version
wsk 1.2.0

$ docker --version
Docker version 25.0.0

$ gcc --version
gcc (Ubuntu 11.4.0) 11.4.0

Schritt-für-Schritt-Anleitung

01

Schreiben Sie Ihre C-Funktion

OpenWhisk kommuniziert mit Ihrer Funktion via stdin/stdout über ein einfaches JSON-Protokoll. Ihr C-Programm liest einen JSON-String von stdin und schreibt einen JSON-String nach stdout.

example.c
/*
 * evrtng functions – ANSI C action example
 * Reads JSON from argv[1] (OpenWhisk passes params as arg),
 * prints a JSON result to stdout.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {

    /* Log to stderr – shows up in wsk activation logs */
    fprintf(stderr,
        "[evrtng] ANSI C action invoked\n");

    /* Safely read the name param from argv[1] */
    const char *args = (argc > 1) ? argv[1] : "undefined";

    /* Print structured JSON result to stdout */
    printf(
        "{ \"msg\": \"Hello from ANSI C!\", \"args\": \"%s\" }\n",
        args
    );

    return 0;
}
compile & test locally
# Compile
$ gcc -o example example.c

# Test with a mock argument
$ ./example '{"name":"evrtng"}'
{ "msg": "Hello from ANSI C!", "args": "{\"name\":\"evrtng\"}" }
02

Dockerfile erstellen

Verpacken Sie das kompilierte Binary in einem minimalen Docker-Image. Wir kompilieren innerhalb des Containers, um sicherzustellen, dass das Binary zur Runtime-Architektur passt.

Dockerfile
# Stage 1 – Build
FROM ubuntu:22.04 AS builder

RUN apt-get update && \
    apt-get install -y gcc libc-dev && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /build
COPY example.c .

# Compile with optimisations and static linking
RUN gcc -O2 -static -o example example.c

# Stage 2 – Runtime (minimal scratch image)
FROM ubuntu:22.04

RUN apt-get update && \
    apt-get install -y libc6 && \
    rm -rf /var/lib/apt/lists/*

COPY --from=builder /build/example /usr/local/bin/example

# OpenWhisk calls this entrypoint
ENTRYPOINT ["/usr/local/bin/example"]

Der Multi-Stage-Build hält das finale Image klein. Die Verwendung von -static für das C-Binary macht es vollständig selbstenthalten, sogar auf einem minimalen Base-Image.

03

Docker-Image builden & pushen

Builden Sie das Image lokal und pushen Sie es in eine Registry, auf die evrtng functions zugreifen kann. Ersetzen Sie YOUR_DOCKERHUB_USER durch Ihren Docker Hub-Benutzernamen.

build & push
# Build the image
$ docker build -t YOUR_DOCKERHUB_USER/openwhisk-ansi-c .

Step 1/8 : FROM ubuntu:22.04 AS builder
...
Successfully built a1b2c3d4e5f6
Successfully tagged yourusername/openwhisk-ansi-c:latest

# Log in to Docker Hub
$ docker login

# Push the image
$ docker push YOUR_DOCKERHUB_USER/openwhisk-ansi-c

The push refers to repository [docker.io/yourusername/openwhisk-ansi-c]
latest: digest: sha256:abc123... size: 27.4 MB

# Verify the image is accessible
$ docker pull YOUR_DOCKERHUB_USER/openwhisk-ansi-c
04

Action in OpenWhisk registrieren

Erstellen Sie die Action mit dem Flag --docker und verweisen Sie auf Ihr gepushtes Image. Das Flag --web true macht sie per HTTP zugänglich.

wsk action create
# Create the action with your Docker image
$ wsk action create ansiCAction \
    --docker YOUR_DOCKERHUB_USER/openwhisk-ansi-c \
    --web true

ok: created action ansiCAction

# Verify it was registered
$ wsk action list | grep ansiCAction
/guest/default/ansiCAction    private  blackbox

# Get the public web URL
$ wsk action get ansiCAction --url
https://fnc.evrtng.cloud/api/v1/web/guest/default/ansiCAction
05

Aufrufen & Überwachen

Testen Sie Ihre C-Funktion über die wsk CLI oder über HTTP. Prüfen Sie Logs, um die stderr-Ausgabe Ihres C-Programms zu sehen.

invoke via CLI
# Blocking invocation with params
$ wsk action invoke ansiCAction \
    --result \
    --param args "test-input"

{
    "msg": "Hello from ANSI C!",
    "args": "test-input"
}

# Async invocation
$ wsk action invoke ansiCAction --param args "async-test"
ok: invoked /guest/default/ansiCAction with id a1b2c3d4...

# View the logs (stderr output from your C program)
$ wsk activation logs a1b2c3d4
2025-01-01T12:00:00.000Z stderr: [evrtng] ANSI C action invoked
invoke via HTTP (Web Action)
# POST via curl
$ curl -X POST \
  https://fnc.evrtng.cloud/api/v1/web/guest/default/ansiCAction \
  -H "Content-Type: application/json" \
  -d '{"args": "curl-test"}'

{
    "msg": "Hello from ANSI C!",
    "args": "curl-test"
}

# GET with query param
$ curl "https://fnc.evrtng.cloud/api/v1/web/guest/default/ansiCAction?args=hello"
{ "msg": "Hello from ANSI C!", "args": "hello" }

JSON mit cJSON parsen

Für den realen Einsatz möchten Sie die JSON-Parameter parsen, die OpenWhisk übergibt. Hier ist ein vollständigeres Beispiel mit der schlanken cJSON-Bibliothek.

action_json.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"   /* cJSON: MIT License */

int main(int argc, char *argv[]) {

    /* OpenWhisk passes JSON as first CLI argument */
    if (argc < 2) {
        printf("{\"error\":\"no params\"}\n");
        return 1;
    }

    /* Parse input JSON */
    cJSON *input = cJSON_Parse(argv[1]);
    if (!input) {
        printf("{\"error\":\"invalid JSON\"}\n");
        return 1;
    }

    /* Extract "name" field */
    cJSON *name_item = cJSON_GetObjectItem(input, "name");
    const char *name = (name_item && cJSON_IsString(name_item))
        ? name_item->valuestring
        : "World";

    /* Build and output the response */
    cJSON *output = cJSON_CreateObject();
    cJSON_AddStringToObject(output, "message",
        strcat(strcpy(malloc(64), "Hello, "), name));
    cJSON_AddStringToObject(output, "runtime", "ANSI C");

    printf("%s\n", cJSON_Print(output));

    cJSON_Delete(input);
    cJSON_Delete(output);
    return 0;
}

Dockerfile mit cJSON

FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    gcc libc-dev wget && \
    rm -rf /var/lib/apt/lists/*

# Download cJSON (MIT license)
RUN wget -q \
  https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.c \
  https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.h

WORKDIR /build
COPY action_json.c cJSON.c cJSON.h .

# Compile with cJSON
RUN gcc -O2 -o action action_json.c cJSON.c -lm

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y libc6 && \
    rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/action /usr/local/bin/action
ENTRYPOINT ["/usr/local/bin/action"]
deploy & test
$ docker build -t YOU/ow-c-json .
$ docker push YOU/ow-c-json

$ wsk action create cJsonAction \
    --docker YOU/ow-c-json \
    --web true

$ wsk action invoke cJsonAction \
    --result \
    --param name "evrtng"

{
  "message": "Hello, evrtng",
  "runtime": "ANSI C"
}

Verwaltung Ihrer C-Action

wsk cheat sheet – C actions
# Update after rebuilding the Docker image
$ wsk action update ansiCAction \
    --docker YOU/openwhisk-ansi-c

# Set memory limit (e.g. 256 MB for C processing)
$ wsk action update ansiCAction \
    --docker YOU/openwhisk-ansi-c \
    --memory 256

# Set a timeout (e.g. 30 seconds)
$ wsk action update ansiCAction \
    --docker YOU/openwhisk-ansi-c \
    --timeout 30000

# View action details
$ wsk action get ansiCAction

# Use in a sequence with other actions
$ wsk action create myPipeline \
    --sequence ansiCAction,anotherAction

# Delete the action
$ wsk action delete ansiCAction
ok: deleted action ansiCAction

Implementierungshinweise

01 OpenWhisk übergibt Parameter als JSON-String in argv[1], nicht via stdin. Ihr C-Programm muss argv[1] auslesen.
02 Der Output muss gültiges JSON auf stdout sein. Alles auf stderr erscheint in den Aktivierungs-Logs (für Debugging verwenden).
03 Das Docker-Image muss öffentlich zugänglich sein oder in einer Registry gehostet werden, die mit evrtng functions konfiguriert ist.
04 Kompilieren Sie aus Sicherheitsgründen mit -fstack-protector-strong und validieren Sie alle Eingaben vor der Verwendung.
05 Wenn Sie Shared Libraries (openssl, curl etc.) benötigen, linken Sie diese statisch oder installieren Sie sie im Docker-Image.
06 cJSON (MIT-Lizenz) ist eine sichere Wahl für die JSON-Verarbeitung in C. libjansson und yyjson sind ebenfalls gute Alternativen.

Nächste Schritte