Deployer 自动化部署教程

老牛浏览 896评论 0发表于
46ef069d-fc58-4dbc-a34f-16c124dc6424

1. 前言

开发完项目,免不了要部署上线。纯手动操作,登录、拉代码、改配置、清缓存,各种服务重启一条龙下来,人生宝贵的时间就又花费了不少。而且手动操作十分容易出错,遗漏部分步骤都有可能产生一些邪门问题。倘若有多台服务器,上述问题还会加剧,呜呼哀哉!常言道,人生苦短,生活中比代码有意思的事情多了去了,所以我很早就开始寻求一种能轻松部署 Laravel 项目的办法。

Laravel 的官方文档里介绍了 Envoy,能满足大部分场景,但仍然有一些限制。直到后来看到了 Deployer,大有相见恨晚之感。

2. 介绍

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

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

主要优势

  • 真正解放双手,一条命令完成部署;

  • 在部署过程中,项目仍然能够正常访问。部署成功后,才切换到新的版本;

  • 可以很方便地进行回滚;

  • 丰富的任务钩子和预置任务,可灵活组合完成各种任务,比如安装前端依赖和编译等;

  • 其它黑科技等你发掘……

我画了一张图来说明它的操作原理:

f26e7c15-ea35-4902-8619-a6a8ec7563cb

绘图工具:https://www.draw.io/

简单介绍一下,Deployer 安装在本地,它通过 SSH 协议登录到服务器 Web Server 上执行一系列我们预定的操作,其中包含从代码库 Git Server 拉取我们的项目代码部署到 Web 服务器指定的目录完成部署。

一共分为以下几个部分:

  • 在本地安装 Deployer;

  • 为服务器设置 SSH 免密登录;

  • 项目 Git 仓库允许服务器访问(Clone 代码);

  • 部署我们的 Web 项目。

我们分开一个个讲,在后面的每一步请注意区分当前逻辑所在的环境:本地/目标服务器。

3. 安装

3.1 本地安装 Deployer

此部分在本地操作。

安装 Deployer,在项目目录下运行如下代码即可:

php
composer require --dev deployer/deployer

安装完成后应该可以使用以下命令来查看它的版本信息:

php
dep --version
# Deployer 7.1.1

如果提示 dep 命令不存在的话,将如下 alias 添加到 .bashrc.zshrc 文件:

php
alias dep='vendor/bin/dep'

然后切一个新的终端窗口,应该就有 dep 命令了。

Deployer 的安装就到这里,接下来我们去目标服务器去折腾一下。

3.2 服务器配置

服务器使用的系统是 Ubuntu Server 22.04 LTS,LNMP 环境使用 LNMP 安装脚本 root 用户搭建。Ubuntu 默认以 ubunntu 用户登录,使用 sudo su 即可切换到 root 用户。

虽然说是无侵入的部署工具,但是还是需要我们来做一些微小的配置。

首先配置 SSH 免密登录,也就是使用密钥方式登录,方便我们从本地登录到服务器。

先查看本地是否有密钥:

php
ls ~/.ssh

如果有 id_rsa(私钥)和 id_rsa.pub(公钥),则无需再次生成密钥,否则手动生成密钥对:

php
ssh-keygen -C "ilaoniu's MacbookPro"

接着将公钥添加到服务器。由于我们使用 root 用户登录服务器,因此需要将 id_rsa.pub 公钥内容(用 cat ~/.ssh/id_rsa.pub 获取)添加到目标服务器 ~/.ssh/authorized_keys 文件(~ 为用户对应的家目录。如果是 ubuntu 用户,则需要添加到 /home/ubuntu/.ssh/authorized_keys 文件)。

3.3 项目仓库配置

此部分在目标服务器上操作。

Deployer 的运行机制是从 Git 或者其它你指定的代码库 Clone 代码到目标服务器,所以如果你的代码不是公开的仓库,我们通常需要添加 SSH 公钥才可以从代码库 Clone 代码,所以接着来创建公钥。

先切换当前登录用户到 deployer

php
su - deployer

然后创建 SSH 密钥:

php
ssh-keygen -t rsa -b 4096 -C "deployer" 
# 这里的 -C 是指定备注
# 一路回车下去即可

然后我们将生成的公钥拷贝出来:

php
cat ~/.ssh/id_rsa.pub # 显示公钥

请完整的复制 cat 出来的结果,然后去你的代码库添加 SSH 公钥(或者是项目的部署公钥,也就是只读权限,不能写入)。

OK,现在你的服务器就可以从代码库 Clone 代码了,你可以在服务器上使用 Git 克隆一下你的代码库测试,如果不成功,请检查你的公钥是否正确完全的复制与粘贴正确(有些软件如 Putty 会自动添加换行符),不正确的话再次复制粘贴即可。

php
alias sudowww='sudo -H -u www sh -c'

3.4 免密码登录服务器

此部分在本地(或者开发机)操作。

在本地(或者开发机)执行部署任务时我们不想每次输入密码,所以我们需要设置 SSH 免密码登录。

