android-emulator: GPU rendering on node1 + scale-to-zero wake gate

Viktor's direction (2026-06-12): the emulator is dev-only, so it should
be on-demand, and it should use the T4 where applicable. (1) api36-v5
runs '-gpu host' on the GPU node (nodeSelector + time-slice + EGL libs;
automatic swiftshader fallback if GPU init dies) — screen-on rendering
moves off the CPU (~5 cores → expected 1-2). (2) The wake gate (stdlib
python, owns / on both hostnames) scales the deployment 0→1 on visit and
hands the browser to noVNC when ready; agents GET /wake + /status. The
idle-sleeper CronJob counts established adb/noVNC connections via
/proc/net/tcp (excluding the in-container loopback adb client) and scales
to zero after 4 idle checks (~1h). TF ignores replicas drift. VRAM cost
(~0.5-1GiB) is held only while awake, protecting llama-swap headroom.
This commit is contained in:
Viktor Barzin 2026-06-12 07:52:50 +00:00
parent 39a22b352e
commit f4dd515fd7
7 changed files with 467 additions and 32 deletions

View file

@ -4,8 +4,8 @@
# cmdline-tools and the native libraries the emulator needs at runtime.
#
# Rebuild + push (rare — only when tool/library versions bump):
# docker build -t forgejo.viktorbarzin.me/viktor/android-emulator:api36-v4 .
# docker push forgejo.viktorbarzin.me/viktor/android-emulator:api36-v4
# docker build -t forgejo.viktorbarzin.me/viktor/android-emulator:api36-v5 .
# docker push forgejo.viktorbarzin.me/viktor/android-emulator:api36-v5
FROM eclipse-temurin:17-jdk-jammy
ENV DEBIAN_FRONTEND=noninteractive
@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libpulse0 libgl1 libglu1-mesa libnss3 libasound2 libfontconfig1 \
libx11-6 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
libxfixes3 libxi6 libxrandr2 libxrender1 libxtst6 libxkbcommon0 \
libegl1 libgles2 \
libxkbfile1 libsm6 libice6 libdbus-1-3 \
# virtual display + browser viewing
xvfb x11vnc novnc websockify openbox \

View file

@ -83,12 +83,32 @@ x11vnc -display :0 -nopw -forever -shared -quiet -bg
websockify --web /usr/share/novnc 6080 localhost:5900 &
# --- emulator -----------------------------------------------------------------
# swiftshader = CPU rendering (no GPU dependency); KVM does the heavy lifting.
emulator -avd "$AVD_NAME" \
-gpu swiftshader_indirect -accel on \
-memory "$EMULATOR_RAM_MB" \
-no-audio -no-boot-anim \
&
# Use the host GPU when the NVIDIA runtime injected one (driver libs +
# /dev/nvidia* appear when the pod requests nvidia.com/gpu), otherwise
# swiftshader (CPU rendering). If the GPU launch dies early, fall back to
# swiftshader automatically so the worst case equals CPU rendering.
GPU_FLAG="swiftshader_indirect"
[ -e /dev/nvidiactl ] && GPU_FLAG="host"
echo "Emulator GPU mode: $GPU_FLAG"
launch_emulator() {
emulator -avd "$AVD_NAME" \
-gpu "$1" -accel on \
-memory "$EMULATOR_RAM_MB" \
-no-audio -no-boot-anim \
&
EMU_PID=$!
}
launch_emulator "$GPU_FLAG"
if [ "$GPU_FLAG" = "host" ]; then
sleep 25
if ! kill -0 "$EMU_PID" 2>/dev/null; then
echo "GPU launch (-gpu host) died early — falling back to swiftshader." >&2
rm -f "${ANDROID_AVD_HOME}/${AVD_NAME}.avd"/*.lock
launch_emulator swiftshader_indirect
fi
fi
adb wait-for-device
echo "Emulator up; waiting for boot completion..."