寫(xiě)在前面
運(yùn)行一個(gè)php站點(diǎn)最簡(jiǎn)單的辦法是缔俄,直接運(yùn)行一個(gè)nginx+php或apache+php的docker鏡像組弛秋,或者直接開(kāi)一臺(tái)虛機(jī)直接安裝nginx+php。
本文情況特殊俐载,由于想用本地nginx+容器php蟹略,且避免二次反向代理,所以踩了一些坑遏佣,在此記錄一下挖炬。新手建議還是先從簡(jiǎn)單的方案上手,而不是本文的方案状婶。
背景
已有一臺(tái)機(jī)器意敛,本地已經(jīng)運(yùn)行nginx(使用systemd運(yùn)行),現(xiàn)在需要增加服務(wù)一個(gè)php站點(diǎn)膛虫,并有要求將php-fpm在docker中運(yùn)行草姻。所以需要配置docker容器,并配置nginx vhost主機(jī)稍刀。
一些事實(shí)
- 一個(gè)php站點(diǎn)需要由兩部分才能組成:
- web服務(wù)器:用于反向代理撩独、負(fù)載均衡、靜態(tài)資源分發(fā)(如站點(diǎn)中圖片账月、html等除.php文件資源综膀,文件本身就是響應(yīng))
- php-fpm / php-cgi 進(jìn)程:站點(diǎn)中的php文件實(shí)際上是腳本,腳本運(yùn)行后的結(jié)果才是要返回客戶端的響應(yīng)捶障,所以需要web服務(wù)器將請(qǐng)求發(fā)到php僧须,獲得運(yùn)行結(jié)果后,web服務(wù)器再將響應(yīng)返回到客戶端项炼。
- 大部分情況下担平,一個(gè)php站點(diǎn)中示绊,靜態(tài)資源和php腳本通常是混合存放的。所以web服務(wù)器和php-fpm這兩個(gè)程序通常都需要訪問(wèn)同一個(gè)站點(diǎn)資源目錄暂论。請(qǐng)求到達(dá)web服務(wù)器后面褐,web服務(wù)器將判斷客戶實(shí)際請(qǐng)求的是普通的靜態(tài)資源,還是一個(gè)php腳本取胎。如果是后者展哭,則web服務(wù)器將通過(guò)fastcgi協(xié)議調(diào)用php,調(diào)用時(shí)闻蛀,需要指定一系列參數(shù)匪傍,其中最重要的一個(gè)就是
SCRIPT_FILENAME
參數(shù),這個(gè)參數(shù)告訴了php本次要運(yùn)行的是哪一個(gè)腳本觉痛,你需要確保這個(gè)參數(shù)指向的文件可被php進(jìn)程讀取役衡。 - 在本文中,web服務(wù)器使用nginx(本地systemd運(yùn)行)薪棒,php使用docker容器(Bitnami/php-fpm)手蝎。關(guān)于fpm和cgi的區(qū)別可以網(wǎng)絡(luò)搜索,一般使用fpm更通用俐芯。
操作過(guò)程
- 創(chuàng)建存放php站點(diǎn)文件的目錄棵介。
從前提部分可知,需要一個(gè)公共的目錄吧史,以同時(shí)供web服務(wù)器和php服務(wù)訪問(wèn)邮辽。此處,可以指定nginx默認(rèn)的html目錄(如/usr/share/nginx/html
或/var/www/html
等贸营,取決于不同的發(fā)行版)逆巍。
由于此php站點(diǎn)計(jì)劃使用專門(mén)的域名提供服務(wù),所以新建一個(gè)文件夾作為站點(diǎn)文件目錄莽使。
mkdir /usr/share/nginx/php-site/
- 將文件拷貝到站點(diǎn)目錄
此處僅作演示目的,創(chuàng)建一個(gè)php文件和一個(gè)html文件笙僚。
mkdir /usr/share/nginx/php-site/app01
echo '<?php echo "The time is " . date("h:i:sa"); ?>' > /usr/share/nginx/php-site/app01/index.php
echo '<html><body><h1>hello</h1></body></html>' > /usr/share/nginx/php-site/app01/example.html
- 創(chuàng)建php docker容器
docker run -d --name=phpfpm\
-e TZ=Asia/Shanghai \
-p 9000:9000 \
-w /usr/share/nginx/php-site \
-v /usr/share/nginx/php-site:/usr/share/nginx/php-site \
bitnami/php-fpm:latest
-w參數(shù)也可以不加芳肌,這個(gè)參數(shù)在這個(gè)鏡像中只影響你docker exec的時(shí)候默認(rèn)會(huì)進(jìn)到哪個(gè)目錄。
-v掛載的目錄/usr/share/nginx/php-site
必須是第1步中創(chuàng)建的路徑肋层,具體看結(jié)尾解釋
- 創(chuàng)建一個(gè)nginx vhost
/etc/nginx/sites-enabled/m01.example.com.conf
# include /etc/nginx/conf.d/php-fpm.conf; # 此文件已在http塊引入
server {
listen 80;
#listen [::]:80; # 監(jiān)聽(tīng)I(yíng)Pv6 80端口
server_name m01.example.com;
root /usr/share/nginx/php-site;
location / {
try_files $uri $uri/index.php;
}
include /etc/nginx/default.d/php.conf; # 發(fā)行版自帶的的php配置文件亿笤,后文貼出
}
此外,還需要指定一個(gè)php-fpm的upstream栋猖,此處直接修改自帶配置文件净薛,注意此文件已經(jīng)在http塊中引入:
/etc/nginx/conf.d/php-fpm.conf
:
# PHP-FPM FastCGI server
# network or unix domain socket configuration
upstream php-fpm {
# 注釋下面這行,nginx默認(rèn)假設(shè)phpfpm是本地socket連接的蒲拉,不適合本文情況
# server unix:/run/php-fpm/www.sock; #
server 127.0.0.1:9000;
}
更新完成后肃拜,執(zhí)行nginx -t
測(cè)試配置文件痴腌,若無(wú)錯(cuò)誤,執(zhí)行nginx -s reload
燃领。
附:自帶配置文件:/etc/nginx/default.d/php.conf
# pass the PHP scripts to FastCGI server
#
# See conf.d/php-fpm.conf for socket configuration
#
index index.php index.html index.htm;
location ~ \.(php|phar)(/.*)?$ {
fastcgi_split_path_info ^(.+\.(?:php|phar))(/.*)$;
fastcgi_intercept_errors on;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php-fpm;
}
附:自帶配置文件:/etc/nginx/fastcgi_params
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
- 測(cè)試運(yùn)行
使用curl http://m01.example.com/app01/example.html
可以獲得響應(yīng)hello士聪,證明html文件serve正常;
使用curl http://m01.example.com/app01/
可以獲得當(dāng)前時(shí)間響應(yīng)猛蔽,說(shuō)明php文件serve正常剥悟。
錯(cuò)誤排除
- Primary script unknown 錯(cuò)誤
日志內(nèi)容:
[error] 21029#21029: *998 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 1.2.3.4, server: m01.example.com, request: "GET /app01/ HTTP/2.0", upstream: "fastcgi://127.0.0.1:9000"
這一般來(lái)說(shuō)是由于fastcgi調(diào)用php時(shí)傳遞的SCRIPT_FILENAME
參數(shù)存在錯(cuò)誤產(chǎn)生的。搜索資料可知曼库,文件不存在区岗、文件權(quán)限錯(cuò)誤都會(huì)導(dǎo)致產(chǎn)生這個(gè)問(wèn)題,本文中php在容器中為root用戶毁枯,不存在權(quán)限問(wèn)題慈缔,所以100%為文件不存在,或參數(shù)錯(cuò)誤后众。
調(diào)試方法是修改nginx.conf胀糜,將error_log /var/log/nginx/error.log error;
一行最后的error
改為debug
,然后執(zhí)行nginx -sreload
蒂誉,然后觀察/var/log/nginx/error.log
文件中的debug信息教藻。(生產(chǎn)環(huán)境謹(jǐn)慎開(kāi)啟debug,建議使用server塊級(jí)別的error配置)
搜索SCRIPT_FILENAME
找到這行fastcgi調(diào)用參數(shù)的打佑蚁恰:
2024/09/21 17:55:50 [debug] 21024#21024: *908 fastcgi param: "SCRIPT_FILENAME: /usr/share/nginx/php-site/app01/index.php"
然后進(jìn)入到php容器(如docker exec)括堤,確認(rèn)一下這個(gè)文件是否存在。此處文件已經(jīng)通過(guò)docker -v掛載绍移,所以訪問(wèn)正常悄窃。
總結(jié)
很久沒(méi)有用過(guò)php,踩坑的原因是對(duì)web服務(wù)蹂窖、php服務(wù)協(xié)作方式不夠了解轧抗,對(duì)SCRIPT_FILENAME
參數(shù)理解不夠,也不了解fastcgi調(diào)用參數(shù)的debug方法瞬测。
實(shí)際上横媚,SCRIPT_FILENAME
參數(shù)怎么設(shè)置都可以,只要在php容器中存在這個(gè)參數(shù)指向的文件月趟,就能正常運(yùn)行到php腳本灯蝴。
但這個(gè)參數(shù)默認(rèn)情況下都是被設(shè)置為$document_root$fastcgi_script_name
,所以web服務(wù)的document_root需要設(shè)置為php資源的目錄孝宗,且php容器掛載目錄時(shí)需要和document_root路徑一致不能修改穷躁。設(shè)置不對(duì)就會(huì)導(dǎo)致報(bào)錯(cuò)404或者返回一個(gè)File not found.
如果一定想要不一致,你只需要:
- 將
SCRIPT_FILENAME
設(shè)置為/app$fastcgi_script_name
; - 掛載時(shí)指定
-v /usr/share/nginx/php-site:/app
關(guān)鍵詞
php-fpm nginx docker script_filename