Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fb23738

Browse files
committed
feat: add CubeSandbox base image and entrypoint script
- Introduced `Dockerfile.cube-base` for building a minimal Ubuntu image with `envd` preinstalled, facilitating CubeSandbox template creation. - Added `cube-entrypoint.sh` to manage `envd` as a background process, ensuring readiness for Cube's health checks. - Created GitHub Actions workflow `build-envd-base-image.yml` to automate the image build and push process. - Updated documentation to include usage instructions for the new base image and entrypoint script, along with a tutorial for creating custom templates. - Added example for a demo image using `nginx` on top of the base image. Signed-off-by: jinlong <[email protected]>
1 parent de16eef commit fb23738

12 files changed

Lines changed: 950 additions & 2 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Build envd Base Image
2+
3+
on:
4+
push:
5+
paths:
6+
- 'docker/Dockerfile.cube-base'
7+
- 'docker/cube-entrypoint.sh'
8+
- '.github/workflows/build-envd-base-image.yml'
9+
pull_request:
10+
paths:
11+
- 'docker/Dockerfile.cube-base'
12+
- 'docker/cube-entrypoint.sh'
13+
- '.github/workflows/build-envd-base-image.yml'
14+
workflow_dispatch:
15+
inputs:
16+
envd_ref:
17+
description: 'Upstream e2b-dev/infra ref (tag/branch/SHA) to compile envd from'
18+
required: false
19+
default: '2026.16'
20+
21+
env:
22+
ENVD_REF_DEFAULT: '2026.16'
23+
IMAGE_NAME: ghcr.io/tencentcloud/cubesandbox-base
24+
BASE_OS_TAG_SUFFIX: ubuntu22.04
25+
26+
concurrency:
27+
group: build-envd-base-image-${{ github.ref }}-${{ github.event.inputs.envd_ref || 'default' }}
28+
cancel-in-progress: false
29+
30+
jobs:
31+
build:
32+
runs-on: ubuntu-latest
33+
permissions:
34+
contents: read
35+
packages: write
36+
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v4
40+
41+
- name: Resolve envd ref
42+
id: meta_ref
43+
run: |
44+
envd_ref="${{ github.event.inputs.envd_ref || env.ENVD_REF_DEFAULT }}"
45+
echo "envd_ref=${envd_ref}" >> "${GITHUB_OUTPUT}"
46+
echo "Compiling envd from e2b-dev/infra@${envd_ref}"
47+
48+
- name: Set up Docker Buildx
49+
uses: docker/setup-buildx-action@v3
50+
51+
- name: Log in to GitHub Container Registry
52+
if: github.repository == 'TencentCloud/CubeSandbox' && github.ref == 'refs/heads/master'
53+
uses: docker/login-action@v3
54+
with:
55+
registry: ghcr.io
56+
username: ${{ github.actor }}
57+
password: ${{ secrets.GITHUB_TOKEN }}
58+
59+
- name: Extract image metadata
60+
id: meta
61+
uses: docker/metadata-action@v5
62+
with:
63+
images: ${{ env.IMAGE_NAME }}
64+
tags: |
65+
type=raw,value=${{ steps.meta_ref.outputs.envd_ref }},enable=${{ github.repository == 'TencentCloud/CubeSandbox' && github.ref == 'refs/heads/master' }}
66+
type=raw,value=${{ steps.meta_ref.outputs.envd_ref }}-${{ env.BASE_OS_TAG_SUFFIX }},enable=${{ github.repository == 'TencentCloud/CubeSandbox' && github.ref == 'refs/heads/master' }}
67+
type=raw,value=latest,enable=${{ github.repository == 'TencentCloud/CubeSandbox' && github.ref == 'refs/heads/master' }}
68+
type=sha,prefix=sha-,format=short
69+
labels: |
70+
io.cubesandbox.envd.ref=${{ steps.meta_ref.outputs.envd_ref }}
71+
72+
- name: Build image (load locally for smoke test)
73+
uses: docker/build-push-action@v6
74+
with:
75+
context: docker
76+
file: docker/Dockerfile.cube-base
77+
platforms: linux/amd64
78+
push: false
79+
load: true
80+
tags: cubesandbox-base:ci-smoke
81+
labels: ${{ steps.meta.outputs.labels }}
82+
build-args: |
83+
ENVD_REF=${{ steps.meta_ref.outputs.envd_ref }}
84+
cache-from: type=gha
85+
cache-to: type=gha,mode=max
86+
87+
- name: Smoke test envd /health
88+
run: |
89+
set -euo pipefail
90+
cid="$(docker run -d --rm cubesandbox-base:ci-smoke)"
91+
trap 'docker rm -f "${cid}" >/dev/null 2>&1 || true' EXIT
92+
93+
ok=""
94+
for attempt in 1 2 3 4 5 6 7 8 9 10; do
95+
code="$(docker exec "${cid}" curl -s -o /dev/null -w "%{http_code}" \
96+
http://127.0.0.1:49983/health || true)"
97+
echo "attempt ${attempt}: envd /health => ${code}"
98+
if [ "${code}" = "204" ]; then
99+
ok="yes"
100+
break
101+
fi
102+
sleep 1
103+
done
104+
105+
if [ -z "${ok}" ]; then
106+
echo "envd /health probe never returned 204" >&2
107+
docker exec "${cid}" cat /var/log/envd.log >&2 || true
108+
exit 1
109+
fi
110+
111+
echo "envd inside container:"
112+
docker exec "${cid}" /usr/bin/envd -version
113+
docker exec "${cid}" /usr/bin/envd -commit
114+
115+
- name: Push image
116+
if: github.repository == 'TencentCloud/CubeSandbox' && github.ref == 'refs/heads/master'
117+
uses: docker/build-push-action@v6
118+
with:
119+
context: docker
120+
file: docker/Dockerfile.cube-base
121+
platforms: linux/amd64
122+
push: true
123+
tags: ${{ steps.meta.outputs.tags }}
124+
labels: ${{ steps.meta.outputs.labels }}
125+
build-args: |
126+
ENVD_REF=${{ steps.meta_ref.outputs.envd_ref }}
127+
cache-from: type=gha
128+
cache-to: type=gha,mode=max

