Deployer 使用攻略(真好,PHP相关项目都可以,例如 ThinkPHP)

   使用 Deployer 有很多年了,网上也有很多的使用教程,不过发现比较难找到一个完整且能跑通的文章,今天写这的这篇教程是一个小白也能完整走通教程,当然了,难免也有失误,欢迎同学在使用过程中反馈遇到的问题!

环境

系统:
➜  /etc cat /etc/redhat-release                         
Rocky Linux release 9.3 (Blue Onyx)# CentOS 替换方案,

# Rocky Linux:Rocky Linux: 一个开源、社区拥有和管理、免费的企业Linux发行版,提供强大的生产级平台。可作为CentOS停止维护(改为滚动更新的Stream版)后,RHEL的下游Linux操作系统替代方案,并继承了原CentOS的开源免费特点。 
#2020128日,CentOS发布公告称CentOS Linux系列将不再更新维护,CentOS 7生命周期将于2024630日终止。2020128日,CentOS发布公告称CentOS Linux系列将不再更新维护,CentOS 8生命周期将于20211231日终止。

PHP 版本:
➜  /etc php -v
PHP 8.3.2 (cli) (built: Jan 25 2024 14:58:00) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies

Nginx 版本:
➜  /etc nginx -v 
nginx version: nginx/1.24.0

MySQL 版本:
➜  /etc mysql -V
mysql  Ver 8.3.0 for Linux on x86_64 (MySQL Community Server - GPL)

Deployer 介绍

   首先,我们先了解一下 Deployer 是什么,Deployer 是一个使用 PHP 开发的基于 SSH 协议的无侵入具有模块化、代码回滚、并行任务的轻量级 web 项目部署工具,它不需要在你目标服务器上装服务之类的东西即可使用,它只需要在你的开发机发起部署动作的一方安装即可。它的原理就是通过 SSH 登录到你的目标机器去创建目录,移动文件,执行指定的动作、然后执行脚本中定义好的 Shell 命令、来完成项目的部署。同时 Deployer 内置了一批常见项目、框架的部署脚本,例如 Laravel、WordPress 等等,当我们需要部署相关项目时,只需要调整一些参数就可以直接使用。

一共分为以下几个部分:

一、Deployer 安装(在本地或开发机操作)

Deployer 安装有多种方法:

第一种 通过 Phar 存档,只需运行一下命令即可:
curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep

第二种 通过 composer 安装:
(1)项目级安装:
composer require deployer/deployer --dev
使用:
php vendor/bin/dep
(2)全局安装(推荐)
composer global require deployer/deployer -vvv

第三种 通过 Github 源代码安装:

clone 最新的代码:
git clone https://github.com/deployphp/deployer.git
在源代码目录下运行:
php ./build
第三种方法会建立 deployer.phar 的 Phar 归档,建议使用第一种方法安装或第二种方法

二、服务器端配置(在目标服务器操作)

   无侵入的部署工具,但还是需要做一些基础配置,出于安全考虑,我们不会开发 root 用户的 SSH 登录,而是使用其它用户,比如 Ubuntu 默认的 ubuntu 用户。
这里 Deployer 是用来部署 web 应用的,我们专门创建一个 deployer 用户来执行工作。

# 新建用户deployer
➜  /etc sudo adduser deployer
➜  /etc sudo passwd deployer
Changing password for user deployer.
New password:
BAD PASSWORD: The password is shorter than 8 characters
Retype new password:
passwd: all authentication tokens updated successfully.

   web 项目一般需要一些上传文件、缓存写入等操作,所以 deployer 还需要有权限对目录进行修改,比如 Laravel 的 storage 目录需要可写权限,这里以 nginx 默认的用户组 nginx 举例,如果你修改过用户或者组名请对应修改下面的命令里的 nginx 用户组:

sudo usermod -aG nginx deployer
-a表示追加(append),G表示组(group),所以 -aG表示将用户追加到指定的组。如果不使用 -a选项,用户将从所有其他组中删除,只保留在新指定的组。

   我们通常需要将 deployer 用户权限分别设置为创建文件 644 与目录 755,这样一来,deployer 用户可以读写,但是组与其它用户只能读:

/etc su deployer
[deployer@dyp etc]$ echo "umask 022" >> ~/.bashrc
[deployer@dyp etc]$ exit
exit
➜  /etc 

