Skip to content

Commit 7d145c3

Browse files
dilyevskyclaude
andcommitted
[edgefunc] Add EdgeController for per-namespace runtime management (APO-408)
Transition from per-function containers to per-namespace runtimes with dynamically-loaded functions via edge-runtime control API. New components: - pkg/edgefunc/controller: RuntimeManager, FunctionDeployer, FunctionRouter - pkg/edgefunc/mainservice: Main service TypeScript for edge-runtime Changes: - runc: Add ExecNamespace() for namespace-based containers with control API - backplane: Support dual-mode reconciliation (controller vs legacy) - ci: Build from apoxy-dev/edge-runtime fork, bundle main-service.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ccc3c8e commit 7d145c3

File tree

13 files changed

+1846
-59
lines changed

13 files changed

+1846
-59
lines changed

ci/main.go

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,18 @@ func (m *ApoxyCli) PublishGithubRelease(
371371
})
372372
}
373373

374+
// EdgeRuntimeVersion is the version of the Apoxy edge-runtime fork.
375+
const EdgeRuntimeVersion = "v0.1.0"
376+
374377
func (m *ApoxyCli) BuildEdgeRuntime(
375378
ctx context.Context,
376379
platform string,
377380
// +optional
378381
src *dagger.Directory,
379382
) *dagger.Container {
380383
if src == nil {
381-
src = dag.Git("https://github.com/supabase/edge-runtime").
382-
Tag("v1.62.2").
384+
src = dag.Git("https://github.com/apoxy-dev/edge-runtime").
385+
Branch("main").
383386
Tree()
384387
}
385388
p := dagger.Platform(platform)
@@ -392,13 +395,47 @@ func (m *ApoxyCli) BuildEdgeRuntime(
392395
WithExec([]string{"cargo", "build", "--release"})
393396
}
394397

395-
// PullEdgeRuntime pulls the edge runtime image from dockerhub.
398+
// PullEdgeRuntime builds the Apoxy edge-runtime fork from source.
399+
// The built container includes the edge-runtime binary and main service.
396400
func (m *ApoxyCli) PullEdgeRuntime(
397401
ctx context.Context,
398402
p dagger.Platform,
403+
// +optional
404+
apoxyCliSrc *dagger.Directory,
399405
) *dagger.Container {
400-
return dag.Container(dagger.ContainerOpts{Platform: p}).
401-
From("docker.io/supabase/edge-runtime:v1.62.2")
406+
goarch := archOf(p)
407+
408+
// Build edge-runtime from source.
409+
edgeRuntimeSrc := dag.Git("https://github.com/apoxy-dev/edge-runtime").
410+
Branch("main").
411+
Tree()
412+
413+
builder := dag.Container(dagger.ContainerOpts{Platform: p}).
414+
From("rust:1.82.0-bookworm").
415+
WithExec([]string{"apt-get", "update"}).
416+
WithExec([]string{"apt-get", "install", "-y", "llvm-dev", "libclang-dev", "gcc", "cmake", "binutils"}).
417+
WithMountedCache("/usr/local/cargo/registry", dag.CacheVolume("cargo-registry-"+goarch)).
418+
WithMountedCache("/src/target", dag.CacheVolume("cargo-target-"+goarch)).
419+
WithWorkdir("/src").
420+
WithDirectory("/src", edgeRuntimeSrc).
421+
WithExec([]string{"cargo", "build", "--release"}).
422+
WithExec([]string{"cp", "/src/target/release/edge-runtime", "/edge-runtime"})
423+
424+
// Create a minimal container with the binary and main service.
425+
ctr := dag.Container(dagger.ContainerOpts{Platform: p}).
426+
From("debian:bookworm-slim").
427+
WithExec([]string{"apt-get", "update"}).
428+
WithExec([]string{"apt-get", "install", "-y", "libssl-dev", "ca-certificates"}).
429+
WithExec([]string{"rm", "-rf", "/var/lib/apt/lists/*"}).
430+
WithFile("/usr/local/bin/edge-runtime", builder.File("/edge-runtime")).
431+
WithExec([]string{"mkdir", "-p", "/etc/main"})
432+
433+
// Copy the main service from this repo.
434+
if apoxyCliSrc != nil {
435+
ctr = ctr.WithFile("/etc/main/index.ts", apoxyCliSrc.File("pkg/edgefunc/mainservice/main-service.ts"))
436+
}
437+
438+
return ctr
402439
}
403440

404441
// BuildAPIServer builds an API server binary.
@@ -421,12 +458,13 @@ func (m *ApoxyCli) BuildAPIServer(
421458
WithEnvVariable("CC", fmt.Sprintf("zig-wrapper cc --target=%s-linux-musl", canonArchFromGoArch(goarch))).
422459
WithExec([]string{"go", "build", "-o", "apiserver", "./cmd/apiserver"})
423460

424-
runtimeCtr := m.PullEdgeRuntime(ctx, p)
461+
runtimeCtr := m.PullEdgeRuntime(ctx, p, src)
425462

426463
return dag.Container(dagger.ContainerOpts{Platform: p}).
427464
From("cgr.dev/chainguard/wolfi-base:latest").
428465
WithFile("/bin/apiserver", builder.File("/src/apiserver")).
429466
WithFile("/bin/edge-runtime", runtimeCtr.File("/usr/local/bin/edge-runtime")).
467+
WithDirectory("/etc/main", runtimeCtr.Directory("/etc/main")).
430468
WithEntrypoint([]string{"/bin/apiserver"})
431469
}
432470

@@ -475,7 +513,7 @@ func (m *ApoxyCli) BuildBackplane(
475513
WithExec([]string{"go", "build", "-o", "/src/" + otelOut}).
476514
WithWorkdir("/src")
477515

478-
runtimeCtr := m.PullEdgeRuntime(ctx, p)
516+
runtimeCtr := m.PullEdgeRuntime(ctx, p, src)
479517

480518
return dag.Container(dagger.ContainerOpts{Platform: p}).
481519
From("cgr.dev/chainguard/wolfi-base:latest").
@@ -484,6 +522,7 @@ func (m *ApoxyCli) BuildBackplane(
484522
WithFile("/bin/dial-stdio", builder.File(dsOut)).
485523
WithFile("/bin/otel-collector", builder.File(otelOut)).
486524
WithFile("/bin/edge-runtime", runtimeCtr.File("/usr/local/bin/edge-runtime")).
525+
WithDirectory("/etc/main", runtimeCtr.Directory("/etc/main")).
487526
WithExec([]string{
488527
"/bin/backplane",
489528
"--project_id=apoxy",

cmd/backplane/main.go

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"github.com/ClickHouse/clickhouse-go/v2"
1818
chdriver "github.com/ClickHouse/clickhouse-go/v2/lib/driver"
19+
"github.com/coredns/coredns/plugin"
1920
"github.com/google/uuid"
2021
"google.golang.org/grpc"
2122
"k8s.io/apimachinery/pkg/runtime"
@@ -34,6 +35,7 @@ import (
3435
"github.com/apoxy-dev/apoxy/pkg/backplane/metrics"
3536
"github.com/apoxy-dev/apoxy/pkg/backplane/wasm/ext_proc"
3637
"github.com/apoxy-dev/apoxy/pkg/backplane/wasm/manifest"
38+
edgefuncctrl "github.com/apoxy-dev/apoxy/pkg/edgefunc/controller"
3739
"github.com/apoxy-dev/apoxy/pkg/edgefunc/runc"
3840
"github.com/apoxy-dev/apoxy/pkg/log"
3941
"github.com/apoxy-dev/apoxy/pkg/net/dns"
@@ -94,6 +96,9 @@ var (
9496

9597
dnsPort = flag.Int("dns_port", 8053, "Port for the DNS server.")
9698
extIface = flag.String("ext_iface", "eth0", "External interface name.")
99+
100+
useEdgeController = flag.Bool("use_edge_controller", false, "Use new per-namespace EdgeController instead of legacy per-function runtime.")
101+
edgeControllerNS = flag.String("edge_controller_namespace", "default", "Default namespace for EdgeController.")
97102
)
98103

99104
func main() {
@@ -306,15 +311,31 @@ func main() {
306311
if err != nil {
307312
log.Fatalf("failed to set up EdgeFunction controller: %v", err)
308313
}
309-
if err := bpctrl.NewEdgeFunctionRevisionReconciler(
310-
mgr.GetClient(),
311-
*replicaName,
312-
net.JoinHostPort(apiServerHost, strconv.Itoa(*wasmStorePort)),
313-
ms,
314-
*goPluginDir,
315-
*esZipDir,
316-
edgeRuntime,
317-
).SetupWithManager(ctx, mgr, *proxyName); err != nil {
314+
315+
var edgeController *edgefuncctrl.EdgeController
316+
if *useEdgeController {
317+
log.Infof("Using new per-namespace EdgeController (namespace=%s)", *edgeControllerNS)
318+
edgeController = edgefuncctrl.NewEdgeControllerFromRuntime(
319+
edgeRuntime,
320+
*esZipDir,
321+
edgefuncctrl.Namespace(*edgeControllerNS),
322+
)
323+
} else {
324+
log.Infof("Using legacy per-function EdgeRuntime")
325+
}
326+
327+
edgeFuncReconciler := bpctrl.NewEdgeFunctionRevisionReconciler(bpctrl.EdgeFunctionRevisionReconcilerArgs{
328+
Client: mgr.GetClient(),
329+
ReplicaName: *replicaName,
330+
ApiserverHost: net.JoinHostPort(apiServerHost, strconv.Itoa(*wasmStorePort)),
331+
WasmStore: ms,
332+
GoStoreDir: *goPluginDir,
333+
JsStoreDir: *esZipDir,
334+
EdgeRuntime: edgeRuntime,
335+
EdgeController: edgeController,
336+
DefaultNamespace: edgefuncctrl.Namespace(*edgeControllerNS),
337+
})
338+
if err := edgeFuncReconciler.SetupWithManager(ctx, mgr, *proxyName); err != nil {
318339
log.Fatalf("failed to set up EdgeFunction controller: %v", err)
319340
}
320341

@@ -333,9 +354,17 @@ func main() {
333354
}
334355

335356
go func() {
357+
// Use EdgeController's resolver if available, otherwise use legacy runtime's resolver.
358+
var edgeFuncResolver func(next plugin.Handler) plugin.Handler
359+
if edgeController != nil {
360+
edgeFuncResolver = edgeController.Resolver
361+
} else {
362+
edgeFuncResolver = edgeRuntime.Resolver
363+
}
364+
336365
if err := dns.ListenAndServe(
337366
fmt.Sprintf(":%d", *dnsPort),
338-
dns.WithPlugins(edgeRuntime.Resolver, tunnelResolver.Resolver),
367+
dns.WithPlugins(edgeFuncResolver, tunnelResolver.Resolver),
339368
dns.WithBlockNonGlobalIPs(),
340369
); err != nil {
341370
log.Fatalf("failed to start DNS server: %v", err)

0 commit comments

Comments
 (0)