基于 php 的项目部署工具 deployer 介绍

介绍

Deployer 是一个基于 SSH 协议的无侵入 web 项目部署工具,因为它不需要你在目标 服务器 上装什么服务之类的东西即可使用,它只需要在你的开发机,或者你的笔记本,就是发起部署动作的一方安装即可。

它的原理就是通过 SSH 到你的机器去创建目录,移动文件,执行指定的动作来完成项目的部署。

Deployer 不但可以部署php项目,也可以部署其他语言醒目

deployer 的优势

真正解放双手,一条命令完成部署。
进行部署的过程中,项目仍然能够正常访问。部署成功完成后才切到新的版本。
能十分方便地进行回滚。
丰富任务钩子和预置任务可灵活的组合完成各种任务,比如执行前端依赖的安装、构建等。

使用 deployer 的前提条件

本地机器(也就是你执行 dep 命令时所在的机器)能够 SSH 连接到目标机器
指定用户有登录目标机器并调整一些设置的权限
目标主机有拉取项目仓库的权限

Deployer 6.x 版本

使用说明

1、安装

curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep

2、生成部署脚本代码

切换到要部署的项目目录下,执行以下命令

dep init

此时会在改目录下生成 deploy.php文件

3、使用说明

未便于管理最好再创建文件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
当你第一次成功部署的时候, Deployer 会自动帮你在服务器上生成一下文件:
releases 包含你部署项目的版本(默认保留 5 个版本)

shared 包含你部署项目的共享文件或目录(如:Laravel 的 Storage 目录、.env 文件等 )

current 软连接到你当前发布的版本

注意

对于 deploy:update_code 使用Git下载新版本的代码。如果您使用的是Git 2.0版,并且git_cache config已打开,则此任务将使用先前发行版中的文件,因此仅下载更改的文件。否则重新clone 代码。

因此最好将Git升级到 2.0 以上版本。

php和composer版本设置

默认情况下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

Deployer 7.x 版本

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