diff --git a/scripts/xun.fish b/scripts/xun.fish new file mode 100755 index 0000000..2cf210e --- /dev/null +++ b/scripts/xun.fish @@ -0,0 +1,97 @@ +#!/usr/bin/env fish + +# Yggdrasil 全量部署到 xun(Podman + fish + nginx-proxy 反代) +# 主应用 + 5 个 Code Runner 沙箱镜像全量重建、传输、导入、滚动重启 +# 用法: fish scripts/xun.fish + +# 5 个 runner 子镜像(base 由脚本自己先建,FROM 它) +set -l RUNNERS python node go rust bun + +echo "==> [1/7] 构建 runner base 镜像" +docker buildx build --platform linux/amd64 --load \ + -t localhost/yggdrasil-runner-base:latest docker/runner-base; or exit 1 +docker tag localhost/yggdrasil-runner-base:latest yggdrasil-runner-base:latest; or exit 1 + +echo "==> [2/7] 构建主应用镜像(透传 git 信息)" +docker buildx build --platform linux/amd64 --load \ + --build-arg YGG_BUILD_GIT_DESCRIBE=(git describe --tags --always --dirty) \ + --build-arg YGG_BUILD_GIT_HASH=(git rev-parse HEAD) \ + --build-arg YGG_BUILD_GIT_COMMIT_DATE=(git log -1 --format=%cd --date=iso-strict) \ + -t localhost/yggdrasil:latest .; or exit 1 + +echo "==> [3/7] 构建 5 个 runner 子镜像" +for img in $RUNNERS + echo " -- $img" + docker buildx build --platform linux/amd64 --load \ + -t localhost/yggdrasil-runner-$img:latest docker/runner-$img; or exit 1 + docker tag localhost/yggdrasil-runner-$img:latest yggdrasil-runner-$img:latest; or exit 1 +end + +echo "==> [4/7] 构建验证(期望全 amd64)" +set -l ALL_IMAGES yggdrasil yggdrasil-runner-base +for img in $RUNNERS + set -a ALL_IMAGES yggdrasil-runner-$img +end +for img in $ALL_IMAGES + set -l arch (docker image inspect localhost/$img:latest --format "{{.Architecture}}") + echo " $img: $arch" + if test "$arch" != "amd64" + echo " 架构错误!期望 amd64" >&2 + exit 1 + end +end + +echo "==> [5/7] 导出 + 传输到 xun" +# 主应用单独一个 tar(滚动重启时只 reload 它) +docker save localhost/yggdrasil:latest -o /tmp/yggdrasil-app.tar; or exit 1 +# 6 个 runner 镜像打包成一个 tar(base + 5 子镜像) +docker save \ + localhost/yggdrasil-runner-base:latest \ + localhost/yggdrasil-runner-python:latest \ + localhost/yggdrasil-runner-node:latest \ + localhost/yggdrasil-runner-go:latest \ + localhost/yggdrasil-runner-rust:latest \ + localhost/yggdrasil-runner-bun:latest \ + -o /tmp/yggdrasil-runners.tar; or exit 1 +gzip -f /tmp/yggdrasil-app.tar /tmp/yggdrasil-runners.tar; or exit 1 +scp /tmp/yggdrasil-app.tar.gz /tmp/yggdrasil-runners.tar.gz xun:/root/docker/yggdrasil/; or exit 1 +rm -f /tmp/yggdrasil-app.tar.gz /tmp/yggdrasil-runners.tar.gz + +echo "==> [6/7] 服务器导入 + runner 去前缀 + 滚动重启 app" +ssh xun 'cd /root/docker/yggdrasil && gunzip -kf yggdrasil-app.tar.gz && gunzip -kf yggdrasil-runners.tar.gz'; or exit 1 +ssh xun 'docker load -i /root/docker/yggdrasil/yggdrasil-app.tar'; or exit 1 +ssh xun 'docker load -i /root/docker/yggdrasil/yggdrasil-runners.tar'; or exit 1 +# runner 去 localhost/ 前缀:LANGUAGES 注册表硬编码 yggdrasil-runner-*:latest(无前缀、无 env 覆盖) +# fish 不认 bash for 循环,逐条 ssh 执行 +ssh xun 'docker tag localhost/yggdrasil-runner-base:latest yggdrasil-runner-base:latest' +ssh xun 'docker tag localhost/yggdrasil-runner-python:latest yggdrasil-runner-python:latest' +ssh xun 'docker tag localhost/yggdrasil-runner-node:latest yggdrasil-runner-node:latest' +ssh xun 'docker tag localhost/yggdrasil-runner-go:latest yggdrasil-runner-go:latest' +ssh xun 'docker tag localhost/yggdrasil-runner-rust:latest yggdrasil-runner-rust:latest' +ssh xun 'docker tag localhost/yggdrasil-runner-bun:latest yggdrasil-runner-bun:latest' +# 滚动重启:只重建 app 容器(postgres 和数据卷不动) +ssh xun 'cd /root/docker/yggdrasil && docker compose --env-file .env up -d app'; or exit 1 +ssh xun 'rm -f /root/docker/yggdrasil/yggdrasil-app.tar* /root/docker/yggdrasil/yggdrasil-runners.tar*' + +echo "==> [7/7] 验证" +echo "--- 容器状态(期望 postgres healthy + app up)---" +ssh xun 'docker ps --filter name=yggdrasil --format "{{.Names}} {{.Status}}"' +echo "--- 迁移日志(期望 applied,无 error/panic)---" +ssh xun 'docker logs yggdrasil-app 2>&1 | grep -iE "migrat|error|panic" | tail' +echo "--- runner 镜像(期望全 found amd64)---" +ssh xun 'docker inspect yggdrasil-runner-base:latest --format "base: found {{.Architecture}}"' +ssh xun 'docker inspect yggdrasil-runner-python:latest --format "python: found {{.Architecture}}"' +ssh xun 'docker inspect yggdrasil-runner-node:latest --format "node: found {{.Architecture}}"' +ssh xun 'docker inspect yggdrasil-runner-go:latest --format "go: found {{.Architecture}}"' +ssh xun 'docker inspect yggdrasil-runner-rust:latest --format "rust: found {{.Architecture}}"' +ssh xun 'docker inspect yggdrasil-runner-bun:latest --format "bun: found {{.Architecture}}"' +echo "--- 健康检查 ---" +ssh xun 'docker exec nginx-proxy curl -s http://yggdrasil-app:3000/healthz'; echo +ssh xun 'docker exec nginx-proxy curl -s http://yggdrasil-app:3000/readyz'; echo +echo "--- git 版本头(确认不再是 unknown)---" +ssh xun 'docker exec nginx-proxy curl -sI http://yggdrasil-app:3000/ | grep -i x-yggdrasil-git' +echo "--- 外部 HTTPS ---" +curl -s https://rua.plus/healthz; echo +curl -sI https://rua.plus/ | grep -iE "HTTP/|strict-transport" + +echo "==> 部署完成"