mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
refactor: Hub二进制改为Docker构建时下载,优化部署流程与worker初始化
- Dockerfile: 移除COPY预编译二进制,改为构建时通过HUB_TAG从GitHub Release下载 - CI: 移除artifact上传/下载步骤,通过build-args传递Hub tag - deploy.sh: 重构参数解析,支持--hub-tag指定版本,跟踪tag变化触发重建 - build.sh: 新增--image模式支持构建并推送Hub Docker镜像 - hub.rs: worker连接时同步所有节点在线状态,避免状态不一致 - proxy_nodes: 启动时主动建立Hub worker连接,消除懒连接窗口期 - docker-compose.yml: app镜像支持APP_IMAGE环境变量配置 - README: 更新部署文档,推荐本地构建方式
This commit is contained in:
54
aether-hub/README.md
Normal file
54
aether-hub/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# aether-hub
|
||||
|
||||
`aether-hub` 是 Tunnel Hub 服务,负责在 proxy 与 worker 之间路由帧。
|
||||
|
||||
## 快速命令
|
||||
|
||||
### 1) 构建并上传 Hub 二进制(推荐生产)
|
||||
|
||||
```bash
|
||||
cd aether-hub
|
||||
./build.sh --upload hub-v0.1.0
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 默认会构建 `amd64 + arm64` 两个二进制并上传到 GitHub Release。
|
||||
- 如只需单架构,可先 `./build.sh amd64` 或 `./build.sh arm64`。
|
||||
|
||||
### 2) 部署端指定 Hub 版本并构建
|
||||
|
||||
```bash
|
||||
cd /path/to/Aether
|
||||
./deploy.sh --hub-tag hub-v0.1.0
|
||||
```
|
||||
|
||||
不指定 `--hub-tag` 时,`./deploy.sh` 会自动解析最新 `hub-v*` release,并在构建 app 镜像时从 GitHub Release 下载对应架构的 Hub 二进制。
|
||||
|
||||
### 3) 镜像模式(可选,调试/实验用)
|
||||
|
||||
仅本地加载镜像(单平台):
|
||||
|
||||
```bash
|
||||
cd aether-hub
|
||||
BUILDKIT_PROGRESS=plain ./build.sh --image --tag local-test --platforms linux/amd64 --load
|
||||
```
|
||||
|
||||
## build.sh 模式说明
|
||||
|
||||
- 默认是 `binary` 模式(`cross` 构建二进制)。
|
||||
- `--upload <hub-vX.Y.Z>` 会把构建产物上传到 GitHub Release。
|
||||
- 加 `--image` 后进入镜像模式(`docker buildx`,可选)。
|
||||
|
||||
常用参数:
|
||||
|
||||
- `--tag <tag>`: 镜像 tag
|
||||
- `--image-name <name>`: 镜像名(默认 `ghcr.io/fawney19/aether-hub`)
|
||||
- `--platforms <list>`: 例如 `linux/amd64,linux/arm64`
|
||||
- `--push`: 推送镜像
|
||||
- `--load`: 加载到本地 Docker(单平台)
|
||||
- `--latest`: 额外打 `latest` tag
|
||||
|
||||
## 与部署脚本关系
|
||||
|
||||
- `./deploy.sh`: 本地构建部署(会本地构建 app/base,并在构建 app 时从 GitHub Release 下载 Hub,可用 `--hub-tag` 固定版本)。
|
||||
@@ -1,131 +1,272 @@
|
||||
#!/bin/bash
|
||||
# 本地构建 aether-hub 多架构二进制
|
||||
# aether-hub 构建脚本
|
||||
#
|
||||
# 前置条件:
|
||||
# cargo install cross --git https://github.com/cross-rs/cross
|
||||
# Docker 或 Podman 运行中(cross 需要容器环境)
|
||||
# 支持两种模式:
|
||||
# 1) binary 模式(默认): 构建多架构二进制并可上传 GitHub Release
|
||||
# 2) image 模式: 构建并推送/加载 Docker 镜像(推荐生产发布用)
|
||||
#
|
||||
# 用法:
|
||||
# ./build.sh # 构建 linux/amd64 + linux/arm64
|
||||
# ./build.sh amd64 # 仅构建 linux/amd64
|
||||
# ./build.sh arm64 # 仅构建 linux/arm64
|
||||
# ./build.sh --upload v0.1.0 # 构建并上传到 GitHub Release(需要 gh CLI)
|
||||
# 示例:
|
||||
# # binary 模式(兼容旧行为)
|
||||
# ./build.sh
|
||||
# ./build.sh amd64
|
||||
# ./build.sh --upload hub-v0.1.0
|
||||
#
|
||||
# # image 模式(多架构推送)
|
||||
# ./build.sh --image --tag v0.2.5 --push --latest
|
||||
# ./build.sh --image --tag sha-abc123 --image-name ghcr.io/fawney19/aether-hub --push
|
||||
# ./build.sh --image --tag local-test --platforms linux/amd64 --load
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
DIST_DIR="$SCRIPT_DIR/dist"
|
||||
|
||||
# 解析参数
|
||||
# -------------------------------
|
||||
# Defaults
|
||||
# -------------------------------
|
||||
MODE="binary" # binary | image
|
||||
|
||||
# binary mode options
|
||||
UPLOAD=false
|
||||
TAG=""
|
||||
BUILD_TARGETS=""
|
||||
UPLOAD_TAG=""
|
||||
BINARY_TARGETS=""
|
||||
|
||||
# image mode options
|
||||
IMAGE_NAME="${IMAGE_NAME:-ghcr.io/fawney19/aether-hub}"
|
||||
IMAGE_TAG=""
|
||||
IMAGE_PLATFORMS="linux/amd64,linux/arm64"
|
||||
IMAGE_PUSH=false
|
||||
IMAGE_LOAD=false
|
||||
IMAGE_LATEST=false
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
./build.sh [binary-args]
|
||||
./build.sh --image [image-args]
|
||||
|
||||
binary 模式(默认):
|
||||
amd64|arm64 仅构建指定架构(可重复)
|
||||
--upload <hub-vX.Y.Z> 上传到 GitHub Release(需要 gh CLI)
|
||||
|
||||
image 模式:
|
||||
--image 启用镜像模式
|
||||
--tag <tag> 镜像 tag(默认自动从 git describe 推导)
|
||||
--image-name <name> 镜像名(默认 ghcr.io/fawney19/aether-hub)
|
||||
--platforms <list> 平台列表,逗号分隔(默认 linux/amd64,linux/arm64)
|
||||
--push 推送镜像到仓库
|
||||
--load 加载到本地 Docker(仅单平台)
|
||||
--latest 额外打 latest tag
|
||||
|
||||
通用:
|
||||
-h, --help 显示帮助
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--image)
|
||||
MODE="image"
|
||||
shift
|
||||
;;
|
||||
--tag)
|
||||
IMAGE_TAG="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--image-name)
|
||||
IMAGE_NAME="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--platforms)
|
||||
IMAGE_PLATFORMS="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--push)
|
||||
IMAGE_PUSH=true
|
||||
shift
|
||||
;;
|
||||
--load)
|
||||
IMAGE_LOAD=true
|
||||
shift
|
||||
;;
|
||||
--latest)
|
||||
IMAGE_LATEST=true
|
||||
shift
|
||||
;;
|
||||
--upload)
|
||||
UPLOAD=true
|
||||
TAG="$2"
|
||||
UPLOAD_TAG="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
amd64|arm64)
|
||||
BUILD_TARGETS="$BUILD_TARGETS $1"
|
||||
BINARY_TARGETS="$BINARY_TARGETS $1"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "用法: $0 [amd64|arm64] [--upload <tag>]"
|
||||
echo "❌ 未知参数: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 默认构建所有平台
|
||||
if [ -z "$BUILD_TARGETS" ]; then
|
||||
BUILD_TARGETS="amd64 arm64"
|
||||
fi
|
||||
build_binary() {
|
||||
if [ -z "$BINARY_TARGETS" ]; then
|
||||
BINARY_TARGETS="amd64 arm64"
|
||||
fi
|
||||
|
||||
# 检查 cross 是否安装
|
||||
if ! command -v cross &> /dev/null; then
|
||||
echo "❌ 需要安装 cross: cargo install cross --git https://github.com/cross-rs/cross"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$DIST_DIR"
|
||||
|
||||
echo "🔨 开始构建 aether-hub..."
|
||||
echo " 目标平台: $BUILD_TARGETS"
|
||||
echo ""
|
||||
|
||||
ARTIFACTS=""
|
||||
|
||||
for arch in $BUILD_TARGETS; do
|
||||
# 映射架构到 Rust target
|
||||
case "$arch" in
|
||||
amd64) target="x86_64-unknown-linux-gnu" ;;
|
||||
arm64) target="aarch64-unknown-linux-gnu" ;;
|
||||
*) echo "❌ 未知架构: $arch"; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo ">>> 构建 $arch ($target)..."
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
cross build --release --target "$target" --locked
|
||||
|
||||
# 提取二进制
|
||||
BIN="target/$target/release/aether-hub"
|
||||
if [ ! -f "$BIN" ]; then
|
||||
echo "❌ 未找到二进制文件: $BIN"
|
||||
if ! command -v cross >/dev/null 2>&1; then
|
||||
echo "❌ 需要安装 cross: cargo install cross --git https://github.com/cross-rs/cross"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 打包
|
||||
ARCHIVE="$DIST_DIR/aether-hub-linux-$arch.tar.gz"
|
||||
tar czf "$ARCHIVE" -C "target/$target/release" aether-hub
|
||||
ARTIFACTS="$ARTIFACTS $ARCHIVE"
|
||||
mkdir -p "$DIST_DIR"
|
||||
|
||||
SIZE=$(du -h "$ARCHIVE" | cut -f1)
|
||||
echo "✅ $arch 构建完成: $ARCHIVE ($SIZE)"
|
||||
echo "🔨 开始构建 aether-hub 二进制..."
|
||||
echo " 目标平台: $BINARY_TARGETS"
|
||||
echo ""
|
||||
done
|
||||
|
||||
# 生成校验和
|
||||
cd "$DIST_DIR"
|
||||
shasum -a 256 aether-hub-*.tar.gz > SHA256SUMS.txt
|
||||
echo "📋 SHA256 校验和:"
|
||||
cat SHA256SUMS.txt
|
||||
echo ""
|
||||
ARTIFACTS=""
|
||||
for arch in $BINARY_TARGETS; do
|
||||
case "$arch" in
|
||||
amd64) target="x86_64-unknown-linux-gnu" ;;
|
||||
arm64) target="aarch64-unknown-linux-gnu" ;;
|
||||
*) echo "❌ 未知架构: $arch"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# 上传到 GitHub Release
|
||||
if [ "$UPLOAD" = true ]; then
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "❌ --upload 需要指定 tag,例如: ./build.sh --upload hub-v0.1.0"
|
||||
echo ">>> 构建 $arch ($target)..."
|
||||
cd "$SCRIPT_DIR"
|
||||
cross build --release --target "$target" --locked
|
||||
|
||||
BIN="target/$target/release/aether-hub"
|
||||
if [ ! -f "$BIN" ]; then
|
||||
echo "❌ 未找到二进制文件: $BIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ARCHIVE="$DIST_DIR/aether-hub-linux-$arch.tar.gz"
|
||||
tar czf "$ARCHIVE" -C "target/$target/release" aether-hub
|
||||
ARTIFACTS="$ARTIFACTS $ARCHIVE"
|
||||
|
||||
SIZE=$(du -h "$ARCHIVE" | cut -f1)
|
||||
echo "✅ $arch 构建完成: $ARCHIVE ($SIZE)"
|
||||
echo ""
|
||||
done
|
||||
|
||||
cd "$DIST_DIR"
|
||||
shasum -a 256 aether-hub-*.tar.gz > SHA256SUMS.txt
|
||||
echo "📋 SHA256 校验和:"
|
||||
cat SHA256SUMS.txt
|
||||
echo ""
|
||||
|
||||
if [ "$UPLOAD" = true ]; then
|
||||
if [ -z "$UPLOAD_TAG" ]; then
|
||||
echo "❌ --upload 需要指定 tag,例如: ./build.sh --upload hub-v0.1.0"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v gh >/dev/null 2>&1; then
|
||||
echo "❌ 需要安装 GitHub CLI: brew install gh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 上传到 GitHub Release: $UPLOAD_TAG"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
if ! git rev-parse "$UPLOAD_TAG" >/dev/null 2>&1; then
|
||||
git tag "$UPLOAD_TAG"
|
||||
git push origin "$UPLOAD_TAG"
|
||||
fi
|
||||
|
||||
gh release create "$UPLOAD_TAG" \
|
||||
--title "aether-hub ${UPLOAD_TAG#hub-}" \
|
||||
--generate-notes \
|
||||
$ARTIFACTS \
|
||||
"$DIST_DIR/SHA256SUMS.txt"
|
||||
|
||||
echo "✅ 上传完成!"
|
||||
fi
|
||||
|
||||
echo "🎉 binary 模式完成!"
|
||||
}
|
||||
|
||||
build_image() {
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "❌ 未找到 docker,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
if ! docker buildx version >/dev/null 2>&1; then
|
||||
echo "❌ 未找到 docker buildx,请先启用 buildx"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v gh &> /dev/null; then
|
||||
echo "❌ 需要安装 GitHub CLI: brew install gh"
|
||||
if [ "$IMAGE_PUSH" = true ] && [ "$IMAGE_LOAD" = true ]; then
|
||||
echo "❌ --push 与 --load 不能同时使用"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 上传到 GitHub Release: $TAG"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 创建 tag(如果不存在)
|
||||
if ! git rev-parse "$TAG" > /dev/null 2>&1; then
|
||||
git tag "$TAG"
|
||||
git push origin "$TAG"
|
||||
if [ "$IMAGE_PUSH" = false ] && [ "$IMAGE_LOAD" = false ]; then
|
||||
# image 模式默认走 push,符合发布场景
|
||||
IMAGE_PUSH=true
|
||||
fi
|
||||
|
||||
# 创建 Release 并上传
|
||||
gh release create "$TAG" \
|
||||
--title "aether-hub ${TAG#hub-}" \
|
||||
--generate-notes \
|
||||
$ARTIFACTS \
|
||||
"$DIST_DIR/SHA256SUMS.txt"
|
||||
if [ -z "$IMAGE_TAG" ]; then
|
||||
IMAGE_TAG=$(git -C "$PROJECT_DIR" describe --tags --always 2>/dev/null | sed 's/^v//')
|
||||
if [ -z "$IMAGE_TAG" ]; then
|
||||
IMAGE_TAG=$(date +%Y%m%d%H%M%S)
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✅ 上传完成!"
|
||||
if [ "$IMAGE_LOAD" = true ] && [[ "$IMAGE_PLATFORMS" == *,* ]]; then
|
||||
echo "❌ --load 仅支持单平台,请用 --platforms linux/amd64(或 arm64)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local ref="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
local cmd=(docker buildx build
|
||||
--platform "$IMAGE_PLATFORMS"
|
||||
-f "$SCRIPT_DIR/Dockerfile"
|
||||
-t "$ref"
|
||||
)
|
||||
|
||||
if [ "$IMAGE_LATEST" = true ]; then
|
||||
cmd+=(-t "${IMAGE_NAME}:latest")
|
||||
fi
|
||||
|
||||
if [ "$IMAGE_PUSH" = true ]; then
|
||||
cmd+=(--push)
|
||||
else
|
||||
cmd+=(--load)
|
||||
fi
|
||||
|
||||
cmd+=("$SCRIPT_DIR")
|
||||
|
||||
echo "🔨 开始构建 aether-hub 镜像..."
|
||||
echo " image: $ref"
|
||||
echo " platforms: $IMAGE_PLATFORMS"
|
||||
echo " mode: $([ "$IMAGE_PUSH" = true ] && echo push || echo load)"
|
||||
echo ""
|
||||
|
||||
"${cmd[@]}"
|
||||
|
||||
if [ "$IMAGE_PUSH" = true ]; then
|
||||
echo "✅ 镜像已推送: $ref"
|
||||
if [ "$IMAGE_LATEST" = true ]; then
|
||||
echo "✅ 镜像已推送: ${IMAGE_NAME}:latest"
|
||||
fi
|
||||
else
|
||||
echo "✅ 镜像已加载到本地: $ref"
|
||||
fi
|
||||
|
||||
echo "🎉 image 模式完成!"
|
||||
}
|
||||
|
||||
if [ "$MODE" = "image" ]; then
|
||||
build_image
|
||||
else
|
||||
build_binary
|
||||
fi
|
||||
|
||||
echo "🎉 全部完成!"
|
||||
|
||||
@@ -258,7 +258,8 @@ impl HubRouter {
|
||||
|
||||
pub fn register_worker(&self, conn: Arc<WorkerConn>) {
|
||||
info!(worker_id = conn.id, "worker connected");
|
||||
self.worker_conns.insert(conn.id, conn);
|
||||
self.worker_conns.insert(conn.id, conn.clone());
|
||||
self.sync_node_status_to_worker(&conn);
|
||||
}
|
||||
|
||||
pub fn unregister_worker(&self, conn_id: u64) {
|
||||
@@ -667,6 +668,28 @@ impl HubRouter {
|
||||
);
|
||||
}
|
||||
|
||||
/// When a worker connects, sync all current node statuses so worker state
|
||||
/// is consistent even if proxies connected before this worker came online.
|
||||
fn sync_node_status_to_worker(&self, worker: &Arc<WorkerConn>) {
|
||||
let snapshot: Vec<(String, usize)> = {
|
||||
let map = self.proxy_conns.read();
|
||||
map.iter()
|
||||
.map(|(node_id, conns)| (node_id.clone(), conns.len()))
|
||||
.collect()
|
||||
};
|
||||
|
||||
for (node_id, conn_count) in &snapshot {
|
||||
let frame = protocol::encode_node_status(node_id, *conn_count > 0, *conn_count);
|
||||
let _ = worker.send(Message::Binary(frame.into()));
|
||||
}
|
||||
|
||||
debug!(
|
||||
worker_id = worker.id,
|
||||
nodes_synced = snapshot.len(),
|
||||
"synced NODE_STATUS snapshot to worker"
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Stats
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user