diff --git a/Dockerfile.embed b/Dockerfile.embed index cd93052..fae14e7 100644 --- a/Dockerfile.embed +++ b/Dockerfile.embed @@ -9,6 +9,7 @@ ARG VERSION=dev ARG BUILD_TIME=unknown +ARG GIT_COMMIT=unknown # ==================== Stage 1: Frontend Builder ==================== FROM node:20-alpine AS frontend-builder @@ -31,6 +32,7 @@ FROM golang:1.24-alpine AS backend-builder ARG VERSION ARG BUILD_TIME +ARG GIT_COMMIT WORKDIR /app @@ -56,7 +58,7 @@ COPY --from=frontend-builder /app/dist/ static/ # 构建后端二进制(前端已嵌入,注入版本号) # local 模式需 CGO(SQLite + redka/mattn-sqlite3)// Logan.Li RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build \ - -ldflags="-w -s -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME}" \ + -ldflags="-w -s -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME} -X main.GitCommit=${GIT_COMMIT}" \ -o aiops . # ==================== Stage 3: Agent Builder ==================== diff --git a/deploy/kubernetes/deployment.yaml b/deploy/kubernetes/deployment.yaml index 4bcefe6..15f80be 100644 --- a/deploy/kubernetes/deployment.yaml +++ b/deploy/kubernetes/deployment.yaml @@ -1,6 +1,6 @@ # deployment.yaml — AIOps 应用 (embed 前端嵌入后端, 单容器单端口) # ---------------------------------------------------------------------------- -# - 镜像 aiops:embed(前端通过 go:embed 内嵌进同一二进制) +# - 镜像 lghost/aiops:(前端通过 go:embed 内嵌进同一二进制) # - 单端口 8080 = API + WebSocket + 前端 SPA 静态资源 # - 配置优先级: 环境变量 > config.yaml > 默认值 (viper AutomaticEnv) # - 环境变量命名: 配置键 database.host → DATABASE_HOST (全大写, 点变下划线) @@ -58,7 +58,7 @@ spec: mountPath: /vol containers: - name: aiops - image: 10.10.10.72/library/aiops:embed + image: lghost/aiops:dev imagePullPolicy: Always ports: - name: http diff --git a/deploy/kubernetes/quick-deploy.yaml b/deploy/kubernetes/quick-deploy.yaml index d8faf90..ca2162e 100644 --- a/deploy/kubernetes/quick-deploy.yaml +++ b/deploy/kubernetes/quick-deploy.yaml @@ -75,7 +75,7 @@ spec: mountPath: /vol containers: - name: aiops - image: aiops:embed + image: lghost/aiops:dev imagePullPolicy: IfNotPresent ports: - containerPort: 8080 diff --git a/docker-compose.k8s.yml b/docker-compose.k8s.yml index c1c4391..ca577bd 100644 --- a/docker-compose.k8s.yml +++ b/docker-compose.k8s.yml @@ -1,14 +1,18 @@ +# AIOps — 单应用 embed 容器(连接外部 MySQL/Redis,无内置数据库) +# 用法: VERSION=v1.0.1 docker compose -f docker-compose.k8s.yml up -d --build +# 署名 Logan.Li + services: - # ======================== Go 后端服务 (连接外部 MySQL/Redis) ======================== - backend: + aiops: + image: ${DOCKERHUB_IMAGE:-lghost/aiops}:${VERSION:-dev} build: context: . - dockerfile: backend/Dockerfile + dockerfile: Dockerfile.embed args: VERSION: ${VERSION:-dev} BUILD_TIME: ${BUILD_TIME:-unknown} - container_name: aiops-backend - hostname: aiops-backend + GIT_COMMIT: ${GIT_COMMIT:-unknown} + container_name: aiops restart: unless-stopped ports: - "8080:8080" @@ -16,32 +20,8 @@ services: - ./backend/config.external.yaml:/app/config.yaml - /var/run/docker.sock:/var/run/docker.sock healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + test: ["CMD", "curl", "-f", "http://localhost:8080/version"] interval: 30s timeout: 10s retries: 3 - start_period: 40s - extra_hosts: - - "host.docker.internal:host-gateway" - networks: - - aiops-network - - # ======================== React 前端 + Nginx ======================== - frontend: - build: - context: ./frontend - dockerfile: Dockerfile - container_name: aiops-frontend - hostname: aiops-frontend - restart: unless-stopped - ports: - - "80:80" - depends_on: - backend: - condition: service_healthy - networks: - - aiops-network - -networks: - aiops-network: - driver: bridge + start_period: 40s \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 5faefd1..6258ebd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,9 @@ +# AIOps Docker Compose — embed 单应用容器 + MySQL + Redis +# 访问: http://localhost:8080 (API + 前端 SPA 同一端口) +# 构建: VERSION=v1.0.1 ./run.sh docker up +# 署名 Logan.Li + services: - # ======================== MySQL 数据库 ======================== mysql: image: mysql:8.0 container_name: aiops-mysql @@ -28,7 +32,6 @@ services: networks: - aiops-network - # ======================== Redis 缓存 ======================== redis: image: redis:7-alpine container_name: aiops-redis @@ -47,24 +50,35 @@ services: networks: - aiops-network - # ======================== Go 后端服务 ======================== - backend: + aiops: + image: ${DOCKERHUB_IMAGE:-lghost/aiops}:${VERSION:-dev} build: context: . - dockerfile: backend/Dockerfile + dockerfile: Dockerfile.embed args: VERSION: ${VERSION:-dev} BUILD_TIME: ${BUILD_TIME:-unknown} - container_name: aiops-backend - hostname: aiops-backend + GIT_COMMIT: ${GIT_COMMIT:-unknown} + container_name: aiops + hostname: aiops restart: unless-stopped ports: - "8080:8080" environment: - - MODE=${MODE:-release} + TZ: Asia/Shanghai + MODE: release + DATABASE_DRIVER: mysql + DATABASE_HOST: mysql + DATABASE_PORT: 3306 + DATABASE_USER: ${MYSQL_USER:-aiops} + DATABASE_PASSWORD: ${MYSQL_PASSWORD:-CHANGE_ME} + DATABASE_DBNAME: ${MYSQL_DATABASE:-aiops} + REDIS_DRIVER: redis + REDIS_HOST: redis + REDIS_PORT: 6379 + JWT_SECRET: ${JWT_SECRET:-aiops-jwt-secret-change-me} volumes: - - ./backend/config.yaml:/app/config.yaml - - ./backend/data:/app/data + - aiops_uploads:/app/uploads - /var/run/docker.sock:/var/run/docker.sock depends_on: mysql: @@ -72,38 +86,19 @@ services: redis: condition: service_healthy healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + test: ["CMD", "curl", "-f", "http://localhost:8080/version"] interval: 30s timeout: 10s retries: 3 start_period: 40s - dns: - - 127.0.0.11 - - 114.114.114.114 - - 223.5.5.5 - networks: - - aiops-network - - # ======================== React 前端 + Nginx ======================== - frontend: - build: - context: ./frontend - dockerfile: Dockerfile - container_name: aiops-frontend - hostname: aiops-frontend - restart: unless-stopped - ports: - - "80:80" - depends_on: - backend: - condition: service_healthy networks: - aiops-network volumes: mysql_data: redis_data: + aiops_uploads: networks: aiops-network: - driver: bridge + driver: bridge \ No newline at end of file diff --git a/run.sh b/run.sh index ed29152..51f2e8a 100755 --- a/run.sh +++ b/run.sh @@ -3,8 +3,8 @@ # AIOps 智能运维平台 - 统一启动脚本 # 支持三种部署方式 + 本地开发模式: # -# 方式 1 — Docker Compose: -# ./run.sh docker up 构建+启动 (MySQL+Redis+Backend+Nginx) +# 方式 1 — Docker Compose (embed 单应用容器): +# ./run.sh docker up 构建+启动 (MySQL+Redis+AIOps) # # 方式 2 — Kubernetes (embed 单容器): # ./run.sh kubernetes deploy 构建镜像+全量部署 (embed 前端嵌入后端) @@ -33,6 +33,7 @@ BACKEND_DIR="backend" FRONTEND_DIR="frontend" BACKEND_BIN="aiops-server" STANDALONE_BIN="aiops" +DOCKERHUB_IMAGE="lghost/aiops" # 日志配置 MAX_LOG_FILES=3 @@ -433,11 +434,19 @@ ensure_go_path() { fi } -# 解析编译版本号(显式 tag 优先,否则取 git describe)// Logan.Li -resolve_build_version() { +# 判断参数是否像版本号 (v1.0.1 / 1.0.1) // Logan.Li +looks_like_version() { + local v=$1 + [[ "$v" =~ ^v[0-9] ]] || [[ "$v" =~ ^[0-9]+\.[0-9] ]] +} + +# 统一版本解析:显式参数 > VERSION 环境变量 > git describe > dev // Logan.Li +resolve_version() { local explicit="${1:-}" if [ -n "$explicit" ]; then BUILD_VERSION="$explicit" + elif [ -n "${VERSION:-}" ]; then + BUILD_VERSION="$VERSION" elif git describe --tags --always --dirty 2>/dev/null | grep -q .; then BUILD_VERSION=$(git describe --tags --always --dirty 2>/dev/null) else @@ -445,6 +454,108 @@ resolve_build_version() { fi BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ') GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + EMBED_IMAGE="${DOCKERHUB_IMAGE}:${BUILD_VERSION}" + export VERSION="$BUILD_VERSION" + export BUILD_TIME + export GIT_COMMIT +} + +# 兼容旧函数名 // Logan.Li +resolve_build_version() { + resolve_version "$@" +} + +# 跨平台 sed -i // Logan.Li +sed_inplace() { + local expr=$1 + local file=$2 + if [ "$OS" = "macos" ]; then + sed -i '' "$expr" "$file" + else + sed -i "$expr" "$file" + fi +} + +# 构建 embed Docker 镜像(二进制/K8s/Docker Compose 共用)// Logan.Li +build_embed_image() { + local version_tag="${1:-}" + local no_cache="${2:-0}" + local do_push="${3:-0}" + + if ! command -v docker &>/dev/null; then + error "Docker 未安装" + return 1 + fi + + resolve_version "$version_tag" + banner "========== 构建 embed 镜像 ($EMBED_IMAGE) ==========" + clean_frontend_artifacts + clean_embed_static + + export DOCKER_BUILDKIT=1 + local build_args=( + -f Dockerfile.embed + --build-arg "VERSION=${BUILD_VERSION}" + --build-arg "BUILD_TIME=${BUILD_TIME}" + --build-arg "GIT_COMMIT=${GIT_COMMIT}" + --label "org.opencontainers.image.version=${BUILD_VERSION}" + -t "$EMBED_IMAGE" + -t "${DOCKERHUB_IMAGE}:dev" + . + ) + if [ "$no_cache" -eq 1 ]; then + build_args=(--no-cache --pull "${build_args[@]}") + info "无缓存全量构建 (--no-cache --pull)" + fi + + info "构建中(前端 + 后端 embed)..." + docker build "${build_args[@]}" || { + error "镜像构建失败" + return 1 + } + + local img_size + img_size=$(docker images "$EMBED_IMAGE" --format "{{.Size}}" | head -1) + success "镜像构建完成: $EMBED_IMAGE ($img_size)" + KUBERNETES_BUILT_IMAGE="$EMBED_IMAGE" + + if [ "$do_push" -eq 1 ]; then + push_embed_image "$BUILD_VERSION" || return 1 + fi + return 0 +} + +# 推送镜像到 Docker Hub // Logan.Li +push_embed_image() { + local version_tag="${1:-}" + resolve_version "$version_tag" + + info "推送镜像到 Docker Hub..." + docker push "$EMBED_IMAGE" || { + error "推送失败: $EMBED_IMAGE(请先 docker login)" + return 1 + } + if [ "$BUILD_VERSION" != "dev" ]; then + docker push "${DOCKERHUB_IMAGE}:dev" 2>/dev/null || true + fi + success "已推送: $EMBED_IMAGE" + return 0 +} + +# 更新 K8s deployment 中的镜像坐标 // Logan.Li +kubernetes_update_manifest_image() { + local image=$1 + local files=( + "$KUBERNETES_MANIFEST_DIR/deployment.yaml" + "$KUBERNETES_MANIFEST_DIR/quick-deploy.yaml" + ) + local f + for f in "${files[@]}"; do + [ -f "$f" ] || continue + sed_inplace "s|image: ${DOCKERHUB_IMAGE}:.*|image: ${image}|g" "$f" + sed_inplace "s|image: .*/aiops:.*|image: ${image}|g" "$f" + done + info "K8s manifest 镜像已更新为: $image" } # 编译独立二进制(含 embed 前端,输出到项目根目录 ./aiops)// Logan.Li @@ -589,6 +700,7 @@ detect_compose_cmd() { cmd_docker() { local action=${1:-help} + shift || true if ! command -v docker &> /dev/null; then error "Docker 未安装,请先安装 Docker" @@ -600,27 +712,38 @@ cmd_docker() { error "Docker Compose 未安装 (需要 docker compose 插件或 docker-compose)" exit 1 fi - # 自动从 git tag 获取版本号 - GIT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) - export VERSION="${GIT_TAG:-dev}" - export BUILD_TIME - info "使用: $DOCKER_COMPOSE" - info "版本: $VERSION (构建时间: $BUILD_TIME)" - # 启用 BuildKit 加速构建(cache mount 跨构建复用 go mod/apk 缓存)// Logan.Li + # 解析可选版本号参数 (up v1.0.1 / push v1.0.1) // Logan.Li + local version_arg="" + local fresh_build=0 + while [ $# -gt 0 ]; do + case "$1" in + fresh) fresh_build=1; shift ;; + tag) + shift + version_arg=${1:-} + shift + ;; + *) + if looks_like_version "$1"; then + version_arg=$1 + fi + shift + ;; + esac + done + resolve_version "$version_arg" + export DOCKERHUB_IMAGE + info "使用: $DOCKER_COMPOSE" + info "版本: $BUILD_VERSION | 镜像: $EMBED_IMAGE" + export DOCKER_BUILDKIT=1 export COMPOSE_DOCKER_CLI_BUILD=1 info "BuildKit: 已启用 (增量编译 + 缓存复用)" case "$action" in up|upgrade|update) - local fresh_build=0 - if [ "${2:-}" = "fresh" ] || [ "$action" = "fresh" ]; then - fresh_build=1 - fi - banner "========== Docker Compose 启动/升级 ==========" - clean_frontend_artifacts + banner "========== Docker Compose 启动/升级 ($BUILD_VERSION) ==========" if [ "$fresh_build" -eq 1 ]; then info "全量重建镜像 (--no-cache)..." $DOCKER_COMPOSE build --no-cache @@ -630,21 +753,19 @@ cmd_docker() { $DOCKER_COMPOSE up -d --build --force-recreate --remove-orphans fi echo "" - # 清理历史 build cache 和悬空镜像,防止占满磁盘 info "清理历史构建缓存..." - local before_prune=$(df -h / | awk 'NR==2{print $5}') docker builder prune -f 2>/dev/null || true docker image prune -f 2>/dev/null || true - local after_prune=$(df -h / | awk 'NR==2{print $5}') - info "磁盘使用: ${before_prune} → ${after_prune}" echo "" info "等待服务就绪..." sleep 5 $DOCKER_COMPOSE ps echo "" - info "前端地址: http://localhost:80" - info "后端地址: http://localhost:8080" - info "健康检查: http://localhost:8080/health" + info "访问地址: http://localhost:8080" + info "版本检查: http://localhost:8080/version" + ;; + push) + build_embed_image "$version_arg" 0 1 || exit 1 ;; down) info "停止 Docker Compose..." @@ -688,16 +809,15 @@ cmd_docker() { cmd_docker up fresh ;; help|--help|-h) - echo "Docker Compose 部署命令:" - echo " ./run.sh docker up 增量构建 + 强制重建容器" - echo " ./run.sh docker up fresh 无缓存全量重建镜像并启动" - echo " ./run.sh docker upgrade 同 up(升级别名)" - echo " ./run.sh docker down 停止并移除容器" - echo " ./run.sh docker build 仅构建镜像" - echo " ./run.sh docker restart 重启容器" - echo " ./run.sh docker logs 查看实时日志" - echo " ./run.sh docker ps 查看容器状态" - echo " ./run.sh docker clean 清理构建缓存/无用镜像" + echo "Docker Compose 部署命令 (embed 单容器 + MySQL + Redis):" + echo " ./run.sh docker up 增量构建 + 启动" + echo " ./run.sh docker up v1.0.1 指定版本构建" + echo " ./run.sh docker up fresh 无缓存全量重建" + echo " ./run.sh docker push v1.0.1 构建并推送到 $DOCKERHUB_IMAGE" + echo " ./run.sh docker down 停止并移除容器" + echo " ./run.sh docker build 仅构建镜像" + echo " ./run.sh docker logs 查看实时日志" + echo " ./run.sh docker ps 查看容器状态" ;; *) error "未知 docker 子命令: $action" @@ -876,7 +996,6 @@ cmd_binary() { # ============================================================ KUBERNETES_NAMESPACE="devops" KUBERNETES_MANIFEST_DIR="$SCRIPT_DIR/deploy/kubernetes" -KUBERNETES_IMAGE="aiops:embed" KUBERNETES_NODE_PORT=30080 KUBERNETES_BUILT_IMAGE="" @@ -918,52 +1037,32 @@ load_image_to_cluster() { warning "如集群无法拉取镜像,请手动推送: docker push /$image" } -# Kubernetes 构建镜像(upgrade 时无缓存 + 时间戳 tag 避免旧层残留)// Logan.Li +# Kubernetes 构建镜像 // Logan.Li cmd_kubernetes_build() { - local no_cache=${1:-0} - if ! command -v docker &>/dev/null; then - error "Docker 未安装" - exit 1 - fi + local version_tag="" + local no_cache=0 + local do_push=0 + while [ $# -gt 0 ]; do + case "$1" in + 1|--no-cache|fresh) no_cache=1; shift ;; + push) do_push=1; shift ;; + tag) + shift + version_tag=${1:-} + shift + ;; + *) + if looks_like_version "$1"; then + version_tag=$1 + fi + shift + ;; + esac + done - banner "========== 构建 embed 镜像 ==========" - clean_frontend_artifacts - clean_embed_static - - local version - version=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev") - local build_time - build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ) - local image_tag - image_tag="${KUBERNETES_IMAGE}-$(date +%Y%m%d%H%M%S)" - info "镜像: $KUBERNETES_IMAGE (+ $image_tag)" - - local build_args=( - -f Dockerfile.embed - --build-arg VERSION="$version" - --build-arg BUILD_TIME="$build_time" - -t "$KUBERNETES_IMAGE" - -t "$image_tag" - ) - if [ "$no_cache" -eq 1 ]; then - build_args+=(--no-cache --pull) - info "无缓存全量构建 (--no-cache --pull)" - fi - build_args+=(.) - - info "构建中(前端 + 后端 embed)..." - docker build "${build_args[@]}" || { - error "镜像构建失败" - exit 1 - } - - local img_size - img_size=$(docker images "$KUBERNETES_IMAGE" --format "{{.Size}}" | head -1) - success "镜像构建完成: $KUBERNETES_IMAGE ($img_size)" - - load_image_to_cluster "$KUBERNETES_IMAGE" - load_image_to_cluster "$image_tag" - KUBERNETES_BUILT_IMAGE="$image_tag" + build_embed_image "$version_tag" "$no_cache" "$do_push" || return 1 + load_image_to_cluster "$KUBERNETES_BUILT_IMAGE" + load_image_to_cluster "${DOCKERHUB_IMAGE}:dev" } # 读取 deployment.yaml 中的 replicas(默认 1)// Logan.Li @@ -1020,8 +1119,10 @@ cmd_kubernetes() { check_kubernetes_deps banner "========== Kubernetes 部署 ($KUBERNETES_NAMESPACE) ==========" - # 1. 构建镜像 - cmd_kubernetes_build + # 1. 构建镜像(支持 deploy v1.0.1) + shift + cmd_kubernetes_build "$@" + kubernetes_update_manifest_image "$KUBERNETES_BUILT_IMAGE" # 2. 部署基础设施 info "部署 Namespace + Secret + ConfigMap..." @@ -1080,7 +1181,9 @@ cmd_kubernetes() { live_replicas=$(kubernetes_live_replicas) live_replicas=${live_replicas:-1} ensure_cluster_mode_for_replicas "$live_replicas" - cmd_kubernetes_build 1 + shift + cmd_kubernetes_build fresh "$@" + kubernetes_update_manifest_image "$KUBERNETES_BUILT_IMAGE" info "更新 Deployment 镜像: $KUBERNETES_BUILT_IMAGE" kubectl -n "$KUBERNETES_NAMESPACE" set image deployment/aiops aiops="$KUBERNETES_BUILT_IMAGE" --record kubectl -n "$KUBERNETES_NAMESPACE" rollout status deployment/aiops --timeout=180s @@ -1108,7 +1211,13 @@ cmd_kubernetes() { ;; build|image) - cmd_kubernetes_build + shift + cmd_kubernetes_build "$@" + ;; + + push) + shift + cmd_kubernetes_build push "$@" ;; status|ps) @@ -1189,9 +1298,10 @@ cmd_kubernetes() { echo " ./run.sh kubernetes port-forward 端口转发到本机 8080" echo "" echo " ${YELLOW}镜像${NC}" - echo " ./run.sh kubernetes build 仅构建 embed 镜像" + echo " ./run.sh kubernetes build v1.0.1 构建 embed 镜像" + echo " ./run.sh kubernetes push v1.0.1 构建并推送到 $DOCKERHUB_IMAGE" echo "" - echo " 镜像: $KUBERNETES_IMAGE" + echo " 镜像仓库: $DOCKERHUB_IMAGE:" echo " 命名空间: $KUBERNETES_NAMESPACE" echo " NodePort: $KUBERNETES_NODE_PORT" ;; @@ -1204,6 +1314,55 @@ cmd_kubernetes() { esac } +# ============================================================ +# 统一发版:git tag + 二进制 + Docker 镜像推送 + K8s manifest +# ============================================================ +cmd_release() { + local version_tag=${1:-} + local skip_tag=${2:-} + + if [ -z "$version_tag" ]; then + error "用法: ./run.sh release v1.0.1" + echo " 将依次: 打 git tag → 编译二进制 → 构建镜像 → 推送 $DOCKERHUB_IMAGE → 更新 K8s manifest" + exit 1 + fi + if ! looks_like_version "$version_tag"; then + error "版本号格式无效: $version_tag (示例: v1.0.1)" + exit 1 + fi + + banner "========== 发版 $version_tag ==========" + resolve_version "$version_tag" + + if [ "$skip_tag" != "--skip-tag" ]; then + if git rev-parse "$version_tag" >/dev/null 2>&1; then + warning "git tag 已存在: $version_tag(跳过创建)" + else + git tag -a "$version_tag" -m "release $version_tag" + success "已创建 git tag: $version_tag" + fi + fi + + build_standalone_binary "$version_tag" || exit 1 + info "二进制版本:" + ./"$STANDALONE_BIN" version + + build_embed_image "$version_tag" 0 1 || exit 1 + kubernetes_update_manifest_image "$EMBED_IMAGE" + + echo "" + success "发版完成: $version_tag" + echo " 二进制: ./$STANDALONE_BIN version" + echo " 镜像: $EMBED_IMAGE" + echo " 推送: docker push $EMBED_IMAGE" + echo " Compose: ./run.sh docker up $version_tag" + echo " K8s: ./run.sh kubernetes upgrade $version_tag" + echo "" + if [ "$skip_tag" != "--skip-tag" ]; then + info "如需推送 git tag: git push origin $version_tag" + fi +} + # ============================================================ # 帮助 # ============================================================ @@ -1218,15 +1377,12 @@ cmd_help() { echo " ./run.sh status 查看本地服务状态" echo " ./run.sh logs 查看实时日志" echo "" - echo -e " ${CYAN}Docker 容器部署${NC}" - echo " ./run.sh docker up 增量构建 + 强制重建容器" - echo " ./run.sh docker up fresh 无缓存全量重建并启动" - echo " ./run.sh docker upgrade 同 up(升级别名)" - echo " ./run.sh docker down 停止并移除容器" - echo " ./run.sh docker build 仅构建镜像" - echo " ./run.sh docker restart 重启容器" - echo " ./run.sh docker logs 查看容器日志" - echo " ./run.sh docker ps 查看容器状态" + echo -e " ${CYAN}Docker 容器部署 (embed 单容器)${NC}" + echo " ./run.sh docker up 构建+启动 (MySQL+Redis+AIOps)" + echo " ./run.sh docker up v1.0.1 指定版本" + echo " ./run.sh docker push v1.0.1 构建并推送到 $DOCKERHUB_IMAGE" + echo " ./run.sh docker down 停止容器" + echo " ./run.sh docker logs 查看日志" echo "" echo -e " ${MAGENTA}Kubernetes 部署 (embed 单容器)${NC}" echo " ./run.sh kubernetes deploy 构建镜像 + 全量部署到 Kubernetes" @@ -1244,6 +1400,7 @@ cmd_help() { echo -e " ${YELLOW}编译 & 独立二进制${NC}" echo " ./run.sh build 编译独立二进制 ./$STANDALONE_BIN (仅根目录)" echo " ./run.sh build tag v1.0.1 指定版本号编译" + echo " ./run.sh release v1.0.1 发版:tag+二进制+镜像推送+K8s" echo " ./run.sh binary upgrade 停服→清旧→重编译→后台启动(生产升级)" echo " ./run.sh binary start|stop|status 进程管理" echo " ./run.sh binary [args] 前台启动(调试)" @@ -1313,6 +1470,12 @@ main() { cmd_kubernetes "$@" ;; + # 统一发版 + release) + shift + cmd_release "$@" + ;; + # 帮助 help|--help|-h) cmd_help