diff --git a/00-init-tools.sh b/00-init-tools.sh index 258065b..44c2335 100755 --- a/00-init-tools.sh +++ b/00-init-tools.sh @@ -7,8 +7,9 @@ # 2. 配置 GOPROXY(自动测速选最快节点) # 3. 安装 Node.js / npm(前端依赖) # 4. 安装 Docker(基础设施容器) -# 5. 安装 Nginx 并写入 PC/OpenIM 反代(本机 HTTP :80;外部 HTTPS 由 LB/CDN 终止) -# 6. 写入 /etc/profile.d/deploy-env.sh(永久生效) +# 5. 安装 etcdctl(查看 Etcd 服务注册) +# 6. 安装 Nginx 并写入 PC/OpenIM 反代(本机 HTTP :80;外部 HTTPS 由 LB/CDN 终止) +# 7. 写入 /etc/profile.d/deploy-env.sh(永久生效) # # 用法: # ./deploy-test/00-init-tools.sh # 安装全部(含 Nginx 反代) @@ -16,6 +17,7 @@ # ./deploy-test/00-init-tools.sh node # 只安装 Node.js # ./deploy-test/00-init-tools.sh docker # 只安装 Docker # ./deploy-test/00-init-tools.sh goproxy # 只配置 GOPROXY +# sudo ./deploy-test/00-init-tools.sh etcdctl # 只安装 etcdctl(需 root) # sudo ./deploy-test/00-init-tools.sh nginx # 只安装 Nginx 反代(需 root) # # 前置条件: root 或 sudo 权限,Ubuntu/Debian 系统 @@ -31,6 +33,7 @@ init_script_log GO_VERSION="${GO_VERSION:-1.22.5}" GO_ARCH="${GO_ARCH:-amd64}" # amd64 / arm64 NODE_VERSION="${NODE_VERSION:-20}" # Node.js LTS 大版本 +ETCDCTL_VERSION="${ETCDCTL_VERSION:-3.5.17}" PROFILE_FILE="/etc/profile.d/deploy-env.sh" TARGET="${1:-all}" @@ -244,7 +247,69 @@ _install_docker() { } # ────────────────────────────────────────────────────────────────────────────── -# 5. Nginx — PC / OpenIM 统一入口(本机 HTTP :80) +# 5. etcdctl +# ────────────────────────────────────────────────────────────────────────────── +_install_etcdctl() { + step "安装 etcdctl ${ETCDCTL_VERSION}" + + if _has etcdctl; then + success " etcdctl 已安装: $(etcdctl version | head -1)" + return 0 + fi + + if [[ "$(id -u)" -ne 0 ]]; then + warn " etcdctl 安装需 root,请执行: sudo $0 etcdctl" + return 0 + fi + + if _has apt-get; then + apt-get update -y + if apt-get install -y etcd-client; then + success " etcdctl 安装完成: $(etcdctl version | head -1)" + return 0 + fi + warn " apt 安装 etcd-client 失败,尝试下载官方二进制" + elif _has dnf; then + if dnf install -y etcd; then + success " etcdctl 安装完成: $(etcdctl version | head -1)" + return 0 + fi + warn " dnf 安装 etcd 失败,尝试下载官方二进制" + elif _has yum; then + if yum install -y etcd; then + success " etcdctl 安装完成: $(etcdctl version | head -1)" + return 0 + fi + warn " yum 安装 etcd 失败,尝试下载官方二进制" + fi + + local arch + case "$(uname -m)" in + x86_64|amd64) arch="amd64" ;; + aarch64|arm64) arch="arm64" ;; + *) + error " 不支持的架构: $(uname -m),请手动安装 etcdctl" + return 1 + ;; + esac + + local tarball="etcd-v${ETCDCTL_VERSION}-linux-${arch}.tar.gz" + local url="https://github.com/etcd-io/etcd/releases/download/v${ETCDCTL_VERSION}/${tarball}" + local tmp="/tmp/${tarball}" + local out_dir="/tmp/etcd-v${ETCDCTL_VERSION}-linux-${arch}" + + info " 下载 ${url}" + curl -fL --progress-bar -o "$tmp" "$url" + rm -rf "$out_dir" + tar -C /tmp -xzf "$tmp" + install -m 0755 "${out_dir}/etcdctl" /usr/local/bin/etcdctl + rm -rf "$tmp" "$out_dir" + + success " etcdctl 安装完成: $(etcdctl version | head -1)" +} + +# ────────────────────────────────────────────────────────────────────────────── +# 6. Nginx — PC / OpenIM 统一入口(本机 HTTP :80) # ────────────────────────────────────────────────────────────────────────────── _install_pc_nginx_proxy() { step "安装 Nginx 并配置 OpenIM/PC 反代" @@ -317,7 +382,7 @@ _install_pc_nginx_proxy() { info " 浏览器打开 https://${proxy_domain}/" } -# all 时非 root 则跳过(不中断 Go/Node/Docker) +# all 时非 root 则跳过(不中断 Go/Node/Docker/etcdctl) _run_nginx_if_root() { if [[ "$(id -u)" -eq 0 ]]; then _install_pc_nginx_proxy @@ -343,6 +408,9 @@ case "$TARGET" in docker) _install_docker ;; + etcdctl) + _install_etcdctl + ;; nginx) _install_pc_nginx_proxy ;; @@ -351,11 +419,12 @@ case "$TARGET" in _config_goproxy _install_node _install_docker + _install_etcdctl _run_nginx_if_root ;; *) error "未知目标: $TARGET" - echo "用法: $0 [all|go|goproxy|node|docker|nginx]" + echo "用法: $0 [all|go|goproxy|node|docker|etcdctl|nginx]" exit 1 ;; esac @@ -373,6 +442,7 @@ _has npm && echo " npm: $(npm --version)" || echo _has pnpm && echo " pnpm: $(pnpm --version)" || echo " pnpm: 未安装" _has yarn && echo " yarn: $(yarn --version)" || echo " yarn: 未安装" _has docker && echo " Docker: $(docker --version | awk '{print $3}' | tr -d ',')" || echo " Docker: 未安装" +_has etcdctl && echo " etcdctl: $(etcdctl version | head -1 | sed 's/^etcdctl version: //')" || echo " etcdctl: 未安装" _has nginx && echo " Nginx: $(nginx -v 2>&1 | sed 's/^nginx version: //')" || echo " Nginx: 未安装" echo "" echo -e "${BOLD}GOPROXY 配置:${NC}" diff --git a/README.md b/README.md index d96ab20..d69f066 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ ssh -T git@github.com ``` deploy-test/ ├── dt.sh # 总控快捷入口:pull/build/start/stop/restart/status -├── 00-init-tools.sh # 步骤0(可选):Linux 服务器安装 Go / Node / Docker、GOPROXY、GitHub HTTPS 重写 +├── 00-init-tools.sh # 步骤0(可选):Linux 服务器安装 Go / Node / Docker / etcdctl、GOPROXY、GitHub HTTPS 重写 ├── common.sh # 公共函数库(路径、日志函数) ├── 01-init-env.sh # 步骤1:写入 .env.deploy-test(已存在则覆盖,旧文件带时间戳备份) ├── 02-patch-config.sh # 步骤2:将 .env.deploy-test 写入各服务 YAML @@ -115,19 +115,19 @@ deploy-test/ ## 步骤 0:`00-init-tools.sh`(裸机 / 新服务器) -在**尚未安装 Go、Node、Docker** 的 Linux 测试服务器上,先执行本脚本再跑 `01-init-env.sh` 及后续步骤。`setup.sh` **不会**自动调用它,需手动执行。 +在**尚未安装 Go、Node、Docker、etcdctl** 的 Linux 测试服务器上,先执行本脚本再跑 `01-init-env.sh` 及后续步骤。`setup.sh` **不会**自动调用它,需手动执行。 **GitHub 访问**:请先完成上文 **「GitHub:优先配置 SSH 密钥」**;本脚本的 **HTTPS 重写** 是在**不能配 SSH** 时的备选,不是首选。 | 项目 | 说明 | |------|------| -| **作用** | 安装 Go(默认 1.22.5,可用 `GO_VERSION` 覆盖)、配置 GOPROXY(测速选节点)、安装 Node.js LTS、全局安装 pnpm/yarn、安装 Docker;**备选**:将 GitHub SSH 克隆地址重写为 HTTPS(当环境无法使用 SSH 时,减轻 `pc` 等项目 `yarn install` 失败) | -| **环境变量** | `GO_VERSION`(如 `1.22.5`)、`GO_ARCH`(`amd64` / `arm64`)、`NODE_VERSION`(Node 大版本,默认 `20`) | +| **作用** | 安装 Go(默认 1.22.5,可用 `GO_VERSION` 覆盖)、配置 GOPROXY(测速选节点)、安装 Node.js LTS、全局安装 pnpm/yarn、安装 Docker、安装 `etcdctl`;**备选**:将 GitHub SSH 克隆地址重写为 HTTPS(当环境无法使用 SSH 时,减轻 `pc` 等项目 `yarn install` 失败) | +| **环境变量** | `GO_VERSION`(如 `1.22.5`)、`GO_ARCH`(`amd64` / `arm64`)、`NODE_VERSION`(Node 大版本,默认 `20`)、`ETCDCTL_VERSION`(默认 `3.5.17`) | | **权限** | 需要 **root** 或 **sudo**(写入 `/usr/local/go`、`/etc/profile.d/deploy-env.sh` 等) | | **系统** | 面向 Ubuntu/Debian(`apt-get`);脚本内注释说明前置条件 | ```bash -# 安装全部(Go + GOPROXY + Node + Docker) +# 安装全部(Go + GOPROXY + Node + Docker + etcdctl) sudo ./deploy-test/00-init-tools.sh # 或只执行其中一项 @@ -135,6 +135,7 @@ sudo ./deploy-test/00-init-tools.sh go # Go + GOPROXY sudo ./deploy-test/00-init-tools.sh goproxy # 仅重配 GOPROXY sudo ./deploy-test/00-init-tools.sh node # 仅 Node / npm / pnpm / yarn sudo ./deploy-test/00-init-tools.sh docker # 仅 Docker +sudo ./deploy-test/00-init-tools.sh etcdctl # 仅安装 etcdctl ``` 执行结束后脚本会提示:新开终端需 `source /etc/profile.d/deploy-env.sh` 或重新登录 SSH,再执行 `./deploy-test/01-init-env.sh`。 @@ -156,7 +157,7 @@ sudo ./deploy-test/00-init-tools.sh docker # 仅 Docker ### 分步执行 ```bash -# 0. (可选)裸机安装 Go / Node / Docker,见上文「步骤 0」 +# 0. (可选)裸机安装 Go / Node / Docker / etcdctl,见上文「步骤 0」 # sudo ./deploy-test/00-init-tools.sh # 1. 写入配置模板(若 .env.deploy-test 已存在会先备份为 .bak.<时间戳> 再覆盖) @@ -402,6 +403,7 @@ TENCENT_SDK_SECRET_KEY=xxx # 查看状态 ./deploy-test/status.sh +# status.sh 会输出 Etcd 中已注册的 RPC 服务 key,用于排查 produced zero addresses # 重启单个后端服务 ./deploy-test/restart.sh chat-api diff --git a/status.sh b/status.sh index 1328153..47e6751 100755 --- a/status.sh +++ b/status.sh @@ -31,6 +31,60 @@ print_container_status "MinIO" "dev-minio" "${MINIO_API_PORT:-9000}" print_container_status "LiveKit" "dev-livekit" "7880" printf " ${CYAN}◉${NC} %-10s 公网 %s:50000-51000/udp (WebRTC)\n" "" "${LIVEKIT_NODE_IP:-?}" +# ── Etcd RPC 注册 ───────────────────────────────────────────────────────────── +echo "" +echo -e "${BOLD}[ Etcd RPC 注册 ]${NC}" +ETCD_ENDPOINT="127.0.0.1:${ETCD_PORT:-2379}" +if ! command -v etcdctl &>/dev/null; then + printf " ${YELLOW}○${NC} %-18s %s\n" "etcdctl" "未安装,请执行: sudo ./deploy-test/00-init-tools.sh etcdctl" +else + if ETCD_KEYS=$(ETCDCTL_API=3 etcdctl --endpoints="$ETCD_ENDPOINT" get --prefix "" --keys-only 2>&1); then + if [[ -z "$ETCD_KEYS" ]]; then + printf " ${YELLOW}○${NC} %-18s %s\n" "registry" "Etcd 可访问,但当前无注册 key" + else + KNOWN_RPC_SERVICES=( + auth-rpc-service + user-rpc-service + friend-rpc-service + group-rpc-service + msg-rpc-service + conversation-rpc-service + third-rpc-service + push-rpc-service + messagegateway-rpc-service + chat-rpc-service + admin-rpc-service + bot-rpc-service + ) + found_rpc=0 + for svc in "${KNOWN_RPC_SERVICES[@]}"; do + count=$(printf '%s\n' "$ETCD_KEYS" | grep -c "$svc" || true) + if [[ "$count" -gt 0 ]]; then + found_rpc=1 + printf " ${GREEN}●${NC} %-28s %s key(s)\n" "$svc" "$count" + else + printf " ${YELLOW}○${NC} %-28s %s\n" "$svc" "未注册" + fi + done + + echo " ${CYAN}原始注册 key(过滤 rpc/service/openim/chat/admin 等关键词,最多 80 行):${NC}" + filtered_keys=$(printf '%s\n' "$ETCD_KEYS" | grep -Ei 'rpc|service|openim|chat|admin|auth|user|friend|group|msg|conversation|third|push|gateway|bot' | head -80 || true) + if [[ -n "$filtered_keys" ]]; then + printf '%s\n' "$filtered_keys" | sed 's/^/ /' + else + echo " (未匹配到 RPC 相关 key)" + fi + + if [[ "$found_rpc" -eq 0 ]]; then + warn " 未发现已知 RPC 服务注册;chat-api/openim-api 调 RPC 时可能出现 name resolver error: produced zero addresses" + fi + fi + else + printf " ${RED}○${NC} %-18s %s\n" "etcd" "不可访问: $ETCD_ENDPOINT" + printf "%s\n" "$ETCD_KEYS" | sed 's/^/ /' + fi +fi + # ── 远程服务 ───────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}[ 远程服务(连接配置)]${NC}"