Sign inSign up

buzzxu/deploy-box

By buzzxu

Updated about 2 months ago

Image
0

2.0K

buzzxu/deploy-box repository overview

Deploy Box

一个基于Go和Echo框架的自动化部署系统,支持通过Webhook触发部署流程,自动下载文件并在多台服务器上执行部署脚本。

功能特性

  • 🚀 Webhook触发部署:支持CI/CD系统通过Webhook触发部署
  • 📦 OSS文件下载:自动从阿里云OSS下载部署文件
  • 🖥️ 多服务器支持:通过SSH在多台服务器上执行部署脚本
  • 🔐 安全认证:每个工作流独立的ID和Token
  • 📋 配置化流程:通过YAML配置部署流程
  • 📊 JSON日志:结构化日志记录,支持API查询
  • 🛠️ 容器化部署:支持Docker和Docker Compose部署

系统架构

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Webhook API   │───▶│   流程引擎       │───▶│   SSH管理器     │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   日志系统      │    │   OSS下载器      │    │   脚本执行器    │
└─────────────────┘    └──────────────────┘    └─────────────────┘

快速开始

1. 下载依赖
go mod download
2. 配置文件

创建 config/config.yaml 配置文件:

# OSS配置
oss:
  endpoint: "oss-cn-hangzhou.aliyuncs.com"
  access_key_id: "your_access_key_id"
  access_key_secret: "your_access_key_secret"
  bucket: "your-deployment-bucket"

# 服务器配置
servers:
  server-1:
    host: "192.168.1.10"
    auth_type: "key"
  
  server-2:
    host: "192.168.1.11"
    auth_type: "password"
    username: "deploy"
    password: "your_password"

# 工作流配置
workflows:
  frontend-deploy:
    download_path: "/tmp/frontend"
    steps:
      - name: "deploy_to_servers"
        servers: ["server-1", "server-2"]
        scp:
          remote_path: "/var/www/"
        scripts: |
          cd /var/www
          unzip -o *.zip
          systemctl reload nginx
3. 运行应用
# 开发模式
go run main.go

# 编译后运行
go build
./deploy-box
4. Docker部署
# 构建镜像
docker build -t deploy-box .

# 使用Docker Compose
docker-compose up -d
Docker中执行宿主机部署任务

当deploy-box在Docker容器中运行,需要在宿主机执行部署任务时,只需配置宿主机的SSH免密登录即可:

servers:
  docker-host:
    host: "宿主机IP"  # Docker Desktop
    # host: "172.17.0.1"          # Linux Docker
    auth_type: "key"
    ssh_key: "/app/.ssh/id_rsa.pub"  # 容器内SSH密钥路径

配置步骤:

  1. 生成SSH密钥对:
ssh-keygen -t rsa -b 4096 -C "deploy-box" -f ./ssh/host_key
  1. 在宿主机设置免密登录:
# 将公钥添加到宿主机authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
  1. 启动容器时挂载SSH密钥:
docker run -d \
  -v ./ssh:/app/.ssh:ro \
  -v ./config:/app/config \
  deploy-box

这样deploy-box容器就能通过SSH连接到宿主机执行部署命令了。

API接口

Webhook接口
POST /webhook/{workflow_id}?token={token}
Content-Type: application/json

# 前端部署(需要文件下载)
{
  "version": "v1.2.3",
  "download_url": "https://bucket.oss-cn-hangzhou.aliyuncs.com/app-v1.2.3.zip"
}

# Docker部署(不需要文件下载)
{
  "version": "v1.2.3",
  "image": "myapp:v1.2.3"
}

注意

  • timestamp 字段会在服务器端自动生成,无需客户端提供
  • 如果工作流配置了 download_path 或步骤中包含 scp,则 download_url 为必需参数
  • 如果缺少必需的 download_url 参数,服务器会返回 400 Bad Request 错误
文件上传接口(仅保存,不部署)

将本地文件直接上传到工作流的下载目录(download.path),不会触发部署脚本

# POST 表单上传
curl -X POST "http://localhost:8080/upload/{workflow_id}?token={token}" \
  -F "[email protected]"

# PUT 直接上传(curl -T,推荐)
curl -T dist.zip "http://localhost:8080/upload/{workflow_id}?token={token}&filename=dist.zip"

上传后需再调用 /webhook(用 filename 指向刚上传的文件)才会执行部署。

上传即部署接口(保存 + 执行工作流)

PUT /deploy/{workflow_id} 在一次调用中完成「保存文件 + 触发工作流」:先把文件写入 download.path,随后按工作流步骤 SCP 到目标服务器 → 执行 scripts 脚本(顺序执行、任一步失败即中止)。

