TaskSSH 文档
TaskSSH 是一个单文件、无外部依赖的 SSH 批量运维工具。其配置模型由两部分构成:
一份 inventory.yaml 清单文件,用于声明服务器组、主机及变量;
以及清单中的 tasks 段,用于将一个或多个 action 编排为有序的执行流程。
用户通过指定任务名与目标主机组,即可对多台服务器批量执行命令、上传与下载文件。
安装
下载预编译二进制
前往 Releases 页面,根据目标平台下载对应的压缩包:
| 平台 | 架构 | 文件名 |
|---|---|---|
| Windows | amd64 | taskssh-windows-amd64.zip |
| Linux | amd64 | taskssh-linux-amd64.zip |
| Linux | arm64 | taskssh-linux-arm64.zip |
| macOS | Intel | taskssh-darwin-amd64.zip |
| macOS | Apple Silicon | taskssh-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 的配置模型由两个对象构成:
- inventory:即
inventory.yaml清单文件,用于声明服务器组、主机及变量。 - task:清单中
tasks段定义的执行流程,由一个或多个有序 step 组成。
执行任务时,用户指定任务名与目标主机组,TaskSSH 按以下流程处理:
- 从 inventory 中解析目标主机列表;
- 按优先级合并 global / 组 / 主机三级变量,生成每台主机的变量池;
- 按声明顺序执行 task 中各 step;
- 每台主机独立执行,任务结束后汇总成功与失败结果。
内置的 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
主机声明方式
-
简写形式:
主机名: IP。 端口、用户名、密码等字段自上层继承。 -
完整形式:包含
host、port、username、password、identity_file、passphrase等字段。
除上述专有字段外,其余字段将进入主机的 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 匹配不到时报错。
变量合并优先级
主机最终获得的变量池按以下顺序合并,优先级由低到高:
| 层级 | 来源 | 作用范围 |
|---|---|---|
| 1 | global_vars | 所有主机 |
| 2 | servers.<组名>.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"
限制
- 不支持括号改变优先级。
- 不支持算术运算、函数调用。
- 条件求值前,表达式中的
{{var}}会先做变量替换;未解析的变量会导致 step 失败。
组合内置 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 | --execute | string | 待执行的命令;亦可从清单的 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 | --file | file or dir | 本地文件或目录路径 |
-d | --dest | path | 远程目标路径;亦可从变量 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
路径与覆盖规则
-d以/结尾时视为目录,最终路径为目录 + 本地文件名。- 若目标已存在且为目录,文件上传至该目录内。
- 若目标已存在且为文件:
- 默认行为为报错;
-F直接覆盖。
- 远程目录不存在时报错。程序不会自动创建远程目录,如有需要,可在任务中预先加入创建目录的
command步骤。
目录上传
- 不传
-z:递归逐文件上传,不依赖远程工具。 - 传
-z:本地 zip 打包上传,远程unzip解包。依赖远程安装unzip。
Action:fetch
从远程主机下载文件或目录至本地。
| 选项 | 长选项 | 参数 | 说明 |
|---|---|---|---|
-f | --file | path | 远程文件或目录路径 |
-d | --dest | path | 本地目标路径,默认 ./ |
-z | --zip | — | 目录下载时,远程打包后下载(依赖远程 zip) |
-T | --tmp-dir | path | 远程临时目录,默认 /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
路径与覆盖规则
-d以/结尾时视为目录,按主机标识分组存放:dest/<主机标识>/<文件名>。- 若
-d为文件路径,则直接下载至该文件。 - 本地文件已存在时,默认覆盖。
- 本地父目录不存在时自动创建。
- 远程路径为目录时:
- 不传
-z:递归逐文件下载; - 传
-z:远程zip -r打包,下载后本地解压,删除远程 zip。
- 不传
Action:script
将本地 shell 脚本上传至远程主机并执行。
| 选项 | 长选项 | 参数 | 说明 |
|---|---|---|---|
-f | --file | file | 本地脚本文件 |
-d | --dest | path | 远程目录,默认 /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
执行规则
- 脚本上传到
-d指定的目录,默认/tmp。 - 上传后自动
chmod +x,然后执行。 -r表示执行后删除远程脚本,不论成功失败。-F表示覆盖已存在的远程脚本。
串行执行
默认情况下,TaskSSH 以串行方式执行任务:
- 主机之间:一台主机完成后再执行下一台;
- 主机内部:一个 step 完成后再执行下一个 step。
串行模式的特性为日志实时输出、失败可即时定位,适用于对执行顺序敏感的场景。
并行执行
通过 -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
- 组间串行:
order的所有批跑完,才轮到stock。 - 批间串行:批1 跑完,才跑批2。
- 批内并发:批1 里的主机同时执行,并发度由
-c控制。 - 组间顺序:按 CLI 传入顺序。用户写
-H order,stock,order先。 - 组内顺序:按主机名字典序。建议用零填充命名(
web_01、web_02)控制顺序。
serial 与 min_available
| 参数 | 位置 | 说明 |
|---|---|---|
serial | 任务级 Task | 每组内每批 N 台 |
min_available | 任务级 Task | 每组至少留 N 台在跑 |
-S / --serial | CLI | 覆盖任务级 serial |
--min-available | CLI | 覆盖任务级 min_available |
--stop-on-failure | CLI | 一批失败后停止后续批次,默认 true |
每批大小由两者共同决定:
batchSize = min(serial, len(group) - minAvailable)
示例:
| 组大小 | serial | min_available | 每批大小 | 分批结果 |
|---|---|---|---|---|
| 4 | 2 | 1 | min(2, 3) = 2 | 批 2 + 2 |
| 2 | 2 | 1 | min(2, 1) = 1 | 批 1 + 1 |
| 2 | 2 | 0 | min(2, 2) = 2 | 批 2(全动) |
| 4 | 0 | 1 | 4 - 1 = 3 | 批 3 + 1 |
| 4 | 0 | 0 | 4 | 批 4(全动) |
优先级
CLI 显式传参时覆盖任务级配置:
CLI flag(显式传了)> 任务级(Task.Serial / Task.MinAvailable)> 默认值(0)
任务级 serial: 0 或不配,表示不启用分批。
CLI -S 0 表示覆盖任务级为"不分批"。
边界处理
-
单台组:
len(group) == 1时,单独一批,不参与min_available计算,不报错。 -
min_available 配大了:
len(group) - minAvailable <= 0时, 规划阶段报错退出,提示用户改参数:group stock: min-available 2 >= group size 2, cannot batch hint: reduce min-available in task config or override with --min-available
任务级配置示例
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
行为说明
- 不建立 SSH 连接,不执行命令、不传输文件。
Stat/ListFiles等只读操作正常执行。- 跳过执行前确认。
- 不输出执行摘要。
delay不实际等待。
启用分批时的输出示例:
>>> 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}} 等价。
变量来源与优先级
变量按以下优先级由低到高合并:
| 层级 | 来源 | 作用范围 |
|---|---|---|
| 1 | global_vars | 所有主机 |
| 2 | servers.<组名>.vars | 组内主机 |
| 3 | 主机专有字段与 Extra | 单台主机 |
| 4 | CLI -D/--define | 所有主机 |
| 5 | date / 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 为本次任务执行的唯一标识,用于生成唯一目录名、版本标识等。
- 生成方式:纯随机,8 位 Base36
- 字符集:
0-9与A-Z - 空间:
36^8 ≈ 2.82 × 10^12 - 不携带时间戳,不具备字典序,不可用于排序
- 一次任务执行生成一次,所有主机共享同一值
典型用法:
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 | --define | key=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
规则
- 格式为
key=value,key 与 value 均做 trim。 - value 可包含
=,以第一个=分隔。 - 重复的 key 以后出现的为准。
- 不能覆盖保留变量(
date/execId)。
优先级
变量优先级从低到高:
global_varsservers.<组名>.vars- 主机专有字段与
Extra - CLI
-D/--define date/execId(运行时注入,不可覆盖)
Vault 密钥
清单中的 password 和 passphrase 必须是密文。
TaskSSH 使用 AES-256-GCM 加密,密钥由用户管理,不硬编码在二进制中。
密钥来源与优先级
| 顺序 | 来源 |
|---|---|
| 1 | --secret-key-file / -V 指定的文件 |
| 2 | TASKSSH_SECRET_KEY_FILE 环境变量指定的文件 |
| 3 | 默认路径 ~/.taskssh/vault-key |
| 4 | 交互输入 |
密钥文件形式
- 普通文本文件:内容即密钥。
- 可执行文件:执行输出作为密钥。适合从密码管理器或 KMS 动态获取。
创建密钥文件
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 |
| 3 | keyboard-interactive(以密码应答) | password |
| 4 | password(后备) | 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 | --inventory | file | 指定清单文件,默认 inventory.yaml |
-l | --list | — | 仅列出目标主机,不执行任务 |
-P | --port | int | 覆盖端口 |
-u | --user | string | 覆盖用户名 |
-p | --password | string | 覆盖密码(明文,不推荐) |
-c | --concurrency | int | 并发数,默认 1(串行) |
-y | --yes | — | 跳过执行前确认 |
-D | --define | key=value | 定义变量,可重复 |
| — | --connect-timeout | int | 连接超时秒数,默认 10 |
-V | --secret-key-file | file | 指定密钥文件或脚本 |
| — | --dry-run | — | 显示执行计划,不实际执行 |
-S | --serial | int | 每组每批 N 台,覆盖任务级 serial,默认 -1(用任务配置) |
| — | --min-available | int | 每组至少留 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 反馈。