156 lines
6.7 KiB
Bash
156 lines
6.7 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# Build both the LiveRecorder WebApi and Frontend (nginx) images for
|
|
# linux/arm64 and export them as a single gzip-compressed `docker load`-able
|
|
# archive.
|
|
#
|
|
# The docker-compose.yml uses a split architecture:
|
|
# nginx (frontend) — Vue SPA served by nginx, proxies /api/* to api:8080
|
|
# api (backend) — .NET 8 WebApi
|
|
#
|
|
# Both images must be ARM64 for a full deployment on an ARM64 host.
|
|
#
|
|
# Target environment: Windows + Docker Desktop (WSL2 backend) + a host HTTP proxy.
|
|
# It encodes the workarounds discovered while building from a network where the
|
|
# Docker daemon cannot reliably reach upstream MCR / Docker Hub:
|
|
#
|
|
# 1. ARM64 QEMU emulation is registered via binfmt_misc with the `F`
|
|
# (fix-binary) flag so the kernel-held fd works inside build containers.
|
|
# 2. The SDK is pulled for the builder platform while runtime images are
|
|
# pulled for ARM64, then the build runs with `--pull=false`.
|
|
# 3. `dotnet restore` goes through the host proxy; apt uses the in-Dockerfile
|
|
# Huawei Cloud mirror directly (NO_PROXY).
|
|
#
|
|
# Usage:
|
|
# scripts/build-arm64-image.sh
|
|
#
|
|
# Override defaults via env vars:
|
|
# PROXY=http://127.0.0.1:10808
|
|
# OUTPUT=live-recorder-arm64.tar.gz
|
|
# WSL_DISTRO=Ubuntu
|
|
set -euo pipefail
|
|
|
|
# --- Resolve repo root (script lives in <root>/scripts) ---
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
cd "$ROOT_DIR"
|
|
|
|
# --- Config ---
|
|
PROXY="${PROXY:-http://127.0.0.1:10808}"
|
|
PROXY_HOST="${PROXY_HOST:-http://host.docker.internal:${PROXY##*:}}" # container-visible proxy
|
|
API_IMAGE_TAG="${API_IMAGE_TAG:-live-recorder-webapi:arm64}"
|
|
FRONTEND_IMAGE_TAG="${FRONTEND_IMAGE_TAG:-live-recorder-frontend:arm64}"
|
|
OUTPUT="${OUTPUT:-live-recorder-arm64.tar.gz}"
|
|
WSL_DISTRO="${WSL_DISTRO:-Ubuntu}"
|
|
DOTNET_SDK_IMAGE="${DOTNET_SDK_IMAGE:-mcr.m.daocloud.io/dotnet/sdk:8.0-bookworm-slim}"
|
|
DOTNET_RUNTIME_IMAGE="${DOTNET_RUNTIME_IMAGE:-mcr.m.daocloud.io/dotnet/aspnet:8.0-bookworm-slim}"
|
|
NGINX_IMAGE="${NGINX_IMAGE:-docker.m.daocloud.io/library/nginx:1.27-alpine}"
|
|
NO_PROXY_HOSTS="mirrors.tuna.tsinghua.edu.cn,.tsinghua.edu.cn,mirrors.huaweicloud.com,.huaweicloud.com,mcr.m.daocloud.io,docker.m.daocloud.io,.m.daocloud.io,localhost,127.0.0.1"
|
|
BUILD_PLATFORM="${BUILD_PLATFORM:-$(docker version --format '{{.Server.Os}}/{{.Server.Arch}}')}"
|
|
|
|
log() { printf '\n\033[1;36m[%s] %s\033[0m\n' "$(date +%H:%M:%S)" "$*"; }
|
|
die() { printf '\n\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; }
|
|
|
|
# --- 1. Ensure ARM64 emulation works -----------------------------------------
|
|
ensure_arm64_emulation() {
|
|
if docker run --rm --platform linux/arm64 "$DOTNET_RUNTIME_IMAGE" uname -m 2>/dev/null | grep -q aarch64; then
|
|
log "ARM64 emulation already works — skipping binfmt registration."
|
|
return 0
|
|
fi
|
|
|
|
log "Registering qemu-aarch64 emulation via the '$WSL_DISTRO' WSL distro..."
|
|
wsl.exe -d "$WSL_DISTRO" -u root -- bash -lc '
|
|
set -e
|
|
if [ ! -x /usr/bin/qemu-aarch64-static ]; then
|
|
for source_file in /etc/apt/sources.list /etc/apt/sources.list.d/*.sources; do
|
|
[ -f "$source_file" ] || continue
|
|
sed -i \
|
|
-e "s|deb.debian.org|mirrors.huaweicloud.com|g" \
|
|
-e "s|security.debian.org/debian-security|mirrors.huaweicloud.com/debian-security|g" \
|
|
-e "s|archive.ubuntu.com|mirrors.huaweicloud.com|g" \
|
|
-e "s|security.ubuntu.com|mirrors.huaweicloud.com|g" \
|
|
"$source_file"
|
|
done
|
|
apt-get update -qq
|
|
apt-get install -y -qq qemu-user-static
|
|
fi
|
|
for name in qemu-aarch64 qemu-aarch64-static; do
|
|
[ -e "/proc/sys/fs/binfmt_misc/$name" ] && echo -1 > "/proc/sys/fs/binfmt_misc/$name" || true
|
|
done
|
|
printf ":qemu-aarch64-static:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64-static:F" \
|
|
> /proc/sys/fs/binfmt_misc/register
|
|
' || die "binfmt registration failed (need a Debian/Ubuntu WSL distro named '$WSL_DISTRO' with apt)."
|
|
|
|
docker run --rm --platform linux/arm64 "$DOTNET_RUNTIME_IMAGE" uname -m 2>/dev/null | grep -q aarch64 \
|
|
|| die "ARM64 emulation still not working after registration."
|
|
log "ARM64 emulation registered and verified."
|
|
}
|
|
|
|
# --- 2. Pre-pull base images (resumable retries) -----------------------------
|
|
pull_with_retry() {
|
|
local platform="$1" img="$2" i
|
|
for i in $(seq 1 8); do
|
|
if [ "$(docker image inspect --format '{{.Os}}/{{.Architecture}}' "$img" 2>/dev/null || true)" = "$platform" ]; then
|
|
return 0
|
|
fi
|
|
log "Pulling $img for $platform (attempt $i/8) ..."
|
|
docker pull --platform "$platform" "$img" >/dev/null 2>&1 || true
|
|
if [ "$(docker image inspect --format '{{.Os}}/{{.Architecture}}' "$img" 2>/dev/null || true)" = "$platform" ]; then
|
|
return 0
|
|
fi
|
|
sleep 3
|
|
done
|
|
die "Could not pull $img for $platform (network)."
|
|
}
|
|
|
|
# --- 3. Build API image ------------------------------------------------------
|
|
build_api_image() {
|
|
log "Building $API_IMAGE_TAG for linux/arm64 (restore via proxy, apt via mirror)..."
|
|
docker buildx build \
|
|
--platform linux/arm64 \
|
|
-f src/LiveRecorder.WebApi/Dockerfile \
|
|
-t "$API_IMAGE_TAG" \
|
|
--build-arg "HTTP_PROXY=$PROXY_HOST" \
|
|
--build-arg "HTTPS_PROXY=$PROXY_HOST" \
|
|
--build-arg "NO_PROXY=$NO_PROXY_HOSTS" \
|
|
--build-arg "DOTNET_SDK_IMAGE=$DOTNET_SDK_IMAGE" \
|
|
--build-arg "DOTNET_RUNTIME_IMAGE=$DOTNET_RUNTIME_IMAGE" \
|
|
--pull=false \
|
|
--load \
|
|
.
|
|
}
|
|
|
|
# --- 4. Build Frontend (nginx) image -----------------------------------------
|
|
# Frontend dist is pre-built locally (npm run build inside frontend/) and copied
|
|
# directly — avoids running Node.js under QEMU ARM64 emulation which is prohibitively
|
|
# slow. Build context is frontend/ (not repo root) to bypass .dockerignore rules.
|
|
build_frontend_image() {
|
|
log "Building $FRONTEND_IMAGE_TAG for linux/arm64 (pre-built dist -> nginx)..."
|
|
docker buildx build \
|
|
--platform linux/arm64 \
|
|
-f frontend/Dockerfile.arm64 \
|
|
-t "$FRONTEND_IMAGE_TAG" \
|
|
--pull=false \
|
|
--load \
|
|
frontend
|
|
}
|
|
|
|
# --- 5. Export both images as a single archive -------------------------------
|
|
export_images() {
|
|
log "Exporting $API_IMAGE_TAG + $FRONTEND_IMAGE_TAG -> $OUTPUT ..."
|
|
local tar="${OUTPUT%.gz}"
|
|
docker save "$API_IMAGE_TAG" "$FRONTEND_IMAGE_TAG" -o "$tar"
|
|
gzip -f "$tar"
|
|
log "Done: $(ls -lh "$OUTPUT" | awk '{print $5, $9}')"
|
|
log "Load on an arm64 host with: docker load < $OUTPUT"
|
|
log "Then start with: docker compose up -d"
|
|
}
|
|
|
|
ensure_arm64_emulation
|
|
pull_with_retry "$BUILD_PLATFORM" "$DOTNET_SDK_IMAGE"
|
|
pull_with_retry linux/arm64 "$DOTNET_RUNTIME_IMAGE"
|
|
pull_with_retry linux/arm64 "$NGINX_IMAGE"
|
|
build_api_image
|
|
build_frontend_image
|
|
export_images
|