TaskSSH 文档

TaskSSH 是一个单文件、无外部依赖的 SSH 批量运维工具。其配置模型由两部分构成: 一份 inventory.yaml 清单文件,用于声明服务器组、主机及变量; 以及清单中的 tasks 段,用于将一个或多个 action 编排为有序的执行流程。 用户通过指定任务名与目标主机组,即可对多台服务器批量执行命令、上传与下载文件。

安装

下载预编译二进制

前往 Releases 页面,根据目标平台下载对应的压缩包:

平台架构文件名
Windowsamd64taskssh-windows-amd64.zip
Linuxamd64taskssh-linux-amd64.zip
Linuxarm64taskssh-linux-arm64.zip
macOSInteltaskssh-darwin-amd64.zip
macOSApple Silicontaskssh-darwin-arm64.zip

解压后获取可执行文件。在 Linux / macOS 环境下,需赋予执行权限:

chmod +x taskssh
./taskssh --help

从源码构建

需 Go 1.22 及以上版本:

git clone https://github.com/melvek/TaskSSH.git
cd TaskSSH
go build -o taskssh main.go

Shell 补全(可选)

# PowerShell
taskssh completion powershell >> $PROFILE

# Bash
echo 'source <(taskssh completion bash)' >> ~/.bashrc

# Zsh
echo 'source <(taskssh completion zsh)' >> ~/.zshrc

快速开始

标准使用流程包含四个阶段:创建密钥、加密密码、编写清单、执行任务。

1. 创建密钥文件

密钥用于加密清单中的密码。只需创建一次:

mkdir -p ~/.taskssh
openssl rand -base64 32 > ~/.taskssh/vault-key
chmod 600 ~/.taskssh/vault-key

后续 taskssh encrypt / decrypt / 执行任务时, 将自动读取该文件,无需每次输入密钥。

2. 加密密码

taskssh encrypt "your-password"
# 输出:xxxxx

将输出密文填入清单的 password 字段。

3. 编写清单

global_vars:
  username: deploy
  password: "xxxxx"

servers:
  prod:
    vars:
      service_path: /opt/myapp/
    hosts:
      web1: 192.168.1.10

tasks:
  release:
    steps:
      - name: "上传新版本"
        action: push
        with:
          file: "./dist/app.jar"
          dest: "{{service_path}}"
          force: true
      - name: "重启服务"
        action: command
        with:
          command: "systemctl restart myapp"

4. 执行任务

taskssh release prod -y

TaskSSH 读取 inventory.yaml,对 prod 组下所有主机 依次执行 release 任务。执行过程中输出每台主机的执行状态,结束后 输出成功/失败摘要。

💡

执行前可用 --dry-run 查看每台主机每个步骤将执行的内容, 确认无误后再实际执行。

inventory.yaml

TaskSSH 的配置模型由两个对象构成:

执行任务时,用户指定任务名与目标主机组,TaskSSH 按以下流程处理:

  1. 从 inventory 中解析目标主机列表;
  2. 按优先级合并 global / 组 / 主机三级变量,生成每台主机的变量池;
  3. 按声明顺序执行 task 中各 step;
  4. 每台主机独立执行,任务结束后汇总成功与失败结果。

内置的 command / push / fetch / script 既可作为独立子命令调用,亦可作为 task 中 step 的 action 使用。

清单文件由三个顶层段组成:

global_vars:   # 全局变量,由所有主机与任务继承
servers:       # 服务器组与主机定义
tasks:         # 任务定义,包含有序步骤

以下各节对三个段分别进行说明。

global_vars

global_vars 定义全局默认值。其字段结构与主机定义一致,包含 host、port、username、password、 identity_file、passphrase 等连接相关字段;除这些专有字段外, 其余字段将作为变量进入变量池。

global_vars:
  username: deploy
  password: "encrypted-password"
  app_name: myapp
  version: 1.0.0

上例中,username 与 password 为连接凭据,用于 SSH 认证; app_name 与 version 为用户自定义变量,可在任务中通过 {{app_name}} 语法引用。

注意: 2.1.0 及之前的版本变量语法为 ${app_name} 格式。从 2.1.0 升级版本后,需要同步替换引用变量格式。

servers

