NGINX: PHP-FPM – “Primary script unknown”

常见原因

出现这个错误的原因一般有两个,一个是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:

  1. Единая точка входа. Нам не нужно помнить и держать в многочисленных конфигурационных файлах разные IP-адреса и порты для каждого экземпляра сервиса — достаточно знать только IP-адрес нашего Nginx-сервера. К тому же, вся информация о местонахождении сервисов (IP и порт) находится в одном месте, что облегчает поддержание актуального состояния всей нашей микросервисной экосистемы.
  2. Балансировка нагрузки. Указывая несколько экземпляров каждого нашего сервиса, мы можем распределить нагрузку, например, по алгоритму round-robin.
  3. 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. Теперь остается только настроить трансфер этих данных.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Мой редактор ОС
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: