feat: 三种部署方式 — embed单容器 + 二进制部署 + K8s
新增三种部署方式(原有 docker-compose 不受影响): 1. Docker embed 单容器 (docker-compose.embed.yml): - go:embed 前端嵌入后端二进制 - 不需要 nginx 容器,3容器→2容器 - 镜像仅 54.8MB (vs 原来 1.16GB) - 用法: make docker-embed 2. 二进制部署 (deploy/binary/): - make build-binary 生成单二进制 - install.sh 自动创建 systemd 服务 - 适合无 Docker 环境 - 用法: scp aiops + install.sh, bash install.sh install 3. Kubernetes 部署 (deploy/k8s/): - devops 命名空间全套 yaml - MySQL StatefulSet + Redis StatefulSet + Backend Deployment - ConfigMap + Secret + Ingress - 用法: kubectl apply -f deploy/k8s/ 核心改动: - backend/cmd/embed.go: go:embed 前端 + SPA fallback 路由 - backend/Dockerfile.embed: 四阶段构建 (前端→后端embed→agent→runtime) - Makefile: build-binary + docker-embed 目标 - deploy/README.md: 四种部署方式对比文档
This commit is contained in:
1 parent
24f955832e
commit
4631b02f13
15 files changed
+1218
-3
No files matched your search
@@ -53,3 +53,6 @@ docker-data/
|
|||||||
*.pid
|
*.pid
|
||||||
agent/aiops-agent
|
agent/aiops-agent
|
||||||
agent/bin/
|
agent/bin/
|
||||||
|
|
||||||
|
# embed 模式临时目录(前端构建产物,编译后清理)
|
||||||
|
backend/cmd/static/
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# AIOps 智能运维平台 - Makefile
|
# AIOps 智能运维平台 - Makefile
|
||||||
# 使用方式: make help
|
# 使用方式: make help
|
||||||
.PHONY: help install dev build build-ui build-api status stop restart logs lint clean \
|
.PHONY: help install dev build build-ui build-api build-binary status stop restart logs lint clean \
|
||||||
docker-up docker-down docker-build docker-logs
|
docker-up docker-down docker-build docker-logs docker-embed
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "AIOps 智能运维平台 - 可用命令:"
|
@echo "AIOps 智能运维平台 - 可用命令:"
|
||||||
@@ -24,7 +24,10 @@ help:
|
|||||||
@echo " make docker-down - 停止并移除所有容器"
|
@echo " make docker-down - 停止并移除所有容器"
|
||||||
@echo " make docker-build - 重新构建镜像(不启动)"
|
@echo " make docker-build - 重新构建镜像(不启动)"
|
||||||
@echo " make docker-logs - 查看容器日志"
|
@echo " make docker-logs - 查看容器日志"
|
||||||
|
@echo " make docker-embed - 单容器部署(embed 模式,前后端合一)"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
@echo " 二进制部署:"
|
||||||
|
@echo " make build-binary - 编译单二进制(前端嵌入后端)"
|
||||||
|
|
||||||
# ==================== 本地开发 ====================
|
# ==================== 本地开发 ====================
|
||||||
install:
|
install:
|
||||||
@@ -71,7 +74,7 @@ clean:
|
|||||||
rm -f backend/logs/*.log frontend/logs/*.log
|
rm -f backend/logs/*.log frontend/logs/*.log
|
||||||
@echo "清理完成"
|
@echo "清理完成"
|
||||||
|
|
||||||
# ==================== Docker 部署 ====================
|
# ==================== Docker 部署(双容器:前端 nginx + 后端) ====================
|
||||||
docker-up:
|
docker-up:
|
||||||
docker-compose up -d --build
|
docker-compose up -d --build
|
||||||
|
|
||||||
@@ -83,3 +86,17 @@ docker-build:
|
|||||||
|
|
||||||
docker-logs:
|
docker-logs:
|
||||||
docker-compose logs -f
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# ==================== Docker 部署(单容器:embed 模式) ====================
|
||||||
|
docker-embed:
|
||||||
|
docker-compose -f docker-compose.embed.yml up -d --build
|
||||||
|
|
||||||
|
# ==================== 二进制部署(embed 模式) ====================
|
||||||
|
build-binary: build-ui
|
||||||
|
@echo "构建单二进制(前端嵌入后端)..."
|
||||||
|
@mkdir -p backend/cmd/static
|
||||||
|
@cp -r frontend/dist/* backend/cmd/static/
|
||||||
|
cd backend && CGO_ENABLED=0 go build -ldflags="-s -w" -o ../aiops ./cmd/
|
||||||
|
@rm -rf backend/cmd/static
|
||||||
|
@echo "✓ 二进制已生成: ./aiops"
|
||||||
|
@echo " 部署: scp aiops deploy/binary/install.sh root@<server>:/tmp/ && ssh root@<server> 'cd /tmp && bash install.sh install'"
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# ============================================================
|
||||||
|
# AIOps 单容器 Dockerfile(embed 模式)
|
||||||
|
# 前端嵌入后端二进制,一个容器搞定前后端
|
||||||
|
# 用法: docker build -f backend/Dockerfile.embed -t aiops-embed .
|
||||||
|
# 署名 Logan.Li
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
ARG VERSION=dev
|
||||||
|
ARG BUILD_TIME=unknown
|
||||||
|
|
||||||
|
# ==================== Stage 1: Frontend Builder ====================
|
||||||
|
FROM node:20-alpine AS frontend-builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_OPTIONS=--max-old-space-size=4096
|
||||||
|
|
||||||
|
# 使用国内 npm 镜像
|
||||||
|
RUN npm config set registry https://registry.npmmirror.com
|
||||||
|
|
||||||
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
|
RUN npm ci --prefer-offline 2>/dev/null || npm install
|
||||||
|
|
||||||
|
COPY frontend/ ./
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ==================== Stage 2: Backend Builder ====================
|
||||||
|
FROM golang:1.24-alpine AS backend-builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
|
|
||||||
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
|
ENV GOSUMDB=off
|
||||||
|
|
||||||
|
COPY backend/go.mod backend/go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY backend/ ./
|
||||||
|
|
||||||
|
# 复制前端构建产物到 static 目录(供 go:embed 嵌入)
|
||||||
|
COPY --from=frontend-builder /app/dist ./cmd/static/
|
||||||
|
|
||||||
|
# 构建(前端已嵌入)
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||||
|
-ldflags="-w -s -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME}" \
|
||||||
|
-o aiops ./cmd/
|
||||||
|
|
||||||
|
# ==================== Stage 3: Agent Builder ====================
|
||||||
|
FROM golang:1.24-alpine AS agent-builder
|
||||||
|
|
||||||
|
WORKDIR /agent
|
||||||
|
|
||||||
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
|
ENV GOSUMDB=off
|
||||||
|
|
||||||
|
COPY agent/go.mod agent/go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY agent/ ./
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/aiops-agent-linux-amd64 . && \
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o bin/aiops-agent-linux-arm64 . && \
|
||||||
|
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o bin/aiops-agent-darwin-arm64 .
|
||||||
|
|
||||||
|
# ==================== Stage 4: Runtime ====================
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories
|
||||||
|
RUN apk --no-cache add ca-certificates tzdata curl
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 单个二进制(前端已嵌入)
|
||||||
|
COPY --from=backend-builder /app/aiops .
|
||||||
|
|
||||||
|
# Agent 二进制 + 安装脚本
|
||||||
|
RUN mkdir -p /app/agent/bin /app/agent/deploy
|
||||||
|
COPY --from=agent-builder /agent/bin/aiops-agent-linux-amd64 /app/agent/bin/
|
||||||
|
COPY --from=agent-builder /agent/bin/aiops-agent-linux-arm64 /app/agent/bin/
|
||||||
|
COPY --from=agent-builder /agent/bin/aiops-agent-darwin-arm64 /app/agent/bin/
|
||||||
|
COPY agent/deploy/install.sh /app/agent/deploy/
|
||||||
|
COPY agent/deploy/aiops-agent.service /app/agent/deploy/
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:8080/health || exit 1
|
||||||
|
|
||||||
|
CMD ["./aiops"]
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed all:static
|
||||||
|
var frontendFS embed.FS
|
||||||
|
|
||||||
|
// setupFrontend 配置前端静态文件服务(go:embed 模式) // Logan.Li
|
||||||
|
// 前端构建产物放在 backend/cmd/static/ 目录,编译时嵌入二进制
|
||||||
|
func setupFrontend(r *gin.Engine) {
|
||||||
|
// 获取 static 子目录的 fs.FS
|
||||||
|
staticFS, err := fs.Sub(frontendFS, "static")
|
||||||
|
if err != nil {
|
||||||
|
// static 目录不存在(开发模式或未构建前端),跳过
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpFS := http.FS(staticFS)
|
||||||
|
|
||||||
|
// 静态资源文件(js/css/图片等)直接返回
|
||||||
|
r.Use(func(c *gin.Context) {
|
||||||
|
// API 和 WebSocket 路径不走静态文件
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
if strings.HasPrefix(path, "/api/") ||
|
||||||
|
strings.HasPrefix(path, "/ws/") ||
|
||||||
|
strings.HasPrefix(path, "/agent/") ||
|
||||||
|
strings.HasPrefix(path, "/health") ||
|
||||||
|
strings.HasPrefix(path, "/version") {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// NoRoute: SPA fallback — 非 API 请求尝试静态文件,找不到则返回 index.html
|
||||||
|
r.NoRoute(func(c *gin.Context) {
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
|
||||||
|
// API/WS 路径返回 JSON 404
|
||||||
|
if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "/ws/") || strings.HasPrefix(path, "/agent/") {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "接口不存在"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试打开静态文件
|
||||||
|
cleanPath := strings.TrimPrefix(path, "/")
|
||||||
|
if cleanPath == "" {
|
||||||
|
cleanPath = "index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := staticFS.Open(cleanPath)
|
||||||
|
if err == nil {
|
||||||
|
f.Close()
|
||||||
|
c.Header("Cache-Control", "public, max-age=31536000")
|
||||||
|
c.FileFromFS(path, httpFS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SPA fallback → index.html(React Router history 模式)
|
||||||
|
c.FileFromFS("/index.html", httpFS)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -162,6 +162,9 @@ func main() {
|
|||||||
// 注册路由
|
// 注册路由
|
||||||
routes.Register(r, db, log, cfg, cache)
|
routes.Register(r, db, log, cfg, cache)
|
||||||
|
|
||||||
|
// 前端静态文件服务(go:embed 模式,二进制部署用) // Logan.Li
|
||||||
|
setupFrontend(r)
|
||||||
|
|
||||||
// 创建HTTP服务器
|
// 创建HTTP服务器
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: ":" + cfg.Port,
|
Addr: ":" + cfg.Port,
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# AIOps 三种部署方式
|
||||||
|
|
||||||
|
## 部署方式对比
|
||||||
|
|
||||||
|
| 方式 | 适用场景 | 优点 | 缺点 |
|
||||||
|
|------|---------|------|------|
|
||||||
|
| **Docker Compose(双容器)** | 生产环境 | 前后端分离,nginx 高性能 | 需要 4 个容器 |
|
||||||
|
| **Docker Compose(embed 单容器)** | 快速部署 | 1 个容器搞定,无需 nginx | 静态文件性能稍弱 |
|
||||||
|
| **二进制部署** | 无 Docker 环境 | 一个文件 + systemd | 需要自行管理 MySQL/Redis |
|
||||||
|
| **Kubernetes** | K8s 集群 | 标准化编排,弹性伸缩 | 需要 K8s 集群 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方式一:Docker Compose(双容器,当前默认)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 开发机(75)
|
||||||
|
cd /srv/aiops
|
||||||
|
make docker-up # 构建并启动
|
||||||
|
|
||||||
|
# 生产机(150)
|
||||||
|
cd /srv/aiops && git pull
|
||||||
|
make docker-up
|
||||||
|
```
|
||||||
|
|
||||||
|
架构:nginx(:80) → backend(:8080) + MySQL + Redis
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方式二:Docker Compose embed 单容器
|
||||||
|
|
||||||
|
前端通过 Go `//go:embed` 嵌入后端二进制,不需要 nginx 容器。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/aiops
|
||||||
|
docker-compose -f docker-compose.embed.yml up -d --build
|
||||||
|
# 或
|
||||||
|
make docker-embed
|
||||||
|
```
|
||||||
|
|
||||||
|
架构:aiops-app(:80→:8080) + MySQL + Redis(3 个容器)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方式三:二进制部署(无 Docker)
|
||||||
|
|
||||||
|
### 在开发机构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/aiops
|
||||||
|
make build-binary
|
||||||
|
# 生成 ./aiops 单二进制(~30MB,前端已嵌入)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在目标服务器部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 传两个文件到目标服务器
|
||||||
|
scp aiops deploy/binary/install.sh root@<target>:/tmp/
|
||||||
|
|
||||||
|
# 安装(自动创建 systemd 服务 + 默认配置)
|
||||||
|
ssh root@<target>
|
||||||
|
cd /tmp && bash install.sh install
|
||||||
|
|
||||||
|
# 修改数据库配置
|
||||||
|
vim /opt/aiops/config.yaml
|
||||||
|
|
||||||
|
# 启动
|
||||||
|
systemctl start aiops
|
||||||
|
```
|
||||||
|
|
||||||
|
### 管理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl start aiops # 启动
|
||||||
|
systemctl stop aiops # 停止
|
||||||
|
systemctl restart aiops # 重启
|
||||||
|
systemctl status aiops # 状态
|
||||||
|
journalctl -u aiops -f # 日志
|
||||||
|
```
|
||||||
|
|
||||||
|
架构:aiops 二进制 + systemd(端口 8080 同时服务 API 和前端)
|
||||||
|
|
||||||
|
> 前提:目标服务器需已安装 MySQL 8.0 + Redis 7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方式四:Kubernetes 部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/aiops
|
||||||
|
kubectl apply -f deploy/k8s/
|
||||||
|
```
|
||||||
|
|
||||||
|
详见 [deploy/k8s/README.md](../k8s/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速选择
|
||||||
|
|
||||||
|
| 环境 | 推荐方式 |
|
||||||
|
|------|---------|
|
||||||
|
| 开发测试(75) | 方式一 docker-compose |
|
||||||
|
| 生产环境(150) | 方式一 docker-compose |
|
||||||
|
| 客户现场快速演示 | 方式二 embed 单容器 |
|
||||||
|
| 内网无 Docker 服务器 | 方式三 二进制部署 |
|
||||||
|
| K8s 集群 | 方式四 Kubernetes |
|
||||||
Executable
+165
@@ -0,0 +1,165 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# AIOps 二进制部署脚本(embed 模式)
|
||||||
|
# 一个二进制 = 前端 + 后端,无需 Docker/Nginx
|
||||||
|
# 署名 Logan.Li
|
||||||
|
# ============================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 颜色
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# 默认路径
|
||||||
|
INSTALL_DIR="/opt/aiops"
|
||||||
|
CONFIG_FILE="$INSTALL_DIR/config.yaml"
|
||||||
|
SERVICE_FILE="/etc/systemd/system/aiops.service"
|
||||||
|
BINARY_NAME="aiops"
|
||||||
|
PID_FILE="/var/run/aiops.pid"
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 命令处理
|
||||||
|
# ============================================================
|
||||||
|
case "$1" in
|
||||||
|
install)
|
||||||
|
info "AIOps 二进制安装"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
error "请使用 root 或 sudo 执行"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查二进制
|
||||||
|
if [ ! -f "./${BINARY_NAME}" ]; then
|
||||||
|
error "当前目录找不到 ./${BINARY_NAME} 二进制文件"
|
||||||
|
echo " 请先在开发机执行: make build-binary"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建安装目录
|
||||||
|
mkdir -p "$INSTALL_DIR/logs" "$INSTALL_DIR/uploads"
|
||||||
|
|
||||||
|
# 复制二进制
|
||||||
|
cp "./${BINARY_NAME}" "$INSTALL_DIR/"
|
||||||
|
chmod +x "$INSTALL_DIR/${BINARY_NAME}"
|
||||||
|
info "二进制已安装到 $INSTALL_DIR/${BINARY_NAME}"
|
||||||
|
|
||||||
|
# 生成默认配置(如果不存在)
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
cat > "$CONFIG_FILE" << 'EOF'
|
||||||
|
mode: release
|
||||||
|
port: "8080"
|
||||||
|
log_level: info
|
||||||
|
|
||||||
|
database:
|
||||||
|
driver: mysql
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 3306
|
||||||
|
user: aiops
|
||||||
|
password: aiops_pass_2026
|
||||||
|
dbname: aiops
|
||||||
|
charset: utf8mb4
|
||||||
|
max_conn: 100
|
||||||
|
max_idle: 10
|
||||||
|
|
||||||
|
redis:
|
||||||
|
driver: redis
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
password: ""
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: ezops-aiops-jwt-secret-2026
|
||||||
|
expire_hour: 24
|
||||||
|
EOF
|
||||||
|
info "默认配置已生成: $CONFIG_FILE"
|
||||||
|
warn "请根据实际环境修改数据库/Redis 配置"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建 systemd 服务
|
||||||
|
cat > "$SERVICE_FILE" << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=AIOps Platform (Embed Mode)
|
||||||
|
After=network-online.target mysql.service redis.service
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=${INSTALL_DIR}
|
||||||
|
ExecStart=${INSTALL_DIR}/${BINARY_NAME}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
LimitNOFILE=65536
|
||||||
|
Environment=GIN_MODE=release
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable aiops
|
||||||
|
info "systemd 服务已创建并启用开机自启"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "安装完成!"
|
||||||
|
echo ""
|
||||||
|
echo " 启动: systemctl start aiops"
|
||||||
|
echo " 状态: systemctl status aiops"
|
||||||
|
echo " 日志: journalctl -u aiops -f"
|
||||||
|
echo " 配置: vim $CONFIG_FILE"
|
||||||
|
echo " 访问: http://<IP>:8080"
|
||||||
|
echo ""
|
||||||
|
;;
|
||||||
|
|
||||||
|
start)
|
||||||
|
systemctl start aiops
|
||||||
|
info "AIOps 已启动"
|
||||||
|
;;
|
||||||
|
|
||||||
|
stop)
|
||||||
|
systemctl stop aiops
|
||||||
|
info "AIOps 已停止"
|
||||||
|
;;
|
||||||
|
|
||||||
|
restart)
|
||||||
|
systemctl restart aiops
|
||||||
|
info "AIOps 已重启"
|
||||||
|
;;
|
||||||
|
|
||||||
|
status)
|
||||||
|
systemctl status aiops --no-pager
|
||||||
|
;;
|
||||||
|
|
||||||
|
uninstall)
|
||||||
|
warn "即将卸载 AIOps(保留数据/配置)"
|
||||||
|
systemctl stop aiops 2>/dev/null
|
||||||
|
systemctl disable aiops
|
||||||
|
rm -f "$SERVICE_FILE"
|
||||||
|
rm -f "$INSTALL_DIR/${BINARY_NAME}"
|
||||||
|
systemctl daemon-reload
|
||||||
|
info "已卸载(配置/日志/数据保留在 $INSTALL_DIR)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "AIOps 二进制部署工具"
|
||||||
|
echo ""
|
||||||
|
echo "用法: $0 {install|start|stop|restart|status|uninstall}"
|
||||||
|
echo ""
|
||||||
|
echo " install - 安装二进制 + 生成配置 + 创建 systemd 服务"
|
||||||
|
echo " start - 启动服务"
|
||||||
|
echo " stop - 停止服务"
|
||||||
|
echo " restart - 重启服务"
|
||||||
|
echo " status - 查看服务状态"
|
||||||
|
echo " uninstall - 卸载二进制(保留数据)"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
# AIOps Kubernetes 部署
|
||||||
|
|
||||||
|
AIOps 平台在 Kubernetes 上的部署清单(Go/Gin 后端 + go:embed 前端 + MySQL 8.0 + Redis 7)。
|
||||||
|
|
||||||
|
所有资源部署在 **`devops`** 命名空间。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `namespace.yaml` | `devops` 命名空间 |
|
||||||
|
| `configmap.yaml` | 后端 `config.yaml`(ConfigMap)+ MySQL/JWT 密码(Secret) |
|
||||||
|
| `mysql.yaml` | MySQL 8.0 StatefulSet + Headless/ClusterIP Service + 8Gi 持久卷 |
|
||||||
|
| `redis.yaml` | Redis 7 StatefulSet + Headless/ClusterIP Service + 2Gi 持久卷 |
|
||||||
|
| `backend.yaml` | 后端 Deployment(端口 8080=API/WS,80=embed 前端)+ ClusterIP Service |
|
||||||
|
| `ingress.yaml` | Ingress:`/api`、`/ws` → 8080;`/` → 80 |
|
||||||
|
|
||||||
|
## 架构与端口
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────── Ingress (nginx) ────────────────┐
|
||||||
|
/api, /ws ──▶│ │──▶ aiops-backend:8080 (API + WebSocket)
|
||||||
|
/ ──▶│ │──▶ aiops-backend:80 (go:embed 前端)
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────────┴──────────────┐
|
||||||
|
▼ ▼
|
||||||
|
aiops-backend aiops-backend
|
||||||
|
│ (DB_HOST=mysql) │ (REDIS_HOST=redis)
|
||||||
|
▼ ▼
|
||||||
|
MySQL 8.0 (StatefulSet) Redis 7 (StatefulSet)
|
||||||
|
aiops / aiops_pass_2026 无密码, db 0
|
||||||
|
数据库 aiops, 8Gi PV 2Gi PV
|
||||||
|
```
|
||||||
|
|
||||||
|
- **8080**:API + WebSocket
|
||||||
|
- **80**:前端(go:embed 嵌入后端二进制,由后端进程提供静态资源)
|
||||||
|
- 配置文件 `config.yaml` 中 DB/Redis 连接均以环境变量 `${VAR}` 占位,由 Deployment 的 `env` 注入;密码走 Secret。
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
1. 一个可用的 Kubernetes 集群(≥ 1.22)。
|
||||||
|
2. 已安装 **nginx Ingress Controller**。
|
||||||
|
3. 集群具备可用的 **StorageClass**(MySQL/Redis 的 `volumeClaimTemplates` 依赖动态卷供应)。
|
||||||
|
- minikube / kind 自带默认 StorageClass(如 `standard`)。
|
||||||
|
- 如需指定,取消各 StatefulSet 中 `storageClassName` 的注释。
|
||||||
|
4. 后端镜像 `aiops-backend:latest` 已构建并推送到集群可访问的镜像仓库(或本地 `kind load docker-image` / `minikube image load`)。
|
||||||
|
|
||||||
|
## 部署步骤
|
||||||
|
|
||||||
|
按顺序 apply(资源间存在依赖:Secret → 数据库 → 后端 → Ingress):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deploy/k8s
|
||||||
|
|
||||||
|
# 1. 命名空间(必须最先)
|
||||||
|
kubectl apply -f namespace.yaml
|
||||||
|
|
||||||
|
# 2. 配置 + 密码(ConfigMap / Secret)
|
||||||
|
kubectl apply -f configmap.yaml
|
||||||
|
|
||||||
|
# 3. MySQL(启动后自动建库 aiops 与用户 aiops)
|
||||||
|
kubectl apply -f mysql.yaml
|
||||||
|
|
||||||
|
# 4. Redis
|
||||||
|
kubectl apply -f redis.yaml
|
||||||
|
|
||||||
|
# 5. 等待数据库就绪
|
||||||
|
kubectl -n devops rollout status statefulset/mysql
|
||||||
|
kubectl -n devops rollout status statefulset/redis
|
||||||
|
|
||||||
|
# 6. 后端
|
||||||
|
kubectl apply -f backend.yaml
|
||||||
|
kubectl -n devops rollout status deployment/aiops-backend
|
||||||
|
|
||||||
|
# 7. Ingress(部署前修改 host 与 ingressClassName)
|
||||||
|
kubectl apply -f ingress.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
一键全量部署(文件名按字典序,namespace 会最先应用):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f deploy/k8s/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 访问
|
||||||
|
|
||||||
|
- 将 Ingress 中的 `host`(默认 `aiops.local`)解析到 Ingress Controller 的入口 IP:
|
||||||
|
```bash
|
||||||
|
echo "<INGRESS_IP> aiops.local" | sudo tee -a /etc/hosts
|
||||||
|
```
|
||||||
|
- 浏览器打开 `http://aiops.local` 即前端;API 在 `http://aiops.local/api`。
|
||||||
|
|
||||||
|
## 常用运维命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看所有资源
|
||||||
|
kubectl -n devops get all,configmap,secret,ingress,pvc
|
||||||
|
|
||||||
|
# 查看后端日志
|
||||||
|
kubectl -n devops logs -f deployment/aiops-backend
|
||||||
|
|
||||||
|
# 进入 MySQL
|
||||||
|
kubectl -n devops exec -it mysql-0 -- mysql -uaiops -paiops_pass_2026 aiops
|
||||||
|
|
||||||
|
# 进入 Redis
|
||||||
|
kubectl -n devops exec -it redis-0 -- redis-cli
|
||||||
|
|
||||||
|
# 进入后端容器
|
||||||
|
kubectl -n devops exec -it deployment/aiops-backend -- sh
|
||||||
|
|
||||||
|
# 查看 PVC 绑定状态
|
||||||
|
kubectl -n devops get pvc
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置与密码
|
||||||
|
|
||||||
|
| 项目 | 来源 | 值 |
|
||||||
|
|------|------|----|
|
||||||
|
| MySQL 业务账号 | Secret `aiops-secret` | 用户 `aiops` / 密码 `aiops_pass_2026` / 库 `aiops` |
|
||||||
|
| MySQL root | Secret `aiops-secret` | `aiops_root_2026` |
|
||||||
|
| JWT | Secret `aiops-secret` | `jwt-secret`(生产请替换为强随机值) |
|
||||||
|
| Redis | 无密码 | db `0` |
|
||||||
|
|
||||||
|
> ⚠️ 生产环境请勿使用仓库内默认密码。修改 `configmap.yaml` 中 Secret 的值后重新 apply,
|
||||||
|
> 或使用 `kubectl create secret` 覆盖。
|
||||||
|
|
||||||
|
## 资源限额
|
||||||
|
|
||||||
|
| 组件 | CPU 请求/上限 | 内存请求/上限 |
|
||||||
|
|------|---------------|---------------|
|
||||||
|
| backend | 100m / 500m | 512Mi / 1Gi |
|
||||||
|
| mysql | 250m / 1 | 1Gi / 2Gi |
|
||||||
|
| redis | 50m / 250m | 128Mi / 256Mi |
|
||||||
|
|
||||||
|
## 关于配置的环境变量占位
|
||||||
|
|
||||||
|
`config.yaml`(位于 ConfigMap)中 DB/Redis 的 `host/port/user/password/dbname`、JWT `secret`
|
||||||
|
均写成 `${ENV_VAR}` 占位形式,实际值在 `backend.yaml` 的 `env` 段注入:
|
||||||
|
非敏感的 host/port/user/dbname 直接给值(指向 K8s Service),密码则 `valueFrom` 引用 Secret。
|
||||||
|
|
||||||
|
后端基于 viper 读取配置。若 `${VAR}` 占位需要由环境变量直接替换嵌套键
|
||||||
|
(如 `DB_PASSWORD` → `database.password`),建议在程序启动时设置键名映射:
|
||||||
|
|
||||||
|
```go
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
```
|
||||||
|
|
||||||
|
否则可改为在 `config.yaml` 中直接写明连接串、密码仅由 Secret 注入。两种方式均可,
|
||||||
|
当前清单采用「环境变量占位 + Secret」的标准 K8s 模式。
|
||||||
|
|
||||||
|
## 清理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl delete -f deploy/k8s/
|
||||||
|
# 持久卷需单独删除(PVC 由 StatefulSet 保护,删除 STS 后 PVC 仍保留)
|
||||||
|
kubectl -n devops delete pvc --all
|
||||||
|
```
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# backend.yaml — AIOps 后端 (Deployment + Service)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# - 镜像 aiops-backend:latest(前端通过 go:embed 内嵌进同一二进制)
|
||||||
|
# - 暴露两个端口:
|
||||||
|
# 8080 = API + WebSocket
|
||||||
|
# 80 = go:embed 前端 (SPA 静态资源)
|
||||||
|
# - 配置: ConfigMap aiops-config 挂载到 /app/configs/config.yaml
|
||||||
|
# - 密码: Secret aiops-secret 以环境变量注入(DB_PASSWORD / JWT_SECRET)
|
||||||
|
# - Service: ClusterIP,对外暴露 80 与 8080
|
||||||
|
#
|
||||||
|
# 部署: kubectl apply -f backend.yaml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: aiops-backend
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/component: backend
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/component: backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/component: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: aiops-backend
|
||||||
|
image: aiops-backend:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
# ---- 数据库连接参数(对应 config.yaml 的 ${DB_*} 占位)----
|
||||||
|
- {name: DB_HOST, value: mysql}
|
||||||
|
- {name: DB_PORT, value: "3306"}
|
||||||
|
- {name: DB_USER, value: aiops}
|
||||||
|
- {name: DB_NAME, value: aiops}
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: aiops-secret
|
||||||
|
key: database-password
|
||||||
|
# ---- Redis 连接参数(本环境无密码)----
|
||||||
|
- {name: REDIS_HOST, value: redis}
|
||||||
|
- {name: REDIS_PORT, value: "6379"}
|
||||||
|
- {name: REDIS_PASSWORD, value: ""}
|
||||||
|
# ---- JWT ----
|
||||||
|
- name: JWT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: aiops-secret
|
||||||
|
key: jwt-secret
|
||||||
|
- {name: TZ, value: Asia/Shanghai}
|
||||||
|
ports:
|
||||||
|
- {name: api, containerPort: 8080} # API + WebSocket
|
||||||
|
- {name: web, containerPort: 80} # go:embed 前端
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 1Gi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: api
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 30
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: api
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /app/configs
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: aiops-config
|
||||||
|
---
|
||||||
|
# Service — 对内暴露 80 (前端) 与 8080 (API)
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: aiops-backend
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/component: backend
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/component: backend
|
||||||
|
ports:
|
||||||
|
- {name: api, port: 8080, targetPort: 8080}
|
||||||
|
- {name: web, port: 80, targetPort: 80}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# configmap.yaml — 后端配置 (ConfigMap) + 敏感信息 (Secret)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# ConfigMap aiops-config:
|
||||||
|
# - 内含 config.yaml,DB/Redis 连接信息以环境变量占位(${VAR})。
|
||||||
|
# - 后端 Deployment 将其挂载到 /app/configs/config.yaml(viper 搜索路径 ./configs)。
|
||||||
|
#
|
||||||
|
# Secret aiops-secret:
|
||||||
|
# - 存放 MySQL 密码与 JWT 密钥(明文敏感信息不放 ConfigMap)。
|
||||||
|
# - 由 backend Deployment / mysql StatefulSet 以环境变量注入。
|
||||||
|
#
|
||||||
|
# 部署: kubectl apply -f configmap.yaml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: aiops-config
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/component: backend
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
# AIOps 后端配置
|
||||||
|
# DB/Redis 连接通过环境变量注入(见 backend Deployment 的 env 段)。
|
||||||
|
# 敏感字段 (password / jwt) 由 Secret aiops-secret 提供。
|
||||||
|
# 注意: viper.AutomaticEnv 默认不替换嵌套键中的点(.),如需 DATABASE_HOST ->
|
||||||
|
# database.host 生效,请在 backend 启动时设置:
|
||||||
|
# viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
mode: release
|
||||||
|
port: "8080"
|
||||||
|
log_level: info
|
||||||
|
|
||||||
|
# ---- 数据库 (MySQL) ----
|
||||||
|
database:
|
||||||
|
driver: mysql
|
||||||
|
host: "${DB_HOST}" # -> mysql (k8s Service)
|
||||||
|
port: 3306
|
||||||
|
user: "${DB_USER}" # -> aiops
|
||||||
|
password: "${DB_PASSWORD}" # <- Secret aiops-secret (database-password)
|
||||||
|
dbname: "${DB_NAME}" # -> aiops
|
||||||
|
charset: utf8mb4
|
||||||
|
max_conn: 100
|
||||||
|
max_idle: 10
|
||||||
|
|
||||||
|
# ---- Redis ----
|
||||||
|
redis:
|
||||||
|
driver: redis # redka(内嵌) / redis(外部)
|
||||||
|
host: "${REDIS_HOST}" # -> redis (k8s Service)
|
||||||
|
port: 6379
|
||||||
|
password: "${REDIS_PASSWORD}" # 本环境 Redis 无密码,留空
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
# ---- JWT ----
|
||||||
|
jwt:
|
||||||
|
secret: "${JWT_SECRET}" # <- Secret aiops-secret (jwt-secret)
|
||||||
|
expire_hour: 24
|
||||||
|
|
||||||
|
# ---- WebSocket ----
|
||||||
|
websocket:
|
||||||
|
read_buffer_size: 1024
|
||||||
|
write_buffer_size: 1024
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: aiops-secret
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# MySQL 业务账号密码
|
||||||
|
database-password: "aiops_pass_2026"
|
||||||
|
# MySQL root 密码(StatefulSet 初始化用)
|
||||||
|
mysql-root-password: "aiops_root_2026"
|
||||||
|
# JWT 签名密钥(生产请替换为强随机值)
|
||||||
|
jwt-secret: "aiops-jwt-secret-change-in-production-2026"
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# ingress.yaml — Ingress 入口路由 (nginx-ingress)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 路由规则:
|
||||||
|
# /api/* -> aiops-backend:8080 (后端 API)
|
||||||
|
# /ws/* -> aiops-backend:8080 (WebSocket 长连接)
|
||||||
|
# / -> aiops-backend:80 (go:embed 前端 SPA)
|
||||||
|
#
|
||||||
|
# 前提: 集群已安装 nginx Ingress Controller。
|
||||||
|
# 部署前请修改 host (aiops.local) 与 ingressClassName 为实际值。
|
||||||
|
# 部署: kubectl apply -f ingress.yaml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: aiops-ingress
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
annotations:
|
||||||
|
# WebSocket 长连接支持(/ws)
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
# 文件上传大小(与后端 upload.max_size 配合)
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||||
|
# 显式转发 WebSocket 升级头
|
||||||
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx # 按集群实际 IngressClass 调整
|
||||||
|
rules:
|
||||||
|
- host: aiops.local # TODO: 修改为实际访问域名
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
# ---- API ----
|
||||||
|
- path: /api
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: aiops-backend
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
# ---- WebSocket ----
|
||||||
|
- path: /ws
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: aiops-backend
|
||||||
|
port:
|
||||||
|
number: 8080
|
||||||
|
# ---- 前端 (go:embed, 默认兜底) ----
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: aiops-backend
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# mysql.yaml — MySQL 8.0 数据库 (StatefulSet + Service + 持久化卷)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 组件:
|
||||||
|
# - mysql-headless (Headless Service): 为 StatefulSet 提供稳定网络标识 mysql-0
|
||||||
|
# - mysql (ClusterIP Service): 供后端以 mysql:3306 连接
|
||||||
|
# - mysql (StatefulSet): 单实例 MySQL 8.0
|
||||||
|
# 账号: 用户 aiops / 密码 aiops_pass_2026 / 数据库 aiops (密码来自 aiops-secret)
|
||||||
|
# 持久化: volumeClaimTemplates 8Gi (依赖集群 StorageClass 动态供应)
|
||||||
|
#
|
||||||
|
# 部署: kubectl apply -f mysql.yaml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
# Headless Service — StatefulSet 稳定标识
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mysql-headless
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
ports:
|
||||||
|
- name: mysql
|
||||||
|
port: 3306
|
||||||
|
targetPort: 3306
|
||||||
|
---
|
||||||
|
# ClusterIP Service — 供应用连接
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mysql
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
ports:
|
||||||
|
- name: mysql
|
||||||
|
port: 3306
|
||||||
|
targetPort: 3306
|
||||||
|
---
|
||||||
|
# StatefulSet
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: mysql
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
spec:
|
||||||
|
serviceName: mysql-headless
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: mysql
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:8.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: MYSQL_ROOT_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: aiops-secret
|
||||||
|
key: mysql-root-password
|
||||||
|
- name: MYSQL_DATABASE
|
||||||
|
value: aiops
|
||||||
|
- name: MYSQL_USER
|
||||||
|
value: aiops
|
||||||
|
- name: MYSQL_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: aiops-secret
|
||||||
|
key: database-password
|
||||||
|
- name: TZ
|
||||||
|
value: Asia/Shanghai
|
||||||
|
args:
|
||||||
|
# 字符集
|
||||||
|
- --character-set-server=utf8mb4
|
||||||
|
- --collation-server=utf8mb4_unicode_ci
|
||||||
|
# 兼容旧客户端认证方式(8.0 默认 caching_sha2_password)
|
||||||
|
- --default-authentication-plugin=mysql_native_password
|
||||||
|
ports:
|
||||||
|
- name: mysql
|
||||||
|
containerPort: 3306
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- 'mysqladmin ping -h 127.0.0.1 -uroot -p"$MYSQL_ROOT_PASSWORD"'
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- 'mysqladmin ping -h 127.0.0.1 -uroot -p"$MYSQL_ROOT_PASSWORD"'
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 1Gi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 2Gi
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /var/lib/mysql
|
||||||
|
# ---- 持久化卷 (8Gi) ----
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: data
|
||||||
|
spec:
|
||||||
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 8Gi
|
||||||
|
# storageClassName: standard # 按集群实际 StorageClass 调整,留空使用默认
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# namespace.yaml — 创建 devops 命名空间
|
||||||
|
# 所有 AIOps 资源(MySQL / Redis / 后端 / Ingress)统一部署在该命名空间下。
|
||||||
|
# 必须最先 apply,其它资源依赖该命名空间存在。
|
||||||
|
# 部署: kubectl apply -f namespace.yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: aiops
|
||||||
|
app.kubernetes.io/part-of: aiops
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# redis.yaml — Redis 7 缓存 (StatefulSet + Service + 持久化卷)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 组件:
|
||||||
|
# - redis-headless (Headless Service): StatefulSet 稳定标识 redis-0
|
||||||
|
# - redis (ClusterIP Service): 供后端以 redis:6379 连接
|
||||||
|
# - redis (StatefulSet): 单实例 Redis 7,无密码,db 0
|
||||||
|
# 持久化: volumeClaimTemplates 2Gi + AOF/RDB 落盘
|
||||||
|
#
|
||||||
|
# 部署: kubectl apply -f redis.yaml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
---
|
||||||
|
# Headless Service — StatefulSet 稳定标识
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: redis-headless
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
app.kubernetes.io/component: cache
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
ports:
|
||||||
|
- name: redis
|
||||||
|
port: 6379
|
||||||
|
targetPort: 6379
|
||||||
|
---
|
||||||
|
# ClusterIP Service — 供应用连接
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
app.kubernetes.io/component: cache
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
ports:
|
||||||
|
- name: redis
|
||||||
|
port: 6379
|
||||||
|
targetPort: 6379
|
||||||
|
---
|
||||||
|
# StatefulSet
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
namespace: devops
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
app.kubernetes.io/component: cache
|
||||||
|
spec:
|
||||||
|
serviceName: redis-headless
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: redis
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: redis
|
||||||
|
image: redis:7-alpine
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: ["redis-server"]
|
||||||
|
args:
|
||||||
|
- --appendonly yes # 开启 AOF
|
||||||
|
- --save 60 1000 # RDB: 60s 内 1000 次变更快照
|
||||||
|
- --databases 1 # 仅使用 db 0
|
||||||
|
- --maxmemory 256mb
|
||||||
|
- --maxmemory-policy allkeys-lru
|
||||||
|
ports:
|
||||||
|
- name: redis
|
||||||
|
containerPort: 6379
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["redis-cli", "ping"]
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["redis-cli", "ping"]
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 256Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
# ---- 持久化卷 (2Gi) ----
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: data
|
||||||
|
spec:
|
||||||
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 2Gi
|
||||||
|
# storageClassName: standard # 按集群实际 StorageClass 调整
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# ============================================================
|
||||||
|
# AIOps 单容器 docker-compose(embed 模式)
|
||||||
|
# 前端嵌入后端二进制,不需要独立 nginx 容器
|
||||||
|
# 用法: docker-compose -f docker-compose.embed.yml up -d --build
|
||||||
|
# 署名 Logan.Li
|
||||||
|
# ============================================================
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: aiops-mysql
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: rootpassword
|
||||||
|
MYSQL_DATABASE: aiops
|
||||||
|
MYSQL_USER: aiops
|
||||||
|
MYSQL_PASSWORD: aiops_pass_2026
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
command: >
|
||||||
|
--default-authentication-plugin=mysql_native_password
|
||||||
|
--character-set-server=utf8mb4
|
||||||
|
--collation-server=utf8mb4_unicode_ci
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p$$MYSQL_ROOT_PASSWORD"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 30s
|
||||||
|
networks:
|
||||||
|
- aiops-network
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: aiops-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- aiops-network
|
||||||
|
|
||||||
|
# 单容器:后端 API (:8080) + 嵌入前端 (:8080)
|
||||||
|
# nginx 不再需要,gin 直接服务静态文件
|
||||||
|
aiops:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: backend/Dockerfile.embed
|
||||||
|
container_name: aiops-app
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:8080" # 前端 + API 都走 8080
|
||||||
|
- "8080:8080" # 也可以直接访问 8080
|
||||||
|
volumes:
|
||||||
|
- ./backend/config.yaml:/app/config.yaml
|
||||||
|
- app_logs:/app/logs
|
||||||
|
- app_uploads:/app/uploads
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
networks:
|
||||||
|
- aiops-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
redis_data:
|
||||||
|
app_logs:
|
||||||
|
app_uploads:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
aiops-network:
|
||||||
|
driver: bridge
|
||||||
Reference in new issue
Block a user