Deployer Databases Recipe 实战指南:一条命令完成 MySQL / MariaDB / PostgreSQL 数据库供给
【免费下载链接】deployerThe PHP deployment tool with support for popular frameworks out of the box项目地址: https://gitcode.com/gh_mirrors/de/deployer
导读
recipe/provision/databases.php是 Deployer 提供服务器供给(provision)能力的核心配方之一,它负责在全新的 Ubuntu 服务器上自动安装数据库服务、创建数据库账号、授权并建库。阅读本文后,你将掌握如何通过provision:databases任务一键部署 MySQL、MariaDB 或 PostgreSQL,理解db_type、db_name、db_user、db_password四个配置项的取值与底层交互逻辑,并能在自己的部署脚本中安全、正确地组合使用这套数据库供给任务。
一、Databases Recipe 是什么
Databases Recipe 是 Deployer 供给体系(Provision)中的数据库子模块,对应仓库文件 recipe/provision/databases.php。它不是一个独立可部署的应用配方,而是被上层 recipe/provision.php 通过require __DIR__ . '/provision/databases.php';引入,作为provision大任务链条中的一环(见 recipe/provision.php)。
它的职责非常聚焦:在目标服务器上完成数据库层面的初始化,包括:
- 安装数据库服务端软件(MySQL / MariaDB / PostgreSQL 三选一);
- 创建可远程登录的数据库用户并授权;
- 按指定字符集与排序规则创建应用数据库。
整套数据库任务都依赖 Deployer 的 SSH 执行能力,属于“服务器供给”而非“应用部署”范畴,通常只在服务器首次初始化时运行一次。
二、引入方式
与 Deployer 中所有配方一致,Databases Recipe 通过require引入。在部署脚本(deploy.php)顶部添加:
require 'recipe/provision/databases.php';如果引入了完整供给配方,则无需单独引入:
require 'recipe/provision.php';因为 recipe/provision.php 已经替你引入了databases.php,同时还会引入nodejs.php、php.php、user.php、website.php等兄弟模块。
注意:仓库中对应的说明文档 docs/recipe/provision/databases.md 为自动生成产物,头部明确标注
DO NOT EDIT THIS FILE!,源码唯一权威入口是 recipe/provision/databases.php,文档由bin/docgen生成。
三、配置项详解(Configuration)
Databases Recipe 定义了 4 个配置项,全部采用**惰性求值(闭包)**方式定义,即只有在任务执行中真正访问它们时才会触发交互式提问。这与 Deployer 中set()+get()的配置体系完全一致(参见 src/functions.php 中set/get的实现:set将值与闭包存入配置容器,get在访问时才求值闭包)。
3.1db_type—— 选择数据库类型(自动生成)
set('db_type', function () { $supportedDbTypes = [ 'none', 'mysql', 'mariadb', 'postgresql', ]; return askChoice(' What DB to install? ', $supportedDbTypes, 0); });- 取值:
none、mysql、mariadb、postgresql,对应源码 recipe/provision/databases.php。 - 交互方式:通过
askChoice()弹出选择列表,默认选中索引0,即none(不安装数据库)。 - 特别说明:该配置项是“autogenerated on access”,意味着它不会预先写入配置,只有在访问时才触发提问。选择
none时,provision:databases任务会直接跳过数据库安装(见下文任务实现)。 - 推荐做法:在部署脚本中通过
set('db_type', 'mysql')预先固定,避免每次执行都进入交互式选择,也便于 CI 环境使用--option或配置注入。
3.2db_name—— 数据库名称
return ask(' DB name: ', 'prod');- 默认值:
prod。 - 交互方式:
ask()普通文本提问,可输入任意数据库名。 - 用途:该名称会用于
CREATE DATABASE IF NOT EXISTS(MySQL/MariaDB)或CREATE DATABASE(PostgreSQL)建库操作。
3.3db_user—— 数据库用户
return ask(' DB user: ', 'deployer');- 默认值:
deployer。 - 用途:用于创建数据库账号并授予权限。Deployer 默认使用与部署用户一致的命名,便于后续应用连接。
3.4db_password—— 数据库密码(隐藏输入)
return askHiddenResponse(' DB password: ');- 交互方式:
askHiddenResponse()以隐藏输入方式提问,不显示回显,且关闭了隐藏回退(setHiddenFallback(false)),确保密码不会出现在终端上(见 src/functions.php)。 - 安全机制:该密码在任务内部通过
secrets机制(%db_password%占位符)传递,在run()输出中会被脱敏,不会泄露到终端日志。
这三个配置项同样支持在部署脚本中预先
set()固定,例如:
host('prod') ->set('db_type', 'mysql') ->set('db_name', 'myapp') ->set('db_user', 'myapp_user') ->set('db_password', 'S3cret!Pass');固定后,provision任务链中的provision:configure会检测到这些参数已显式配置(通过hasOwn判断),从而跳过交互式收集,并直接打印已生成的配置代码块(见 recipe/provision.php)。
四、任务详解(Tasks)
Databases Recipe 共定义 4 个任务,其中 1 个调度任务 + 3 个具体数据库供给任务。
4.1provision:databases—— 供给调度任务
desc('Provision databases'); task('provision:databases', function () { set('remote_user', get('provision_user')); $dbType = get('db_type'); if ($dbType === 'none') { return; } invoke('provision:' . $dbType); }) ->limit(1);- 核心逻辑:先设置
remote_user为provision_user(默认root,见 recipe/provision.php),随后读取db_type:若为none直接返回;否则通过invoke('provision:' . $dbType)动态调用对应子任务(见 recipe/provision/databases.php)。 invoke()机制:invoke是 Deployer 提供的内置函数,用于在任务内部调用另一个任务(见 src/functions.php),它会启动/结束日志记录并执行目标任务。这里通过字符串拼接实现“任务分发”,是典型的策略模式用法。->limit(1):限定该任务在多主机并行场景下只在一台主机上执行,避免在集群环境中重复安装数据库。
4.2provision:mysql—— 安装并初始化 MySQL
desc('Provision MySQL'); task('provision:mysql', function () { run('apt-get install -y mysql-server', env: ['DEBIAN_FRONTEND' => 'noninteractive'], timeout: 900); run("mysql --user=\"root\" -e \"CREATE USER IF NOT EXISTS '{{db_user}}'@'0.0.0.0' IDENTIFIED BY '%db_password%';\"", secrets: ['db_password' => get('db_password')]); run("mysql --user=\"root\" -e \"CREATE USER IF NOT EXISTS '{{db_user}}'@'%' IDENTIFIED BY '%db_password%';\"", secrets: ['db_password' => get('db_password')]); run("mysql --user=\"root\" -e \"GRANT ALL PRIVILEGES ON *.* TO '{{db_user}}'@'0.0.0.0' WITH GRANT OPTION;\""); run("mysql --user=\"root\" -e \"GRANT ALL PRIVILEGES ON *.* TO '{{db_user}}'@'%' WITH GRANT OPTION;\""); run("mysql --user=\"root\" -e \"FLUSH PRIVILEGES;\""); run("mysql --user=\"root\" -e \"CREATE DATABASE IF NOT EXISTS {{db_name}} character set UTF8mb4 collate utf8mb4_bin;\""); });执行步骤如下(见 recipe/provision/databases.php):
- 安装:
apt-get install -y mysql-server,通过env: ['DEBIAN_FRONTEND' => 'noninteractive']禁止 apt 交互式提问,timeout: 900将命令超时放宽到 900 秒(默认超时为default_timeout,即 300 秒,见 src/functions.php),确保大体积安装包能在慢速网络上完成。 - 创建用户:同时创建
'{{db_user}}'@'0.0.0.0'与'{{db_user}}'@'%'两个主机匹配项,分别覆盖通过 IPv4 任意地址和任意主机名连接的场景。 - 授予权限:
GRANT ALL PRIVILEGES ON *.* ... WITH GRANT OPTION赋予全部库表的全权限并允许继续授权。 - 刷新权限:
FLUSH PRIVILEGES。 - 建库:
CREATE DATABASE IF NOT EXISTS {{db_name}} character set UTF8mb4 collate utf8mb4_bin,字符集使用UTF8mb4、排序规则使用utf8mb4_bin,兼容 Emoji 与二进制排序语义。
安全细节:密码通过secrets: ['db_password' => get('db_password')]传入,命令中的%db_password%占位符在run()执行时被脱敏替换(run()的secrets参数签名见 src/functions.php),确保密码不会明文出现在进程参数与日志中。
4.3provision:mariadb—— 安装并初始化 MariaDB
desc('Provision MariaDB'); task('provision:mariadb', function () { run('apt-get install -y mariadb-server', env: ['DEBIAN_FRONTEND' => 'noninteractive'], timeout: 900); // ...与 MySQL 相同的建用户、授权、建库 SQL });逻辑与provision:mysql完全一致(见 recipe/provision/databases.php),唯一区别是安装包改为mariadb-server,其余CREATE USER、GRANT、FLUSH PRIVILEGES、CREATE DATABASE语句与 MySQL 分支相同——因为 MariaDB 使用mysql命令行客户端,SQL 方言兼容。
4.4provision:postgresql—— 安装并初始化 PostgreSQL
desc('Provision PostgreSQL'); task('provision:postgresql', function () { run('apt-get install -y postgresql postgresql-contrib', env: ['DEBIAN_FRONTEND' => 'noninteractive'], timeout: 900); run("sudo -u postgres psql <<< $'CREATE DATABASE {{db_name}};'"); run("sudo -u postgres psql <<< $'CREATE USER {{db_user}} WITH ENCRYPTED PASSWORD \'%db_password%\';'", secrets: ['db_password' => get('db_password')]); run("sudo -u postgres psql <<< $'GRANT ALL PRIVILEGES ON DATABASE {{db_name}} TO {{db_user}};'"); });PostgreSQL 分支实现差异明显(见 recipe/provision/databases.php):
- 安装:安装
postgresql与postgresql-contrib(后者提供常用扩展与工具),同样使用noninteractive环境变量与 900 秒超时。 - 建库:通过
sudo -u postgres psql切换到 postgres 超级用户执行CREATE DATABASE {{db_name}}。注意 PostgreSQL 没有IF NOT EXISTS语法(此处为直接创建),重复执行会报错。 - 建用户:
CREATE USER {{db_user}} WITH ENCRYPTED PASSWORD '...',使用加密密码存储,密码经secrets机制脱敏传递。 - 授权:
GRANT ALL PRIVILEGES ON DATABASE {{db_name}} TO {{db_user}},将目标库的全部权限授予应用用户。
与 MySQL/MariaDB 的差异:PostgreSQL 使用
psql工具、以postgres系统用户身份执行 SQL、权限作用域是单个数据库而非*.*。同时其run()调用以sudo开头,会触发 src/functions.php 中内置的 sudo 密码处理逻辑:若当前用户无免密 sudo,Deployer 会通过sudo_askpass机制交互式收集 sudo 密码。
五、与 Provision 主任务链的集成
Databases Recipe 是整个provision供给流程的组成部分。在 recipe/provision.php 中,provision是一个包含 15 个子任务的组任务:
task('provision', [ 'provision:check', 'provision:configure', 'provision:update', 'provision:upgrade', 'provision:install', 'provision:ssh', 'provision:firewall', 'provision:user', 'provision:php', 'provision:node', 'provision:databases', // ← 数据库供给在此位置 'provision:composer', 'provision:server', 'provision:website', 'provision:verify', ]);provision:databases被编排在 PHP、Node 供给之后、Composer 与 Web 站点配置之前,这符合依赖顺序:先有数据库,才能为站点与应用提供数据存储。你可以单独运行:
dep provision:databases也可以随完整供给流程运行:
dep provision运行dep provision时,provision:configure(见 recipe/provision.php)会提前收集db_type,并仅在db_type !== 'none'时继续收集db_user、db_name、db_password,随后把收集到的参数以host(...)->set(...)代码块的形式打印出来,方便固化到部署脚本中。
运行示例
以默认交互方式单独执行数据库供给:
dep provision:databases终端将依次出现如下交互(默认值用括号标注):
What DB to install? [0] none: [1] mysql [2] mariadb [3] postgresql > 1 DB name: (prod) myapp DB user: (deployer) myapp_user DB password: ********全部回答后,Deployer 通过 SSH 以root(provision_user)身份在服务器上完成安装、建用户、授权与建库。注意:由于数据库密码默认来自交互输入,在 CI/CD 等非交互环境中务必提前通过set('db_password', ...)固定,或在host()定义中注入。
六、源码级要点总结
| 要点 | 实现位置 | 说明 |
|---|---|---|
| 配置项惰性求值 | recipe/provision/databases.php | set()+ 闭包,访问时才触发askChoice/ask/askHiddenResponse |
| 交互式选择 | src/functions.php | askChoice基于 SymfonyChoiceQuestion,none为默认索引 0 |
| 隐藏密码输入 | src/functions.php | askHiddenResponse设置setHidden(true)且禁止回退明文 |
| 密码脱敏 | src/functions.php | run()的secrets参数,%db_password%占位符不落入日志 |
| 任务分发 | src/functions.php | invoke('provision:' . $dbType)动态调用子任务 |
| 单机限制 | recipe/provision/databases.php | ->limit(1)避免多主机重复建库 |
| 安装超时 | src/functions.php | timeout: 900覆盖默认 300 秒 |
| Ubuntu 支持 | recipe/provision.php | provision:check仅校验 Ubuntu 20+,其余系统会告警 |
七、实践建议与注意事项
- 首次供给务必交互式运行:
db_password默认无默认值,非交互环境(-q)下askHiddenResponse会返回空字符串,导致创建出空密码用户,务必显式配置。 db_type固定为none可跳过:若服务器已有数据库(如云厂商托管 RDS),将db_type设为none,provision:databases会立即返回,不干扰既有环境。- MySQL/MariaDB 是幂等的:
CREATE USER IF NOT EXISTS、GRANT、CREATE DATABASE IF NOT EXISTS均可重复执行;PostgreSQL 分支非幂等(CREATE DATABASE/CREATE USER无IF NOT EXISTS),重复执行会报错,需注意脚本重跑场景。 - 远程访问授权:MySQL/MariaDB 分支创建了
@'0.0.0.0'和@'%'两个主机项,允许数据库用户从任意地址连接;若仅需本机访问,可自行覆写任务收紧 host 匹配。 - 安全基线:供给完成后建议在防火墙(
provision:firewall已放行 22/80/443,见 recipe/provision.php)基础上,根据实际需要决定是否对外暴露数据库端口;本配方默认不修改 MySQL/MariaDB 的监听配置。
相关资源
- 配方源码:recipe/provision/databases.php
- 自动生成文档:docs/recipe/provision/databases.md
- 上层供给配方:recipe/provision.php 与 docs/recipe/provision.md
- 交互函数实现:src/functions.php(
ask/askChoice/askHiddenResponse/run/invoke/set/get) - 兄弟供给模块:recipe/provision/nodejs.php、recipe/provision/php.php、recipe/provision/user.php、recipe/provision/website.php
【免费下载链接】deployerThe PHP deployment tool with support for popular frameworks out of the box项目地址: https://gitcode.com/gh_mirrors/de/deployer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考