Deployer Selector 完全指南:用标签精确调度主机与任务
【免费下载链接】deployerThe PHP deployment tool with support for popular frameworks out of the box项目地址: https://gitcode.com/gh_mirrors/de/deployer
导读
Selector(选择器)是 Deployer 中用于挑选主机来执行任务的机制:每台主机携带一组键值对labels(标签)(例如stage: production、role: web),Selector 负责根据这些标签过滤出目标主机。无论是命令行传参(dep info env=prod)、任务体内动态筛选(select()/on()),还是为任务绑定固定选择器(->select()),都建立在同一套标签匹配语义之上。读完本文,你将完整掌握 Deployer Selector 的语法(OR/AND/取反/别名)、源码级匹配原理,以及它在 MAML 配置、任务调度与once语义中的实际应用方式。
一、标签(Labels):Selector 的匹配基础
Selector 不是凭空选主机,而是基于主机上的键值对标签进行匹配。标签通过host()定义主机时一并声明:
host('web.example.com') ->setLabels([ 'type' => 'web', 'env' => 'prod', ]); host('db.example.com') ->setLabels([ 'type' => 'db', 'env' => 'prod', ]);已存在的主机可用->addLabels()追加/合并标签。其底层实现位于 Host.php:setLabels()直接写入配置键labels,而addLabels()会先取出已有标签,再通过array_replace_recursive与新增标签递归合并后回写,因此可以安全地分多次、分层地补充标签而不覆盖已有内容:
host('api.example.com') ->setLabels(['type' => 'api']) ->addLabels(['env' => 'staging', 'region' => 'eu']);labels在内部就是主机配置中的一个数组键。Host::get('labels', [])读取它,而Selector::apply()在匹配时同样通过$host->get('labels', [])取出标签集(见 Selector.php),两者共用同一份数据,不存在两套标签源。
二、第一个实战:用选择器运行任务
定义打印标签的任务:
task('info', function () { writeln('type:' . get('labels')['type'] . ' env:' . get('labels')['env']); });get('labels')取的是当前主机的标签(Context中已切到该主机)。不带选择器运行,会作用于所有主机;带上选择器则只作用于匹配的主机:
$ dep info env=prod task info [web.example.com] type:web env:prod [db.example.com] type:db env:prod两台主机都匹配env=prod。进一步收窄条件:
$ dep info type=web task info [web.example.com] type:web env:prod命令行选择器由SelectCommand统一处理(SelectCommand.php):取到的参数数组会用implode(',', ...)拼成单个选择表达式后交给Deployer::get()->selector->select()。若不传任何选择器且配置了default_selector全局配置,则使用该默认值;交互式终端下还会弹出多选清单让你挑选主机。
三、Selector 语法:OR、AND、值内 OR 与取反
一个选择器是由条件组成的列表,条件之间用,(OR)或&(AND)连接。下面是完整语法,均以第一节的两台主机为例。
OR(,):type=web,env=prod匹配type=web或env=prod:
$ dep info 'type=web,env=prod' task info [web.example.com] type:web env:prod [db.example.com] type:db env:prodAND(&):type=web & env=prod要求同时满足两者:
$ dep info 'type=web & env=prod' task info [web.example.com] type:web env:prod值内 OR(|):type=web|db & env=prod表示(type=web OR type=db) AND env=prod,注意运算符优先级——|只在单个值内部生效,不会跨越&:
$ dep info 'type=web|db & env=prod' task info [web.example.com] type:web env:prod [db.example.com] type:db env:prod取反(!=):type!=web排除标签为type=web的主机:
$ dep info 'type!=web' task info [db.example.com] type:db env:prod关于解析顺序,源码给出了精确的层级(Selector::parse()):
- 先用
explode(',')按 OR 切分整个表达式,得到若干个子条件组; - 每个子条件组再按
explode('&')切分 AND 部分; - 每个部分
trim()后,若不含=则当作别名条件(见下文),否则用正则/(?<var>.+?)(?<op>!?=)(?<value>.+)/拆出变量 / 操作符 / 值; - 值部分再按
|拆成候选数组。
而apply()(Selector.php)的求值逻辑是:同一组&条件全部为真,则整组命中;任一|候选值命中即视为该条件为真;组与组之间是 OR,任一组命中即匹配。这与直觉完全一致:&的优先级高于顶层,。
:::note多个选择器参数等价于逗号拼接:dep info type=web env=prod≡dep info 'type=web,env=prod'。另外,bash 自动补全支持选择器提示——参考 installation。 :::
四、特殊选择器:all与别名匹配
all—— 匹配每一台主机。alias=...—— 按主机别名匹配。
任何不含=的 token 都被视作别名(见parse()中 else 分支:['=', 'alias', trim($part)]),因此dep info web.example.com≡dep info alias=web.example.com:
$ dep info web.example.com task info [web.example.com] type:web env:prod多别名可并列,等价于逗号连接的别名条件:
$ dep info 'web.example.com' 'db.example.com' $ # Same as: $ dep info 'alias=web.example.com,alias=db.example.com'别名与true标签的源码细节
在apply()中,匹配时实际参与比较的标签集合会额外注入两个隐式标签(Selector.php):
$labels = $host->get('labels', []); $labels['alias'] = $host->getAlias(); // 把主机别名当作可匹配标签 $labels['true'] = 'true'; // 恒真标签,供 all 使用alias标签让alias=...和裸 token 生效;true标签让all变为等价于['=', 'true', 'true']的条件,从而"无条件命中所有主机"。
compare()支持=与!=两种操作符:=要求标签值严格等于候选值(===比较,且支持标签值为数组时逐个元素比对,这与"值内 OR"天然兼容),!=则取反。当标签缺失时,按null参与比较——因此type!=web同样会命中没有type标签的主机,这一点在编写排除条件时值得留意。
五、在 PHP 代码中使用选择器:select()与on()
除了命令行,任务内部也能动态地按选择器取主机。
select(expr)返回匹配主机的数组,定义见 functions.php,实现为Deployer::get()->selector->select($selector):
task('info', function () { $hosts = select('type=web|db,env=prod'); foreach ($hosts as $host) { writeln('type:' . $host->get('labels')['type'] . ' env:' . $host->get('labels')['env']); } });on(hosts, callback)对每个匹配主机依次执行回调,并自动完成Context的压栈/出栈、配置加载与保存(functions.php):
task('info', function () { on(select('all'), function () { writeln('type:' . get('labels')['type'] . ' env:' . get('labels')['env']); }); });on()的回调里get()/run()都作用于当前遍历到的主机;它同样接受单台主机或主机集合,甚至可以传Deployer::get()->hosts遍历全部主机。相关完整 API 见 api.md。
六、任务级选择器:->select()
可以把选择器固定绑定到某个任务上,这样无论谁调用该任务,都只会作用到匹配的主机(tasks.md 中的select配置):
task('info', function () { // ... })->select('type=web|db,env=prod');Task::select()只是把表达式缓存为解析结果(Task.php):$this->selector = Selector::parse($selector);。真正消费它的是执行器 Master.php——在执行每个任务前,用Selector::apply($task->getSelector(), $currentHost)逐主机判定,未命中的主机直接从本任务的执行计划中剔除。
这个机制还与 Deployer 的任务调度特性深度耦合:
once(单次执行):只在第一个匹配选择器的主机上运行一次;oncePerNode(每节点一次):按hostname(或标签node)去重,每组节点只跑一次,见 Master.php;- 子任务继承:脚本中的子任务会通过
addSelector()合并父任务的 selector(ScriptManager.php),保证过滤条件在整个调用链上传递。
在并行场景(--parallel、--limit)下,Master 同样在每个分块内先按 selector 过滤再派发(Master.php),因此任务级选择器在串行与并行模式下行为一致。
七、MAML 中的标签
MAML 配方同样支持 labels。标签写在主机的labels键下,与env(环境配置)平级:
{ hosts: { "web.example.com": { remote_user: "deployer" env: { environment: "production" } labels: { env: "prod" } } } }不要把env(配置键)和labels.env(标签)混为一谈,二者相互独立:env存放的是 MAML 里定义的部署环境配置,labels.env只是参与 selector 匹配的标签值。可以在同一任务里同时读取验证:
task('info', function () { writeln('env:' . get('env')['environment'] . ' labels.env:' . get('labels')['env']); });$ dep info env=prod task info [web.example.com] env:production labels.env:prod这里命令行选择器匹配的是labels.env=prod,而打印出的env:production来自配置键env——两者互不干扰。
八、命令行交互细节与调试
几个有助于日常使用的命令行行为:
- 默认选择器:全局配置
default_selector可在不传参时生效(SelectCommand.php); - 交互式选择:配置了多台主机且未给选择器时,终端会弹出多选清单(
ChoiceQuestion支持逗号分隔多选); - 无命中即报错:表达式写错或没有主机匹配时,命令会抛出异常并回显选择器原文,便于排查(SelectCommand.php);
- Shell 自动补全:selector 参数的补全建议来自
all、所有主机别名以及各主机的label=value组合(SelectCommand.php),安装方式参见 installation。
此外,Selector的单测 SelectorTest.php 覆盖了典型组合:all全量匹配、stage=prod单条件、stage=prod & tier=frontendAND 匹配、prod.domain.com/front, stage=beta别名+条件 OR、all & tier != frontend取反排除,以及标签值为数组(如'stage' => ['prod', 'beta'])时的多值匹配——这些用例可直接作为你验证选择器行为的参考基准。
九、小结
Deployer 的 Selector 体系可以归纳为三层:
- 数据层:主机通过
setLabels()/addLabels()(或 MAML 中的labels:键)携带任意键值标签,匹配时还会注入alias与恒真的true两个隐式标签; - 匹配层:
Selector::parse()将表达式拆为,(OR)→&(AND)→|(值内 OR)三层结构,Selector::apply()按"组内全真、组间任一"规则求值,支持=与!=; - 应用层:CLI 参数经 SelectCommand.php 拼接后统一求值,任务内可用
select()/on()动态筛选,任务定义可用->select()固化范围,once/oncePerNode与并行调度则复用同一套apply()判定。
掌握这套机制后,无论是"只部署生产环境 Web 节点""排除某个机房的主机",还是"每节点只跑一次数据库迁移",都能用一行选择器精准表达,让 Deployer 的批量部署始终落在你真正想要的主机集合上。
【免费下载链接】deployerThe PHP deployment tool with support for popular frameworks out of the box项目地址: https://gitcode.com/gh_mirrors/de/deployer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考