servers 段定义服务器组。每个组可声明自己的 vars, 组内所有主机会继承这些变量。主机可选用简写或完整写法声明。

servers:
  prod:
    vars:
      service_path: /opt/myapp/
    hosts:
      web1: 192.168.1.10
      web2:
        host: 192.168.1.11
        port: 2222
        username: root
        password: "encrypted-password"

  uat:
    vars:
      service_path: /opt/myapp-uat/
    hosts:
      uat1: 10.1.0.1

主机声明方式

除上述专有字段外,其余字段将进入主机的 Extra 映射, 参与变量替换。例如为主机声明 role: db,任务中即可通过 {{role}} 引用。

主机查找

执行任务时,位置参数可以是以下任意一种:

形式示例说明
组名prod展开该组下所有主机
组名/主机名prod/web1精确到单台主机
组内主机名web1唯一匹配时返回该主机;多组同名时报错
独立主机名localhost不在任何组内时,按独立主机处理
IP 地址192.168.1.10直接作为主机地址
组匹配g:web*glob 匹配组名
主机匹配h:web*glob 匹配所有组内主机名
默认匹配web*同时匹配组名和主机名,去重合并

glob 匹配

g: 前缀匹配组名,h: 前缀匹配主机名。 支持 glob 语法:

模式含义示例
*任意多个字符g:web* 匹配 web_server1
?任意单个字符h:web? 匹配 web1
[abc]字符集合h:web[12] 匹配 web1、web2
[a-z]范围h:web[1-3] 匹配 web1、web2、web3
# 所有以 web 开头的组
taskssh command 'g:web*' -e "uptime" -y

# 所有组内以 web 开头的主机
taskssh command 'h:web*' -e "uptime" -y

# 默认匹配:组名和主机名都匹配
taskssh command 'web*' -e "uptime" -y

主机名会自动解析为 IP。解析结果用于连接,同时用于 -l 输出显示。

排除主机

位置参数以 ! 开头时表示排除。无 include 时,默认 include 为 g:*(所有组)。

注意:在 Bash / Zsh 中,! 是历史展开符,参数需用单引号包裹(双引号无效), PowerShell 无此限制。

# 所有主机,排除 web* 组
taskssh command 'g:*' '!g:web*' -e "uptime" -y

# 只指定排除,默认从所有主机中排除
taskssh command '!g:web*' -e "uptime" -y

# 排除单台主机
taskssh command 'g:*' '!h:web1' -e "uptime" -y

exclude 匹配不到任何主机时不报错。include 匹配不到时报错。

变量合并优先级

主机最终获得的变量池按以下顺序合并,优先级由低到高:

层级来源作用范围
1global_vars所有主机
2servers.<组名>.vars组内主机
3主机专有字段与 Extra单台主机

tasks

tasks 段用于定义任务。每个任务具有唯一名称,由若干 steps 组成,step 按声明顺序执行。

tasks:
  release:
    description: "发布新版本"   # 可选
    steps:
      - name: "上传新版本"
        action: push
        with:
          file: "./dist/{{app_name}}-{{version}}.jar"
          dest: "{{service_path}}"
          force: true
      - name: "重启服务"
        action: command
        with:
          command: "systemctl restart {{app_name}}"
        delay: 5

上例中的 release 任务在执行时,将对 prod 组下 每台主机依次执行两个步骤:先上传文件,等待 5 秒,再执行服务重启命令。

ℹ️

command / push / fetch / script / encrypt / decrypt 为保留命令名。在 tasks 段中定义同名任务不会生效,因为 CLI 路由优先于任务查找。定义自定义任务时需使用其他任务名。

任务级字段

字段必填说明
description否任务描述,用于日志输出
serial否每组内每批 N 台,默认 0(不分批)
min_available否每组至少留 N 台在跑,默认 0(不留余量)
steps是步骤列表

step 字段

每个 step 表示一次 action 调用,其字段定义如下:

字段必填说明
name否步骤名称,仅用于日志输出
action是action 类型:command / push / fetch / script
with是action 参数映射,值支持 {{var}} 变量替换
delay否本步骤执行后等待的秒数,默认 0
ignore_errors否该 step 失败时输出警告但不中断后续步骤,默认 false
when否条件表达式,满足时才执行,默认无

with 中的键名与对应 action 的 CLI 选项保持一致。例如 push 支持 file / dest / force / zip 四个参数。

ignore_errors

标记为 ignore_errors: true 的 step,执行失败时输出警告日志, 但不中断当前主机的后续步骤。

- name: "清理临时文件"
action: command
  with:
    command: "rm -f /tmp/deploy-*.tmp"
  ignore_errors: true

注意:该字段仅作用于 step 执行失败。连接失败属于主机级问题, 会跳过该主机的所有步骤,并在摘要中计为失败主机,不受此字段影响。

条件执行 when

step 可通过 when 字段声明执行条件。条件求值为 true 时才执行, 否则输出 Skip step 并继续下一步。

tasks:
  release:
    steps:
      - name: "备份数据库"
        action: command
        when: "{{env}} == prod"
        with:
          command: "pg_dump mydb > /backup/mydb.sql"

支持的运算符

运算符示例说明
=={{env}} == prod相等
!={{env}} != prod不等
>{{version}} > 2大于
>={{version}} >= 2大于等于
<{{version}} < 5小于
<={{version}} <= 5小于等于
in{{role}} in (web,db)在列表中
&&{{env}} == prod && {{role}} == web逻辑与
||{{env}} == prod || {{env}} == uat逻辑或

数值与字符串比较

> >= < <= 的左右两侧若均能解析为数字,则按数字比较,否则按字符串比较。

# 数字比较
when: "{{version}} >= 2"

# 字符串比较
when: "{{name}} > abc"
⚠️

数字比较使用 float64。版本号如 1.10 与 1.2 的比较结果可能不符合直觉(1.10 被解析为 1.1,小于 1.2)。如需精确比较,建议使用整数版本号。

列表匹配 in

in 右侧为逗号分隔的列表,可加括号也可不加:

when: "{{role}} in (web,db,cache)"
when: "{{role}} in web,db,cache"

逻辑组合

&& 优先级高于 ||。 例如 a && b || c && d 等价于 (a && b) || (c && d)。

- name: "生产环境备份数据库"
  action: command
  when: "{{env}} == prod && {{role}} == db"
  with:
    command: "pg_dump mydb > /backup/mydb.sql"

- name: "非生产环境启用调试"
  action: command
  when: "{{env}} != prod"
  with:
    command: "systemctl restart {{app_name}} --debug"

限制

组合内置 action

任务可由四种 action 自由组合而成。以下列出若干典型编排模式。

模式一:版本发布

tasks:
  release:
    steps:
      - name: "备份当前版本"
        action: command
        with:
          command: "cp {{service_path}}{{app_name}}.jar {{service_path}}{{app_name}}.jar.bak"

      - name: "上传新版本"
        action: push
        with:
          file: "./dist/{{app_name}}-{{version}}.jar"
          dest: "{{service_path}}"
          force: true

      - name: "重启服务"
        action: command
        with:
          command: "systemctl restart {{app_name}}"
        delay: 5

      - name: "健康检查"
        action: command
        with:
          command: "curl -sf http://localhost:8080/health"

模式二:批量拉取日志

tasks:
  pull-logs:
    steps:
      - name: "打包日志"
        action: command
        with:
          command: "tar czf /tmp/app-logs.tar.gz /var/log/app/"

      - name: "下载日志"
        action: fetch
        with:
          file: "/tmp/app-logs.tar.gz"
          dest: "./logs/"

      - name: "清理临时文件"
        action: command
        with:
          command: "rm -f /tmp/app-logs.tar.gz"

模式三:执行部署脚本

tasks:
  deploy-script:
    steps:
      - name: "上传并执行部署脚本"
        action: script
        with:
          file: "./scripts/deploy.sh"
          dest: "/tmp"
          remove: true
          force: true

模式四:版本目录与软链接切换

tasks:
  deploy:
    steps:
      - name: "创建版本目录"
        action: command
        with:
          command: "mkdir -p {{service_path}}/releases/{{date}}_{{execId}}"

      - name: "上传新版本"
        action: push
        with:
          file: "./dist/app.jar"
          dest: "{{service_path}}/releases/{{date}}_{{execId}}/"
          force: true

      - name: "切换软链接"
        action: command
        with:
          command: "ln -sfn {{service_path}}/releases/{{date}}_{{execId}} {{service_path}}/current"

      - name: "查看软链接指向"
        action: command
        with:
          command: "ls -l {{service_path}}/current"

模式五:多环境复用同一任务

任务本身不与环境绑定,环境差异由组级变量承载。例如 service_path 在 prod 组为 /opt/myapp/,在 uat 组为 /opt/myapp-uat/,同一 release 任务即可分别对两个组执行。

taskssh release prod -y
taskssh release uat -y

完整示例

以下为清单文件与任务的完整示例。

global_vars:
  username: deploy
  password: "encrypted-password"
  app_name: myapp
  version: 1.0.0

servers:
  prod:
    vars:
      service_path: /opt/myapp/
    hosts:
      web1: 192.168.1.10
      web2: 192.168.1.11

  uat:
    vars:
      service_path: /opt/myapp-uat/
    hosts:
      uat1: 10.1.0.1

tasks:
  release:
    steps:
      - name: "上传新版本"
        action: push
        with:
          file: "./dist/{{app_name}}-{{version}}.jar"
          dest: "{{service_path}}"
          force: true

      - name: "重启服务"
        action: command
        with:
          command: "systemctl restart {{app_name}}"
        delay: 5

执行方式:

taskssh release prod -y
taskssh release uat -y

Action:command

在远程主机上执行指定命令。当远程命令返回的退出码非 0 时,该 step 判定为失败。

选项长选项参数说明
-e--executestring待执行的命令;亦可从清单的 command 字段读取
taskssh command web-server-01 -e "ls -la /opt" -y

作为 step 使用时,将命令写入 with.command:

- name: "重启服务"
  action: command
  with:
    command: "systemctl restart {{app_name}}"
ℹ️

TaskSSH 变量使用 {{var}} 语法;命令中的 $VAR 和 ${VAR} 均为 shell 变量,原样传递给远程 shell。

Action:push

将本地文件或目录上传至远程主机。

选项长选项参数说明
-f--filefile or dir本地文件或目录路径
-d--destpath远程目标路径;亦可从变量 service_path 读取
-F--force—覆盖已存在的远程文件
-z--zip—目录上传时,本地 zip 打包后上传(依赖远程 unzip)
# 单文件
taskssh push app-server -f app.jar -d /opt/app/ -y

# 目录,递归上传
taskssh push prod -f ./dist/assets/ -d /opt/app/ -y

# 目录,打包上传
taskssh push prod -f ./dist/assets/ -d /opt/app/ -z -y

路径与覆盖规则

  1. -d 以 / 结尾时视为目录,最终路径为 目录 + 本地文件名。
  2. 若目标已存在且为目录,文件上传至该目录内。
  3. 若目标已存在且为文件:
    • 默认行为为报错;
    • -F 直接覆盖。
  4. 远程目录不存在时报错。程序不会自动创建远程目录,如有需要,可在任务中预先加入创建目录的 command 步骤。

目录上传

Action:fetch

从远程主机下载文件或目录至本地。

选项长选项参数说明
-f--filepath远程文件或目录路径
-d--destpath本地目标路径,默认 ./
-z--zip—目录下载时,远程打包后下载(依赖远程 zip)
-T--tmp-dirpath远程临时目录,默认 /tmp,仅 -z 时生效
# 下载至目录(按主机标识分组存放)
taskssh fetch prod -f /var/log/app.log -d ./logs/ -y

# 下载至指定文件
taskssh fetch prod_web_1 -f /var/log/app.log -d ./app.log -y

# 下载目录,递归
taskssh fetch prod -f /var/log/app/ -d ./logs/ -y

# 下载目录,打包
taskssh fetch prod -f /var/log/app/ -d ./logs/ -z -y

路径与覆盖规则

  1. -d 以 / 结尾时视为目录,按主机标识分组存放:dest/<主机标识>/<文件名>。
  2. 若 -d 为文件路径,则直接下载至该文件。
  3. 本地文件已存在时,默认覆盖。
  4. 本地父目录不存在时自动创建。
  5. 远程路径为目录时:
    • 不传 -z:递归逐文件下载;
    • 传 -z:远程 zip -r 打包,下载后本地解压,删除远程 zip。

Action:script

将本地 shell 脚本上传至远程主机并执行。

选项长选项参数说明
-f--filefile本地脚本文件
-d--destpath远程目录,默认 /tmp
-r--remove—执行后删除远程脚本
-F--force—覆盖已存在的远程脚本
# 上传并执行
taskssh script prod -f ./scripts/deploy.sh -y

# 执行后删除远程脚本
taskssh script prod -f ./scripts/deploy.sh -r -y

# 覆盖已存在的远程脚本
taskssh script prod -f ./scripts/deploy.sh -r -F -y

执行规则

  1. 脚本上传到 -d 指定的目录,默认 /tmp。
  2. 上传后自动 chmod +x,然后执行。
  3. -r 表示执行后删除远程脚本,不论成功失败。
  4. -F 表示覆盖已存在的远程脚本。

串行执行

默认情况下,TaskSSH 以串行方式执行任务:

串行模式的特性为日志实时输出、失败可即时定位,适用于对执行顺序敏感的场景。

并行执行

通过 -c 或 --concurrency 指定并发数:

taskssh release prod -c 5 -y

上述命令表示:最多同时操作 5 台主机,一台完成后自动补充下一台。

ℹ️

并行执行时,开始时实时输出 [Processing x/y] 提示; 每台主机执行过程中的日志先写入独立缓冲,执行完毕后统一输出,以避免多主机日志交错。

并发数控制

并发数适用场景
1(默认)生产发布、对顺序敏感的操作
2-5小规模集群的滚动操作
10+只读命令、日志收集、批量诊断
# 生产发布,串行执行
taskssh release prod -y

# 分批滚动,最多 3 台并发
taskssh release prod -c 3 -y

# 批量诊断,高并发
taskssh command prod -e "df -h" -c 10 -y
💡

并发数并非越大越好。建议从 3~5 开始,结合实际网络与目标主机负载逐步调整。

按组分批

通过 serial 和 min_available,可将主机按组切分, 每组内按每批 N 台滚动执行。适用于服务部署、滚动重启等需要控制节奏的场景。

调度模型

所有批排成一条队列,依次执行。每批只包含同一组的主机。 批内并发,批间串行,组间串行。

输入:order(4台), stock(2台),serial: 2, min_available: 1

        队列:
        order 批1:order1, order2
        order 批2:order3, order4
        stock 批1:stock1
        stock 批2:stock2

        执行:order批1 → order批2 → stock批1 → stock批2

serial 与 min_available

参数位置说明
serial任务级 Task每组内每批 N 台
min_available任务级 Task每组至少留 N 台在跑
-S / --serialCLI覆盖任务级 serial
--min-availableCLI覆盖任务级 min_available
--stop-on-failureCLI一批失败后停止后续批次,默认 true

每批大小由两者共同决定:

batchSize = min(serial, len(group) - minAvailable)

示例:

组大小serialmin_available每批大小分批结果
421min(2, 3) = 2批 2 + 2
221min(2, 1) = 1批 1 + 1
220min(2, 2) = 2批 2(全动)
4014 - 1 = 3批 3 + 1
4004批 4(全动)

优先级

CLI 显式传参时覆盖任务级配置:

CLI flag(显式传了)> 任务级(Task.Serial / Task.MinAvailable)> 默认值(0)

任务级 serial: 0 或不配,表示不启用分批。 CLI -S 0 表示覆盖任务级为"不分批"。

边界处理

任务级配置示例

tasks:
        deploy:
            description: "发布新版本"
            serial: 2
            min_available: 1
            steps:
            - name: "创建版本目录"
                action: command
                with:
                command: "mkdir -p {{service_path}}/releases/{{date}}_{{execId}}"
            - name: "上传新版本"
                action: push
                with:
                file: "./{{app_name}}"
                dest: "{{service_path}}/releases/{{date}}_{{execId}}/"
                force: true
            - name: "切换软链接"
                action: command
                with:
                command: "ln -sfn {{service_path}}/releases/{{date}}_{{execId}}/{{app_name}} {{service_path}}/{{link_name}}"

执行:

