ko
ko copied to clipboard
Compile to WASM
Go can compile to WASM (GOOS=js GOARCH=wasm
), and WASM can be packaged into images and executed on K8s clusters using krustlet: https://developer.okta.com/blog/2022/01/28/webassembly-on-kubernetes-with-rust
So, maybe ko
can build a WASM executable, and put it in a multiarch image, alongside the other ko-built archs.
This wouldn't work for Go executables that need to call into their base images (I don't think WASM images have multiple layers, and the environment isn't very linux-ey...yet?), but it should mostly maybe kinda work if they're just static binaries, which is exactly ko's sweet spot.
An example WASM image that kruslet can apparently run: https://explore.ggcr.dev/?image=webassembly.azurecr.io%2Fhello-wasm%3Av1 -- note media types and the annotation.
Questions:
-
What OCI platform does kruslet expect when it's given a multiplatform image?
js/wasm
? -
How should users express this?
ko build ./ --wasm
to additionally produce a WASM image in the index?
- What OCI platform does kruslet expect when it's given a multiplatform image?
js/wasm
?
Kruslet doesn't appear to support pulling from indexes at all, only OCI artifacts with the WASM media types. So this is probably not going to be something worth shoehorning into ko.
This might still make sense as a separate Go-specific pipeline for building WASM images, a la wasm-to-oci with fewer steps, but it won't coexist alongside regular OCI images.
A braindump after thinking about this a bit more:
So in order for this to work, I think we need to agree on a few things with whatever run-this-OCI-image-as-wasm runtime we're working with:
If the runtime:
- looks for an image in a multiarch index with platform
js/wasm
, and - looks at that image's entrypoint to find the wasm file in the image that it should run when it starts (these seem like reasonable expectations to me 🤷♂️)
Then ko
can build with that expectation, and produce an image that the runtime can pull and run.
What this would enable is, a ko
-built app that can run on linux/amd64
, or linux/amd64
, or our magical wasm runtime platform. We really benefit from ko
's assumption that all this image business is just a formality to let us package and distribute a Go binary that can be pulled and run.
Advantages:
- a user can build for standard K8s or some K8s-compatible-ish wasm runtime (like krustlet), and even migrate an app between the two without much change to their build/push process 🤞
- this helps adoption of our magical wasm runtime platform, since it doesn't require a new custom build process targeting the runtime platform
- OCI signing and SBOMing tools (including
ko
) should "just work" here more or less the same as for non-WASM
Some limitations:
-
kodata
probably won't work, or at least won't work for now. That's probably fine for now - the ✨ magical wasm runtime ✨ that adheres to the contract doesn't exist ...yet
Reopening as I still think this is an idea worth pulling on.
cf: Istio's WASM plugins are also packaged as OCI images (building tutorial, spec)
TL;DR: In this case, the format seems to be:
- the image must be a single-arch OCI image (not index)
- the image must contain one layer
- the layer must contain a file named
plugin.wasm
(entrypoint is ignored) - and optionally a
runtime-config.json
This seems fine for Istio, there's not a lot of use cases for multi-platform WASM and non-WASM plugins there I think, but it's something to consider as folks have gone down this path before and made certain decisions.
ref: Similar discussion in buildpacks, to allow buildpacks to run and produce a wasm image instead of the standard OCI image: https://github.com/buildpacks/lifecycle/issues/820
In this case, it would be either-or, and buildpacks wouldn't create a single multi-platform index containing the wasm image and the standard linux/amd64 (e.g.) image. But maybe if that was supported somewhere, they could.
This is indeed very interesting! I think that if we target wasm, we should focus on WASI first. WASI implements a way to run WASM binaries on a runtime, e.g. WasmEdge.
crun has implementations for handling WASI Images already, but is toggled via a feature flag which needs to bet set during crun compile time: https://github.com/second-state/wasmedge-containers-examples/blob/fb049e4d9884699622ffea0963a77f9d7f0b1f39/containerd/install.sh#L38
Here's an example how to create a suitable image: https://github.com/containers/crun/blob/main/docs/wasm-wasi-example.md.
A container Image is considered to be run by a WASI runtime, if the ENTRYPOINT is set to a .wasm binary and an annotation module.wasm.image/variant=compat exists.
See https://github.com/containers/crun/blob/main/src/libcrun/handlers/handler-utils.c for the exact check.
Kubernetes pods, if containerd, cri-o etc. don't use the rebuilt crun binary by default, include a runtimeClass in their spec.
But all of that has some issues: std go doesn't yet provide a way to generate binaries for target WASI. We would need to use tinygo to target wasi, so the options would be:
a) use tinygo's build package in ko (already tried that a bit here, but tinygo uses wasi-libc via cgo bindings and it's a pain to handle)
b) wrap around the tinygo binary with exec.Cmd. This is currently done with the go binary too: https://github.com/google/ko/blob/e4a01f683fdc0ed2b8f4d6729b9149b56ac716be/pkg/build/gobuild.go#L284
If this is something to look forward to, I'd be happy to try and provide an experimental PR.
This issue is stale because it has been open for 90 days with no activity. It will automatically close after 30 more days of inactivity. Keep fresh with the 'lifecycle/frozen' label.