# 单行、纯英文引号、使用 -T(PUT 原始上传)
curl --http2 -v -A "Mozilla/5.0" -T dist.zip "http://localhost:8080/deploy/{workflow_id}?token={token}&filename=dist.zip"

# 部署完成后自动清理上传的文件(默认不清理)
curl -T dist.zip "http://localhost:8080/deploy/{workflow_id}?token={token}&filename=dist.zip&cleanup=true"

⚠️ 常见坑(务必注意)

  • 必须用 -T(PUT),不要用 -F/deploy 只注册了 PUT-F 会发 POST(返回 405);且服务端是把整个请求体当原始文件写盘的,-F 的 multipart 分隔头会被写进文件,导致 zip 损坏。
  • 建议显式带 &filename=xxx:否则文件名解析会落到默认值 upload_<时间戳>.tar,可能与脚本预期的文件名不符。
  • 命令写成单行、用英文直引号:多行 \ 续行或全角引号 "” 会导致 curl: (3) URL rejected: Malformed input to a URL function(curl 解析 URL 阶段就失败,尚未发出请求)。
  • 确认 dist.zip 在当前目录-T 用相对路径,文件不存在会报 Can't open 'dist.zip'
  • 经反向代理时注意路径前缀:如线上地址为 https://jd.xingyunka.com/ayouok-rehu90/deploy/{workflow_id}?...,前缀 /ayouok-rehu90 由 nginx 转发到后端 /deploy/{workflow_id}

文件名解析优先级/deployPUT /upload):

  1. 查询参数 filename
  2. 请求头 Content-Disposition: filename="..."
  3. 工作流配置 download.rename
  4. /deploy 兜底默认 upload_<时间戳>.tarPUT /upload 无法确定时返回 400

