Deployer 是一个基于 SSH 协议的无侵入 web 项目部署工具,因为它不需要你在目标 服务器 上装什么服务之类的东西即可使用,它只需要在你的开发机,或者你的笔记本,就是发起部署动作的一方安装即可。
它的原理就是通过 SSH 到你的机器去创建目录,移动文件,执行指定的动作来完成项目的部署。
Deployer 不但可以部署php项目,也可以部署其他语言醒目
真正解放双手,一条命令完成部署。
进行部署的过程中,项目仍然能够正常访问。部署成功完成后才切到新的版本。
能十分方便地进行回滚。
丰富任务钩子和预置任务可灵活的组合完成各种任务,比如执行前端依赖的安装、构建等。
本地机器(也就是你执行 dep 命令时所在的机器)能够 SSH 连接到目标机器
指定用户有登录目标机器并调整一些设置的权限
目标主机有拉取项目仓库的权限
curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep
切换到要部署的项目目录下,执行以下命令
dep init
此时会在改目录下生成 deploy.php文件
未便于管理最好再创建文件deploy.config.php文件,提出公共配置,便于维护部署脚本。
以deploy部署go语言项目为例:
deploy.config.php
<?php
return [
'application' => 'miner-proxy', # 进程名称
'git' => '[email protected]:miner/miner-proxy.git',
'branch' => 'master',
'deploy_path' => '/alidata/deploy/miner-proxy',
'servers' => [
'proxy0' => '192.134.45.45',
'proxy1' => '192.45.67.8',
'proxy2' => '193.168.45.67',
],
'rsync' => [
'rsync -avz /alidata/deploy/miner-proxy/current/build/bin/* public@{ip}:/alidata/service/miner-proxy/',
'rsync -avz /alidata/deploy/miner-proxy/current/build/supervisord.conf public@{ip}:/alidata/service/miner-proxy/'
]
];
deploy.php
<?php
namespace Deployer;
require 'recipe/common.php';
$config = require 'deploy.config.php';
// 项目名
set('application', $config['application']);
// 项目仓库地址
set('repository', $config['git']);
// Shared files/dirs between deploys
set('shared_files', []);
set('shared_dirs', []);
// Writable dirs by web server
set('writable_dirs', []);
set('keep_releases', 5);
if (strtolower(substr(PHP_OS, 0, 3)) === 'win') {
// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', false);
set('ssh_multiplexing', false);
} else {
set('git_tty', true);
}
$stage = $_SERVER['argv'][2];
if(!array_key_exists($stage, $config['servers'])){
exit('stage ' . $stage . ' 不存在');
}
# 目标主机配置
# 192.168.45.67 机器作为程序构建机器
# 不要用root用户,新创建一个普通用户用作部署,在部署之前设置好改账号的各个目录权限
host('192.168.45.67')
->user('test')
->port(22)
->set('branch', $config['branch'])
->stage($stage)
->set('deploy_path', $config['deploy_path']);
// Tasks
desc('Deploy super-miner-proxy project');
task('deploy', [
'deploy:info',
'deploy:prepare',
'deploy:lock',
'deploy:release',
'deploy:update_code',
'deploy:shared',
'deploy:writable',
'deploy:clear_paths',
'deploy:symlink',
'deploy:unlock',
'cleanup',
'success'
]);
# 自定义任务
# 构建任务
task('make', function () use ($config, $stage) {
run('cd ' . $config['deploy_path'] . '/current && (export PATH=$PATH:/usr/local/go/bin;make)');
});
# 打包同步任务,最好先压缩再同步
task('rsync', function () use ($config, $stage){
foreach ($config['rsync'] as $val){
$val = str_replace('{ip}', $config['servers'][$stage], $val);
run($val);
}
});
# 可执行程序同步完成后重启进程
# 对于需要sudo操作的命令,可以在指定机器上设置该命令免密
task('restart', function () use ($config, $stage) {
run('ssh test@' . $config['servers'][$stage] . ' "sudo /usr/bin/supervisorctl restart ' . $config['application'] . '"');
});
# 如果部署失败,自动解除部署锁定状态,以免影响下次执行
after('deploy:failed', 'deploy:unlock');
# 部署
after('deploy', 'make');
# 回退
after('rollback', 'make');
# 同步
after('make', 'rsync');
# 重启
after('rsync', 'restart');
部署:
dep deploy {stage} -vvv
回退:
dep rollback {stage} -vvv
releases 包含你部署项目的版本(默认保留 5 个版本)
shared 包含你部署项目的共享文件或目录(如:Laravel 的 Storage 目录、.env 文件等 )
current 软连接到你当前发布的版本
对于 deploy:update_code 使用Git下载新版本的代码。如果您使用的是Git 2.0版,并且git_cache config已打开,则此任务将使用先前发行版中的文件,因此仅下载更改的文件。否则重新clone 代码。
因此最好将Git升级到 2.0 以上版本。
默认情况下php命令使用的是/usr/bin/php
,但有时系统中安装了多个php版本,而我们并不想使用默认的php版本,可做如下设置:
在 require 'recipe/laravel.php'; 下方增加:
# 设置php命令路径
set('bin/php', function () {
return '/usr/local/php8.0/bin/php';
});
# 设置composer命令
set('bin/composer', function () {
return '/usr/local/php8.0/bin/php /usr/local/bin/composer';
});
其他设置点可以通过以下网址查看
https://deployer.org/docs/6.x/getting-started
https://github.com/deployphp/deployer
7.x 版本相比6.x做了一些改变
详细文档参考 https://deployer.org/docs/7.x/getting-started
<?php
namespace Deployer;
require 'vendor/deployer/deployer/recipe/laravel.php';
// Project name
set('application', 'charming_voice');
// Project repository
set('repository', '[email protected]:xxx/xxxx.git');
set('keep_releases', 5);
set('ssh_multiplexing', true);
set('git_tty', true);
set('writable_use_sudo', false);
set('writable_mode', 'chmod');
set('writable_chmod_mode', '0777');
set('writable_chmod_recursive', false);
set('allow_anonymous_stats', false);
set('use_relative_symlink', false);
// Shared files/dirs between deploys
add('shared_dirs', ['public/vendor']);
// Writable dirs by web server
add('writable_dirs', ['public/vendor']);
set('allow_anonymous_stats', false);
// 注意这个地方和 6.x 的不同
set('bin/php', function(){
return '{{which_php}}';
});
// Hosts 注意这与 6.x 的不同
host('test')
->set('labels', ['env' => 'test'])
->set('hostname', 'test.a.com')
->set('remote_user', 'ubuntu')
->set('which_php', '/usr/local/php8.1/bin/php')
->set('branch', 'master')
->set('identity_file', '~/.ssh/id_rsa')
->set('deploy_path', '/alidata/www/test');
host('pre')
->set('labels', ['env' => 'pre'])
->set('hostname', 'pre.a.com')
->set('remote_user', 'ubuntu')
->set('which_php', '/usr/local/php8.1/bin/php')
->set('branch', 'master')
->set('identity_file', '~/.ssh/id_rsa')
->set('deploy_path', '/alidata/www/pre');
// Tasks
task('file:set_permissions', function(){
run('cd {{release_path}} && if [ -d storage/logs/ ]; then sudo chmod -R 777 storage/logs/; fi');
});
// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');
task('module:publish', function(){
run('cd {{release_path}} && {{bin/php}} artisan module:publish && {{bin/php}} artisan vendor:publish --tag views --force');
});
# 重启php
task('php-fpm:restart', function(){
run('sudo /bin/lnmp php-fpm reload');
});
task('admin:publish', function(){
run('cd {{release_path}} && {{bin/php}} artisan admin:publish && {{bin/php}} artisan MigrateTinymce && {{bin/php}} artisan optimize:clear');
});
task('npm:install', function(){
# 判断环境也与 6.x 不同
$env = get('labels')['env'];
if ($env == 'test') {
run('cd {{release_path}} && source ~/.bashrc && /home/ubuntu/.nvm/versions/node/v14.19.0/bin/npm install --save-dev chokidar');
}
});
# 重启守护进程,队列 和 octane 服务
# 不能直接 reload, 应该每个进程单独restart, reload 会同时重启 supervisord,导致重启时间很长,请求报大量502错误
task('supervisor:reload', function(){
$env = get('labels')['env'];
if ($env == 'pro') {
run('sudo /usr/bin/supervisorctl restart server:');
run('sudo /usr/bin/supervisorctl restart queue:');
} elseif ($env == 'pre') {
run('sudo /usr/bin/supervisorctl restart server:');
}
});
# 查看日志
task('tail_log', function(){
run('cd {{release_path}}/ && tail -f storage/logs/*.log');
});
# 部署
after('deploy', 'module:publish');
after('module:publish', 'admin:publish');
after('admin:publish', 'file:set_permissions');
after('file:set_permissions', 'php-fpm:restart');
after('php-fpm:restart', 'npm:install');
after('npm:install', 'supervisor:reload');
after('supervisor:reload', 'tail_log');
[1]
https://deployer.org/docs/6.x/getting-started: https://deployer.org/docs/6.x/getting-started[2]
https://github.com/deployphp/deployer: https://github.com/deployphp/deployer[3]
https://deployer.org/docs/7.x/getting-started: https://deployer.org/docs/7.x/getting-started