# 使用任务级配置
        taskssh deploy order,stock -y

        # CLI 覆盖:每批 3 台,留 1 台
        taskssh deploy order,stock -S 3 --min-available 1 -y

        # CLI 覆盖:不分批,全量执行
        taskssh deploy order,stock -S 0 -y
💡

serial 和 min_available 是任务的安全约束,建议写入任务定义。 CLI 参数适合临时覆盖或调试。

Dry-run

--dry-run 不连接远程主机,输出每台主机每个 step 将执行的内容, 供执行前确认。

taskssh release prod --dry-run

输出示例:

>>> Target hosts
prod/web1 : 10.0.0.1

>>> Dry run (no changes will be made)
Steps: 2
Targets: 1

[Processing 1/1] prod/web1 [10.0.0.1]

[STEP 1/2] 上传新版本
Upload /home/user/project/dist/app.jar to /opt/app/

[STEP 2/2] 重启服务
Execute command: systemctl restart myapp

行为说明

启用分批时的输出示例:

>>> Execution Plan
Batch 1: group web_server, 2 hosts
- web_server/web_01 [172.21.1.74]
- web_server/web_02 [172.21.1.74]
Batch 2: group web_server2, 2 hosts
- web_server2/web_03 [172.21.1.74]
- web_server2/web_04 [172.21.1.74]
        

变量替换

TaskSSH 支持在 inventory.yaml 中使用 {{变量名}} 语法进行变量替换。 花括号内允许前后空格,{{ var }} 与 {{var}} 等价。

变量来源与优先级

变量按以下优先级由低到高合并:

层级来源作用范围
1global_vars所有主机
2servers.<组名>.vars组内主机
3主机专有字段与 Extra单台主机
4CLI -D/--define所有主机
5date / execId所有主机(运行时注入)

前四层合并后形成每台主机的变量池,因此不同主机最终的同名变量值可能不同。 date 与 execId 为运行时全局注入,所有主机共享同一值。

注意:CLI 选项 -P / -u / -p 仅覆盖连接三要素,不会将 -e / -f / -d 注入变量池。命令参数通过 with 传递给对应 action。

递归展开

变量值本身可引用其他变量,最多递归展开 4 层。

global_vars:
  app_name: myapp
  version: 1.0.0
  package: "{{app_name}}-{{version}}.jar"   # 递归展开后的结果

servers:
  prod:
    vars:
      service_path: /opt/myapp/
    hosts:
      web1: 192.168.1.10

tasks:
  restart:
    steps:
      - name: "重启服务"
        action: command
        with:
          command: "systemctl restart {{app_name}}"

未解析变量的处理

TaskSSH 对变量替换采用严格模式:若某 {{变量名}} 在所有来源中均未定义, 执行将立即中断并报错,而非以空字符串替换后继续运行。

⚠️

例如 rm -rf {{target_dir}},若 target_dir 未定义, 该命令不会被执行。

shell 变量与 TaskSSH 变量

TaskSSH 变量使用 {{var}} 语法。命令中的 $VAR 与 ${VAR} 均为 shell 变量,原样传递给远程 shell。

# shell 变量,原样传给远程
taskssh command prod -e "echo $HOME" -y

# TaskSSH 变量,执行前替换
taskssh command prod -e "echo {{app_name}}" -y

内置变量

除清单与 CLI 定义的变量外,TaskSSH 在运行时注入以下内置变量。 这些变量全局共享:一次任务执行只生成一次,所有目标主机使用同一个值。

变量说明示例值
{{date}} 当前日期,格式 YYYYMMDD 20260926
{{execId}} 本次执行的唯一标识,8 位 Base36(数字 + 大写字母) 0K8X7A2B

内置变量为保留变量,不可被清单或 CLI -D 覆盖。

date

date 为清单加载时的日期,格式 YYYYMMDD。 一次任务执行只生成一次,所有主机共享同一值。

注意:date 是 TaskSSH 的内置变量,会覆盖清单中的同名变量。 如需在命令中使用 shell 的 date 命令,请使用 $(date ...)。

execId

execId 为本次任务执行的唯一标识,用于生成唯一目录名、版本标识等。

典型用法:

tasks:
  deploy:
    steps:
      - name: "创建版本目录"
        action: command
        with:
          command: "mkdir -p {{service_path}}/releases/{{date}}_{{execId}}"

      - name: "上传新版本"
        action: push
        with:
          file: "./dist/app.jar"
          dest: "{{service_path}}/releases/{{date}}_{{execId}}/"
          force: true

      - name: "切换软链接"
        action: command
        with:
          command: "ln -sfn {{service_path}}/releases/{{date}}_{{execId}} {{service_path}}/current"

      - name: "查看软链接指向"
        action: command
        with:
          command: "ls -l {{service_path}}/current"

回退时,通过 -D revert_id=<date>_<execId> 指定目标版本目录:

tasks:
  revert:
    description: "回退到指定版本"
    steps:
      - name: "校验目标存在"
        action: command
        with:
          command: "test -d {{service_path}}/releases/{{revert_id}}"

      - name: "回退软链接"
        action: command
        with:
          command: "ln -sfn {{service_path}}/releases/{{revert_id}} {{service_path}}/current"

      - name: "查看软链接指向"
        action: command
        with:
          command: "ls -l {{service_path}}/current"
taskssh revert prod -D revert_id=20260926_0K8X7A2B -y

CLI 变量注入

通过 -D/--define 参数可在命令行注入变量,优先级最高, 覆盖清单中的所有同名变量,作用于所有目标主机。

选项长选项参数说明
-D--definekey=value定义变量,可重复
# 单变量
taskssh deploy prod -D version=1.2.3 -y

# 多变量
taskssh deploy prod -D version=1.2.3 -D env=uat -y

# 回退到指定版本
taskssh revert prod -D revert_id=20260926_0K8X7A2B -y

规则

优先级

变量优先级从低到高:

  1. global_vars
  2. servers.<组名>.vars
  3. 主机专有字段与 Extra
  4. CLI -D/--define
  5. date / execId(运行时注入,不可覆盖)

Vault 密钥

清单中的 password 和 passphrase 必须是密文。 TaskSSH 使用 AES-256-GCM 加密,密钥由用户管理,不硬编码在二进制中。

密钥来源与优先级

顺序来源
1--secret-key-file / -V 指定的文件
2TASKSSH_SECRET_KEY_FILE 环境变量指定的文件
3默认路径 ~/.taskssh/vault-key
4交互输入

密钥文件形式

创建密钥文件

mkdir -p ~/.taskssh
openssl rand -base64 32 > ~/.taskssh/vault-key
chmod 600 ~/.taskssh/vault-key

加密 / 解密

# 加密(首次使用时会提示输入密钥)
taskssh encrypt "your-password"

# 解密
taskssh decrypt "xxxxx"

自定义密钥文件

# 命令行指定
taskssh encrypt "your-password" -V /path/to/key

# 环境变量指定
export TASKSSH_SECRET_KEY_FILE=/path/to/key
taskssh encrypt "your-password"

多环境

servers:
  prod:
    vars:
      service_path: /opt/myapp/
      env: prod
    hosts:
      prod1: 10.0.0.1
      prod2: 10.0.0.2

  uat:
    vars:
      service_path: /opt/myapp-uat/
      env: uat
    hosts:
      uat1: 10.1.0.1
taskssh release prod -y
taskssh release uat -y

认证方式

TaskSSH 按以下顺序尝试认证方式:

顺序方式配置字段
1公钥认证identity_file
2私钥口令passphrase
3keyboard-interactive(以密码应答)password
4password(后备)password
5终端交互输入未配置任何认证时
hosts:
  prod_1:
    host: 1.2.3.4
    username: deploy
    identity_file: ~/.ssh/id_rsa
    passphrase: "密文"
    # 公钥认证失败时回退至密码认证
    # password: "密文"

~ 将自动展开为当前用户的 home 目录。passphrase 与 password 一样,必须使用 taskssh encrypt 加密后填写。

命令行选项

全局选项