也可用路径形式携带文件名:PUT /deploy/{workflow_id}/dist.zip(对应 /deploy/:id/* 路由)。

接口对比

接口保存文件执行部署脚本
POST/PUT /upload/{id}
POST /webhook/{id}
PUT /deploy/{id}
查询接口
# 获取执行日志
GET /api/v1/logs/webhook/{execution_id}

# 获取最新日志
GET /api/v1/logs/latest

# 获取工作流列表
GET /api/v1/workflows

# 生成curl命令
GET /gen/curl/{workflow_name}

# 健康检查
GET /health
生成Curl命令

获取指定工作流的curl命令示例:

# 获取工作流的curl命令
GET /gen/curl/vue3-admin-frontend

# 响应示例:
{
  "workflow_name": "vue3-admin-frontend",
  "workflow_id": "wf_a1b2c3d4e5f6",
  "webhook_url": "http://localhost:8080/webhook/wf_a1b2c3d4e5f6?token=tk_abc123def456",
  "curl_command": "curl -X POST \"http://localhost:8080/webhook/wf_a1b2c3d4e5f6?token=tk_abc123def456\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"version\": \"v1.2.3\",\n  \"download_url\": \"https://your-bucket.oss-cn-hangzhou.aliyuncs.com/app-v1.2.3.zip\",\n  \"timestamp\": \"2024-01-01T10:00:00Z\"\n}'",
  "curl_simple": "curl -X POST \"http://localhost:8080/webhook/wf_a1b2c3d4e5f6?token=tk_abc123def456\" -H \"Content-Type: application/json\" -d '{\"version\":\"v1.2.3\",\"timestamp\":\"2024-01-01T10:00:00Z\",\"download_url\":\"https://your-bucket.oss-cn-hangzhou.aliyuncs.com/app-v1.2.3.zip\"}'",
  "example_payload": {
    "version": "v1.2.3",
    "download_url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/app-v1.2.3.zip"
  },
  "instructions": {
    "description": "Use this curl command to trigger the webhook",
    "notes": [
      "Replace the version with your actual version",
      "For frontend deployments, update the download_url",
      "For Docker deployments, update the image tag",
      "timestamp will be auto-generated by the server"
    ]
  }
}

配置说明

服务器认证

支持两种认证方式:

servers:
  # 密钥认证(默认)
  server-key:
    host: "192.168.1.10"
    auth_type: "key"
    ssh_key: "/path/to/private/key"  # 可选,默认~/.ssh/id_rsa
  
  # 密码认证
  server-pwd:
    host: "192.168.1.11"
    auth_type: "password"
    username: "deploy"
    password: "your_password"
工作流步骤

每个步骤支持以下配置:

steps:
  # 本地执行
  - name: "local_step"
    scripts: "echo 'local command'"
  
  # 远程执行
  - name: "remote_step"
    servers: ["server-1", "server-2"]
    scripts: |
      echo 'multi-line'
      echo 'commands'
  
  # OSS下载+文件传输+远程执行
  - name: "oss_download_deploy"
    download:
      type: "oss"                    # OSS下载
      path: "./downloads"            # 下载到的本地目录
      rename: "frontend.zip"         # 重命名文件
    servers: ["server-1"]
    scp:
      remote_path: "/var/www/"       # SCP会自动传输 ./downloads/frontend.zip
    scripts: |
      cd /var/www
      unzip -o frontend.zip
  
  # HTTP下载+文件传输+远程执行
  - name: "http_download_deploy"
    download:
      type: "default"                # HTTP下载(默认值)
      path: "./downloads"            # 下载到的本地目录
      rename: "app.tar.gz"           # 重命名文件
    servers: ["server-1"]
    scp:
      remote_path: "/opt/app/"       # SCP会自动传输 ./downloads/app.tar.gz
    scripts: |
      cd /opt/app
      tar -xzf app.tar.gz

部署场景

OSS前端部署
workflows:
  vue-frontend:
    steps:
      - name: "download_and_deploy"
        download:
          type: "oss"
          path: "./downloads"
          rename: "frontend.zip"
        servers: ["web-server-1", "web-server-2"]
        scp:
          remote_path: "/var/www/app/"  # SCP会自动传输 ./downloads/frontend.zip
        scripts: |
          cd /var/www/app
          unzip -o frontend.zip -d current/
          chown -R www-data:www-data current/
          systemctl reload nginx

Webhook调用

curl -X POST "http://localhost:8080/webhook/wf_abc123?token=tk_def456" \
  -H "Content-Type: application/json" \
  -d '{"version": "v1.2.3", "download_url": "frontend/releases/v1.2.3/dist.zip"}'
HTTP外部文件部署
workflows:
  external-app:
    steps:
      - name: "download_and_deploy"
        download:
          type: "default"
          path: "./downloads"
          rename: "app.tar.gz"
        servers: ["app-server"]
        scp:
          remote_path: "/opt/app/"  # SCP会自动传输 ./downloads/app.tar.gz
        scripts: |
          cd /opt/app
          tar -xzf app.tar.gz
          ./install.sh

Webhook调用

curl -X POST "http://localhost:8080/webhook/wf_abc123?token=tk_def456" \
  -H "Content-Type: application/json" \
  -d '{"version": "v1.2.3", "download_url": "https://github.com/user/repo/releases/download/v1.2.3/app.tar.gz"}'
Docker部署
workflows:
  api-service:
    steps:
      - name: "update_containers"
        servers: ["app-server-1", "app-server-2"]
        scripts: |
          docker pull myapp:latest
          docker stop myapp || true
          docker run -d --name myapp \
            --restart=always \
            -p 8080:8080 \
            myapp:latest

安全说明

  • 每个工作流拥有唯一的ID和Token
  • Token在首次启动时自动生成
  • 支持SSH密钥和密码认证
  • 建议在生产环境中验证SSH主机密钥

开发

运行测试
go test ./tests/...
构建
# 本地构建
go build

# 交叉编译
GOOS=linux GOARCH=amd64 go build
项目结构
deploy-box/
├── api/handlers/          # API处理器
├── cmd/                   # 命令行入口
├── config/               # 配置文件
├── internal/
│   ├── config/          # 配置管理
│   ├── logger/          # 日志系统
│   ├── oss/             # OSS下载
│   ├── ssh/             # SSH客户端
│   └── workflow/        # 工作流引擎
├── pkg/models/          # 数据模型
├── tests/               # 测试用例
└── main.go             # 主程序

故障排除

常见问题
  1. SSH连接失败

    • 检查服务器地址和端口
    • 确认SSH密钥权限(600)
    • 验证用户名和认证方式
  2. OSS下载失败

    • 检查OSS配置和权限
    • 确认网络连接
    • 验证文件URL格式
  3. 脚本执行失败

    • 查看执行日志详情
    • 检查命令权限和路径
    • 确认依赖软件已安装
日志查看
# 查看应用日志
docker logs deploy-box

# 查看执行日志
curl http://localhost:8080/api/v1/logs/latest

许可证

MIT License

Tag summary

Content type

Image

Digest

sha256:2d96b649d

Size

15.7 MB

Last updated

about 2 months ago

docker pull buzzxu/deploy-box