这篇文章介绍deployer工具.
它支持并行部署,版本回退,可以很方便的编写命令并设置执行时机,并且支持很多主流的php框架提供开箱可用的一些方案.
使用deployer的情况下:
circleCI配置文件中的deployment模块需要修改,其中dep -v deploy staging
命令会执行当前目录中的deploy.php文件
deployment:
staging:
branch: develop
commands:
- curl -LO https://deployer.org/deployer.phar
- sudo mv deployer.phar /usr/local/bin/dep
- chmod +x /usr/local/bin/dep
- dep -v deploy test
- dep -v deploy staging
production:
branch: master
commands:
- curl -LO https://deployer.org/deployer.phar
- sudo mv deployer.phar /usr/local/bin/dep
- chmod +x /usr/local/bin/dep
- dep -v deploy production
我使用的deployer的一个例子:
要说明一下脚本中的几个host,其实是有三组host,里面的stage分别是test/staging/production对应不同的环境,这个不是这篇文章的重点所以不介绍.
并且host配置的这么奇怪是因为host不能重名...所以我就各种办法...
<?php
namespace Deployer;
require 'recipe/laravel.php';
use Symfony\Component\Console\Input\InputOption;
//dep -vv deploy --first true --branch develop -- test
option('first', null, InputOption::VALUE_OPTIONAL, 'If first deploy.');
// Configuration
set('repository', 'git@github.com:never615/easy-admin.git');
set('allow_anonymous_stats', false);
// Hosts
host('server-1.xxx.com', 'server-2.xxx.com')
->stage('production')
->set("branch", "master")
->user("user")
->port("88888")
->set('deploy_path', '/app/xxx/easy_admin/production')
->forwardAgent(true)
->multiplexing(true)
->addSshOption('UserKnownHostsFile', '/dev/null')
->addSshOption('StrictHostKeyChecking', 'no');
host('119.23.111.111', '120.76.111.112')
->stage('staging')
->set("branch", "develop")
->user("user")
->port("88888")
->set('deploy_path', '/app/xxx/easy_admin/staging')
->forwardAgent(true)
->multiplexing(true)
->addSshOption('UserKnownHostsFile', '/dev/null')
->addSshOption('StrictHostKeyChecking', 'no');
host('test')
->hostname('119.23.111.111')
->stage('test')
->set("branch", "develop")
->user("user")
->port("88888")
->set('deploy_path', '/app/xxx/easy_admin/test')
->forwardAgent(true)
->addSshOption('UserKnownHostsFile', '/dev/null')
->addSshOption('StrictHostKeyChecking', 'no');
// Tasks
desc('first deploy');
task('deploy:first', function () {
if (input()->hasOption('first')) {
$first = input()->getOption('first');
if ($first) {
run('{{bin/php}} {{release_path}}/artisan tool:install');
run('{{bin/php}} {{release_path}}/artisan admin:install');
run('{{bin/php}} {{release_path}}/artisan user:install');
run('{{bin/php}} {{release_path}}/artisan mall:install');
run('{{bin/php}} {{release_path}}/artisan activity:install');
//生成的key需要各个地方一样
// run('{{bin/php}} {{release_path}}/artisan passport:install');
// run('{{bin/php}} {{release_path}}/artisan passport:keys');
run('{{bin/php}} {{release_path}}/artisan passport:client --personal');
}
}
});
desc('update');
task('deploy:update',function(){
run('yes|{{bin/php}} {{release_path}}/artisan tool:update');
run('yes|{{bin/php}} {{release_path}}/artisan admin:update');
run('yes|{{bin/php}} {{release_path}}/artisan user:update');
run('yes|{{bin/php}} {{release_path}}/artisan mall:update');
run('yes|{{bin/php}} {{release_path}}/artisan activity:update');
});
task('deploy:chown_chgrp',function(){
run('sudo chown -R nginx {{release_path}}');
run('sudo chgrp -R www {{release_path}}');
run('sudo chmod -R 775 {{release_path}}');
});
desc('copy env');
task('deploy:cp_env', 'cp .env.{{stage}} .env');
desc('copy composer');
task('deploy:cp_composer', 'cp composer.json.circle composer.json');
desc('Restart PHP-FPM service');
task('php-fpm:restart', function () {
// The user must have rights for restart service
// /etc/sudoers: username ALL=NOPASSWD:/bin/systemctl restart php-fpm.service
run('sudo systemctl restart php-fpm.service');
});
task("artisan:vendor", '
php artisan queue:restart;
');
after('deploy:symlink', 'php-fpm:restart');
after('deploy:update_code', 'deploy:cp_env');
after('deploy:update_code', 'deploy:cp_composer');
// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');
// first deploy run
after('artisan:vendor', 'deploy:first');
// plugin install
after('artisan:vendor', 'deploy:update');
after('deploy:symlink', 'deploy:chown_chgrp');
// publish vendor
before('deploy:symlink', 'artisan:vendor');
// Migrate database before symlink new release.
before('deploy:symlink', 'artisan:migrate');
于是新的部署流程如下:
部署流程
- 平时在开发环境直接使用文件上传的方式更新代码
- 每开发完成一个功能,就提交代码到开发分支(github)
- 提交到github后会触发钩子,CircleCI开始执行测试,然后使用deployer部署代码到各服务器
- 测试没有问题后,提交代码到master分支,会自动部署到各服务器的正式环境
整个部署流程,不需要人参与,就像平常一样,开发完一个功能提交到github而已.
参考: