标签 Nginx 下的文章

部署过才发现,使用Docker部署PHP网站,没想象中的简单。顺便总结一下使用Docker Compose部署Typecho的经验。

1. 概述

重点注意:

  • 一般PHP网站的Docker镜像,只提供了网站的PHP源码和php-fpm服务,在生产环境不能单独部署。
  • 生产环境需要前端搭配Nginx或Apache等接收请求,再交给php-fpm服务处理。一般还需要后端连上MySQL、MariaDB、PostgreSQL等数据库。
  • 建议使用Docker Compose部署。比Docker run更好管理,比K8S更省资源。

2. 建立网络

建议先建立一个Docker网络,把相关服务部署到该网络。

  • 所有服务使用service名称作为host名称,配置相互访问时不用填IP。
  • 同一网络内的服务,不用在宿主机暴露端口,各个服务之间也相互访问。一般只需暴露Nginx的端口。

建立Docker网络的参考命令如下。其中网络名称为docker-net

docker network create --driver "bridge" docker-net

3. Nginx服务

Nginx的docker-compose.yaml,路径在/opt/docker_deploy/nginx/,内容如下:

services:
  nginx:
    image: nginx:1.27.0-alpine
    restart: always
    ports:
      - 80:80
      - 443:443
    environment:
      - NGINX_PORT=80
    volumes:
      - ./etc/nginx.conf/:/etc/nginx/nginx.conf:ro
      - ./etc/conf.d:/etc/nginx/conf.d
      - /etc/letsencrypt/archive:/etc/nginx/cert:ro
      - ./logs:/var/log/nginx
      - ./www:/var/www
      - /opt/docker_deploy/typecho/app:/var/www/typecho:ro
    networks:
      - docker-net

networks:
  docker-net:
    external: true

Type的Nginx配置文件,在/opt/docker_deploy/nginx/etc/conf.d/typecho.conf,内容如下:

server {
  server_name blog.xxxx.com;
  listen 80;

  rewrite ^(.*)$  https://$host$1 permanent;
}

server {
  server_name blog.xxxx.com;
  listen 443 ssl;
  http2 on;
  index index.php index.htm index.html;
  root /var/www/typecho;

  ssl_certificate /etc/nginx/cert/blog.xxxx.com/fullchain8.pem;
  ssl_certificate_key /etc/nginx/cert/blog.xxxx.com/privkey8.pem;

  add_header X-Frame-Options SAMEORIGIN;
  add_header Content-Security-Policy "frame-ancestors 'self';";

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    include fastcgi_params;
    #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param SCRIPT_FILENAME /app$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_pass typecho:9000;
    fastcgi_buffers 8 16k;
    fastcgi_buffer_size 32k;
  }

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

4. Typecho

部署要点:

  • Typecho的Docker部署说明,可参考:https://github.com/typecho/Dockerfile
  • 由于服务器上还有其它网站或系统,需要Nginx做反向代理,所以Typecho的Docker镜像选择了fpm版本。
  • 需要把PHP源码文件映射出来,提供给Nginx访问。

Typecho的docker-compose.yaml,路径在/opt/docker_deploy/typecho/,内容如下:

ervices:
  typecho:
    image: joyqi/typecho:1.2.1-php8.0-fpm-alpine
    restart: always
    #ports:
    #  - "9000:9000"
    environment:
      - "TYPECHO_SITE_URL=https://blog.xxxx.com"
      - "TYPECHO_DB_ADAPTER=Pdo_Mysql"
      - "TYPECHO_DB_HOST=mariadb"
      - "TYPECHO_DB_PORT=3306"
      - "TYPECHO_DB_USER=typecho"
      - "TYPECHO_DB_PASSWORD=123456"
      - "TYPECHO_DB_DATABASE=typecho"
    volumes:
      - ./app:/app
    networks:
      - docker-net

networks:
  docker-net:
    external: true

要注意,如果Nginx的Typecho没有开启SSL,但需要全局https访问Typecho,可以在配置文件config.inc.php添加以下配置:

/** 全站开启https */
define('__TYPECHO_SECURE__', true);

5. MariaDB

MariaDB的docker-compose.yaml,路径在/opt/docker_deploy/mariadb/,内容如下:

ervices:
  mariadb:
    image: mariadb:11.4.2
    restart: always
    #ports:
    #  - 3306:3306
    volumes:
      - ./dbdata:/var/lib/mysql
    environment:
      MARIADB_ROOT_PASSWORD: root123456
      #MYSQL_USER: ${DB_USER}
      #MYSQL_PASSWORD: ${DB_PASSWORD}
      #MYSQL_DATABASE: ${DB_DATABASE}
    networks:
      - docker-net

networks:
  docker-net:
    external: true

利用Nginx部署了图片服务,为了实现请求图片时进行鉴权,使用了auth_request的配置。

参考官网文档:Authentication Based on Subrequest Result | NGINX Documentation

整理后的示例代码,如下:

server {
    ...... # 略过一些配置
    underscores_in_headers on; # 可选。允许请求header名称带下划线(_)

    location ^~ /images/ {
        # 配置鉴权规则
        auth_request /auth;

        # 生成鉴权URL,并添加请求参数。
        set $auth_request_uri $uri; # 创建变量,传递鉴权URL
        if ($uri ~ ^/images/(.*)$) {
            # 利用if实现对URL进行字符串替换,生成想要的鉴权URL,并带上请求参数
            # 由于静态文件,尤其是图片,一般使用GET方法,所以URL的请求参数可以作为鉴权参数
            set $auth_request_uri /auth/$1?$args;
        }

        # 将401、403状态码映射为404。即鉴权失败时,让浏览器以为文件不存在。
        # 由于“/404.html”不存在,Nginx会返回默认的404错误页面。
        error_page 401 403 =404 /404.html;

        # 设置图片存放目录,/images/有/结尾,这里也要有/结尾
        alias /opt/sites/pub_img/;
    }

    # 这里配置鉴权后端的请求,即鉴权URL
    location = /auth {
        internal; # 只能内部访问
        proxy_pass http://127.0.0.1:8080$auth_request_uri; # 设置完整的鉴权URL
        proxy_pass_request_headers on; # 默认值。转发所有原始的请求header到鉴权URL
        proxy_pass_request_body off; # 不向鉴权URL接发送原始的请求体
        proxy_set_header Content-Length ""; # 不向鉴权URL发送原始的请求体
        proxy_set_header X-Original-URI $request_uri; # 传递原始的请求URL
    }
}

说明和总结

  1. 此方式能轻松实现静态文件进行灵活的鉴权,甚至是无权限服务添加访问权限。

    • Nginx支持的鉴权配置,有4种:
  2. 使用鉴权URL时,基本目前基于请求头和URL参数的鉴权方式都支持,非常灵活。

    • proxy_pass_request_headers默认为on,会把原始请求Header转发到鉴权URL
    • 生成鉴权URL时,会把原始URL参数传过去。
  3. 鉴权URL的处理,最好跟原始URL无关,并把原始URL作为鉴权参数传过去。本示例代码做了URL字符串替换,对后面扩展不够灵活。
  4. 不建议使用Nginx实现复杂的功能。其配置文件不是代码,不是按顺序执行,也不方便调试。

Nginx中配置,去掉CodeIgniter URI中的index.php。参考了人家的文章和自己实践,得出配置如下(Nginx-1.2.0中通过):

location /php/ { # "/php" is the location of CodeIgniter
# Hide index.php in URI
rewrite ^(/php)/(?!index\.php|robots\.txt|images|js|style|fckeditor|upload)(.*)$ $1/index.php/$2 last;
}

把以上配置内容添加到Nginx的配置文件nginx.conf即可。

再看看这段配置,其实就是用URI跳转来实现,而且是用正则表达式匹配和替换。感觉很优美~

今天尝试使用CodeIgniter(一个高效的PHP框架)来做点东西,但是根据官方教程(http://codeigniter.org.cn/user_guide/)去做,也总是显示404页面。于是寻求Goolge帮助。原因很简单,就是Nginx不支持PATH_INFO,地址是一截一截的那种。最后找到的有效解决方法是http://linux008.blog.51cto.com/2837805/546489

主要的Nignx配置如下:

location ~ \.php { # no "$" at the end of "php"
# root f:/httpd/www;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_split_path_info ^(.+\.php)(.*)$; #support PATH_INFO
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
}