在本机生成 SSH 密钥,然后拷贝公钥:

php
ssh-keygen -t rsa -b 4096 -f  ~/.ssh/deployer

然后将公钥保存到目标服务器(注意,这一步还是在本机操作):

php
ssh-copy-id -i  ~/.ssh/deployer.pub deployer@123.45.67.89 # 请填写服务器域名或 IP
# 应该会让你输入 deployer 在服务器上的登录密码,输入后回车即可

也可以直接将 deployerkey.pub 公钥添加到服务器的 /home/deployer/.ssh/authorized_keys 中,结果是一样的。

然后你应该就可以直接以 deployer 用户免密码登录到服务器了,测试方式:

php
ssh deployer@123.45.67.89 -i ~/.ssh/deployerkey
# 应该就能直接进到服务器上了,然后 exit 退出

OK,这一步搞定了 deployer 免密码登录,接下来我们聊项目的部署。

3.5 部署项目

这些都在本地操作哦~

假设我们的项目在本地 /www/demo-project 下,那么我们切换到该目录:

php
cd /www/demo-project

然后执行 Deployer 的初始化命令:

php
dep init
2fe1d822-4484-498d-ab22-6e141547b992

它会让你选择项目类型,比如 Laravel,Symfony 等,如果你都不是,选择 Common 类型即可。我们选择 Laravel。

这一步操作将会在当前目录生成一个 deploy.php 文件,这个文件就是部署清单,也就是告诉 Deployer 怎样去部署你的项目。初始配置如下:

php
<?php
namespace Deployer;

require 'recipe/laravel.php';

// Project name
// 项目名称
set('application', 'my_project');

// Project repository
// 项目仓库地址
set('repository', 'git@github.com:ilaoniu/demo-project.git');

// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', true); 

// Shared files/dirs between deploys 
// 部署时各版本之间共享的文件和目录
add('shared_files', []);
add('shared_dirs', []);

// Writable dirs by web server
// 可写入的目录
add('writable_dirs', []);

// Hosts
// 目标主机配置,最重要的
host('project.com')
    ->set('deploy_path', '~/{{application}}');    

// Tasks
// 自定义任务示例
task('build', function () {
    run('cd {{release_path}} && build');
});

// [Optional] if deploy fails automatically unlock.
// 部署失败后执行,自动解除部署锁定状态,以免影响下次执行
after('deploy:failed', 'deploy:unlock');

// Migrate database before symlink new release.
// 执行数据库迁移,建议删掉,迁移虽好,但毕竟高风险,只推荐用于开发环境。
before('deploy:symlink', 'artisan:migrate');

需要关心的几个配置是:

php
// 指定你的代码所在的服务器 SSH 地址,请不要使用 HTTPS 方式哦。
set('repository', 'git@github.com:ilaoniu/demo-project.git');

// 这里填写目标服务器的 IP 或者域名
host('your_server_ip') 
    ->user('deployer') // 这里填写 deployer 
      // 并指定公钥的位置
    ->identityFile('~/.ssh/deployerkey')
    // 指定项目部署到服务器上的哪个目录
    ->set('deploy_path', '/home/wwwroot/demo-app'); 

最后修改后的配置如下:

php
<?php

namespace Deployer;

require 'recipe/laravel.php';

// Project name
set('application', 'xxx');

// Project repository
set('repository', 'git@github.com:ilaoniu/demo-project.git');

// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', true);

// Shared files/dirs between deploys
add('shared_files', []);
add('shared_dirs', []);

// Writable dirs by web server
add('writable_dirs', []);

// 生产环境主机
host('172.16.1.1')
    ->stage('prod')
    ->user('deployer')
    ->identityFile('~/.ssh/deployerkey')
    ->set('branch', 'master') // 最新的主分支部署到生产环境主机
    ->set('deploy_path', '/home/wwwroot/demo-project');

// 测试环境主机
host('172.16.3.2')
    ->stage('debug')
    ->user('deployer')
    ->identityFile('~/.ssh/deployerkey')
    ->set('branch', 'dev') // 一般是把 dev 分支弄到测试环境测试,没问题再合并
    ->set('deploy_path', '/home/wwwroot/demo-project');

// 自定义任务:重置 opcache 缓存
task('opcache_reset', function () {
    run('{{bin/php}} -r \'opcache_reset();\'');
});

// 自定义任务:重启 php-fpm 服务
task('php-fpm:restart', function () {
    run('systemctl restart php-fpm.service');
});

// 自定义任务:supervisor reload
task('supervisor:reload', function () {
    run('sudo supervisorctl reload');
});

// 自定义任务:部署成功了用 bearychat 发消息给大佬和自己
task('send_message', function () {
    run('{{bin/php}} {{release_path}}/artisan deployed');
});

// 自定义任务:缓存路由,recipe/laravel.php 默认的流程里没有这个,所以加上,息看需要
after('artisan:config:cache', 'artisan:route:cache');

