Envoy 任务运行器 Blade 语法
解读
在国内一线/二线互联网公司的 PHP 后端面试中,Laravel 生态是高频考点。Envoy 作为 Laravel 官方出品的轻量级部署脚本工具,其 Blade 语法常被用来考察候选人对「服务器编排」与「模板渲染」双重概念的理解深度。面试官通常不会只问“怎么用”,而是通过“写一段 Envoy 脚本”或“这段脚本会输出什么”来验证你是否真正明白 Blade 在 CLI 场景下的编译机制、变量逃逸规则以及与服务端 SSH 指令的交互边界。答不上 Blade 语法细节,会被直接判定为“只会 Composer 拉包,不会写部署”。
知识点
- Envoy 文件本质:Blade 模板(*.blade.php),由
illuminate/view组件编译,运行期变量来自@setup、@story、@task及命令行--var=xxx。 - 指令分类
2.1 结构指令:@task、@setup、@story、@slack、@error、@success、@finished
2.2 流程控制:@if、@foreach、@for、@while、@unless
2.3 输出控制:{{ }}原生 echo、{!! !!}不转义、{{{ }}}Laravel 5 以前三重转义(现在已废弃,面试里可能故意挖坑)
2.4 注释:{{-- --}}在编译阶段直接丢弃,不会出现在最终 bash 脚本里 - 变量作用域:
@setup里用 PHP 定义:$foo = 'bar';- 命令行注入:
envoy run deploy --branch=main在模板里用$branch直接读取
- 编译产物:Envoy 把 Blade 编译成纯 PHP 后再
eval(),最终输出 bash 文本,通过 SSH 在远端执行;因此 PHP 变量必须解析完毕,不能出现“未定义”残留。 - 常见坑:
- 在
{{ }}里写date('Y-m-d')会被转义,导致远端 shell 收到2019-07-18而不是$()命令替换;正确写法是@php echo date('Y-m-d'); @endphp或直接在@setup里算好。 - 使用
@if($env === 'prod')时,$env必须是 PHP 变量,不能写 bash 变量$env,否则编译期就报错。
- 在
- 国内镜像加速:在
@setup里写putenv('COMPOSER_REPO_PACKAGIST=composer.aliyun.com');可提升 Composer 安装速度,面试时主动提及是加分项。 - 回滚策略:利用
@story把deploy和rollback做成两个任务,结合releases/{$timestamp}软链,是国内灰度发布的通用方案;能现场写出模板骨架可直通二面。
答案
下面给出一段可直接运行的 Envoy.blade.php,覆盖“变量注入、条件分支、循环、注释、输出转义、回滚”六个考点,并在关键行用中文注释说明 Blade 语法要点。面试官若让你“解释每一行”,按注释回答即可。
{{-- 1. 注释:这行不会出现在最终 bash 脚本里 --}}
@setup
// PHP 代码段,定义变量
$user = 'webop'; // 远端 SSH 用户
$base = '/data/webroot'; // 项目根目录
$repo = 'git@gitlab.xxx.cn:group/project.git';
$branch = $branch ?? 'master'; // 允许命令行 --branch=xxx 覆盖
$keep = 5; // 保留最近 5 个版本
$now = date('YmdHis'); // 时间戳目录
@endsetup
@story('deploy')
clone
composer
link
clean
reload
@endstory
@task('clone', ['on' => 'web'])
echo '==== 拉取代码 {{ $now }} ===='
cd {{ $base }}/releases
git clone {{ $repo }} {{ $now }}
cd {{ $now }}
git checkout {{ $branch }}
@endtask
@task('composer', ['on' => 'web'])
echo '==== 安装依赖 ===='
cd {{ $base }}/releases/{{ $now }}
{{-- 使用国内镜像加速,面试加分项 --}}
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer install --no-dev -q
@endtask
@task('link', ['on' => 'web'])
echo '==== 切换软链 ===='
ln -nfs {{ $base }}/releases/{{ $now }} {{ $base }}/current
@endtask
@task('clean', ['on' => 'web'])
echo '==== 清理旧版本 ===='
cd {{ $base }}/releases
{{-- Blade 的 foreach 在编译期展开,生成纯 bash --}}
@foreach (range(1, $keep) as $skip)
# 保留最近 {{ $keep }} 个目录,其余 rm -rf
@endforeach
ls -t | tail -n +{{ $keep + 1 }} | xargs rm -rf
@endtask
@task('reload', ['on' => 'web'])
echo '==== 平滑重启 ===='
sudo -u {{ $user }} php-fpm -t && sudo -u {{ $user }} service php-fpm reload
@endtask
{{-- 回滚任务:直接指向上一个目录 --}}
@task('rollback', ['on' => 'web'])
echo '==== 回滚到上一个版本 ===='
cd {{ $base }}/releases
last=$(ls -t | head -n 2 | tail -n 1)
ln -nfs {{ $base }}/releases/$last {{ $base }}/current
sudo -u {{ $user }} service php-fpm reload
@endtask
@finished
@if ($task === 'deploy')
@slack('hook-url', '#ops', "部署成功:{{ $branch }} - {{ $now }}")
@endif
@endfinished
使用示例
envoy run deploy --branch=main
envoy run rollback
拓展思考
- 多机并发:把
@servers声明为数组,利用@parallel让clone、composer在 10 台机器上同时执行,面试可延伸问“如何防止并发写同一缓存目录”。 - 灰度验证:在
@story里插入healthcheck任务,调用内网接口https://gray.xxx.cn/api/health,超时 5 秒则自动@slack告警并触发rollback,体现你对 SRE 理念的落地。 - 秘钥安全:国内企业对“把私钥写进 Envoy”零容忍,正确姿势是把
.envoy/id_rsa放到 CI 的 Secret 管理(如阿里云 KMS、腾讯云 SSM),模板里只写{{ $privateKey }},并设置文件权限 600,面试主动提到可展示安全意识。 - 与 Kubernetes 对比:Envoy 适合“少量云主机 + 传统 LNMP”场景,如果公司全面 K8s,可用 Helm 或 ArgoCD 替代;面试官若追问“为什么不用 Jenkins + Ansible”,可从“ Envoy 无守护进程、学习成本低、与 Laravel 生态无缝”三点回应,体现技术选型思辨。