说明:
这条命令在Linux或UNIX系统中执行了以下操作:
echo: 这是一个常用的命令,用于在终端中输出文本或变量的值。
"umask 022": 这是要输出的文本内容。umask是一个特殊的权限设置,它决定了新创建的文件的默认权限。022是一个八进制数,对应的权限是:
0: 特殊权限(例如setuid、setgid、sticky bit)
2: 用户可写(用户有w权限)
2: 组可写(组有w权限)
0: 其他可写(其他用户没有w权限)这意味着新创建的文件的默认权限是644(rw-r--r--),而新创建的目录的默认权限是755(rwxr-xr-x)。
>>: 这是一个重定向操作符,它将命令的输出附加到指定的文件中,而不是覆盖文件的内容。如果文件不存在,它会被创建。
~/.bashrc: 这是一个隐藏文件,位于用户的家目录中。.bashrc文件是bash shell的配置文件之一,它在每次启动新的bash实例时执行其中的命令。通过将umask的命令添加到.bashrc文件中,你可以确保每次启动新的bash实例时都会使用这个umask设置。
总的来说,这条命令将umask设置为022,并将这个设置添加到用户的.bashrc文件中,以确保新创建的文件和目录有适当的默认权限。

我们需要将 depoloyer 用户加到 sudoers 中:

vim /etc/sudoers
# 在最后加入
deployer ALL=(ALL) NOPASSWD: ALL
# 保存并退出(x!)

说明:
deployer ALL=(ALL) NOPASSWD: ALL 是一个用于Linux系统的sudoers文件中的条目,它定义了deployer用户可以以其他用户的身份执行命令,并且不需要输入密码。让我们逐一解释这个条目的各个部分:

deployer: 这是用户名,即有权使用sudo命令的用户。
ALL=(ALL): 这定义了deployer用户可以以哪些用户身份执行命令。在这里,ALL表示deployer可以以系统上的任何用户的身份执行命令。第一个ALL表示目标用户,第二个ALL表示源用户。
NOPASSWD:: 这表示在执行sudo命令时不需要输入密码。
ALL: 这表示deployer用户可以执行的命令没有限制。
因此,整个条目的意思是:deployer用户可以以系统上的任何用户的身份执行任何命令,而不需要输入密码。

这个条目通常出现在sudoers文件中,该文件定义了哪些用户和组可以执行哪些命令,以及这些命令是否需要密码。编辑sudoers文件需要谨慎,因为不正确的配置可能会导致安全问题。如果你需要修改sudoers文件,建议使用visudo命令,这是一个专门用于编辑sudoers文件的文本编辑器,它可以帮助检查语法错误。

   接下来要对我们的 web 根目录授权,假设我们的 web 服务的根目录在 /var/www/ 下,那么需要将这个目录的用户设置为 deployer ,组设置为 nginx 用户 nginx:

sudo chown deployer:nginx /var/www/ziyou-space # 最后这里不要加斜线哦

说明:
这条命令 sudo chown deployer:nginx /var/www/ziyou-space 在Linux系统中执行了以下操作:

sudo: 允许普通用户以超级用户(通常是root)的权限执行命令。
chown: 用于改变文件或目录的所有者和所属组。
deployer:nginx: 指定新的所有者和所属组。这里,deployer 是用户名,表示该文件或目录将属于这个用户。nginx 是组名,表示该文件或目录将属于这个组。
/var/www/ziyou-space: 是要改变所有者和所属组的文件或目录的路径。
综上所述,这条命令的意思是:“以超级用户的权限,将 /var/www/ziyou-space 的所有者更改为 deployer,并将其所属组更改为 nginx。”这通常用于配置文件或目录的所有权,以便特定的用户或服务可以访问或修改它。

   OK,Deployer 的用户操作到此结束,还需要检查以下配置:
确认 php 的可执行文件在全局 PATH 中,或者手动添加到 deployer 用户目录的 .bash_profile PATH 中也可,使用命令确认(登录用户 deployer 后执行):php -v,如果报错的话,一般建议是将 php 的 bin 文件软链接到 /usr/local/bin/(推荐) 或者 /usr/bin/ 下。
同样检查你的 Deployer 任务清单所需要用到的其它命令,比如 npm,nginx,composer 都在 deployer 用户下可以使用,否则在部署的时候会出错。

/etc su deployer
[deployer@dyp etc]$ php -v
PHP 8.3.2 (cli) (built: Jan 25 2024 14:58:00) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies
[deployer@dyp etc]$ nginx -v
nginx version: nginx/1.24.0
[deployer@dyp etc]$ mysql -V
mysql  Ver 8.3.0 for Linux on x86_64 (MySQL Community Server - GPL)
[deployer@dyp etc]$ 

三、项目 git 仓库允许服务器访问(在目标服务器操作)

   我们 deployer 的运行机制是从 git 或者其它你指定的代码库 clone 代码到目标服务器,所以如果你的代码不是公开的仓库,我们通常需要添加 SSH 公钥才可以从代码库 clone 代码,所以接着来创建公钥:
先切换当前登录用户到 deployer

su - deployer
sudo git config --global user.name 'ThinkCsly'
git config --global user.email 'thinkcsly@163.com'

$ ssh-keygen -t rsa -b 4096 -C "thinkcsly@163.com" 
# 这里的 -C 是指定备注
# 一路回车下去即可

说明:
这条命令是在Linux或类Unix系统中使用ssh-keygen工具来生成一个新的SSH密钥对。下面是对这条命令的详细解释:

ssh-keygen: 这是用于生成、管理和转换认证密钥的工具。SSH密钥对通常用于无密码登录到远程服务器。
-t rsa: 这指定了要生成的密钥类型为RSARSA是最常见的SSH密钥类型。
-b 4096: 这指定了密钥的长度为4096位。较长的密钥长度提供了更好的安全性,但也会增加加密和解密操作的计算成本。
-C "thinkcsly@163.com": 这指定了一个注释或描述,通常是用户的名字或与其相关的其他信息。在这里,注释是"thinkcsly@163.com"。这个描述不是用于加密目的,而是为了帮助用户记住或识别密钥。
执行这条命令后,系统会要求你为私钥设置一个密码短语(passphrase)。私钥用于解密公钥,因此为了安全起见,最好为私钥设置一个密码短语。

执行完这条命令后,会在当前目录下生成两个文件:id_rsa(私钥)和id_rsa.pub(公钥)。私钥应该妥善保管,而公钥可以安全地分享给任何你想要无密码登录到的远程服务器。

[deployer@dyp etc]$ cat ~/.ssh/id_rsa.pub # 显示公钥