// 执行自定义任务,注意时间点是 current 已经成功链向新部署的目录之后
after('deploy:symlink', 'php-fpm:restart');
after('deploy:symlink', 'supervisor:reload');

// 部署成功后重置 opcache 缓存
after('deploy:symlink', 'opcache_reset');

// 部署成功后调用 laravel 命令行发送通知
after('success', 'send_message');

// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');

正确填写完配置清单以后,我们就可以部署我们的项目了,确认你的代码已经提交到代码仓库,因为执行部署的时候并不是将当前代码部署到服务器,而是从代码库拉最新的版本。

然后在当前目录执行:

php
dep deploy debug // 部署到测试环境
dep deploy prod // 部署到生产环境

就可以看到整个部署过程了,一般正常会是像下面这样子:

php
dep deploy production
Deployer's output
✈︎ Deploying master on your_server_ip
✔ Executing task deploy:prepare
✔ Executing task deploy:lock
✔ Executing task deploy:release
➤ Executing task deploy:update_code
✔ Ok
✔ Executing task deploy:shared
✔ Executing task deploy:vendors
✔ Executing task deploy:writable
✔ Executing task artisan:storage:link
✔ Executing task artisan:view:clear
✔ Executing task artisan:cache:clear
✔ Executing task artisan:config:cache
✔ Executing task artisan:optimize
✔ Executing task deploy:symlink
✔ Executing task deploy:unlock
✔ Executing task cleanup
Successfully deployed!

可以在命令后 -vvv 命令执行的显示详细信息。

如果失败的话就需要检查一下哪一步出错了,通常根据报错信息即可定位。

首次部署后还需要设置 .env,并生成应用密钥。然后配置 Nginx 站点。修改 .env 后记得重新缓存配置。

4. 目录结构

Deployer 部署完成后,在服务器上的结构会是这样子:

php
drwxr-sr-x 5 deployer www-data 4096 Jun 14 09:53 ./
drwxr-sr-x 6 deployer www-data 4096 Jun 11 14:25 /assets
drwxr-sr-x 2 deployer www-data 4096 Jun 14 09:53 .dep/
lrwxrwxrwx 1 deployer www-data   10 Jun 14 09:52 current -> releases/7/
drwxr-sr-x 4 deployer www-data 4096 Jun 14 09:53 releases/
drwxr-sr-x 3 deployer www-data 4096 Jun 10 14:16 shared/

其中,.dep 为 Deployer 的一些版本信息,不用去研究,我们需要关心的是下面这几个:

  • current - 它是指向一个具体的版本的软链接,你的 Nginx 配置中 root 应该指向它,比如 Laravel 项目的话 root 就指向:/home/wwwroot/demo-app/current/public

  • releases - 部署的历史版本文件夹,里面可能有很多个最近部署的版本,可以根据你的配置来设置保留多少个版本,建议 5 个。保留版本可以让我们在上线出问题时使用 dep rollback 快速回滚项目到上一个版本。

  • shared - 共享文件夹,它的作用就是存储我们项目中版本间共享的文件,比如 Laravel 项目的 .env 文件,storage 目录,或者你项目的上传文件夹,它会以软链接的形式链接到当前版本中。

OK,基本上这样子就完成了整体 Deployer 需要考虑的地方以及使用细节了,相信大部分同学的问题都出在权限问题上。所以上面在创建用户时,一定要仔细操作。

5. 总结

Deployer 确实非常好用,一条命令完成部署,回滚等操作,但是它目前还不是很完美,大家有问题可以去 GitHub 官方仓库提 issue 或者搜索相关问题解决方案。

个人用它已经两年了,非常喜欢这样简单的部署方式,但是新手刚用的时候难免在服务器权限这块碰壁不少,我总结了以下几个建议:

  • 尽量使用系统提供的包管理工具来安装软件,比如 Nginx,PHP 等,毕竟它是人家通过 N 年的实践总结出来的合理使用方式,包括配置文件的写法等都是科学的方式,另外一点就是当我们遇到问题的时候搜索到的结果也比较通用,当然你已经是系统高手了,那就不要看这条了。

  • 不要让你的项目结构设计的太复杂,简单统一为原则,这样你不必多次重复去折腾这些东西。

  • 最好关掉服务器 SSH 的密码登录,相关操作请自行 Google。

  • 多看文档,很多你遇到的问题其实都是你没仔细看使用文档造成的结果。

6. 注意事项

如果使用 Horizon,并使用 Supervisor 来监控进程,需要在 Supervisor 配置文件中将运行 Horizon 的用户指定为 deployer(我们使用 deployer 用户连接服务器部署代码并重启 Horizon 进程,权限要一致),不然会重启失败。

7. 参考资料

又一篇 Deployer 的使用攻略
Deployer 实战经验分享

点赞
收藏
暂无评论,快来发表评论吧~
私信
老牛@ilaoniu
老牛,俗称哞哞。单纯的九零后理工小青年。喜欢折腾,爱玩,爱音乐,爱游戏,爱电影,爱旅游...
最后活跃于