Symfony 4环境变量部署咨询:Nginx+PHP-FPM多环境配置建议
我来帮你理清这个场景下的最佳实践——毕竟从传统的parameters.yml转到Symfony 4+的环境变量配置,确实需要把Web请求、控制台命令这两个不同运行上下文的变量传递逻辑搞清楚,还要解决同服务器多应用的隔离问题,而且你还没用到Docker,得靠原生服务器配置来实现。
核心原则先明确
Symfony 4+的环境变量读取逻辑是优先拿服务器注入的环境变量,其次才会读项目里的.env文件,所以生产环境我们可以完全弃用.env,全靠服务器来注入变量,这样既安全,又能实现你要的“单点修改”需求。
1. Web请求(Nginx+PHP-FPM):优先用PHP-FPM池配置变量
官方文档提了Nginx里设变量,但我更推荐用PHP-FPM的池配置——为啥?因为Nginx的变量得通过fastcgi_param传递给PHP,万一漏写一个就麻烦了;而FPM池里定义的环境变量是直接注入到PHP进程中的,不会有传递遗漏的问题,而且和后面控制台的配置逻辑能统一起来。
具体操作步骤
针对你的两个应用(prod/qualif),分别创建独立的PHP-FPM池配置文件:
- Ubuntu 16.04:在
/etc/php/7.x/fpm/pool.d/下新建myapp-prod.conf和myapp-qualif.conf - FreeBSD:在
/usr/local/etc/php-fpm.d/下新建对应文件
每个池的配置里,用env[变量名]来定义Symfony需要的环境变量:
# myapp-prod.conf示例 [myapp-prod] user = www-data group = www-data listen = /run/php/php7.x-fpm-myapp-prod.sock listen.owner = www-data listen.group = www-data # 注入Symfony核心变量,按需添加其他变量(比如REDIS_URL、MAILER_DSN) env[APP_ENV] = prod env[APP_SECRET] = 你的生产环境密钥 env[DATABASE_URL] = mysql://用户名:密码@localhost/生产库名 # 其他FPM进程配置按需调整(比如进程数、超时时间) pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3
然后修改Nginx配置,让每个应用的站点指向对应的FPM sock:
# 生产环境站点配置示例 server { server_name prod.myapp.com; root /var/www/myapp/prod/public; location / { try_files $uri /index.php$is_args$args; } location ~ ^/index\.php(/|$) { fastcgi_pass unix:/run/php/php7.x-fpm-myapp-prod.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; internal; } } # 预生产环境的Nginx配置类似,只需要把root路径换成/var/www/myapp/qualif/public,fastcgi_pass换成对应的qualif sock即可
这样Web请求就能稳稳获取到FPM池里定义的环境变量,而且两个应用的变量完全隔离,互不干扰。
2. 控制台命令:用应用专属启动脚本加载变量
控制台命令是直接在系统shell里运行的,不会自动继承PHP-FPM池的环境变量,所以我们得给每个应用做一个专属的启动脚本,用来加载对应的环境变量,这样运行bin/console时就能自动拿到正确的变量了。
简单实现:直接写启动脚本
在每个应用的根目录(比如/var/www/myapp/prod/)创建console.sh脚本:
#!/bin/bash # 加载当前应用的环境变量 export APP_ENV=prod export APP_SECRET=你的生产环境密钥 export DATABASE_URL=mysql://用户名:密码@localhost/生产库名 # 其他需要的变量都在这里加 # 执行传入的console命令 php bin/console "$@"
给脚本加执行权限:
chmod +x /var/www/myapp/prod/console.sh
以后运行控制台命令就用这个脚本代替直接敲bin/console:
# 比如清缓存 /var/www/myapp/prod/console.sh cache:clear
进阶:用单独的环境变量文件统一管理
如果变量很多,把变量都写在脚本里太乱,可以把变量存在一个单独的文件里(比如/var/www/myapp/prod/.env.prod),注意这个文件要设置权限为600,只允许root和应用用户读取,防止泄露:
APP_ENV=prod APP_SECRET=你的生产环境密钥 DATABASE_URL=mysql://用户名:密码@localhost/生产库名 # 其他变量...
然后修改console.sh来加载这个文件:
#!/bin/bash # 加载环境变量文件(set -a会自动导出所有读取到的变量) set -a source /var/www/myapp/prod/.env.prod set +a # 执行console命令 php bin/console "$@"
这样所有变量都存在一个文件里,实现了你要的单点修改——不管是Web还是控制台,修改这个文件后,Web端重启PHP-FPM生效,控制台直接用脚本运行就会读取新变量。
3. 多应用隔离的关键细节
- PHP-FPM池完全隔离:每个应用用独立的FPM池,不仅环境变量分开,进程资源也能单独配置(比如生产环境给更多进程数,预生产少一些)
- 文件权限严格控制:每个应用的代码目录权限设置为对应FPM用户所有,防止跨应用读取文件
- 环境变量文件隔离:每个应用的
.env.prod/.env.qualif单独存放,权限设为600,确保只有授权用户能访问
4. 为啥不要用系统全局环境变量?
如果在/etc/profile或者/etc/environment里设置全局变量,同一服务器上的所有应用都会读到相同的变量,根本没法区分prod和qualif的不同值,所以绝对不要这么做。
验证配置是否生效
可以写个简单的Symfony命令来打印环境变量,验证是否正确:
// src/Command/PrintEnvCommand.php namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class PrintEnvCommand extends Command { protected static $defaultName = 'app:print-env'; protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('APP_ENV: ' . getenv('APP_ENV')); $output->writeln('DATABASE_URL: ' . getenv('DATABASE_URL')); return Command::SUCCESS; } }
然后用控制台脚本运行app:print-env,再写个控制器打印getenv('APP_ENV'),验证Web和控制台的变量是否一致且正确。
内容的提问来源于stack exchange,提问作者Ben