然后我们将生成的公钥完整拷贝出来配置到代码仓库,我这里是码云(https://gitee.com):

可以自己验证一下是否成功:

ssh -T git@gitee.com

注意:查看OpenSSH版本:

[deployer@dyp www]$ ssh -V
OpenSSH_8.7p1, OpenSSL 3.0.7 1 Nov 2022
我这里OpenSSH 版本是8.7;由于从OpenSSH 8.2 版本开始,客户端默认禁用了ssh-rsa,OpenSSH 8.2 发布,禁用 ssh-rsa 算法。
为了解决这个问题,我们可以在本地开启ssh-rsa的支持:
(1)手动在 .ssh/config 文件里加上如下内容:
Host gitee.com
        PubKeyAcceptedKeyTypes=+ssh-rsa
(2)或者使用其他加密算法, eg.ed25519
ssh-keygen -t ed25519 -C "thinkcsly@163.com"

如果 OpenSSH 是8.2+,不开启ssh-rsa的支持的话会报下面错误:
[deployer@dyp www]$ sudo git clone git@gitee.com:ThinkCsly/ziyou-space.git
Cloning into 'ziyou-space'...
git@gitee.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

clone 代码测试:

Are you sure you want to continue connecting (yes/no/[fingerprint])?
说明:
在尝试建立连接或访问某个系统时。它询问用户是否确定要继续进行连接,并提供了三种响应选项:

yes:表示用户确定要继续连接。
no:表示用户不想继续连接。
[fingerprint]:这可能意味着用户可以通过提供某种指纹信息(可能是数字指纹或其他身份验证方法)来确认他们想要继续连接。不过,这里的[fingerprint]更像是一个占位符,实际使用时可能会被具体的指纹信息或操作所替代。
在实际应用中,这样的提示通常用于确保用户明确知道他们正在尝试连接的系统或设备,并作为一种安全措施来防止未经授权的访问。如果用户不确定连接的合法性或安全性,他们应该选择no或寻求更多的信息,而不是盲目地继续连接。

此处,我们选择yes即可。

   OK, 现在你的服务器就可以从代码库 clone 代码了,你可以在服务器上 git clone 一下你的代码库测试,如果不成功,请检查你的公钥是否正确完全的复制与粘贴正确,不正确的话再次重复复制粘贴即可,检查OpenSSH 版本问题。

四、服务器免密码登录 deployer(在本地或开发机操作)

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

方式一:在本机生成 deployer 专用密钥,然后拷贝公钥:

ssh-keygen -t rsa -b 4096 -f  ~/.ssh/deployerkey

这个命令是用来生成一个新的SSH密钥对。SSH密钥对通常用于安全地访问远程服务器,而不需要每次都输入密码。这里详细解释一下命令的各个部分:
ssh-keygen: 这是用于生成、管理和转换认证密钥的工具。
-t rsa: 这指定了要生成的密钥类型为RSARSA是最常见的SSH密钥类型。
-b 4096: 这指定了密钥的长度为4096位。较长的密钥长度提供了更好的安全性,但也会增加加密和解密操作的计算成本。
-f ~/.ssh/deployerkey: 这指定了私钥的保存路径和名称。在这个例子中,私钥将被保存在用户主目录下的.ssh文件夹中,并命名为deployerkey。公钥的默认名称是deployerkey.pub,并且它也会被保存在同一个目录中。
执行这个命令后,系统会要求你为新生成的密钥对设置一个密码短语(passphrase)。这个密码短语用于保护私钥,防止未授权的访问。当你使用私钥进行SSH连接时,系统会要求你输入这个密码短语。

生成的公钥可以用于配置远程服务器的SSH访问,而私钥则保存在本地,用于建立安全的SSH连接。

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

/SoftWare ssh-copy-id -i  ~/.ssh/deployerkey.pub deployer@47.95.xx.x  # 请填写服务器 IP
# 应该会让你确认连接,然后再让你输入 deployer 在服务器上的登录密码,输入后回车即可

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

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

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

方式二:在本机生成 deployer 专用密钥,然后拷贝公钥:

ssh-keygen -t rsa -b 4096 -f ~/.ssh/aliyun-47.95 -C "thinkcsly@163.com"
说明:其中,-t rsa表示选择RSA算法,-b 4096表示设置密钥长度为4096位,-C后面的参数是注释信息,-f 表示生成的文件路径(通常位于~/.ssh/目录下)与文件名称,可根据自己的喜好修改。

配置 config:

vim ~/.ssh/config

说明:
Host <主机名>   # 将<主机名>替换为目标服务器的IP地址或域名(根据情况命名)
    HostName <主机名>   # 同样替换成目标服务器的IP地址或域名
    User <用户名>       # 替换成目标服务器的用户名
    IdentityFile ~/.ssh/id_rsa     # 指定私钥文件路径,默认情况下为~/.ssh/id_rsa

将公钥复制到目标服务器的authorized_keys文件中:

$ cat ~/.ssh/aliyun-47.95.pub | ssh deployer@47.95.xx.xx 'cat >> ~/.ssh/authorized_keys'

这里的user是目标服务器的用户名,host是目标服务器的IP地址或域名。

目标服务deployer用户的authorized_keys在添加公钥前后对比:

现在就可以直接使用ssh <主机名>命令来进行无需输入密码的SSH远程连接了:

/SoftWare ssh aliyun_4795

Welcome to Alibaba Cloud Elastic Compute Service !

Last login: Sat Jan 27 14:07:47 2024
[deployer@dyp ~]$ exit
logout
Connection to 47.95.xxx.x closed./SoftWare 

五、Deployer 的使用(在本地操作)

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

cd /var/www/ziyou-space
然后执行 Deployer 的初始化命令:

免密登录(一)

   在项目根目录下会生成 deploy.php 文件,这个文件就是部署清单,说明 Deployer 怎样去部署你的项目,这部分不需要过多的介绍,大家参考 Deployer 官网的详细说明操作即可。
需要关心的几个配置是:

 // 指定你的代码所在的服务器 SSH 地址,请不要使用 https 方式哦。
set('repository', 'git@gitee.com:ThinkCsly/ziyou-space.git');
// 部署的分支
set('branch', 'master');
// 目标服务器保留的版本数
set('keep_releases', 3);
// 目标服务器的 IP 或者域名
host('your_server_ip') 
    ->user('deployer') // 这里填写 deployer 
      // 并指定公钥的位置
    ->identityFile('~/.ssh/aliyun-47.95')
    // 指定项目部署到服务器上的目录
    ->set('deploy_path', '/var/www/zi-space'); 
    desc('Upload .env file');
task('env:upload', function() {
    // 将本地的 .env.staging 文件上传到代码目录的 .env
    upload('.env.staging', '/var/www/ziyou-space/shared/.env');
});
// 定义一个后置钩子,在 deploy:shared 之后执行 env:upload 任务
after('deploy:shared', 'env:upload');

免密登录(二)

在项目根目录下会生成 deploy.php 文件,配置如下:

<?php
namespace Deployer;

// 引入官方的 Laravel 部署脚本
require 'recipe/laravel.php';

// 设置一个名为 application 的环境变量,值为 ziyou-space
set('application', 'ziyou-space');
// Config
// 设置一个名为 repository,值为初始化脚本时输入的 github 仓库地址,初始为空,就手动添加即可,指定你的代码所在的服务器 SSH 地址,请不要使用 https 方式
set('repository', 'git@gitee.com:ThinkCsly/ziyou-space.git');
set('branch', 'master');
set('keep_releases', 3);
// shared_files / shared_dirs 这两个环境变量是数组格式,add 函数可以往数组里添加值
add('shared_files', []);
add('shared_dirs', []);
// Deployer 会把这个变量下的目录加上写权限
add('writable_dirs', []);

// 这里填写目标服务器的 IP 或者域名(这里填写 .ssh/config 配置的 Host 名称即可),可以同时部署到多个机器上,配置多个host即可。
host('aliyun_4795')
    ->set('deploy_path', '/var/www/ziyou-space');// 指定部署目
// Hosts

after('deploy:failed', 'deploy:unlock');
before('deploy:symlink', 'artisan:migrate');


// 定义一个上传 .env 文件的任务
desc('Upload .env file');
task('env:upload', function() {
    // 将本地的 .env 文件上传到代码目录的 .env
    upload('.env.staging', '{{release_path}}/.env');
});

// 定义一个后置钩子,在 deploy:shared 之后执行 env:upload 任务
after('deploy:shared', 'env:upload');

服务器上删除刚才 git 测试 clone 的代码:

[deployer@dyp www]$ cd /var/www/
[deployer@dyp www]$ rm -rf ziyou-space

在本地部署:

到服务器查看部署的代码:


我门上传的.env.staging 文件也 OK。

    .dep 为 Deployer 的一些版本信息,不用去研究:

current - 它是指向一个具体的版本的软链接,nginx 配置中 root 指向它,比如 zi-space 项目的, root 就指向:/var/www/zi-space/current/public
releases - 部署的历史版本文件夹,里面保留最近部署的版本,版本数在deplo.php中的keep_releases设置,保留多少个版本根据自己情况设置,建议 5 个。保留版本可以让我们在上线出问题时使用 dep rollback 快速回滚项目到上一个版本。
shared - 共享文件夹,它的作用就是存储我们项目中版本间共享的文件,比如 Laravel 项目的 .env 文件,storage 目录,比如我们上传的.env.staging文件,它会以软链接的形式链接到当前版本中。
到此为止基本完成了 Deployer部署; 过程中需要注意使用细节了,大部分的问题都出在权限问题上。在创建用户时,一定要仔细操作。

结论

   个人用它已经五多年了, Deployer 非常好用,轻量方便,一条命令完成部署,回滚等操作,如果有碰到问题,可以去 GitHub 官方仓库提 issue 或者搜索相关问题解决方案。多看文档,很多时候遇到的问题其实都是没仔细看使用文档造成的结果,同时欢迎大家随时交流。

部分参考文章:博客:又一篇 Deployer 的使用攻略

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 3周前 自动加精
ThinkQ
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4
JaguarJack

:joy: 蛮好的。但可以试试 github action,也比较方便,直接扔上去,按照依赖,像 Laravel 前端现在还需要 build,直接交给他了

3周前 评论
CodingHePing 3周前
sanders

有没有那种类似 Laravel migration 的那种迭代部署方案?因为目前在用 k8s 每次都自己写部署文档,逐项确认升级脚本执行结果,还得分容器启动前和启动后,操作繁复容易出差错。我就想如果能有一种识别当前部署进度且有回退策略的部署方案就好了。

3周前 评论

几年前使用过deployer,确实好用。 后来容器化火热之后,觉得还是docker镜像部署最方便,jenkins 编写脚本打包镜像部署。

2天前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!