docker下Laravel无法找到除index.php外的php文件

出问题的docker-compose.yml文件

version: '2.0'
services:
  php-fpm:
    image: daocloud.io/xss_er/docker-php7:latest
    container_name: strcpy-php
    restart: always
    ports:
      - "9000"

    volumes:
      - ~/strcpy/wwwroot:/var/www/app #work dir
      - /tmp/php7-fpm:/var/run/php7-fpm
    networks:
      - back-net
  nginx:
    image: daocloud.io/xss_er/nginx-1_13_7:latest
    container_name: strcpy-nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      - ~/strcpy/wwwlog:/var/log/nginx #logfile dir
      - /tmp/php7-fpm:/var/run/php7-fpm
      - ~/strcpy/nginx:/etc/nginx/conf.d
    networks:
      - back-net
  mysql:
    # image: mariadb:10.3
    image: daocloud.io/xss_er/mariadb10_3:master
    container_name: strcpy-mariddb10_3
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=123456
    ports:
      - "3306:3306"
    volumes:
      - ~/strcpy/data/mysql:/var/lib/mysql
    networks:
      - back-net
  redis:
    image: daocloud.io/xss_er/docker-redis:master
    container_name: strcpy-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - ~/strcpy/data/redis:/data
    networks:
      - back-net
networks:
  back-net:
    external: true

nginx的default配置文件

server {
    listen       80;
    server_name _;

    index index.html index.htm index.php default.html default.htm default.php;

    #不给反代,直接通用一个目录了
    root /var/www/app;
    #error_page   404   /404.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php7-fpm/php7.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_index index.php;
        include fastcgi_params;
    }

    #静态文件,nginx自己处理
    location ~ ^/(images|javascript|js|css|flash|media|static)/ {
        #过期30天,静态文件不怎么更新,过期可以设大一点,
        #如果频繁更新,则可以设置得小一点。
        expires 30d;
    }
    
    # 看一下Nginx的状态
    location /nginx_status {
        stub_status on;
        access_log   off;
    }

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    #禁止访问 .htxxx 文件
    location ~ /.ht {
        deny all;
    }

    # 禁止访问.开头的文件夹
    location ~ /\. {
        deny all;
    }

}

Laravel 的环境其实只需要把所有的正常请求导入到index.php(server.php)中进行处理就可以了。docker和普通环境都是这样的。

然而今天的坑就在于对于Nginx的文件检测并不是很了解(应该是不了解),以为nginx的请求打到php,php中返回文件是否存在,然后进行解析。相信很多人都是这么以为的,今天遇到的另一个问题,我就知道我公司的号称4年多经验的程序也是这么认为的。

先讲点别的

思路先拉出去(都是Nginx的问题),今天后台发现页面上上传一个2M以上的图片,就会出现上传“413 Request Entity Too Large”,600多KB的还是可以顺利上传的。

几个后台开发的果真是不了解Nginx,硬说是php的问题。从图上就可以看出来,这是Nginx报的错误。最简单的方式就是百度一下,“413 Request Entity Too Large”,为啥会出现这个问题。然后查看服务器里面的nginx.conf 和 conf.d 文件夹里面的所有文件,发现并没有设置客户端最大可以操作的数据包大小。没设置Nginx自然会给你设置一下,默认1M。对比出现的问题,2M以上的不能上传,600KB正常(1M图片没有,就不测了,也是懒),问题应该就在这里了。找个地方填入如下代码:

client_max_body_size 20M;

给点力直接给他来个20M,解决后患。

在Nginx中的配置的位置有两种:

  1. nginx.conf:在http{}中间加入上面代码。这个是全局的,影响到你配置的所有引用这个文件的虚拟机
  2. conf.d 文件下的 conf 文件:在 server{} 中间加入上面的代码。这个是局部的,只影响到添加的相关虚拟机

上面可以知道了,nginx在某些请求时是不会直接落到php的,nginx这边先判断完了,再走php,Nginx都走不通,php就不可能收到请求。

回归正题

并不是所有的请求都直接落到php的。

root /var/www/app;
location / {
   try_files $uri $uri/ /index.php?$query_string;
}

这个代码可以进行分析一下。

在根目录中有一个请求。比如说,https://idea.jinfeijie.cn/abc.php

$uri = abc.php

这时Nginx会去root后面紧跟的目录中查找是否又/var/www/app/abc.php文件存在。不存在,就把abc.php当成目录去查找,看目录/var/www/app/abc.php/ 是否存在,如果还不存在,就要重定向到index.php文件中了,构成/var/www/app/index.php?abc.php ,如果index.php也不存在,就通过Nginx直接返回404。

 

上面时我的代码目录片段,有两个php文件。访问index.php,没有任何的问题,然而访问别的东西,比如:https://idea.jinfeijie.cn/abc.php 或者 https://idea.jinfeijie.cn/phpinfo.php 都会出现下面的情况。

 

这是Laravel的404的报错页面。

从目录上看,所访问的目录只存在index.php 和 phpinfo.php 两个php文件。访问 abc.php 出现404报错页面是正常的,然而在目录中真实存在的文件却同样报了 404 的问题(文件不存在?不存在的),文件明明有的。

反推一下,既然时出现了laravel的404保持页面,肯定是进入了laravel的入口文件了(至于显示出来的内容是什么,完全是入口进入后根据输入输出的反馈),而在nginx.conf 中设置了最终跳转是index.php,所以入口就应该是那个正常显示的index.php了。何时会进入到index呢?两种情况吧,1.正常访问   2.到不到任何文件,Nginx给他重定向到index.php?xxxx

再反推一下,啥?phpinfo.php 文件找不到?明明在啊?这里又可以回到上一部分的总结了。

nginx在某些请求时是不会直接落到php的,nginx这边先判断完了,再走php,Nginx都走不通,php就不可能收到请求。

只看半句就行了,nginx这边先判断完了,再走php 。看个先后顺序,Nginx先,它先处理。nginx先判断文件是否存在,并不是落到php那边,先去解析。如果先落到php中去解析,这个phpinfo.php是完全可以解析出来的。

再看一下nginx的docker-compose.yml文件中的Nginx服务部分。

  nginx:
    image: daocloud.io/xss_er/nginx-1_13_7:latest
    container_name: strcpy-nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      - ~/strcpy/wwwlog:/var/log/nginx #logfile dir
      - /tmp/php7-fpm:/var/run/php7-fpm
      - ~/strcpy/nginx:/etc/nginx/conf.d
    networks:
      - back-net

虽然再root中定义了code代码的路径是/var/www/app,但是构建镜像的时候并没有把代码放到这个目录中,同时,在建立服务的时候也没有把代码目录挂载到业务机的代码目录上去。此时的代码目录一直处于空状态,而我一直认为nginx代理了php-fpm的socket文件,是会同时可以读取php服务上的代码目录的,结果代理的只是个协议而已。nginx每一次的请求都找不到文件,又被try_files 到 index.php$query_string,往后没有其他规则,就会走到 php的解析的规则下,index.php落到了php-fpm,就返回出内容。由于index.php被强制解析了,就出现了只解析index.php的情况。

解决这个问题就需要把nginx的root目录挂载上php的代码目录。

docker与普通模式

docker下和普通模式最大的区别是docker每一个功能模块都被单独拎出来了,需要每一个细节都去把控,做的好就是做成集群也跟配一台服务器一样并且可以快速部署。而普通的lnmp架构,全部在一台服务器上,目录也是共享的,不会存在配了目录文件不存在的问题(挂载的情况)。

正确代码

正确的代码修改是非常简单的,并不需要修改什么东西,删掉原来部署好的服务,在nginx的docker-compose.yml文件上加入一条新的目录挂载指令。

- ~/strcpy/wwwroot:/var/www/app #Code

nginx部分的完整部署代码:

  nginx:
    image: daocloud.io/xss_er/nginx-1_13_7:latest
    container_name: strcpy-nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      - ~/strcpy/wwwlog:/var/log/nginx #logfile dir
      - /tmp/php7-fpm:/var/run/php7-fpm
      - ~/strcpy/nginx:/etc/nginx/conf.d
      - ~/strcpy/wwwroot:/var/www/app #Code
    networks:
      - back-net