docker/Dockerfile.cube-base

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# syntax=docker/dockerfile:1.7
2+
#
3+
# CubeSandbox base image: a minimal Ubuntu image with envd preinstalled so
4+
# that sandboxes built FROM this image expose :49983/health out of the box
5+
# and can be turned into Cube templates without any extra wiring.
6+
#
7+
# envd is compiled from e2b-dev/infra@${ENVD_REF} in a builder stage,
8+
# producing a static linux/amd64 binary that is copied into the final
9+
# ubuntu:22.04 runtime stage alongside a generic entrypoint.
10+
#
11+
# Build example:
12+
# docker build \
13+
# -f docker/Dockerfile.cube-base \
14+
# -t ghcr.io/tencentcloud/cubesandbox-base:2026.16 \
15+
# docker/
16+
17+
ARG ENVD_REF=2026.16
18+
ARG GO_VERSION=1.25.4
19+
ARG UBUNTU_VERSION=22.04
20+
21+
# -------- Stage 1: compile envd from e2b-dev/infra --------
22+
FROM golang:${GO_VERSION}-bookworm AS envd-builder
23+
24+
ARG ENVD_REF
25+
ARG ENVD_UPSTREAM_REPO=https://github.com/e2b-dev/infra.git
26+
27+
RUN apt-get update \
28+
&& apt-get install -y --no-install-recommends git ca-certificates \
29+
&& rm -rf /var/lib/apt/lists/*
30+
31+
WORKDIR /src
32+
33+
# Shallow-clone the upstream tag.
34+
RUN git clone --depth 1 --branch "${ENVD_REF}" "${ENVD_UPSTREAM_REPO}" infra
35+
36+
WORKDIR /src/infra/packages/envd
37+
38+
RUN --mount=type=cache,target=/root/.cache/go-build \
39+
--mount=type=cache,target=/go/pkg/mod \
40+
commit_sha="$(git -C /src/infra rev-parse --short HEAD)" \
41+
&& echo "Building envd from ${ENVD_UPSTREAM_REPO}@${ENVD_REF} (commit=${commit_sha})" \
42+
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
43+
go build -a \
44+
-ldflags "-X=main.commitSHA=${commit_sha} -s -w" \
45+
-o /out/envd \
46+
. \
47+
&& chmod +x /out/envd \
48+
&& /out/envd -version \
49+
&& /out/envd -commit
50+
51+
# -------- Stage 2: runtime image --------
52+
FROM ubuntu:${UBUNTU_VERSION}
53+
54+
ARG ENVD_REF
55+
ARG DEBIAN_FRONTEND=noninteractive
56+
57+
ENV ENVD_REF=${ENVD_REF} \
58+
ENVD_PORT=49983 \
59+
LANG=C.UTF-8 \
60+
LC_ALL=C.UTF-8
61+
62+
RUN apt-get update \
63+
&& apt-get install -y --no-install-recommends \
64+
ca-certificates \
65+
curl \
66+
sudo \
67+
tini \
68+
&& rm -rf /var/lib/apt/lists/*
69+
70+
# envd defaults to running file/command operations as the `user` account
71+
# (uid=1000) — the same convention used by e2b's official sandbox templates.
72+
# Provision it here so SDK calls like `sandbox.files.read(path)` work
73+
# out of the box without needing to pass `user="root"` everywhere.
74+
RUN useradd --create-home --uid 1000 --shell /bin/bash user \
75+
&& echo 'user ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/user \
76+
&& chmod 0440 /etc/sudoers.d/user
77+
78+
COPY --from=envd-builder /out/envd /usr/bin/envd
79+
COPY cube-entrypoint.sh /usr/local/bin/cube-entrypoint.sh
80+
81+
RUN chmod +x /usr/bin/envd /usr/local/bin/cube-entrypoint.sh \
82+
&& mkdir -p /var/log \
83+
&& printf '%s\n' "${ENVD_REF}" > /etc/cubesandbox-envd-ref \
84+
&& /usr/bin/envd -version \
85+
&& /usr/bin/envd -commit
86+
87+
LABEL org.opencontainers.image.source="https://github.com/TencentCloud/CubeSandbox" \
88+
org.opencontainers.image.title="cubesandbox-base" \
89+
org.opencontainers.image.description="CubeSandbox base image with envd compiled from e2b-dev/infra@${ENVD_REF}" \
90+
org.opencontainers.image.licenses="Apache-2.0" \
91+
io.cubesandbox.envd.ref="${ENVD_REF}"
92+
93+
EXPOSE 49983
94+
95+
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/cube-entrypoint.sh"]
96+
CMD []

docker/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# docker/
2+
3+
Dockerfiles used by CubeSandbox CI.
4+
5+
## `Dockerfile.builder`
6+
7+
Toolchain image used to compile CubeSandbox components (Go, Rust, kernel
8+
tooling, etc.). Published as `ghcr.io/tencentcloud/cubesandbox-builder`
9+
by [`.github/workflows/build-builder-image.yml`](../.github/workflows/build-builder-image.yml).
10+
11+
## `Dockerfile.cube-base` (+ `cube-entrypoint.sh`)
12+
13+
Base image for user-supplied sandbox templates. It is `ubuntu:22.04`
14+
with `envd` preinstalled on `:49983`, so any image built `FROM` it is
15+
already ready for Cube's readiness probe. Published as
16+
`ghcr.io/tencentcloud/cubesandbox-base` by
17+
[`.github/workflows/build-envd-base-image.yml`](../.github/workflows/build-envd-base-image.yml),
18+
which compiles `envd` in-place from
19+
[`e2b-dev/infra`](https://github.com/e2b-dev/infra) at tag `2026.16`
20+
(override via `workflow_dispatch` input `envd_ref`) before baking the
21+
image.
22+
23+
Minimal consumer example:
24+
25+
```dockerfile
26+
FROM ghcr.io/tencentcloud/cubesandbox-base:2026.16
27+
RUN pip install pandas
28+
```
29+
30+
Full user-facing tutorial (path A vs path B, entrypoint contract,
31+
troubleshooting) lives in the Cube docs site:
32+
33+
- English: [Bring Your Own Image (envd)](../docs/guide/tutorials/bring-your-own-image.md)
34+
- 中文:[自带镜像接入 (envd)](../docs/zh/guide/tutorials/bring-your-own-image.md)

docker/cube-entrypoint.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/sh
2+
# CubeSandbox base image entrypoint.
3+
#
4+
# Contract:
5+
# 1. Always start envd in the background on ${ENVD_PORT:-49983} so that
6+
# CubeMaster's readiness probe (:49983/health) passes within ~1s.
7+
# 2. If a user CMD is provided (i.e. $# > 0), exec it as the foreground
8+
# process; envd stays alive in the background. On SIGTERM/SIGINT we
9+
# forward the signal to the user process; envd is reaped by tini.
10+
# 3. If no CMD is provided, wait on envd as the foreground process so the
11+
# container stays up as a pure envd sandbox.
12+
#
13+
# Environment variables:
14+
# ENVD_PORT Port envd listens on (default: 49983).
15+
# ENVD_EXTRA_ARGS Extra flags appended to the envd invocation (optional).
16+
# ENVD_LOG_FILE Where to redirect envd stdout/stderr (default:
17+
# /var/log/envd.log). Set to "-" to inherit the container
18+
# stdio.
19+
20+
set -eu
21+
22+
ENVD_BIN="${ENVD_BIN:-/usr/bin/envd}"
23+
ENVD_PORT="${ENVD_PORT:-49983}"
24+
ENVD_LOG_FILE="${ENVD_LOG_FILE:-/var/log/envd.log}"
25+
ENVD_EXTRA_ARGS="${ENVD_EXTRA_ARGS:-}"
26+
27+
if [ ! -x "${ENVD_BIN}" ]; then
28+
echo "cube-entrypoint: envd binary not found or not executable at ${ENVD_BIN}" >&2
29+
exit 127
30+
fi
31+
32+
start_envd() {
33+
# shellcheck disable=SC2086
34+
if [ "${ENVD_LOG_FILE}" = "-" ]; then
35+
"${ENVD_BIN}" -port "${ENVD_PORT}" ${ENVD_EXTRA_ARGS} &
36+
else
37+
mkdir -p "$(dirname "${ENVD_LOG_FILE}")"
38+
"${ENVD_BIN}" -port "${ENVD_PORT}" ${ENVD_EXTRA_ARGS} \
39+
>>"${ENVD_LOG_FILE}" 2>&1 &
40+
fi
41+
ENVD_PID=$!
42+
echo "cube-entrypoint: started envd (pid=${ENVD_PID}) on port ${ENVD_PORT}" >&2
43+
}
44+
45+
start_envd
46+
47+
if [ "$#" -eq 0 ]; then
48+
# No user command: keep envd as the foreground process. tini is PID 1,
49+
# so we simply wait for envd to exit (or be signalled).
50+
wait "${ENVD_PID}"
51+
exit $?
52+
fi
53+
54+
# User command provided: forward termination signals so the user process can
55+
# shut down cleanly; envd will be reaped by tini when the container stops.
56+
USER_PID=""
57+
forward_signal() {
58+
sig="$1"
59+
if [ -n "${USER_PID}" ]; then
60+
kill -s "${sig}" "${USER_PID}" 2>/dev/null || true
61+
fi
62+
}
63+
64+
trap 'forward_signal TERM' TERM
65+
trap 'forward_signal INT' INT
66+
trap 'forward_signal HUP' HUP
67+
68+
"$@" &
69+
USER_PID=$!
70+
echo "cube-entrypoint: exec user command (pid=${USER_PID}): $*" >&2
71+
72+
# Wait on the user command; propagate its exit status.
73+
set +e
74+
wait "${USER_PID}"
75+
rc=$?
76+
set -e
77+
exit "${rc}"

docs/.vitepress/config.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ export default withMermaid(defineConfig({
4343
text: 'Tutorials',
4444
items: [
4545
{ text: 'Create Templates from OCI Image', link: '/guide/tutorials/template-from-image' },
46-
{ text: 'Examples', link: '/guide/tutorials/examples' }
46+
{ text: 'Examples', link: '/guide/tutorials/examples' },
47+
{ text: 'Custom Image', link: '/guide/tutorials/bring-your-own-image' }
4748
]
4849
},
4950
{
@@ -101,7 +102,8 @@ export default withMermaid(defineConfig({
101102
text: '场景教程',
102103
items: [
103104
{ text: '从 OCI 镜像制作模板', link: '/zh/guide/tutorials/template-from-image' },
104-
{ text: '示例项目', link: '/zh/guide/tutorials/examples' }
105+
{ text: '示例项目', link: '/zh/guide/tutorials/examples' },
106+
{ text: '自定义镜像', link: '/zh/guide/tutorials/bring-your-own-image' }
105107
]
106108
},
107109
{

0 commit comments

Comments
 (0)