release v0.1.8

This commit is contained in:
gpuk-release
2026-09-18 00:11:58 +00:00
parent 983733003b
commit a9df4fbc1f
4 changed files with 168 additions and 26 deletions
+109 -13
View File
@@ -92,7 +92,9 @@ install_binary() { # [local-path]
command -v curl >/dev/null || die "curl is required to download the binary" command -v curl >/dev/null || die "curl is required to download the binary"
_url="$GPUK_RELEASE_BASE/$(arch_asset)" _url="$GPUK_RELEASE_BASE/$(arch_asset)"
echo "==> downloading $_url" echo "==> downloading $_url"
curl -fsSL "$_url" -o "$BIN_DEST.new" # Not -s: the binary is tens of MB and a silent download reads as a hang.
curl -fL --progress-bar "$_url" -o "$BIN_DEST.new" \
|| { rm -f "$BIN_DEST.new"; die "cannot download $_url"; }
chmod 0755 "$BIN_DEST.new" chmod 0755 "$BIN_DEST.new"
mv "$BIN_DEST.new" "$BIN_DEST" mv "$BIN_DEST.new" "$BIN_DEST"
elif [ -x "$BIN_DEST" ]; then elif [ -x "$BIN_DEST" ]; then
@@ -170,6 +172,22 @@ port_owner() { # $1 = ss|netstat, $2 = port
fi fi
} }
# This host's LAN IPv4 — what a browser, a worker or a container on the docker
# bridge dials to reach the machine. The route to a public address names the
# source the default route uses (never a docker or libvirt bridge); failing that,
# the first global address on a physical-looking interface; failing that, the
# resolver's word. Empty when nothing answers — the caller says so. Same
# derivation as dev.sh best_host_lan_ipv4 and install.sh's banner.
host_lan_ipv4() {
_ip=$(ip route get 1.1.1.1 2>/dev/null | sed -n 's/.* src \([0-9.]*\).*/\1/p' | head -1)
if [ -z "$_ip" ]; then
_ip=$(ip -o -4 addr show scope global 2>/dev/null \
| awk '$2 !~ /^(virbr|docker|br-|veth|mpqemubr|tun|tap|vnet)/ {sub(/\/.*/, "", $4); print $4; exit}')
fi
[ -n "$_ip" ] || _ip=$(hostname -I 2>/dev/null | awk '{print $1}')
printf '%s' "$_ip"
}
seed_secrets() { # DATA_ROOT seed_secrets() { # DATA_ROOT
_sd="$1/secrets" _sd="$1/secrets"
mkdir -p "$_sd"; chmod 0700 "$_sd" mkdir -p "$_sd"; chmod 0700 "$_sd"
@@ -307,7 +325,12 @@ hint_existing_caches() {
cmd_install() { cmd_install() {
IMAGE=""; MODE="worker"; CLUSTER="default"; DATA_ROOT="/var/lib/gpu-kitchen" IMAGE=""; MODE="worker"; CLUSTER="default"; DATA_ROOT="/var/lib/gpu-kitchen"
CACHE_DIR=""; NETWORK="host"; CONTROLLER_URL=""; BIN_SRC=""; HEALTH_PORT="8001" # bridge, not host (INS-49): on the host network every listener the image opens
# is a host-wide claim, and a box that already runs something on 3000 or 8000
# killed Nitro. Bridged, only the three published ports touch the host; what
# that costs — the host daemon must be told its LAN address and the engines'
# docker network — is written to worker.env below. `--network host` remains.
CACHE_DIR=""; NETWORK="bridge"; CONTROLLER_URL=""; BIN_SRC=""; HEALTH_PORT="8001"
# 1337, not 8080: kept in lockstep with install.sh's default (INS-01). The # 1337, not 8080: kept in lockstep with install.sh's default (INS-01). The
# worker channel and the inference endpoint move the same way (INS-46). # worker channel and the inference endpoint move the same way (INS-46).
ENROLL_TOKEN=""; HTTP_PORT="1337"; MTLS_PORT="8443"; INFERENCE_PORT="8200" ENROLL_TOKEN=""; HTTP_PORT="1337"; MTLS_PORT="8443"; INFERENCE_PORT="8200"
@@ -315,10 +338,11 @@ cmd_install() {
# GPUK_MODEL_TRANSFER_PORT and GPUK_HANDOVER_DATA_PORT from worker.env and # GPUK_MODEL_TRANSFER_PORT and GPUK_HANDOVER_DATA_PORT from worker.env and
# announces the first two to the controller, so moving them here is complete. # announces the first two to the controller, so moving them here is complete.
DATA_PORT="8300"; TRANSFER_PORT="8301"; HANDOVER_PORT="8302" DATA_PORT="8300"; TRANSFER_PORT="8301"; HANDOVER_PORT="8302"
PROFILE=""; DOMAIN=""; DRY_RUN=0 PROFILE=""; DOMAIN=""; DRY_RUN=0; RESET_MANIFEST=0
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case "$1" in
--image) IMAGE="$2"; shift 2 ;; --image) IMAGE="$2"; shift 2 ;;
--reset-manifest) RESET_MANIFEST=1; shift ;;
--mode) MODE="$2"; shift 2 ;; --mode) MODE="$2"; shift 2 ;;
--cluster) CLUSTER="$2"; shift 2 ;; --cluster) CLUSTER="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;; --profile) PROFILE="$2"; shift 2 ;;
@@ -514,6 +538,23 @@ cmd_install() {
write_controller_manifest write_controller_manifest
# The host daemon and app container share DATA_ROOT. Only controller-mode # The host daemon and app container share DATA_ROOT. Only controller-mode
# workerd gets this private bootstrap channel; remote workers stay tokenless. # workerd gets this private bootstrap channel; remote workers stay tokenless.
#
# A bridged app container (INS-49) needs two more lines, read by the daemon
# from its OWN env — the daemon runs on the host, not in the container:
# - GPUK_DATA_HOST: the daemon dials the published mTLS port through docker
# NAT, so the controller sees it arrive from the bridge gateway (172.17.0.1)
# and would persist THAT as the node's address (WRK-93). The LAN address is
# what peers, the proxy and the UI must dial instead.
# - BACKEND_DOCKER_NETWORK: the engines the daemon launches join the app
# container's docker network, so the backend and gpuk-proxy reach them by
# container IP (apps/worker/src/modules/docker.rs, staging/engine.rs).
# Exactly what dev.sh hands the dev worker; host networking needs neither.
_data_host=""
if [ "$NETWORK" != "host" ]; then
_data_host="${GPUK_DATA_HOST:-$(host_lan_ipv4)}"
[ -n "$_data_host" ] || echo "==> warning: this host's LAN IPv4 could not be determined (no ip, no hostname -I)." \
"Set GPUK_DATA_HOST=<lan ip> in $WORKER_ENV, or the controller will address its own worker through the docker bridge."
fi
{ {
echo "GPUK_CLUSTER=$CLUSTER" echo "GPUK_CLUSTER=$CLUSTER"
echo "GPUK_WORKER_HEALTH_PORT=$HEALTH_PORT" echo "GPUK_WORKER_HEALTH_PORT=$HEALTH_PORT"
@@ -521,9 +562,13 @@ cmd_install() {
echo "GPUK_CONTROLLER_URLS=wss://127.0.0.1:$MTLS_PORT" echo "GPUK_CONTROLLER_URLS=wss://127.0.0.1:$MTLS_PORT"
echo "GPUK_SELF_ENROLL_FILE=$SELF_ENROLL_FILE" echo "GPUK_SELF_ENROLL_FILE=$SELF_ENROLL_FILE"
echo "NODE_DISPLAY_NAME=$(hostname)" echo "NODE_DISPLAY_NAME=$(hostname)"
if [ "$NETWORK" != "host" ]; then
[ -z "$_data_host" ] || echo "GPUK_DATA_HOST=$_data_host"
echo "BACKEND_DOCKER_NETWORK=$NETWORK"
fi
} > "$WORKER_ENV" } > "$WORKER_ENV"
chmod 0600 "$WORKER_ENV" chmod 0600 "$WORKER_ENV"
echo "==> wrote $MANIFEST (controller: app container $IMAGE)" echo "==> wrote $MANIFEST (controller: app container $IMAGE, network $NETWORK)"
fi fi
chmod 0600 "$MANIFEST" chmod 0600 "$MANIFEST"
[ -z "$DOMAIN" ] || write_caddyfile [ -z "$DOMAIN" ] || write_caddyfile
@@ -561,11 +606,25 @@ cmd_install() {
# ── Controller: start the daemon, then bring up the app container ── # ── Controller: start the daemon, then bring up the app container ──
systemctl enable --now "$SERVICE_NAME" systemctl enable --now "$SERVICE_NAME"
echo "==> $SERVICE_NAME enabled and started" echo "==> $SERVICE_NAME enabled and started"
echo "==> applying manifest (first app-container start) ..." # The pull is the long part of a first install (a multi-GB image) and workerd
# runs docker with its output captured, so pulling from inside `apply` is
# minutes of silence that read as a hang. Pull here, on the terminal, where
# docker's own per-layer progress is what the operator sees; `apply` then finds
# the image present and skips its pull (INS-03).
# Feedback only, never the verdict: `apply` pulls again whatever happened here
# and reports the cause itself (the CI shell gate installs a stub daemon against
# an image that does not exist anywhere — the daemon's pull is the one that counts).
if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
echo "==> pulling $IMAGE (docker shows the progress per layer) ..."
docker pull "$IMAGE" \
|| echo "==> the pull did not complete here; the daemon retries it during apply and reports the cause if it fails again"
fi
echo "==> applying manifest — starting the app container and waiting for its health check (up to 60s) ..."
# No unix socket any more: workerd reconciles the app container in-process from # No unix socket any more: workerd reconciles the app container in-process from
# the on-disk manifest (there is no backend to relay through on the very first # the on-disk manifest (there is no backend to relay through on the very first
# boot). Steady-state updates go through the daemon over WS. # boot). Steady-state updates go through the daemon over WS.
GPUK_MANIFEST_PATH="$MANIFEST" "$BIN_DEST" apply || die "apply failed. Check: gpuk logs" GPUK_MANIFEST_PATH="$MANIFEST" "$BIN_DEST" apply \
|| die "the app container did not come up — the [worker] lines above say why (a [controller] FATAL line names the component and the fix). Full container logs: gpuk logs"
echo echo
echo "Done. The worker daemon is running and the app container is up." echo "Done. The worker daemon is running and the app container is up."
echo " Status : gpuk status App logs: gpuk logs Daemon: journalctl -u $SERVICE_NAME" echo " Status : gpuk status App logs: gpuk logs Daemon: journalctl -u $SERVICE_NAME"
@@ -605,12 +664,31 @@ INSTALL_ENV_KEYS="NODE_ENV,GPUK_MODE,GPUK_CLUSTER,GPUK_SELF_ENROLL_FILE,NODE_DIS
# cluster, network, data root, ports, secret references, the primary cache disk, # cluster, network, data root, ports, secret references, the primary cache disk,
# the env keys above) and keeps the rest. The first write is the render as-is. # the env keys above) and keeps the rest. The first write is the render as-is.
write_manifest() { # $1 = render function write_manifest() { # $1 = render function
if [ -f "$MANIFEST" ]; then if [ -f "$MANIFEST" ] && [ "$RESET_MANIFEST" -eq 1 ]; then
# The operator's explicit regeneration (WRK-191): the daemon archives the
# existing document — readable or not — writes the render as-is and NAMES what
# the archive carried that the render does not. The flags install.sh inherited
# from the old file (ports, profile, data root) are already in the render.
"$1" > "$MANIFEST.new"
chmod 0600 "$MANIFEST.new"
GPUK_MANIFEST_PATH="$MANIFEST" "$BIN_DEST" install-manifest \
--from "$MANIFEST.new" --reset >/dev/null \
|| { rm -f "$MANIFEST.new"; die "could not reset $MANIFEST — the [worker] line above names the cause"; }
rm -f "$MANIFEST.new"
echo "==> $MANIFEST rebuilt from this install's settings (--reset-manifest); the previous file is archived beside it"
elif [ -f "$MANIFEST" ]; then
"$1" > "$MANIFEST.new" "$1" > "$MANIFEST.new"
chmod 0600 "$MANIFEST.new" chmod 0600 "$MANIFEST.new"
GPUK_MANIFEST_PATH="$MANIFEST" "$BIN_DEST" install-manifest \ GPUK_MANIFEST_PATH="$MANIFEST" "$BIN_DEST" install-manifest \
--from "$MANIFEST.new" --own-env "$INSTALL_ENV_KEYS" >/dev/null \ --from "$MANIFEST.new" --own-env "$INSTALL_ENV_KEYS" >/dev/null \
|| { rm -f "$MANIFEST.new"; die "could not merge the new settings into $MANIFEST"; } || {
rm -f "$MANIFEST.new"
die "could not merge the new settings into $MANIFEST — the [worker] line above names the cause.
A manifest written by an older release can carry a field this release removed. Re-run the same
command with --reset-manifest: the file is archived beside itself as manifest.json.before-reset.<time>,
rebuilt from this install's settings, and every setting the archive carried that the rebuild does
not (extra cache disks, LED binary, eviction…) is listed so you can set it again from the UI."
}
rm -f "$MANIFEST.new" rm -f "$MANIFEST.new"
echo "==> merged into the existing $MANIFEST (controller-owned settings kept)" echo "==> merged into the existing $MANIFEST (controller-owned settings kept)"
else else
@@ -1017,7 +1095,7 @@ cmd_update() {
# ── uninstall ────────────────────────────────────────────────────────────────── # ── uninstall ──────────────────────────────────────────────────────────────────
# Plain: stop and remove the service, touch nothing else (a pause, reversible by # Plain: stop and remove the service, touch nothing else (a pause, reversible by
# `gpuk install`). --purge: everything the installer created goes — the app # `gpuk install`). --purge: everything the installer created goes — the app
# container (and a leftover -old twin), $ETC_DIR with the manifest and the # container (and its -old / -failed twins), $ETC_DIR with the manifest and the
# enrolled identity, the binary — EXCEPT the data root: database, models and the # enrolled identity, the binary — EXCEPT the data root: database, models and the
# secrets (ENCRYPTION_KEY above all, OPS-04) are the operator's to delete, by # secrets (ENCRYPTION_KEY above all, OPS-04) are the operator's to delete, by
# hand, knowingly. After a purge the next install is a first install (INS-03); # hand, knowingly. After a purge the next install is a first install (INS-03);
@@ -1043,7 +1121,7 @@ cmd_uninstall() {
return 0 return 0
fi fi
if [ -n "$_cn" ] && command -v docker >/dev/null 2>&1; then if [ -n "$_cn" ] && command -v docker >/dev/null 2>&1; then
for _c in "$_cn" "$_cn-old"; do for _c in "$_cn" "$_cn-old" "$_cn-failed"; do
docker rm -f "$_c" >/dev/null 2>&1 && echo "==> removed container $_c" docker rm -f "$_c" >/dev/null 2>&1 && echo "==> removed container $_c"
done done
fi fi
@@ -1065,7 +1143,10 @@ gpuk — GPU Kitchen host daemon (gpu-kitchen-worker)
gpuk install --mode controller --image <ref> [--profile homelab|studio|enterprise|public] gpuk install --mode controller --image <ref> [--profile homelab|studio|enterprise|public]
[--cluster N] [--cache-dir P] [--data-root P] [--cluster N] [--cache-dir P] [--data-root P]
[--http-port P] [--mtls-port P] [--inference-port P] [--http-port P] [--mtls-port P] [--inference-port P]
[--network host|bridge|<net>] [--binary <path>] [--network bridge|host|<net>] [--binary <path>] [--reset-manifest]
--network: bridge by default — the container publishes its three
ports and nothing else it listens on touches the host; host makes
every listener a host-wide claim (a re-run keeps the mode installed)
gpuk install --mode worker --controller wss://<host>:<port> --enroll-token gk_enroll_... gpuk install --mode worker --controller wss://<host>:<port> --enroll-token gk_enroll_...
[--cluster N] [--cache-dir P] [--binary <path>] [--cluster N] [--cache-dir P] [--binary <path>]
[--health-port P] [--data-port P] [--transfer-port P] [--handover-port P] [--health-port P] [--data-port P] [--transfer-port P] [--handover-port P]
@@ -1079,7 +1160,8 @@ gpuk — GPU Kitchen host daemon (gpu-kitchen-worker)
gpuk channel Fetch + VERIFY the release channel and print what it offers gpuk channel Fetch + VERIFY the release channel and print what it offers
gpuk backup [DIR] (controller) Cold snapshot of the embedded pgdata (stop → tar → restart) gpuk backup [DIR] (controller) Cold snapshot of the embedded pgdata (stop → tar → restart)
gpuk manifest Print the current manifest gpuk manifest Print the current manifest
gpuk logs Follow the app container logs (controller) or the daemon journal gpuk logs Follow the app container logs (controller) or the daemon journal;
after a failed start, the kept <container>-failed log
gpuk uninstall Remove the systemd service, leave everything else in place gpuk uninstall Remove the systemd service, leave everything else in place
gpuk uninstall --purge Also remove the app container, /etc/gpu-kitchen and the binary gpuk uninstall --purge Also remove the app container, /etc/gpu-kitchen and the binary
(never the data root: database, models, secrets) (never the data root: database, models, secrets)
@@ -1102,7 +1184,21 @@ case "$cmd" in
manifest) cat "$MANIFEST" ;; manifest) cat "$MANIFEST" ;;
logs) logs)
_img=$(manifest_image) _img=$(manifest_image)
if [ -n "$_img" ]; then exec docker logs -f "$(container_name)"; else exec journalctl -u "$SERVICE_NAME" -f; fi if [ -n "$_img" ]; then
_cn=$(container_name)
if docker inspect "$_cn" >/dev/null 2>&1; then
exec docker logs -f "$_cn"
elif docker inspect "$_cn-failed" >/dev/null 2>&1; then
# No live app container, but the last failed start was kept (INS-15):
# its whole log is the diagnosis, not the daemon journal.
echo "==> no running app container; showing the full log of the last failed start ($_cn-failed)" >&2
exec docker logs "$_cn-failed"
else
exec journalctl -u "$SERVICE_NAME" -f
fi
else
exec journalctl -u "$SERVICE_NAME" -f
fi
;; ;;
uninstall) cmd_uninstall "$@" ;; uninstall) cmd_uninstall "$@" ;;
help|-h|--help) usage ;; help|-h|--help) usage ;;
+51 -5
View File
@@ -56,6 +56,10 @@ PORT="${GPUK_PORT:-1337}"
MTLS_PORT="${GPUK_MTLS_PORT:-8443}" MTLS_PORT="${GPUK_MTLS_PORT:-8443}"
INFERENCE_PORT="${GPUK_INFERENCE_PORT:-8200}" INFERENCE_PORT="${GPUK_INFERENCE_PORT:-8200}"
CLUSTER="${GPUK_CLUSTER:-default}" CLUSTER="${GPUK_CLUSTER:-default}"
# The app container's docker network. Empty = gpuk's default (bridge, INS-49);
# an existing install keeps the mode it runs in (INS-03) — flipping the default
# must never move a host-mode install to bridge on its next re-run.
NETWORK="${GPUK_NETWORK:-}"
# Which of those came from the operator (flag or env) — an existing install keeps # Which of those came from the operator (flag or env) — an existing install keeps
# its own value for everything the operator did not ask to change (INS-03). # its own value for everything the operator did not ask to change (INS-03).
PORT_GIVEN=0; [ -z "${GPUK_PORT:-}" ] || PORT_GIVEN=1 PORT_GIVEN=0; [ -z "${GPUK_PORT:-}" ] || PORT_GIVEN=1
@@ -64,6 +68,7 @@ INFERENCE_GIVEN=0; [ -z "${GPUK_INFERENCE_PORT:-}" ] || INFERENCE_GIVEN=1
DATA_ROOT_GIVEN=0; [ -z "${GPUK_DATA_ROOT:-}" ] || DATA_ROOT_GIVEN=1 DATA_ROOT_GIVEN=0; [ -z "${GPUK_DATA_ROOT:-}" ] || DATA_ROOT_GIVEN=1
CACHE_GIVEN=0; [ -z "${GPUK_CACHE_DIR:-}" ] || CACHE_GIVEN=1 CACHE_GIVEN=0; [ -z "${GPUK_CACHE_DIR:-}" ] || CACHE_GIVEN=1
CLUSTER_GIVEN=0; [ -z "${GPUK_CLUSTER:-}" ] || CLUSTER_GIVEN=1 CLUSTER_GIVEN=0; [ -z "${GPUK_CLUSTER:-}" ] || CLUSTER_GIVEN=1
NETWORK_GIVEN=0; [ -z "${GPUK_NETWORK:-}" ] || NETWORK_GIVEN=1
# Where an existing install keeps its manifest. Same override as gpuk's, and for # Where an existing install keeps its manifest. Same override as gpuk's, and for
# the same reason: it is the only way to exercise the re-run path without root. # the same reason: it is the only way to exercise the re-run path without root.
ETC_DIR="${GPUK_ETC_DIR:-/etc/gpu-kitchen}" ETC_DIR="${GPUK_ETC_DIR:-/etc/gpu-kitchen}"
@@ -71,6 +76,7 @@ MANIFEST="$ETC_DIR/manifest.json"
UNIT_DEST="${GPUK_UNIT_DEST:-/etc/systemd/system/gpu-kitchen-worker.service}" UNIT_DEST="${GPUK_UNIT_DEST:-/etc/systemd/system/gpu-kitchen-worker.service}"
BIN_DEST="${GPUK_BIN_DEST:-/usr/local/bin/gpu-kitchen-worker}" BIN_DEST="${GPUK_BIN_DEST:-/usr/local/bin/gpu-kitchen-worker}"
PROFILE="" PROFILE=""
RESET_MANIFEST=0
DOMAIN="" DOMAIN=""
VERSION="" VERSION=""
IMAGE="" IMAGE=""
@@ -124,10 +130,17 @@ Options:
--data-root <path> Where the database and secrets live (default /var/lib/gpu-kitchen) --data-root <path> Where the database and secrets live (default /var/lib/gpu-kitchen)
--cache-dir <path> Model cache (default <data-root>/hf) --cache-dir <path> Model cache (default <data-root>/hf)
--cluster <name> Cluster name workers join (default "default") --cluster <name> Cluster name workers join (default "default")
--network <mode> bridge (default) | host. Bridged, only the three ports above
touch the host; host makes every listener of the container a
host-wide claim. An existing install keeps its mode.
--profile <p> homelab | studio | enterprise | public (no flag + a terminal --profile <p> homelab | studio | enterprise | public (no flag + a terminal
= the script asks; no flag + no terminal = first-run asks) = the script asks; no flag + no terminal = first-run asks)
--domain <d> Domain for the public profile: writes a filled TLS --domain <d> Domain for the public profile: writes a filled TLS
reverse-proxy example to <data-root>/caddy/Caddyfile reverse-proxy example to <data-root>/caddy/Caddyfile
--reset-manifest Rebuild /etc/gpu-kitchen/manifest.json from this run's settings
when the daemon refuses the existing one (a field an older
release wrote); the old file is archived beside it and what
it carried that the rebuild does not is listed
--non-interactive Never ask anything, even with a terminal attached --non-interactive Never ask anything, even with a terminal attached
--worker-binary <p> Use a locally-built gpu-kitchen-worker instead of downloading one --worker-binary <p> Use a locally-built gpu-kitchen-worker instead of downloading one
--gpuk-script <p> Use a local copy of the gpuk installer --gpuk-script <p> Use a local copy of the gpuk installer
@@ -150,9 +163,11 @@ while [ $# -gt 0 ]; do
--data-root) DATA_ROOT="$2"; DATA_ROOT_GIVEN=1; shift 2 ;; --data-root) DATA_ROOT="$2"; DATA_ROOT_GIVEN=1; shift 2 ;;
--cache-dir) CACHE_DIR="$2"; CACHE_GIVEN=1; shift 2 ;; --cache-dir) CACHE_DIR="$2"; CACHE_GIVEN=1; shift 2 ;;
--cluster) CLUSTER="$2"; CLUSTER_GIVEN=1; shift 2 ;; --cluster) CLUSTER="$2"; CLUSTER_GIVEN=1; shift 2 ;;
--network) NETWORK="$2"; NETWORK_GIVEN=1; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;; --profile) PROFILE="$2"; shift 2 ;;
--domain) DOMAIN="$2"; shift 2 ;; --domain) DOMAIN="$2"; shift 2 ;;
--non-interactive) NON_INTERACTIVE=1; shift ;; --non-interactive) NON_INTERACTIVE=1; shift ;;
--reset-manifest) RESET_MANIFEST=1; shift ;;
--worker-binary) WORKER_BINARY="$2"; shift 2 ;; --worker-binary) WORKER_BINARY="$2"; shift 2 ;;
--gpuk-script) GPUK_SCRIPT="$2"; shift 2 ;; --gpuk-script) GPUK_SCRIPT="$2"; shift 2 ;;
--skip-gpu-check) SKIP_GPU_CHECK=1; shift ;; --skip-gpu-check) SKIP_GPU_CHECK=1; shift ;;
@@ -177,6 +192,13 @@ case "$DOMAIN" in
*[!A-Za-z0-9.-]*) die "--domain must be a bare domain name (got '$DOMAIN')" ;; *[!A-Za-z0-9.-]*) die "--domain must be a bare domain name (got '$DOMAIN')" ;;
esac esac
# bridge, host, or the name of a docker network gpuk hands to `docker create
# --network` — never a value that could be read as another flag or as whitespace.
case "$NETWORK" in
""|bridge|host) ;;
-*|*[!A-Za-z0-9_.-]*) die "--network must be bridge, host or a docker network name (got '$NETWORK')" ;;
esac
for _pv in "$PORT" "$MTLS_PORT" "$INFERENCE_PORT"; do for _pv in "$PORT" "$MTLS_PORT" "$INFERENCE_PORT"; do
case "$_pv" in case "$_pv" in
''|*[!0-9]*) die "not a port number: '$_pv'" ;; ''|*[!0-9]*) die "not a port number: '$_pv'" ;;
@@ -274,11 +296,17 @@ case "$EXISTING" in
[ "$PORT_GIVEN" -eq 1 ] || PORT="$OURS_UI" [ "$PORT_GIVEN" -eq 1 ] || PORT="$OURS_UI"
[ "$MTLS_GIVEN" -eq 1 ] || MTLS_PORT="$OURS_MTLS" [ "$MTLS_GIVEN" -eq 1 ] || MTLS_PORT="$OURS_MTLS"
[ "$INFERENCE_GIVEN" -eq 1 ] || INFERENCE_PORT="$OURS_INF" [ "$INFERENCE_GIVEN" -eq 1 ] || INFERENCE_PORT="$OURS_INF"
# The network mode is a setting like the ports: an install that runs on the
# host network stays there when the default is bridge (INS-49), and the
# reverse — only --network moves it. An old manifest without the field is
# read as the daemon reads it (host).
[ "$NETWORK_GIVEN" -eq 1 ] || NETWORK="${C_NETMODE:-host}"
case "$_profile" in case "$_profile" in
homelab|studio|enterprise|public) [ -n "$PROFILE" ] || PROFILE="$_profile" ;; homelab|studio|enterprise|public) [ -n "$PROFILE" ] || PROFILE="$_profile" ;;
esac esac
ok "data root $DATA_ROOT" ok "data root $DATA_ROOT"
ok "ports UI $OURS_UI, worker channel $OURS_MTLS, inference $OURS_INF" ok "ports UI $OURS_UI, worker channel $OURS_MTLS, inference $OURS_INF"
ok "network ${C_NETMODE:-host}"
[ -z "$_profile" ] || ok "profile $_profile" [ -z "$_profile" ] || ok "profile $_profile"
ok "re-running updates it in place. Its settings are kept unless a flag says otherwise." ok "re-running updates it in place. Its settings are kept unless a flag says otherwise."
;; ;;
@@ -606,6 +634,7 @@ if [ "$DRY_RUN" -eq 1 ]; then
echo " UI port : $PORT" echo " UI port : $PORT"
echo " worker channel: $MTLS_PORT" echo " worker channel: $MTLS_PORT"
echo " inference : $INFERENCE_PORT" echo " inference : $INFERENCE_PORT"
echo " network : ${NETWORK:-bridge}"
case "$EXISTING" in case "$EXISTING" in
manifest) echo " existing : yes — updated in place" ;; manifest) echo " existing : yes — updated in place" ;;
leftovers) echo " existing : traces of a previous install — taken over" ;; leftovers) echo " existing : traces of a previous install — taken over" ;;
@@ -728,6 +757,10 @@ set -- install \
[ -z "$PROFILE" ] || set -- "$@" --profile "$PROFILE" [ -z "$PROFILE" ] || set -- "$@" --profile "$PROFILE"
[ -z "$DOMAIN" ] || set -- "$@" --domain "$DOMAIN" [ -z "$DOMAIN" ] || set -- "$@" --domain "$DOMAIN"
[ "$RESET_MANIFEST" -eq 0 ] || set -- "$@" --reset-manifest
# Inherited from the manifest or given by flag; unset on a first install, so
# gpuk's own default (bridge, INS-49) applies and this script never restates it.
[ -z "$NETWORK" ] || set -- "$@" --network "$NETWORK"
if [ -n "$WORKER_BINARY" ]; then if [ -n "$WORKER_BINARY" ]; then
[ -f "$WORKER_BINARY" ] || die "no such worker binary: $WORKER_BINARY" [ -f "$WORKER_BINARY" ] || die "no such worker binary: $WORKER_BINARY"
@@ -741,7 +774,11 @@ fi
# else: gpuk reuses an already-installed binary, or fails with its own message. # else: gpuk reuses an already-installed binary, or fails with its own message.
step "Installing — this pulls the image, so it can take a few minutes" step "Installing — this pulls the image, so it can take a few minutes"
sh "$GPUK_SCRIPT" "$@" || die "the install failed. See: journalctl -u gpu-kitchen-worker" # gpuk names its own failure on stderr before exiting (a refused manifest, a
# failed apply, a missing binary…), so the cause is the line right above this
# one. journalctl only has something to say once the daemon has started, and
# gpuk points at `gpuk logs` itself in that case.
sh "$GPUK_SCRIPT" "$@" || die "the install failed — gpuk reported the cause just above"
# ── 5. Wait for the app, then say where it is ──────────────────────────────── # ── 5. Wait for the app, then say where it is ────────────────────────────────
step "Waiting for the controller to answer" step "Waiting for the controller to answer"
@@ -776,10 +813,19 @@ else
echo " ${BOLD}Claim code:${RESET} consumed (the first account already exists)" echo " ${BOLD}Claim code:${RESET} consumed (the first account already exists)"
fi fi
echo echo
# The wizard's first step asks for a Kitchen ACCOUNT key (CPT-04). It is the
# person's credential, never the machine's — this script cannot create or print
# it, and the browser must not receive it in a URL (CPT-05/06). What it can do
# is say so, and say where the key comes from, before the page does.
echo " ${BOLD}Next:${RESET} open the URL above. Its first step asks for your GPU Kitchen account key"
echo " (gpuk_…). Sign in to your GPU Kitchen account and create one under Install keys:"
echo " https://gpu.kitchen/account#install-keys"
echo " That key is yours, not this machine's: the installer never sees it,"
echo " and the page exchanges it for a revocable installation token."
case "$PROFILE" in case "$PROFILE" in
public) public)
echo " ${BOLD}Next:${RESET} create the first administrator in the UI" echo " ${BOLD}Then:${RESET} create the first administrator in the UI"
echo echo
# The public profile REQUIRES a TLS reverse proxy (OPS-13, INS-47) — the UI # The public profile REQUIRES a TLS reverse proxy (OPS-13, INS-47) — the UI
# port speaks plain HTTP. The product cannot verify the proxy's presence # port speaks plain HTTP. The product cannot verify the proxy's presence
@@ -799,15 +845,15 @@ case "$PROFILE" in
echo echo
;; ;;
enterprise) enterprise)
echo " ${BOLD}Next:${RESET} create the first administrator in the UI" echo " ${BOLD}Then:${RESET} create the first administrator in the UI"
echo echo
;; ;;
homelab|studio) homelab|studio)
echo " ${BOLD}Next:${RESET} finish first-run in the UI" echo " ${BOLD}Then:${RESET} finish first-run in the UI"
echo echo
;; ;;
"") "")
echo " ${BOLD}Next:${RESET} choose an installation profile in the first-run assistant" echo " ${BOLD}Then:${RESET} choose an installation profile in the first-run assistant"
echo echo
;; ;;
esac esac
+5 -5
View File
@@ -1,11 +1,11 @@
{ {
"version": "v0.1.5", "version": "v0.1.8",
"semver": "0.1.5", "semver": "0.1.8",
"controllerImage": "repo.byterain.io/gpukitchen/gpukitchen-controller", "controllerImage": "repo.byterain.io/gpukitchen/gpukitchen-controller",
"controllerImageEnterprise": "repo.byterain.io/gpukitchen-private/gpukitchen-controller-ee", "controllerImageEnterprise": "repo.byterain.io/gpukitchen-private/gpukitchen-controller-ee",
"controllerImageDigest": "sha256:5a126247a2f24a29338ba2a84414a3ef8e270a1696dd7a32a3894889836594b1", "controllerImageDigest": "sha256:4bceef1f73703ddd4dbcf2af45627dfa15e268f7d94929daf0b70989977324fd",
"controllerImageDigestEnterprise": "sha256:a8f55534aa09a1f077dc5d72116db2b1f2f153b0e2f3c6299d777a1d80c3dfab", "controllerImageDigestEnterprise": "sha256:32eaf7a7ce4203d5d723af95f2d55c1fb9370b2a74fe4524997cb3f6b21c9894",
"workerBase": "https://repo.byterain.io/api/packages/gpukitchen/generic/gpu-kitchen-worker/v0.1.5", "workerBase": "https://repo.byterain.io/api/packages/gpukitchen/generic/gpu-kitchen-worker/v0.1.8",
"gpukScript": "https://repo.byterain.io/gpukitchen/channel/raw/branch/main/gpuk", "gpukScript": "https://repo.byterain.io/gpukitchen/channel/raw/branch/main/gpuk",
"releaseNotes": "https://repo.byterain.io/gpukitchen/channel" "releaseNotes": "https://repo.byterain.io/gpukitchen/channel"
} }
+3 -3
View File
@@ -1,4 +1,4 @@
untrusted comment: signature from minisign secret key untrusted comment: signature from minisign secret key
RUQ7BKXJqGX2jabBqcSYwUGmejKh6OS+qKIyVqxpzwJ4PwIBVDAAuoxgF6CkfyxiNgQvpS63RUIRE485PVdRvHTW8UQd10fuIAQ= RUQ7BKXJqGX2jXfsXMmaGO2bObez5DagrH233W/mWwx1u3yu+MuMkzbtyVc59cCwGk97UTG5l2qwvZ9cpWpvWxFHvdmiibMRkgo=
trusted comment: gpu-kitchen channel v0.1.5 trusted comment: gpu-kitchen channel v0.1.8
f+6uK6GJBgurIcd7L2l53/SPV/VqRv9DfJfRaFbc5tJTBVRL5I2zgzEF66YytLXHUHScAM7MZsJi9VUCeoGxAw== GWhwdOcKDhFj2nPvMYY1NDiTha2SGawzERQYs2tdf4i2uqlrfcWJLhnVZirrGtBewBOwcC+oRCEXX56I1si3Cg==