Skip to content

Commit cc84df0

Browse files
authored
build: consume the distill-fs v0.1.1 static release (#62)
Signed-off-by: Tianyu Zhou <albert.zty@antgroup.com>
1 parent 770ed20 commit cc84df0

11 files changed

Lines changed: 234 additions & 61 deletions

File tree

.dockerignore

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Git metadata is not part of the image build. Component versions come from
2-
# their source manifests, and exact revisions remain traceable through the
3-
# parent repository's submodule gitlinks.
2+
# source manifests or pinned releases; revisions remain traceable through
3+
# submodule gitlinks and packaged release provenance.
44
.git
55
**/.git
66

@@ -20,9 +20,9 @@ deploy/standalone/data/
2020
deploy/standalone/logs/
2121
deploy/standalone/output/
2222

23-
# Local component build outputs. The image build compiles these components in
24-
# dedicated Docker stages.
25-
src/distill-fs/target/
23+
# Local source build outputs.
24+
# distill-fs is consumed as a release artifact, not from this checkout.
25+
src/distill-fs/
2626
src/sandboxd/output/
2727
src/yuanrong/
2828

.github/workflows/ci.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102

103103
- name: Initialize build submodules
104104
run: |
105-
git submodule update --init src/sandboxd src/distill-fs
105+
git submodule update --init src/sandboxd
106106
107107
- name: Set up Python
108108
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -210,6 +210,18 @@ jobs:
210210
echo "=== akernel-node: service journal ==="
211211
docker exec akernel-node journalctl --no-pager -n 500 \
212212
-u sandboxd.service -u yuanrong.service || true
213+
docker exec akernel-node bash -c '
214+
shopt -s nullglob
215+
for logfile in \
216+
/home/akernel/logs/sandboxd/sandboxd*.log \
217+
/home/yuanrong/logs/*function_master*.log \
218+
/home/yuanrong/logs/*function_proxy*.log \
219+
/home/yuanrong/logs/*function_agent*.log \
220+
/home/akernel/sandboxd/image_manager/daemons/*/daemon.log; do
221+
echo "=== ${logfile} ==="
222+
tail -n 300 "${logfile}"
223+
done
224+
' || true
213225
fi
214226
215227
- name: Stop standalone AKernel

AGENTS.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ tunnels. The project overview and deployment quick start are in
4848
The open-source AKernel repository contains the SDK, deployment configuration,
4949
build tooling, and examples. Node runtime components such as `sandboxd` and
5050
`distill-fs` are maintained in their own upstream repositories and pinned as
51-
Git submodules. The all-in-one build compiles those revisions and packages the
52-
runtime payloads described in the Build section below.
51+
Git submodules. The all-in-one build compiles sandboxd and downloads the
52+
checksum-pinned static distill-fs release recorded in
53+
`builder/distill-fs-versions.env`. The distill-fs submodule is an optional
54+
source reference, not a build input. See the Build section below for runtime
55+
payloads.
5356

5457
## Common Commands
5558

@@ -129,9 +132,9 @@ actor backend is deprecated and retained only for compatibility with existing
129132
applications. Keep it on its explicitly pinned legacy version; do not advance
130133
it with the default `openyuanrong-sandbox` backend or use it for new features.
131134

132-
Initialize submodules with `git submodule update --init --recursive` before
135+
Initialize sandboxd with `git submodule update --init src/sandboxd` before
133136
building. The all-in-one image builds the sandboxd binaries, including
134-
`firecracker-agent`, and `distill_fs`; installs checksum-pinned gVisor and Kata
137+
`firecracker-agent`; installs checksum-pinned static distill-fs, gVisor, and Kata
135138
artifacts; installs the Firecracker VMM and guest kernel; and constructs the
136139
matching guest-agent initrd. Runc remains build-time optional, and
137140
`AKERNEL_ENABLE_FIRECRACKER=false` excludes the Firecracker payload.
@@ -149,14 +152,24 @@ pins it rather than overriding manifest fields from the AKernel build. Keep
149152
sandboxd's pooled-TAP contract and the matching gVisor compatibility patches
150153
validated together when upgrading.
151154

152-
The submodule gitlinks are the single source of truth for the sandboxd and
153-
distill-fs revisions included in a clean release. `make build` always compiles
154-
the local submodule worktrees, so developers may check out a different commit
155-
or edit either directory and rebuild without pushing first. Each component
156-
maintains and embeds its own semantic version: sandboxd uses
157-
`version/VERSION`, while distill-fs uses the package version in `Cargo.toml`.
158-
AKernel does not inject parent-repository version metadata into component
159-
compilation.
155+
The sandboxd gitlink fixes the source revision compiled by `make build`.
156+
AKernel's `builder/distill-fs-versions.env` fixes the distill-fs release URL
157+
and SHA-256. `make build` compiles the local sandboxd worktree and consumes the
158+
static distill-fs release through `builder/scripts/install-distill-fs.sh`; editing
159+
`src/distill-fs` no longer affects the image. The installer verifies the archive,
160+
provenance, version, binary hash, and static ELF contract, and packages its
161+
licenses and manifest under `/usr/local/share/distill-fs`.
162+
163+
Publish and verify a distill-fs release before updating the AKernel manifest
164+
pin. This dependency does not require a sandboxd source or gitlink change.
165+
Never use a guessed checksum or silently fall back to a source build.
166+
Missing or invalid release pins prevent builds. `make versions` reports the
167+
release tag and archive digest without requiring the distill-fs submodule.
168+
169+
Each component embeds its own semantic version: sandboxd uses
170+
`version/VERSION`, while distill-fs uses its release package version in
171+
`Cargo.toml`. AKernel does not inject parent-repository version metadata into
172+
component compilation.
160173

161174
To test an unreleased openYuanRong core wheel without rebuilding YuanRong,
162175
provide both `OPEN_YR_CORE_WHEEL_URL` and `OPEN_YR_CORE_WHEEL_SHA256` to
@@ -175,7 +188,8 @@ make versions
175188

176189
The final image uses standard OCI labels for the AKernel version and revision.
177190
Component semantic versions are reported by their binaries, and their exact
178-
source revisions are traceable through the AKernel commit's submodule gitlinks.
191+
source revisions are traceable through the sandboxd gitlink and the pinned
192+
distill-fs release's packaged manifest.
179193

180194
## Deploy
181195

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ See the complete [basic usage example](./sdk/python/examples/basic_usage.py), th
187187
- **Sandbox runtimes**: gVisor by default; Kata Containers and Firecracker on
188188
KVM-capable nodes; and an explicitly enabled native Linux runc backend
189189
- **sandboxd**: Sandbox lifecycle daemon with pluggable sandbox runtime integration
190-
- **distill-fs**: Rust-based FUSE filesystem for lazy rootfs access, chunk caching, and deduplication
190+
- **distill-fs**: Rust-based FUSE filesystem for lazy rootfs access, chunk caching, and deduplication; packaged from a static GitHub Release with its version and checksum pinned in AKernel
191191

192192
**Cluster-Wide Services**
193193
- **Distributed Scheduler**: Workload-aware placement and scaling

builder/distill-fs-versions.env

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2026 Ant Group Corporation.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# AKernel's distill-fs release dependency. Verify the published release archive
5+
# before updating its version, URL, and SHA-256 together.
6+
DISTILL_FS_RELEASE=v0.1.1
7+
DISTILL_FS_AMD64_URL=https://github.com/inclusionAI/distill-fs/releases/download/v0.1.1/distill-fs-v0.1.1-linux-amd64.tar.gz
8+
DISTILL_FS_AMD64_SHA256=b16e225e5b777f673bd98dd0968a15c190e333690356552b69a2b2624c3d5dcb

builder/node.Dockerfile

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ ARG AKERNEL_ENABLE_KATA=true
99
ARG AKERNEL_ENABLE_RUNC=false
1010
ARG AKERNEL_ENABLE_FIRECRACKER=true
1111
ARG SANDBOXD_BUILD_IMAGE=golang:1.25.5-bookworm
12-
ARG DISTILL_FS_BUILD_IMAGE=rust:1.85.0-bookworm
1312
ARG OPEN_YR_VERSION=0.10.2rc2
1413
ARG OPEN_YR_CORE_WHEEL_URL=
1514
ARG OPEN_YR_CORE_WHEEL_SHA256=
@@ -214,23 +213,17 @@ RUN mkdir -p /runc/usr/local/bin
214213

215214
FROM runc-runtime-${AKERNEL_ENABLE_RUNC} AS runc-runtime
216215

217-
FROM ${DISTILL_FS_BUILD_IMAGE} AS distill-fs-builder
218-
ENV DEBIAN_FRONTEND=noninteractive \
219-
CARGO_NET_GIT_FETCH_WITH_CLI=true
216+
FROM ubuntu:24.04 AS distill-fs-runtime
217+
ARG TARGETARCH
218+
ARG DISTILL_FS_RELEASE
219+
ARG DISTILL_FS_AMD64_URL
220+
ARG DISTILL_FS_AMD64_SHA256
220221
RUN apt-get update && \
221-
apt-get install -y --no-install-recommends \
222-
ca-certificates \
223-
cmake \
224-
g++ \
225-
gcc \
226-
git \
227-
make \
228-
perl \
229-
pkg-config && \
222+
apt-get install -y --no-install-recommends ca-certificates curl jq binutils && \
230223
rm -rf /var/lib/apt/lists/*
231-
WORKDIR /src/distill-fs
232-
COPY ./src/distill-fs/ ./
233-
RUN cargo build --locked --release --bin distill_fs
224+
COPY ./builder/scripts/install-distill-fs.sh /install-distill-fs.sh
225+
RUN sh /install-distill-fs.sh "$DISTILL_FS_RELEASE" \
226+
"$DISTILL_FS_AMD64_URL" "$DISTILL_FS_AMD64_SHA256" /distill-fs
234227

235228
FROM ${AKERNEL_NODE_BASE_IMAGE}
236229
# Let PID 1 systemd avoid remounting shared host filesystems during shutdown.
@@ -371,7 +364,8 @@ COPY --from=gvisor-runtime /gvisor/runsc /usr/local/bin/runsc
371364
COPY --from=sandboxd-builder /src/sandboxd/output/sandboxd /usr/local/bin/sandboxd
372365
COPY --from=sandboxd-builder /src/sandboxd/output/sbox /usr/local/bin/sbox
373366
COPY --from=sandboxd-builder /src/sandboxd/output/sandbox-logger /usr/local/bin/sandbox-logger
374-
COPY --from=distill-fs-builder /src/distill-fs/target/release/distill_fs /usr/local/bin/distill_fs
367+
COPY --from=distill-fs-runtime /distill-fs/bin/distill_fs /usr/local/bin/distill_fs
368+
COPY --from=distill-fs-runtime /distill-fs/share/distill-fs/ /usr/local/share/distill-fs/
375369
COPY --from=kata-runtime /kata/opt/kata /opt/kata
376370
COPY --from=runc-runtime /runc/usr/local/bin/ /usr/local/bin/
377371
COPY --from=firecracker-runtime /firecracker/ /
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/sh
2+
# Copyright (c) 2026 Ant Group Corporation.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Install the distill-fs release for AKernel. Callers pass pins from
6+
# builder/distill-fs-versions.env, never checksums downloaded with the archive.
7+
set -eu
8+
9+
DISTILL_FS_RELEASE=$1
10+
DISTILL_FS_AMD64_URL=$2
11+
DISTILL_FS_AMD64_SHA256=$3
12+
destination=$4
13+
14+
case "${TARGETARCH:-$(uname -m)}" in
15+
amd64|x86_64) ;;
16+
*) echo "distill-fs release supports linux/amd64 only" >&2; exit 1 ;;
17+
esac
18+
: "${DISTILL_FS_RELEASE:?distill-fs release is not pinned}"
19+
: "${DISTILL_FS_AMD64_URL:?distill-fs release URL is not pinned}"
20+
: "${DISTILL_FS_AMD64_SHA256:?publish and pin the distill-fs release before building}"
21+
printf '%s\n' "$DISTILL_FS_AMD64_SHA256" | grep -Eq '^[0-9a-f]{64}$' || {
22+
echo "invalid distill-fs SHA-256 pin" >&2
23+
exit 1
24+
}
25+
26+
# Keep the download separate from installed files. Image consumers copy only
27+
# bin/ and share/ from this staging directory.
28+
mkdir -p "$destination/download" "$destination/bin" "$destination/share/distill-fs"
29+
archive="$destination/download/distill-fs.tar.gz"
30+
curl -fSL --retry 5 --retry-delay 2 --retry-all-errors \
31+
"$DISTILL_FS_AMD64_URL" -o "$archive"
32+
printf '%s %s\n' "$DISTILL_FS_AMD64_SHA256" "$archive" | sha256sum -c -
33+
tar -xzf "$archive" -C "$destination/download" \
34+
distill_fs manifest.json LICENSE NOTICE Cargo.lock
35+
bundle="$destination/download"
36+
jq -e --arg release "$DISTILL_FS_RELEASE" \
37+
'.component == "distill-fs" and .release_tag == $release and
38+
.version == ($release | ltrimstr("v")) and
39+
.repository == "inclusionAI/distill-fs" and
40+
.target == "x86_64-unknown-linux-musl" and
41+
(.source_revision | test("^[0-9a-f]{40}$")) and
42+
(.binary_sha256 | test("^[0-9a-f]{64}$"))' \
43+
"$bundle/manifest.json" >/dev/null
44+
printf '%s %s\n' "$(jq -r .binary_sha256 "$bundle/manifest.json")" \
45+
"$bundle/distill_fs" | sha256sum -c -
46+
readelf -h "$bundle/distill_fs" >/dev/null
47+
if readelf -l "$bundle/distill_fs" | grep -q INTERP ||
48+
readelf -d "$bundle/distill_fs" | grep -q NEEDED; then
49+
echo "distill-fs release must be a static executable" >&2
50+
exit 1
51+
fi
52+
chmod 0755 "$bundle/distill_fs"
53+
test "$("$bundle/distill_fs" --version)" = "distill_fs ${DISTILL_FS_RELEASE#v}"
54+
install -m 0755 "$bundle/distill_fs" "$destination/bin/distill_fs"
55+
install -m 0644 "$bundle/manifest.json" "$bundle/LICENSE" \
56+
"$bundle/NOTICE" "$bundle/Cargo.lock" "$destination/share/distill-fs/"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python3
2+
"""Exercise the AKernel installer with a real candidate/release archive.
3+
4+
Usage: python3 builder/scripts/test-install-distill-fs.py /path/to/release.tar.gz
5+
Requires curl, jq, binutils, and a Linux/amd64 host. No network is needed.
6+
"""
7+
8+
import hashlib
9+
import io
10+
import json
11+
import os
12+
from pathlib import Path
13+
import subprocess
14+
import sys
15+
import tarfile
16+
import tempfile
17+
18+
19+
archive = Path(sys.argv[1]).resolve()
20+
installer = Path(__file__).with_name("install-distill-fs.sh")
21+
with tarfile.open(archive) as bundle:
22+
files = {name: bundle.extractfile(name).read() for name in (
23+
"distill_fs", "manifest.json", "LICENSE", "NOTICE", "Cargo.lock"
24+
)}
25+
manifest = json.loads(files["manifest.json"])
26+
release = manifest["release_tag"]
27+
digest = hashlib.sha256(archive.read_bytes()).hexdigest()
28+
29+
with tempfile.TemporaryDirectory(prefix="distill-fs-installer-") as work:
30+
work = Path(work)
31+
32+
def check(name, asset=archive, checksum=digest, tag=release, arch="amd64", error=None):
33+
destination = work / name
34+
result = subprocess.run(
35+
["sh", str(installer), tag, asset.as_uri(), checksum, str(destination)],
36+
env={**os.environ, "TARGETARCH": arch},
37+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True,
38+
)
39+
binary = destination / "bin/distill_fs"
40+
if error is None:
41+
assert result.returncode == 0, result.stdout
42+
assert binary.read_bytes() == files["distill_fs"]
43+
for filename in ("manifest.json", "LICENSE", "NOTICE", "Cargo.lock"):
44+
assert (destination / "share/distill-fs" / filename).read_bytes() == files[filename]
45+
else:
46+
assert result.returncode != 0, name
47+
assert error in result.stdout, result.stdout
48+
assert not binary.exists(), "a rejected artifact must not be installed"
49+
print(f"PASS {name}")
50+
51+
def altered_archive(name, binary, binary_hash):
52+
data = {**files, "distill_fs": binary}
53+
metadata = {**manifest, "binary_sha256": binary_hash}
54+
data["manifest.json"] = json.dumps(metadata).encode()
55+
path = work / f"{name}.tar.gz"
56+
with tarfile.open(path, "w:gz") as tar:
57+
for filename, content in data.items():
58+
entry = tarfile.TarInfo(filename)
59+
entry.size = len(content)
60+
tar.addfile(entry, io.BytesIO(content))
61+
return path, hashlib.sha256(path.read_bytes()).hexdigest()
62+
63+
check("valid")
64+
check("missing-pin", checksum="", error="publish and pin")
65+
check("bad-pin", checksum="not-a-digest", error="invalid distill-fs SHA-256")
66+
check("corrupt-archive", checksum="0" * 64, error="FAILED")
67+
check("wrong-version", tag="v999.0.0", error="")
68+
check("unsupported-arch", arch="arm64", error="linux/amd64 only")
69+
bad, sha = altered_archive("bad-binary-hash", files["distill_fs"], "0" * 64)
70+
check("bad-binary-hash", asset=bad, checksum=sha, error="FAILED")
71+
# A correctly checksummed bundle must still reject a dynamic executable.
72+
dynamic = Path("/bin/true").read_bytes()
73+
headers = subprocess.check_output(["readelf", "-l", "/bin/true"], text=True)
74+
assert "INTERP" in headers, "this negative fixture needs a dynamic /bin/true"
75+
bad, sha = altered_archive("dynamic-binary", dynamic, hashlib.sha256(dynamic).hexdigest())
76+
check("dynamic-binary", asset=bad, checksum=sha, error="must be a static executable")

deploy/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,11 @@ deploy/
367367
├── terraform/ # multi-cloud provisioning (aliyun, huaweicloud, shared)
368368
└── scripts/ # deployment and image helper scripts
369369
```
370+
371+
### distill-fs release dependency
372+
373+
The all-in-one image downloads the static Linux/amd64 distill-fs release pinned in `builder/distill-fs-versions.env`. It does not compile `src/distill-fs`; that checkout is optional source reference. `make versions` reports the release tag and archive SHA-256. The AKernel installer at `builder/scripts/install-distill-fs.sh` checks the archive, package provenance, CLI version, and static ELF linkage, and retains licenses and provenance in `/usr/local/share/distill-fs`.
374+
375+
Publish and verify the distill-fs release before updating the AKernel version, URL, and checksum pin together. Missing or invalid pins stop `make build` before either image is built. There is no source-build fallback.
376+
377+
Validate installation against a downloaded candidate or release with `python3 builder/scripts/test-install-distill-fs.py /path/to/distill-fs-vX.Y.Z-linux-amd64.tar.gz` on Linux/amd64 with curl, jq, and binutils. This checks normal installation and rejects missing/invalid pins, corrupted archives, version/architecture mismatch, binary hash mismatch, and dynamically linked executables. The sandboxd pipeline and gitlink are independent of this dependency.

0 commit comments

Comments
 (0)