选项长选项参数说明
-i--inventoryfile指定清单文件,默认 inventory.yaml
-l--list—仅列出目标主机,不执行任务
-P--portint覆盖端口
-u--userstring覆盖用户名
-p--passwordstring覆盖密码(明文,不推荐)
-c--concurrencyint并发数,默认 1(串行)
-y--yes—跳过执行前确认
-D--definekey=value定义变量,可重复
—--connect-timeoutint连接超时秒数,默认 10
-V--secret-key-filefile指定密钥文件或脚本
—--dry-run—显示执行计划,不实际执行
-S--serialint每组每批 N 台,覆盖任务级 serial,默认 -1(用任务配置)
—--min-availableint每组至少留 N 台,覆盖任务级 min_available,默认 -1(用任务配置)
—--stop-on-failure—一批失败后停止后续批次,默认 true
-v--version—显示版本信息
-h--help—显示帮助信息

子命令

命令说明
command执行远程命令
push上传本地文件或目录至远程主机
fetch从远程主机下载文件或目录至本地
script上传本地脚本至远程主机并执行
encrypt加密字符串
decrypt解密字符串

常用示例

# 执行自定义任务
taskssh release prod -y

# 指定清单文件
taskssh release prod -i prod.yaml -y

# 列出目标主机
taskssh release prod -l

# 查看执行计划
taskssh release prod --dry-run

# 覆盖端口与用户名
taskssh release prod -P 2222 -u root -y

# 并发执行
taskssh release prod -c 5 -y

# 注入变量
taskssh deploy prod -D version=1.2.3 -y

# glob 匹配主机
taskssh command 'g:web*' -e "uptime" -y
taskssh command 'h:web*' -e "uptime" -y
taskssh command 'web*' -e "uptime" -y

# 免清单,直接对指定主机执行命令
taskssh command 10.0.0.1 -u deploy -e "uptime" -y

# 加密 / 解密密码
taskssh encrypt "your-password"
taskssh decrypt "xxxxx"

常见问题

不编写 inventory.yaml 是否可以使用?

可以。command / push / fetch / script 支持通过位置参数直接指定主机,并通过 -u / -p 传入认证信息。 自定义任务则必须依赖清单文件。

执行时报变量未解析错误,如何处理?

请检查该变量是否在 global_vars、servers.vars 或主机 Extra 中定义,或通过 CLI -D 传入。未解析的变量会直接中断执行, 属内置的严格策略。

为何在 tasks 段中定义 command 未生效?

command / push / fetch / script / encrypt / decrypt 为保留命令名。CLI 路由优先于任务查找, 因此同名任务不会被触发。请改用其他任务名。

执行时提示 ambiguous host name 是什么原因?

表示提供的主机名在多个组中都存在。请使用 组名/主机名 格式明确指定, 例如 prod/web1。

串行与并行应如何选择?

生产发布及对顺序敏感的操作建议采用串行(默认);只读命令、批量诊断等场景可 通过 -c 提高并发。并发数建议从 3~5 开始。

ignore_errors 对连接失败有效吗?

无效。ignore_errors 只作用于 step 本身执行失败。 连接失败属于主机级问题,会跳过该主机的所有后续步骤,并在摘要中计为失败主机。

when 条件不满足时会怎样?

step 被跳过,输出 Skip step <name> (when: ...), 继续执行下一个 step。跳过的 step 不计入失败。

when 中可以使用未定义的变量吗?

不可以。条件求值前会对 {{var}} 做变量替换, 未解析的变量会导致该 step 失败并中断当前主机。

分批执行时,某组主机太少怎么办?

如果 min_available 大于等于组大小,规划阶段会报错退出。 可通过 --min-available 临时覆盖,或修改任务级配置。 单台组不参与 min_available 计算,单独一批执行。

同一台物理机上部署多个服务,会重复执行吗?

会。TaskSSH 不去重主机,同一 IP 上属于不同组的主机会分别执行。 执行前请用 Target hosts 列表和确认步骤校对目标主机, 确认无误后再继续。

密码是否可以使用明文?

清单中的 password 必须是 taskssh encrypt 生成的密文, 明文将导致报错。CLI -p/--password 允许传明文(会给出警告), 适用于临时排查场景。

忘记密钥怎么办?

密钥用于加密和解密清单中的密码。如果密钥丢失,已有密文无法解密, 需要用新密钥重新执行 taskssh encrypt 生成密文。

更新日志

完整更新记录见 CHANGELOG.md。

📌

文档随版本更新。若发现与代码不一致之处,欢迎通过 Issues 反馈。