常见原因
出现这个错误的原因一般有两个,一个是nginx或php-fpm确实找不到php文件,一个是php-fpm没有权限读取和执行php文件。
1) 排查 nginx 是否访问到正确的文件路径
可以通过 nginx 访问日志查看文件路径是否正确。
打开 nginx 主配置文件(我的是 ),在http代码块中增加:
log_format scripts '$document_root$fastcgi_script_name > $request';
继续打开 nginx server 配置文件(我的是 ),在server代码块增加:
access_log /var/log/nginx/scripts.log scripts;
重启nginx服务( ),再次访问接口,可以在日志文件中看到:
/home/demo/Code/coco/dl/frontend/web/index.php > GET /api HTTP/1.1
再次确认日志文件中的文件路径,确实没错。
2) 排查 php-fpm 是否访问到正确的文件
同样是通过 php-fpm 的访问日志排查。
编辑 php-fpm 配置文件(我的是 ):
access.log = /var/log/php-fpm/$pool.access.log
重启php-fpm( ),再次访问接口,可以在日志文件中看到:
- - 09/Feb/2020:17:19:50 +0800 "GET /index.php" 404
可以看到 nginx 传递给 php-fpm 的文件确实是 , 但 php-fpm 响应的是 404, 说明是 php-fpm 无法(无权限)访问到文件。
如果日志显示 或 则说明 nginx 传递给 php-fpm 的文件名是错误的。
如果一切正常,日志显示应该是 。
Consul Template
Consul Template позволяет нам получать данные с Consul и записывать их в файл с определенным, указанным нами же, шаблоном. При каждой смене данных в Consul файл будет перегенерироваться, к тому же есть возможность выполнить определенную команду при этом. Например, перезагрузка изменения в Nginx, когда Consul Template обновил данные в конфиг-файле.
Для начала попробуем достать данные из Consul и поместить их в обычный файл. Для этого нам нужно создать:
- конфигурационный файл для Consul Template;
- шаблон, по которому будет происходить генерация файла с результатом.
Создаем конфигурационный файл:
consul { address = "localhost:8500" //адреса консула retry { enabled = true attempts = 10 backoff = "250ms" } } template { source = "in.tpl" //входной файл, он же темплейт destination = "out.conf" //выходной файл perms = 0600 command = "nginx -s reload" //команда, которая выполняется после генерации файла error_on_missing_key = false } wait { min = "1s" max = "2s" }
Теперь создаем темплейт с названием “in.tpl” и содержимым:
{{range service "service1" }} server {{index .ServiceMeta "InternalAddress" |replaceAll "http://" "" }} `end`
Запускаем Consul Template и в исходном файле “out.conf” получаем:
server 10.0.0.1:5000 server 10.0.0.2:5000
Следующий шаг — создание шаблонов для генерации конфигурационного файла Nginx:
http { include mime.types; default_type application/octet-stream; `range services -` {{$service := service .Name -}} {{range $service -}} upstream ` `.`Name ` { keepalive 512; {{range $service -}} server {{index .ServiceMeta "InternalAddress" |replaceAll "http://" "" }} max_fails=5 fail_timeout=1m; `end -` } `end` `end` server { listen 80; proxy_next_upstream error timeout invalid_header http_500 http_503; `range services -` {{$service := service .Name -}} `range service `.`Name -` location ~* ^/``.`Name`/(.*) { set $path /$1$is_args$args; proxy_pass http://``.`Name`$path; proxy_pass_request_headers on; } `end` `end` } }
И после запуска можем проверить, какой конфиг для Nginx мы получили:
http { include mime.types; default_type application/octet-stream; upstream service1 { keepalive 512; server 10.0.0.1:5000 max_fails=5 fail_timeout=1m; server 10.0.0.2:5000 max_fails=5 fail_timeout=1m; } upstream service2 { keepalive 512; server 10.0.0.3:5000 max_fails=5 fail_timeout=1m; server 10.0.0.4:5000 max_fails=5 fail_timeout=1m; } upstream service3 { keepalive 512; server 10.0.0.5:5000 max_fails=5 fail_timeout=1m; server 10.0.0.6:5000 max_fails=5 fail_timeout=1m; } server { listen 80; proxy_next_upstream error timeout invalid_header http_500 http_503; server_name localhost; location ~* ^/service1/(.*) { set $path /$1$is_args$args; proxy_pass http://service1$path; proxy_pass_request_headers on; } location ~* ^/service2/(.*) { set $path /$1$is_args$args; proxy_pass http://service2$path; proxy_pass_request_headers on; } location ~* ^/service3/(.*) { set $path /$1$is_args$args; proxy_pass http://service3$path; proxy_pass_request_headers on; } } }
Установка
Для работы Magento требуется установить Composer:
# Перемещение файла
mv composer.phar /usr/bin/composer
# Проверка работы
composer -v
## Загрузка и установка Composer
После этого можно [https://www.magentocommerce.com/download скачать] и разместить Magento в нужной директории:
mv magento2-2.1.0/ /var/www/magento2/
## Размещение системы в /var/www/magento2/
Теперь можно установить систему. Для этого [http://devdocs.magento.com/guides/v2.1/install-gde/bk-install-guide.html существует несколько методов], мы воспользуемся консолью:
bin/magento setup:install –backend-frontname=”adminlogin” # URI для доступа к панели администратора
–db-host=”localhost” # Адрес БД (IP, localhost или URL с портом)
–db-name=”magentodb” # Имя БД
–db-user=”magentouser” # Пользователь БД
–language=”en_US” # Язык панели администратора
–currency=”USD” # Валюта по умолчанию
–timezone=”America/New_York” # Часовой пояс
–use-rewrites=1 # Используется перезапись ссылок веб-сервером
–use-secure=0 # Включение TLS/SSL
–base-url=”http://www.newshop.com” # Адрес сайта
–base-url-secure=”https://www.newshop.com” # HTTPS-адрес сайта
**–admin-user=adminuser ** # Администратор Magento
**–admin-firstname=admin ** # Имя пользователя
**–admin-lastname=user ** # Фамилия пользователя
–cleanup-database
## Установка Magento с дополнительными параметрами, выделенные обязательны
继续排查
以上列出的常见问题全部排查过了,没有发现什么异常,这却难住我了。
回想一下,这套开发环境早就部署好了,而且一直都是正常在使用的,为什么会突然出现这个问题呢。 上一次正常使用到现在突然出现问题这段时间,我干了什么“坏事”吗? 记不清了,如果有的话,那可能就是升级了系统和软件。 莫不是因为php版本或nginx版本升级的问题。 反正暂时也没其他思路了,软件降级试试吧。
先从php入手,因为生产版本使用的是 ,那就再安装个 php 7.2 吧。
yay -S php72 php72-fpm php72-gd php72-intl --removemake --nodiffmenu --noconfirm
不同的操作系统安装方式不尽相同,我这里只是给自己做下记录,请结合自己的系统自行安装。
具体配置就不再赘述了,记得配置好 php72-fpm 后把 nginx server 代码块中的 指向 php72-fpm,然后重启服务。
服务重启后,神奇的事情发生了,问题解决了。
现在可以确定的是问题确实和php版本有关,去看源码找bug是不可能的了,这辈子都不可能。 那就还是只能试着找找本机上两个版本配置上的差异。
使用 查看一下 php-fpm 和 php72-fpm 两个进程的状态:
➜ systemctl status php-fpm.service ● php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled) Active: active (running) since Thu 2020-02-13 12:05:44 CST; 3h 12min ago Main PID: 13022 (php-fpm) Status: "Processes active: 0, idle: 2, Requests: 95, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 4915) Memory: 36.3M CGroup: /system.slice/php-fpm.service ├─13022 php-fpm: master process (/etc/php/php-fpm.conf) ├─13041 php-fpm: pool www └─13042 php-fpm: pool www 2月 13 12:05:43 Macy systemd: Starting The PHP FastCGI Process Manager... 2月 13 12:05:44 Macy php-fpm: fpm is running, pid 13022 2月 13 12:05:44 Macy php-fpm: ready to handle connections 2月 13 12:05:44 Macy php-fpm: systemd monitor interval set to 10000ms 2月 13 12:05:44 Macy systemd: Started The PHP FastCGI Process Manager.
➜ systemctl status php72-fpm.service ● php72-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/php72-fpm.service; disabled; vendor preset: disabled) Active: active (running) since Thu 2020-02-13 12:05:43 CST; 3h 13min ago Main PID: 13024 (php-fpm72) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 4915) Memory: 13.9M CGroup: /system.slice/php72-fpm.service ├─13024 php-fpm: master process (/etc/php72/php-fpm.conf) ├─13027 php-fpm: pool www └─13028 php-fpm: pool www 2月 13 12:05:43 Macy systemd: Starting The PHP FastCGI Process Manager... 2月 13 12:05:43 Macy php-fpm: fpm is running, pid 13024 2月 13 12:05:43 Macy php-fpm: ready to handle connections 2月 13 12:05:43 Macy php-fpm: systemd monitor interval set to 10000ms 2月 13 12:05:43 Macy systemd: Started The PHP FastCGI Process Manager.
进程状态都是正常的,但是我好像又发现了两个可以对比的配置文件,。 和 。 虽然没抱多大希望,但也没其他思路,就当随便看看吧。 然而,猜猜我发现了什么,此处必须加“握操”。 php 7.4.2 的 systemd service 配置文件中多出了这么几个配置项,注释也是清晰明了:
PrivateTmp=true ProtectHome=true ProtectSystem=full PrivateDevices=true ProtectKernelModules=true ProtectKernelTunables=true ProtectControlGroups=true RestrictRealtime=true RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX RestrictNamespaces=true
对我来说最值得注意的是 ,因为我的代码是放在了 目录下。 真相就要浮出水面了,抓紧改成 false,重启一下 php-fpm 试试。“握草”,好了。
我也不知道说什么好了,只是给大家提供一个思路。 如果你碰到了和我一样的情况,基本情况都排查完了,问题还是没解决,可以再查看一下 配置里是不是把代码所在目录设为保护了。 这个真的是没曾想到过的问题,也是因为对 的不熟悉吧,只是知道使用 。
当然,这个原因总结下来,还是因为 php-fpm 进程找不到文件,而找不到文件的原因并不是文件不存在,而是文件被服务管理器 保护起来了。
再强行结个尾,不要轻言放弃。
Конфигурация Nginx
Для начала нужно создать файл конфигурации для платформы /etc/nginx/sites-available/magento.conf и заполнить его. Блок server будет иметь вид:
listen 80;
**server_name newshop.com www.newshop.com;**
root /var/www/magento2;
}
## Укажите свой домен
Первый блок location:
index index.html index.php;
**try_files $uri $uri/ @handler;**
**expires max;**
}
location ^~ /app/ { deny all; }
location ^~ /includes/ { deny all; }
location ^~ /lib/ { deny all; }
location ^~ /media/downloadable/ { deny all; }
location ^~ /pkginfo/ { deny all; }
location ^~ /report/config.xml { deny all; }
location ^~ /var/ { deny all; }
## Передает URI, если путь не существует, указывает срок жизни кэша, запрещает доступ к внутренним директориям
Далее нужно указать доступ к директории export:
auth_basic “Restricted”;
**auth_basic_user_file htpasswd;**
autoindex on;
}
## Разрешает доступ для пользователя, указанного в файле /etc/nginx/htpassword
Дополнительные блоки location:
return 404;
}
location @handler {
rewrite / /index.php;
}
location ~ .php/ {
rewrite ^(.*.php)/ $1 last;
}
## Закрывает доступ к скрытым файлам, передает скрипты
Параметры php-fpm:
try_files $uri $uri/ /index.php last;
expires off;
fastcgi_pass unix:/tmp/php5-fpm.sock;
fastcgi_param HTTPS $fastcgi_https;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_param MAGE_RUN_CODE default;
fastcgi_param MAGE_RUN_TYPE store;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 4k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
}
## Указывает стандартные параметры fastcgi и дополнительные буферы, чтобы избежать распространенных ошибок
Осталось активировать хост:
systemctl restart nginx
## Создает ссылку на файл конфигурации и перезагружает сервер
Nginx
Какие проблемы мы хотим решить с помощью Nginx:
- Единая точка входа. Нам не нужно помнить и держать в многочисленных конфигурационных файлах разные IP-адреса и порты для каждого экземпляра сервиса — достаточно знать только IP-адрес нашего Nginx-сервера. К тому же, вся информация о местонахождении сервисов (IP и порт) находится в одном месте, что облегчает поддержание актуального состояния всей нашей микросервисной экосистемы.
- Балансировка нагрузки. Указывая несколько экземпляров каждого нашего сервиса, мы можем распределить нагрузку, например, по алгоритму round-robin.
- Failover. Мы можем перестать использовать экземпляры сервисов, которые в данный момент работают некорректно, — timeout или статус-код ответа нас не устраивают.
Допустим, у нас есть сервис, трафик на который мы хотим направлять через Nginx. Для примера мы опустим большинство параметров настроек самого Nginx и сконцентрируемся на части, касающейся проксирования запросов. В таком случае наш конфиг будет иметь вид:
http { include mime.types; default_type application/octet-stream; upstream service1 { keepalive 512; // для каждого экземпляра сервиса мы указываем адрес, // разрешенное количество неудачных запросов и время, // в течение которого этот экземпляр сервиса не будет принимать запросы //после достижения максимального количества неудачных запросов server 10.0.0.1:5000 max_fails=5 fail_timeout=1m; server 10.0.0.2:5000 max_fails=5 fail_timeout=1m; } server { listen 80; //перечень условий, по которым запрос будет передан к следующему экземпляру сервиса в рамках upstream proxy_next_upstream error timeout invalid_header http_500 http_503; server_name localhost; location ~* ^/service1/(.*) { set $path /$1$is_args$args; proxy_pass http://service1$path; proxy_pass_request_headers on; } } }
Пока все довольно компактно, но что будет, когда мы начнем масштабировать систему, вводя все новые и новые сервисы? Давайте посмотрим на наш новый конфиг:
http { include mime.types; default_type application/octet-stream; upstream service1 { keepalive 512; server 10.0.0.1:5000 max_fails=5 fail_timeout=1m; server 10.0.0.2:5000 max_fails=5 fail_timeout=1m; } upstream service2 { keepalive 512; server 10.0.0.3:5000 max_fails=5 fail_timeout=1m; server 10.0.0.4:5000 max_fails=5 fail_timeout=1m; } upstream service3 { keepalive 512; server 10.0.0.5:5000 max_fails=5 fail_timeout=1m; server 10.0.0.6:5000 max_fails=5 fail_timeout=1m; } ... ... ... upstream service100 { keepalive 512; server 10.0.0.199:5000 max_fails=5 fail_timeout=1m; server 10.0.0.200:5000 max_fails=5 fail_timeout=1m; } server { listen 80; proxy_next_upstream error timeout invalid_header http_500 http_503; server_name localhost; location ~* ^/service1/(.*) { set $path /$1$is_args$args; proxy_pass http://service1$path; proxy_pass_request_headers on; } location ~* ^/service2/(.*) { set $path /$1$is_args$args; proxy_pass http://service2$path; proxy_pass_request_headers on; } location ~* ^/service3/(.*) { set $path /$1$is_args$args; proxy_pass http://service3$path; proxy_pass_request_headers on; } ... ... ... location ~* ^/service100/(.*) { set $path /$1$is_args$args; proxy_pass http://service100$path; proxy_pass_request_headers on; } } }
Когда у нас десятки сервисов, то вместо простого и лаконичного конфигурационного файла мы получаем условно бесконечный набор похожих между собой конструкций. Теперь этот конфиг будет крайне трудно как читать, так и модифицировать.
Можно попробовать разделить один большой конфиг на набор мелких, но это не решает главную проблему масштабирования системы, ведь все изменения мы должны будем делать вручную и далеко не самым удобным способом.
Единственный логичный выход из ситуации — автоматизация процесса внесения изменений в конфиг Nginx. Но для этого нужно где-то хранить данные обо всех наших сервисах.
Consul
Consul — это веб-сервис, который решает нашу проблему сохранения данных обо всех сервисах. Он автоматически поддерживает данные о сервисах в актуальном состоянии и предоставляет удобный доступ к этим данным через API.
Для production-среды поднимают кластер Consul, который имеет несколько серверных нод и множество агентов.
Как это работает:
- каждый сервис при старте отправляет информацию на consul agent;
- consul agent передает далее данные серверным нодам Consul;
- происходит синхронизация между всеми серверными нодами.
Это позволяет при обращении к любой из серверных нод получать информацию обо всех сервисах данного кластера. Агенты мы поднимаем на всех серверах, где только есть микросервисы.
Схематично процесс выглядит так:
Какая же информация о сервисах нам нужна? Вот ответ от Consul на запрос по получению информации о service1:
{ "ID": "service1-5000", "Service": "service1", "Tags": , "Meta": { "Cluster": "prod1", "ExternalAddress": "http://10.0.0.1:5000", "InternalAddress": "http://10.0.0.1:5000", "Version": "69" }, "Port": 5000, "Address": "http://10.0.0.1", "Weights": { "Passing": 0, "Warning": 0 }, "EnableTagOverride": true, "ContentHash": "7be933df34d7e251" }
Есть адрес сервиса в Address и название в Service — это уже тот минимум, который позволит нам сгенерировать правильный конфиг для Nginx. Теперь остается только настроить трансфер этих данных.