ubuntu+django+uwsgi+nginx前后端分离部署完整版

1.部署之前确定你已经将你的项目上传到你的服务器当中。在我之前的文章已经提到,本文默认项目已经运行成功,并且django项目中允许所有地址进行访问。下面也有我之前的部署,其实按照下面做到虚拟环境能够运行django就可以继续向下读了。

2. 将uwsgi安装到你的virtualenv中,初步设定项目名字为induapp,项目存放在/home/ubuntu/induapp

pip install uwsgi

基础测试(这也是uwsgi文档直接就有的)

创建一个test.py文件

# test.py
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"] # python3

运行uwsgi:

uwsgi --http :8000 --wsgi-file test.py

如果云主机能够直接外网访问的话

在浏览器访问 http://公网IP:8000即可,会提供一个“hello world”消息

如果云主机禁止外网访问

curl 127.0.0.1:8000

也能看到效果

这样意味下面的组建已经正常,不需要重复验证

web client <-> uWSGI <-> python

3.uwsgi测试运行django项目

uwsgi --http :8000 --module induapp.wsgi

module induapp.wsgi:加载指定的wsgi模块

跟上面相同的做法来验证是否能够访问到django项目,这样意味下面的组建已经正常,不需要重复验证

web client<->uWSGI<->Django

4.基本的nginx

安装nginx

sudo apt-get install nginx
sudo /etc/init.d/nginx start

同上我们能够在浏览器访问80端口或者curl访问80端口

如果会报错的话,这里建议两个比较快的命令

vim /var/log/nginx/error.log

或者

sudo nginx -t

如果正常运行,意味着

web client<->the web server

其实默认的80端口很容易会被占用,并且你想要在哪里使用nginx,那么你将必须重新配置nginx来提供另一个端口的服务,所以我们选择8080来当作后台的端口,8001来当作前台的端口。

4.为站点配置nginx

首先我们需要uwsgi_params文件,可用在uwsgi发行版本的nginx目录下,或者下面的连接就有,直接复制到induapp项目根目录中,不需要进行修改。

现在就可以在项目induapp根目录下面创建induapp_nginx.conf的文件,然后编写下面文件。

# mysite_nginx.conf

# the upstream component nginx needs to connect to
upstream django {
    # server unix:///path/to/your/mysite/mysite.sock; # for a file socket
    server 127.0.0.1:8000; # for a web port socket (we'll use this first)
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8080;
    # the domain name it will serve for
    server_name 127.0.0.1; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Django media
    location /media  {
        alias /home/ubuntu/induapp/media;  # your Django project's media files - amend as required
    }

    location /static {
        alias /home/ubuntu/induapp/static; # your Django project's static files - amend as required
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /home/ubuntu/induapp/uwsgi_params; # the uwsgi_params file you installed
    }
}

将这个文件链接到/etc/nginx/sites-enabled,这样nginx就可以看到它了:
sudo ln -s ~/induapp/induapp_nginx.conf /etc/nginx/sites-enabled/

5.基本nginx测试

sudo /etc/init.d/nginx restart

让nginx来测试test.py

我们可以查看端口号占用情况并杀掉进程

netstat -apn|grep 8000
kill -9 <pid>
uwsgi --socket :8000 --wsgi-file test.py

显然可以看出,已经配置了nginx在8000端口与uWSGI通信,而对外使用8080端口,访问8080端口来进行检查。

6.使用Unix socket而不是端口

使用Unix socket会比端口更好,开销更少。

编写induapp_nginx.conf,修改它来匹配:

upstream django {
    server unix:///home/ubuntu/induapp/induapp.sock; # for a file socket
    # server 127.0.0.1:8000; # for a web port socket (we'll use this first)
}

然后重启nginx

sudo /etc/init.d/nginx restart

再次运行uwsgi

uwsgi --socket mysite.sock --wsgi-file test.py

这次,socket会告诉uwsgi使用哪个文件

在浏览器或者curl尝试访问8080端口

如果那不行

检查nginx错误日志(/var/log/nginx/error.log)。如果你看到像这样的信息:

connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permission denied)

那么可能你需要管理这个socket上的权限,从而允许nginx使用它。

尝试:

uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=666 # (very permissive)

或者:

uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=664 # (more sensible)

你可能还必须添加你的用户到nginx的组 (可能是 www-data),反之亦然,这样,nginx可以正确地读取或写入你的socket。

值得保留nginx日志的输出在终端窗口中滚动,这样,在解决问题的时候,你就可以容易的参考它们了。

6.使用uwsgi和nginx运行django应用

运行我们的Django应用

uwsgi --socket mysite.sock --module mysite.wsgi --chmod-socket=664

现在,uWSGI和nginx应该不仅仅可以为一个”Hello World”模块服务,还可以为你的Django项目服务。

在induapp项目中配置induapp_uwsgi.ini文件

[uwsgi]
project = induapp
base = /home/ubuntu
socket = 127.0.0.1:8000

chdir = %(base)/%(project)
home = %(base)/anaconda3/envs/djangoEnv
module = %(project).wsgi

master = true
processes = 10

socket = %(base)/%(project)/%(project).sock
chmod-socket = 666
vacuum = true

pythonpath = /home/ubuntu/anaconda3/envs/djangoEnv/lib/python3.6/site-packages

值得注意的是要是没有最后一行的pythonpath很可能在初始化uwsgi应用的时候出现 no moudle named xxx的错误。

chdir是项目路径,home是虚拟环境路径。

使用这个文件运行

uwsgi --ini induapp_uwsgi.ini # the --ini option is used to specify a file

打印出来的结果能够预测Django站点是否预期工作

7.系统安装uwsgi

停用虚拟环境

source deactivate

然后在系统中安装uWSGI:

sudo pip install uwsgi

再次检查是否能运行

uwsgi --ini induapp_uwsgi.ini

Emperor模式

# 系统启动时运行uWSGI
# 最后一步是让这一切在系统启动的时候自动发生。
# 对于许多系统来说,最简单 (如果不是最好的)的方式是使用 rc.local 文件。
# 编辑 /etc/rc.local 然后在”exit 0”行前添加:
/usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid ubuntu --gid ubuntu --daemonize /var/log/uwsgi-emperor.log

选项表示:

  • emperor: 查找vassals (配置文件)的地方
  • uid: 进程一旦启动后的用户id
  • gid: 进程一旦启动后的组id

文档中的用户是www-data,

/usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data --daemonize /var/log/uwsgi-emperor.log

但常常因为权限不够高,访问时候会出现502的错误,所以我们这里直接使用Ubuntu来提高我们的权限。

7.完成前台的部署

将前台的静态文件打包到一个文件夹上传到服务器中,我们这里是/home/ubuntu/knowGraph

我们在项目根目录创建induapp_web_nginx.conf文件并进行编写。

server {
        listen       8001;
        server_name  127.0.0.1;

        location / {
            root /home/ubuntu/knowledgeGraph;
            index index.html;
        }
        location /neo4j {
            proxy_pass http://127.0.0.1:8080;
            proxy_send_timeout 1800;
            proxy_read_timeout 1800;
            proxy_connect_timeout 1800;
            client_max_body_size 2048m;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header  Host              $http_host;   # required for docker client's sake
            proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
            proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
            proxy_set_header  X-Forwarded-Proto $scheme;
        }
        location /admin {
            proxy_pass http://127.0.0.1:8080;
            proxy_send_timeout 1800;
            proxy_read_timeout 1800;
            proxy_connect_timeout 1800;
            client_max_body_size 2048m;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header  Host              $http_host;
            proxy_set_header  X-Real-IP         $remote_addr;
            proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
            proxy_set_header  X-Forwarded-Proto $scheme;

        }
}

下面就是对这个文件的解析

该文件监听的是8001端口,location / 对应的是前端静态文件的初始化界面。/neo4j和/admin对应的是django的url。基本上格式可以不做修改,我们值得注意的是proxy_pass http://127.0.0.1:8080(也就是刚刚我们后台运行在nginx的端口。)注意的是http://127.0.0.1:8080后面不应该带/号,不然访问127.0.0.1:8001/neo4就会变成访问127.0.0.1:8080,跟我们想要的结果不同。

将这个文件链接到/etc/nginx/sites-enabled,这样nginx就可以看到它了: sudo ln -s ~/induapp/induapp_web_nginx.conf /etc/nginx/sites-enabled/

最后的最后,重启服务

sudo /etc/init.d/nginx restart
uwsgi --ini induapp_uwsgi.ini -d /home/induapp/induapp.log

nginx+uwsgi启动Django项目

1.安装项目环境

  • 系统环境:ubuntu16.04
  • python环境:python3.5.2
  • Django版本:django1.11.7
  • nginx环境:nginx_1.10.3
  • 虚拟环境:virtualenv15.1.0
  • uwsgi版本:uwsgi2.0.17.1

安装并进入项目虚拟环境:

sudo apt-get install virtualenv
virtualenv -p python3 env_my_project 
source env_my_project/bin/activate
pip install -r requirements.txt 

2.项目配置及运行测试

修改项目配置文件:
cp my_project/settings_local.py.example my_project/settings_local.py

修改es配置文件:
cp rs_es/es_settings.py.example rs_es/es_settings.py

wsgi.py:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_project.settings_local")
application = get_wsgi_application()

项目运行测试:
python manage.py collectstatic  # 收集静态文件
python manage.py makemigrations
python manage.py migrate
python manage.py runserver 0.0.0.0:8001

未分类

3.NGINX和UWSGI相关配置

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/my_project
sudo ln -s /etc/nginx/sites-available/my_project /etc/nginx/sites-enabled/
sudo vim /etc/nginx/sites-enabled/my_project

nginx配置:
upstream my_project{
    server unix:///var/run/my_project.sock;
}

server {
    listen      8001;  //服务的端口号   服务通过nginx与uwsgi通信来启动

    server_name 192.168.xx.xx;  //nginx代理的ip  
    charset     utf-8;

    # max upload size
    client_max_body_size 10M;

    # send all non-media requests to the Django server.
    location / {
        uwsgi_pass  my_project;
        include     /etc/nginx/uwsgi_params;
    }

    location /static/ {
        root /home/ubuntu/my_project;
    }
}

Uwsgi配置:
sudo mkdir /var/log/uwsgi
sudo chmod -R 777 /var/log/uwsgi

uwsgi.ini:
[uwsgi]
chdir=/home/ubuntu/my_project
home=/home/ubuntu/my_project/env_my_project
module=my_project.wsgi:application

socket=/var/run/my_project.sock
chmod-socket = 666

master=True
processes = 5
max-requests=5000

# clear environment on exit
vacuum=True

pidfile=/var/run/my_project.pid
daemonize=/var/log/uwsgi/my_project.log

# git pull 自动重启服务
touch-reload=.git/index

4.配置Emperor mode监听和系统自动启动uwsgi

配置Emperor mode监听
sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/vassals
sudo ln -s /home/ubuntu/my_project/uwsgi.ini /etc/uwsgi/vassals/

系统自动启动uwsgi
sudo vim /etc/rc.local
/usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals

5.通过uwsgi启动django服务

启动uwsgi
uwsgi --ini uwsgi.ini

重启nginx
sudo service nginx restart

启动django服务
sudo uwsgi --reload /var/run/my_project.pid

此时在浏览器通过ngnix代理出来的ip和端口即可访问服务

Django+uwsgi+nginx+angular.js项目

这次部署的前后端分离的项目:

前端采用angular.js,后端采用Django(restframework),他俩之间主要以json数据作为交互

Django+uwsgi的配置可以参考我之前的博客:http://www.cnblogs.com/52-qq/p/8873328.html

这篇博客里面都是静态页面,而不是采用restapi的形式,不过都是类似的,配置完之后采用uwsgi启动Django项目

前端:吧前端所有的代码拷贝在Django项目根路径下(也可以不用,当然我这么说是为了方便),

然后直接在nginx中配置,就可以了(不过前端框架也需要启动)

未分类

这段代码的配置才是比较重要的,类似的vue项目的部署也都类似

Centos7 系统下搭建.NET Core2.0+Nginx+Supervisor+Mysql环境

内容预览:

  • 直到微软推出完全开源的.NET Core~
  • 一方面,这个小巧的框架可以让某…~
  • 问题3:如果服务器宕机或需要重启我们则还是需要连入shell进行启动~

好记性不如烂笔头!

一、简介

一直以来,微软只对自家平台提供.NET支持,这样等于让这个“理论上”可以跨平台的框架在Linux和macOS上的支持只能由第三方项目提供(比如Mono .NET)。直到微软推出完全开源的.NET Core。这个开源的平台兼容.NET Standard,并且能在Windows、Linux和MacOS上提供完全一致的API。虽然这个小巧的.NET框架只是标准.NET的一个子集,但是已经相当强大了。

一方面,这个小巧的框架可以让某些功能性应用同时运行在三个平台上(就像某些功能性的Python脚本一样),另一方面,这也可以让服务器运维人员将ASP .NET服务程序部署在Linux服务器上(特别是对于运行Windows Server较为吃力的服务器)。

官网参考资料:https://www.microsoft.com/net/core#linuxcentos

二、.NET Core2.0 环境部署前准备

1.环境说明

服务器系统:CentOS 7.2.1511

2.安装前准备(关闭防火墙、关闭selinux)

1)关闭firewall:

systemctl stop firewalld.service #停止firewall
systemctl disable firewalld.service #禁止firewall开机启动
firewall-cmd --state #查看默认防火墙状态(关闭后显示notrunning,开启后显示running)

2)关闭selinux

sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config

查看改后文件如下:

[root@localhost ~]# cat /etc/selinux/config 

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of three two values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

3)重启Centos

reboot

三、部署.NET Core2.0 环境

1.添加DOTNET产品

在安装.NET核心之前,您需要注册微软产品提要。这只需要做一次。首先,注册微软签名密钥,然后添加微软产品提要。

rpm --import https://packages.microsoft.com/keys/microsoft.asc                                     
sh -c 'echo -e "[packages-microsoft-com-prod]nname=packages-microsoft-com-prod nbaseurl=https://packages.microsoft.com/yumrepos/microsoft-rhel7.3-prodnenabled=1ngpgcheck=1ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/dotnetdev.repo'

2.安装.NET核心SDK

在下一步之前,请从您的系统中删除.NET .NET以前的任何预览版本。

以下命令更新用于安装的产品列表,安装.NET核心所需的组件,然后安装.NET核心SDK。

yum update
yum install libunwind libicu -y
yum install dotnet-sdk-2.0.0 -y

3.检查dotnet是否安装成功与版本查看

dotnet --info

dotnet --version

四、测试.NET Core2.0 环境

1.在home目录下初始化一个测试环境并输出”Hello World “内容 (测试方式一,可忽略)

cd /home
dotnet new console -o hwapp
cd hwapp
dotnet run

输出空内容如下:

[root@localhost hwapp]# dotnet run
Hello World!

2.上传.net core的实例页面进行测试 (测试方式二、推荐)

Centos 下.net core 2 环境测试用例 (把它上传到/home目录下或自定义的目录)

下载地址:

http://down.51cto.com/data/2334968

执行以下命令

cd /home/WebApplication1
dotnet restore   //如果使过用测试方式一,就需先执行这命令重新加载一下当前新的网站文件
dotnet run

运行后如下图:

未分类

通过IE访问测试页

五、安装配置nginx对ASP.NET Core应用的转发

1.安装Nginx环境

[root@localhost ~]#curl -o  nginx.rpm http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
[root@localhost ~]#rpm -ivh nginx.rpm
[root@localhost ~]#yum install nginx -y

输入:systemctl start nginx 来启动nginx。

[root@localhost ~]# systemctl start nginx

输入:systemctl enable nginx 来设置nginx的开机启动(linux宕机、重启会自动运行nginx不需要连上去输入命令)

[root@localhost ~]#systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

2.通过iE检查能否访问

[root@localhost nginx-1.8.1]# ps -ef|grep nginx
root      14626      1  0 08:47 ?        00:00:00 nginx: master process nginx
nginx     14627  14626  0 08:47 ?        00:00:00 nginx: worker process
root      14636   3269  0 08:49 pts/1    00:00:00 grep --color=auto nginx

nginx常用的操作命令

systemctl start nginx.service               #启动nginx服务

systemctl enable nginx.service             #设置开机自启动

systemctl disable nginx.service            #停止开机自启动

systemctl status nginx.service             #查看服务当前状态

systemctl restart nginx.service           #重新启动服务

systemctl list-units –type=service        #查看所有已启动的服务

3.防火墙配置(如果系统有防火墙就需要进行写入规则)

命令:firewall-cmd –zone=public –add-port=80/tcp –permanent(开放80端口)

命令:systemctl restart firewalld(重启防火墙以使配置即时生效)

4.配置nginx对ASP.NET Core应用的转发

修改 /etc/nginx/conf.d/default.conf 文件。

将文件内容替换为

    server {
        listen 80;
        location / {
            proxy_pass http://localhost:88;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection keep-alive;
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }

重新加载nignx

[root@localhost nginx]# nginx -s reload

nginx的配置己完成

5.开启dotnet run进行测试

[root@localhost ~]# cd /home/WebApplication1/
[root@localhost WebApplication1]# dotnet run
Using launch settings from /home/WebApplication1/Properties/launchSettings.json...
Hosting environment: Development
Content root path: /home/WebApplication1
Now listening on: http://[::]:88
Application started. Press Ctrl+C to shut down.

通过IP 80端口访问

六、配置守护服务(Supervisor)

目前存在三个问题

问题1:ASP.NET Core应用程序运行在shell之中,如果关闭shell则会发现ASP.NET Core应用被关闭,从而导致应用无法访问,这种情况当然是我们不想遇到的,而且生产环境对这种情况是零容忍的。

问题2:如果ASP.NET Core进程意外终止那么需要人为连进shell进行再次启动,往往这种操作都不够及时。

问题3:如果服务器宕机或需要重启我们则还是需要连入shell进行启动。

为了解决这个问题,我们需要有一个程序来监听ASP.NET Core 应用程序的状况。在应用程序停止运行的时候立即重新启动。这边我们用到了Supervisor这个工具,Supervisor使用Python开发的。

1.安装Supervisor

[root@localhost /]# yum install python-setuptools -y
[root@localhost /]#easy_install supervisor

2.配置Supervisor

[root@localhost /]#mkdir /etc/supervisor
[root@localhost /]#echo_supervisord_conf > /etc/supervisor/supervisord.conf

修改supervisord.conf文件,将文件尾部的配置

[root@localhost /]# vi /etc/supervisor/supervisord.conf

将里面的最后两行:

;[include]                                                   
;files = relative/directory/*.ini

改为

[include]
files = conf.d/*.conf

ps:如果服务已启动,修改配置文件可用“supervisorctl reload”命令来使其生效

3.配置对ASP.NET Core应用的守护

创建一个 WebApplication1.conf文件,内容大致如下

[root@localhost /]# vi WebApplication1.conf
[program:WebApplication1]
command=dotnet WebApplication1.dll ; 运行程序的命令
directory=/home/WebApplication1/ ; 命令执行的目录
autorestart=true ; 程序意外退出是否自动重启
stderr_logfile=/var/log/WebApplication1.err.log ; 错误日志文件
stdout_logfile=/var/log/WebApplication1.out.log ; 输出日志文件
environment=ASPNETCORE_ENVIRONMENT=Production ; 进程环境变量
user=root ; 进程执行的用户身份
stopsignal=INT

将文件拷贝至:“/etc/supervisor/conf.d/WebApplication1.conf”下

[root@localhost /]#mkdir /etc/supervisor/conf.d
[root@localhost /]#cp WebApplication1.conf /etc/supervisor/conf.d/

运行supervisord,查看是否生效

[root@localhost /]#supervisord -c /etc/supervisor/supervisord.confsupervisord -c /etc/supervisor/supervisord.conf
[root@localhost /]# ps -ef | grep WebApplication1
root      29878  29685  0 09:57 ?        00:00:00 dotnet WebApplication1.dll
root      29892  29363  0 09:57 pts/3    00:00:00 grep --color=auto WebApplication1 

如果存在dotnet WebApplication1.dll 进程则代表运行成功,这时候在使用浏览器进行访问。

至此关于ASP.NET Core应用程序的守护即配置完成。

Supervisor守护进程常用操作

【启动supervisord】
确保配置无误后可以在每台主机上使用下面的命令启动supervisor的服务器端supervisord
supervisord

【停止supervisord】
supervisorctl shutdown

【重新加载配置文件】
supervisorctl reload

七 、配置Supervisor开机启动

新建一个“supervisord.service”文件

[root@localhost /]# vi supervisord.service
# dservice for systemd (CentOS 7.0+)
# by ET-CS (https://github.com/ET-CS)
[Unit]
Description=Supervisor daemon
[Service]
Type=forking
ExecStart=/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
ExecStop=/usr/bin/supervisorctl shutdown
ExecReload=/usr/bin/supervisorctl reload
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target

将文件拷贝至:“/usr/lib/systemd/system/supervisord.service”

[root@localhost /]# cp supervisord.service /usr/lib/systemd/system/

执行命令:systemctl enable supervisord

[root@localhost /]# systemctl enable supervisord
Created symlink from /etc/systemd/system/multi-user.target.wants/supervisord.service to /usr/lib/systemd/system/supervisord.service.

执行命令:systemctl is-enabled supervisord #来验证是否为开机启动

[root@localhost /]# systemctl is-enabled supervisord

重启系统看能否能成功访问

[root@localhost /]# reboot

首页关于归档友链 NGINX在SYSTEMD中开机自启动失败

最近遇到一个玄学问题,我在 Arch 中使用 Systemd 管理 Nginx 的自启动服务。当时在某次我服务器所在的腾讯云母机半夜宕机自动拉起后,我发现 Nginx 没有正确启动,我没有在意,直接手动拉起了 Nginx 。但是在上次,将内核切换到长期支持版后我发现 Nginx 仍然没有在开机后自启动,systemctl status nginx显示 timeout,但是随后使用systemctl start nginx启动 Nginx 没有遇到任何错误。

$ sudo systemctl status nginx
* nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: failed (Result: timeout) since Sat 2018-08-04 16:26:14 CST; 10s ago

Aug 04 16:24:44 isthnew systemd[1]: Starting A high performance web server and a reverse proxy server...
Aug 04 16:26:14 isthnew systemd[1]: nginx.service: Start operation timed out. Terminating.
Aug 04 16:26:14 isthnew systemd[1]: nginx.service: Failed with result 'timeout'.
Aug 04 16:26:14 isthnew systemd[1]: Failed to start A high performance web server and a reverse proxy server.

我检查了我的 nginx.service

[Unit]
Description=A high performance web server and a reverse proxy server
After=network.target network-online.target nss-lookup.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
PrivateDevices=yes
SyslogLevel=err

ExecStart=/usr/local/nginx/sbin/nginx -g 'pid /usr/local/nginx/logs/nginx.pid; error_log stderr;'
ExecReload=/usr/local/nginx/sbin/nginx -s reload
KillMode=mixed

[Install]
WantedBy=multi-user.target

感觉上是没有问题的,因为这个配置我在另外的服务器上也在使用。重启之后马上 ssh 登录服务器,检查 Systemd 启动 Nginx 的状态,Systemd 一直在等待 Nginx 启动,直到1min30s后提示 Nginx 启动失败。Systemd 只记录到了启动超时的日志,Nginx 的日志里面并没有任何东西。

我尝试过 Google 出的办法,例如将 Type = forking 改为 Type = simple,或者给 Nginx 加上参数 daemon on; 依然不工作。之后我甚至怀疑是部分服务或组建还没有启动的时候 Nginx 就开始启动了,所以启动不正常,但是 After=network.target network-online.target nss-lookup.target 是 Nginx 官方文档中建议的参数,我甚至在将 php-fpm.service 放在了里面也没有效果。

后面我发现手动启动 Nginx 之后 Systemd 中会提示无法读取它的 PID

nginx.service: Failed to read PID from file /usr/local/nginx/logs/nginx.pid: Invalid argument

在 https://bugs.launchpad.net/ubuntu/+source/nginx/+bug/1581864 中发现有人记录了这个错误,在单核服务器中 Systemd 与 Nginx 会产生竞争。解决方法就是让 Nginx 等一下 Systemd

mkdir /etc/systemd/system/nginx.service.d

printf "[Service]nExecStartPost=/bin/sleep 0.1n" > /etc/systemd/system/nginx.service.d/override.conf

systemctl daemon-reload

无法读取PID的错误是解决了,但是依然自启动超时,所以暂时是未解之迷了。

在docker中使用ansible来源码编译nginx服务

说明:
1)在VM上装了一个4核8G的centos7.5系统
2)docker版本为 18.06.0-ce

docker的安装不再讲述

1、创建一个带有含有ssh的镜像,通过编写Dockerfile

# Set the base image to centos
FROM centos:latest
MAINTAINER fei
#mount volume
VOLUME ["/root/docker/ansible-demo/volume2"]
################## BEGIN INSTALLATION ######################
#install EPEL
RUN rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 
&& rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 
&& yum install -y yum-priorities
RUN yum install -y sudo
RUN yum install -y 
net-tools 
openssh-clients 
openssh-server 
ansible 
vim
################## END INSTALLATION ######################
# 将sshd的UsePAM参数设置成no,优化ssh连接
RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
# 修改root用户密码,这里密码为:devilf
RUN echo "root:devilf"|chpasswd
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
# 启动sshd服务并且暴露22端口
RUN mkdir /var/run/sshd
EXPOSE 22
ENTRYPOINT ["/usr/sbin/sshd","-D"]

2、开始构建镜像

docker build --no-cache -t fei/centos:ssh_ansible .

3、启动容器(需要开启特权模式,否则会报错:Failed to get D-Bus connection: Operation not permitted)

docker run -itd -p 20021:22 --privileged=true --name node1 fei/centos:ssh_ansible
docker run -itd -p 20022:22 --privileged=true --name node2 fei/centos:ssh_ansible
...
...
docker run -itd -p 20020:22 --privileged=true --name ansible_server fei/centos:ssh_ansible

4、配置ansible主机清单,并建立互信关系

修改ansible.cfg文件,将默认的hosts文件改为一个目录,修改为:
inventory     = /etc/ansible/conf.d

设置清单

# cat conf.d/docker
[nodes]
172.17.0.2
172.17.0.3
172.17.0.4
172.17.0.5

生成密钥

ssh-keygen

下发密钥

ssh-copy-id [email protected]

5、测试

ansible nodes -m ping

注意:
查看容器IP的方法:

docker inspect --format '{{ .NetworkSettings.IPAddress }}' container_id

停止容器:

docker stop container_id

删除容器:

docker container rm container_id

下面就要开始通过playbook来源码编译安装nginx

可以针对所有的服务安装创建一个专门的目录,例如这里安装nginx,可以创建一个目录,目录结构为:

tree roles/
roles/
├── conf
│   ├── default
│   ├── files
│   ├── handlers
│   │   └── main.yml
│   ├── meta
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   │   └── temp_server.conf
│   └── vars
│       └── main.yml
├── install
│   ├── default
│   ├── files
│   │   └── nginx-1.12.0.tar.gz
│   ├── handlers
│   │   └── main.yml
│   ├── meta
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   │   ├── nginx.conf
│   │   ├── web1.conf
│   │   └── web2.conf
│   └── vars
│       └── main.yml
├── nginx.retry
├── nginx.yaml
└── site.yml

分为两部分,conf目录主要是方便增加站点,存放配置文件;install目录主要是为了安装nginx,该目录下会存放安装所用的源码包,配置文件等
install目录下定义一个任务:

# cat tasks/main.yml
- name: cp nginx package to remote host
  copy: src=nginx-1.12.0.tar.gz dest=/tmp/nginx-1.12.0.tar.gz   #去files目录中拉取源码包
  tags: cp-nginx-pkg
- name: tar nginx package
  shell: cd /tmp; tar zxf nginx-1.12.0.tar.gz
- name: install nginx depend pkg
  yum: name={{ item }} state=latest       #item是一个变量,用来指定下面的一些依赖包名
  with_items:
    - openssl-devel
    - pcre-devel
    - gcc
    - gcc-c++
    - autoconf
    - automake
    - libtool
    - make
    - cmake
    - zlib
    - zlib-devel
    - openssl
    - pcre-devel
    - libxslt-devel
- name: install nginx
  shell: cd /tmp/nginx-1.12.0; ./configure --user=www --group=www --prefix=/usr/local/nginx 
         --with-http_stub_status_module 
         --with-http_ssl_module 
         --with-pcre && make && make install
- name: cp conf
  template: src=nginx.conf dest=/usr/local/nginx/conf/nginx.conf     #这个是去templates目录中拉取配置文件
  tags: nginx-conf
- name: cp shell
  copy: src=/ansible/script/create_users.sh dest=/tmp/create_users.sh    #这个脚本的目的是检测目标机器是否已经存在所建的用户,如果存在机会创建用户会报错
- name: create nginx user
  shell: /bin/bash /tmp/create_users.sh
  tags: add-nginx
  notify: start nginx service

上面脚本内容:

# cat /ansible/script/create_users.sh
#!/bin/bash
name="www"
num=$(grep -c $name /etc/passwd)
if [ $num -eq 0 ];then
        groupadd $name
        useradd -g $name $name -s /sbin/nologin
fi

给nginx的主配置文件指定一个端口,通过设置一个变量,后面主配置里面会去引用

# cat vars/main.yml
ngxport: "8080"

主配置文件

# cat templates/nginx.conf
user  www;
worker_processes  {{ ansible_processor_vcpus }};

events {
    worker_connections  65535;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format main  '$remote_addr - $remote_user[$time_local] "$request" '
                      '$status $body_bytes_sent"$http_referer" '
                      '"$http_user_agent""$http_x_forwarded_for"';
    access_log logs/access.log  main;

    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       {{ ngxport }};  
        server_name  www.a.com;
        access_log logs/a.com;

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
   include conf.d/*.conf;
}

定义触发器

# cat handlers/main.yml
- name: start nginx service
  shell: /usr/local/nginx/sbin/nginx

我们这里要新增一个站点做测试,需要修改的目录(需要切换到conf目录中)有:
定义变量,用于配置文件的引用:

# cat vars/main.yml
server_name: "www.a.com"    #每次新增站点时,可以修改此域名
root_dir: "/data/web"

因为新增站点时,是基于域名的虚拟主机,所以端口均为默认的80端口
编写新增站的配置文件:

# cat templates/temp_server.conf
server
{
listen 80;
server_name {{server_name}};
index index.php index.html;
root {{root_dir}};
}

在var目录中定义变量:

cat main.yml
server_name: "www.a.com"
root_dir: "/data/web"

编写配置nginx的tasks步骤哦:

cd tasks
cat main.yml
- name: create vhosts
  shell: mkdir -p /usr/local/nginx/conf/conf.d/
  tags: create_dir
- name: cp file nginx.conf
  template: src=temp_server.conf dest=/usr/local/nginx/conf/conf.d/{{server_name}}.conf
  tags: ngxconf
  notify: reload nginx service

定义角色路径

#回到roles的上级目录下
cat nginx.yaml
- hosts: web1
  remote_user: root
  roles:
    - install
    - conf

测试:

ansible-playbook -C nginx.yaml

测试通过后可以真正去执行

ansible-playbook nginx.yaml

Zabbix企业级监控之监控Nginx

1.如何监控Nginx?

需要添加http_stub_status_module 模块

2.安装Nginx需要的模块

(1)安装依赖包

yum -y install vim gcc wget pcre-devel openssl-devel gcc-c++ pcre-dev  zlib-devel 

(2)安装nginx

wget http://101.96.10.63/nginx.org/download/nginx-1.12.1.tar.gz

解压

tar -zxvf nginx-1.12.1.tar.gz

添加nginx组并添加nginx用户

useradd -M -s /sbin/nologin nginx

(3)编译安装

cd nginx-1.12.1
./configure --prefix=/etc/nginx --user=nginx --group=nginx --with-http_stub_status_module
make && make install

(4)vim nginx.conf

在server {} 添加nginx统计模块

#nginx状态监控
        location /nginx_status {
              stub_status on;
              access_log off;
              allow all;        }

(5)重启nginx

3.重启agent

systemctl restart zabbix-agent

4.添加nginx模板

vim /etc/zabbix/zabbix_agentd.d/userparameter_nginx.conf
UserParameter=nginx_status[*],/bin/bash /etc/zabbix/shell/monitor_nginx.sh "$1"

5.编写shell脚本

vim /etc/zabbix/shell/monitor_nginx.sh

#!/bin/bash

nginx_port=81
nginx_ip=127.0.0.1
curl_addres="http://$nginx_ip:$nginx_port/nginx_status"
nginx_active() {
    curl -s $curl_addres|grep "Active"|awk '{print $3}'
}

nginx_reading() {
   curl -s $curl_addres |grep "Reading"| awk '{print $2}'
}
nginx_writing() {
   curl -s $curl_addres |grep "Writing"|awk '{print $4}'
}

nginx_waiting() {
   curl -s $curl_addres |grep "Waiting"|awk '{print $6}'
}

nginx_accept() {
   curl -s $curl_addres |awk 'NR==3{print $1}'
}
nginx_handled() {
   curl -s $curl_addres |awk 'NR==3{print $2}'
}

nginx_requests() {
   curl -s $curl_addres |awk 'NR==3{print $3}'
}

case $1 in 
    nginx_active)
        nginx_active;
    ;;
    nginx_reading)
        nginx_reading;
    ;;
    nginx_writing)
        nginx_writing;
    ;;
    nginx_waiting)
        nginx_waiting;
    ;;
    nginx_accept)
        nginx_accept;
    ;;
    nginx_handled)
        nginx_handled;
    ;;
    nginx_requests)
        nginx_requests;
    ;;
    *)
esac

nginx 499 日志记录client closed connection while waiti

error.log 中出现大量以下日志(info级别才会记录如下日志):

2013/11/13 11:26:10 [info] 18382#0: *2799 client closed connection while waiting for request, client: 127.0.0.1, server: 0.0.0.0:80

应该是客户端设置了HTTP请求超时,比如1秒后就超时,这时会给服务端发送一个关闭TCP连接的包(TCP四次挥手的FIN包),Nginx检测到客户端关闭连接后,就会记录一条这样的日志,并且此时 nginx access log 里面记录的是499这个 status code。

出现这个问题,通常可能是因为你接口响应时间太长了,超过了客户端设置的超时设置,建议在 nginx access log 里记录一下 $upstreamresponsetime $request_time 两个时间,看一下一般请求响应时间是多少。如果确实时间长,那就优化代码吧。

nginx+php-fpm故障排查

小明初到一家公司做运维的工作,刚来的第一天就开始部署LNMP(Linux+Nginx+MySQL+PHP)环境,结果出现了问题。

他来向我请教,具体问题现象、原因和解决思路如下:

问题一
nginx进程CPU和内存不均衡,某个进程占用资源特别高,如何解决?
回答:我让小明绑定下CPU的亲缘性(设置nginx配置worker_cpu_affinity项为auto,auto这个特殊值(1.9.10版本)允许自动绑定工作进程到可用的CPU上。),绑定后CPU和内存使用就均衡了。

问题二
小明又问close系统调用消耗很高怎么解决?

回答:且听我娓娓道来,继续看下文。

strace−cpstrace−cp(pgrep -n nginx)

未分类

$ top

未分类

系统32c的,top查看负载去到75.14,

查看过nginx和php-fpm的

错误日志也没有什么发现。

strace 跟踪close的系统调用,

都是以下的信息:

strace−T−ttpstrace−T−ttp(pgrep -n nginx) 2&>1 |grep -B 10 close > ./close.log

未分类

$ lsof | more

未分类

遂怀疑是连接创建关闭消耗了太多的资源,便让小明加了台机器专门跑nginx,
前端挂了个nginx用长连接跟后端的nginx连接,接了个nginx之后负载果然就下来了。

未分类

前端未挂nginx压测ab压测结果:

未分类

前端挂了nginx压测ab压测结果:

未分类

tps基本没变第一个Time per,requset快了87.52%。
接着继续排查tps上不去的原因,继续strace后端的nginx。

strace−cpstrace−cp(pgrep -n nginx)

未分类

发现现在是wrtiev占用高了,strace 跟踪close的系统调用,
发现很多以下的输出:

connect(26, {sa_family=AF_INET, 
sin_port=htons(9000), 
sin_addr=inet_addr("127.0.0.1")},
16) = -1 EINPROGRESS
(Operation now in progress)

问题应该不是在nginx上,
应该是在php-fpm上了。
继续

strace−cpstrace−cp(pgrep -n php-fpm)

显示下图所示:

未分类

access cpu时间消耗最多那就先
排查access
系统调用:

strace−T−ttpstrace−T−ttp(pgrep -n php-fpm) 2&>1 |
grep -B 10 access >
./access.log

未分类

php-fpm进程频繁的去读取文件,整个操
作下来花费4ms的时间。

然后排查recvfrom:

strace−T−ttpstrace−T−ttp(pgrep -n 
php-fpm) 2&>1 |
grep -B 10 recvfrom > 
./recvfrom.log

未分类

频繁的去访问10.0.0.156的6379,端口,明显就是访问redis读取数据的过程,
整个过程花费12ms。
让小明把上面两个strace信息发给开发,第一个得到回复是老版本的流程,
新版本改了,但还是有些判断没有处理。
第二个问题让开发使用redis连接池,无需频繁创建连接读取数据,
频繁创建连接开销很大的。

总 结
当遇上性能问题时,排查日志无法解决时,使用strace工具来查看一下系统调用,看时间到底消耗在哪里了,可以轻松的找到问题所在。

Nginx、FPM配置及优化

Nginx配置

nginx的配置主要分为6个区域,main(全局设置),events(工作模式),http(http服务),upstream(负载均衡),server(主机设置),location(url规则)。

main
events {

}
http {
    upstream domain {

    }
    server {
        location {

        }
    }
}

main模块

user www www;
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
error_log   /home/git/logs/error_log error;
pid        /usr/local/var/run/nginx/nginx.pid;
worker_rlimit_nofile 65535;
  • user 用来指定worker进程运行的用户及用户组
  • worker_processes 来指定开启的worker进程数,如果是多核CPU,建议指定和CPU的数量一样的进程数即可,这里的CPU数量指物理核数。
  • worker_cpu_affinity 将不同的woker进程绑定到不同的cpu,这里指绑定到CPU[0-3],降低由于多CPU核切换造成的寄存器等现场重建带来的性能损耗。
  • error_log 用来定义全局错误日志文件。日志输出级别有debug、info、notice、warn、error、crit可供选择,其中,debug输出日志最为最详细,而crit输出日志最少。
  • pid 指定master进程ID存储位置。
  • worker_rlimit_nofile 指定最多打开的文件描述符(fd),查看用户级fd限制(ulimit -n),查看系统级fd限制(cat /proc/sys/fs/file-max),系统fd与系统内存有关。

events 模块

events用来指定nginx的事件模型及连接数上限。

events {
    use epoll;
    worker_connections  65535;
    multi_accept on;
}
  • use 用来指定具体的事件模型,包括select、poll、epoll、kqueue、/dev/poll、eventport、rtsig,其中select、poll是标准的事件模型,epoll(用于linux平台)和kqueue(用于BSD平台)是高效的事件模型。
  • worker_connections 定义每个nginx进程接收的最大连接数,最大客户端连接数Max_clients = worker_processes * worker_connections / 2,而作为反向代理时,Max_clients = worker_processes * worker_connections / 4。
  • multi_accept 让NGINX在接收到一个新连接通知后调用accept()来接受尽可能多的连接。

http模块

负责http服务器的设置,包括虚拟主机和反向代理。

http{
    server_tokens off;
    include    mime.types;
    default_type  application/octet-stream;
    log_format  main '$http_host$clientip$time_local$request_time$request$status$body_bytes_sent$http_referer$http_user_agent';
    access_log  /home/git/logs/access_log  main;
    add_header DPOOL_HEADER $hostname;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_timeout 30;
    keepalive_requests 1024;

    send_timeout 10;
    client_body_timeout 10;
    client_header_timeout 10;

    map $http_x_forwarded_for $clientip {
            default $http_x_forwarded_for;
            ""      $remote_addr;
    }
    proxy_set_header X-Forwarded-For $clientip;

    proxy_redirect off;
    chunked_transfer_encoding off;
    proxy_set_header Host $host;
    proxy_ignore_client_abort on;

    proxy_connect_timeout 75;
    proxy_send_timeout 150;
    proxy_read_timeout 150;

    proxy_buffer_size 4k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;

    open_file_cache max=65535 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 3;

    client_header_buffer_size 4k;
    large_client_header_buffers 4 8k;
    client_max_body_size 8m;
    client_body_buffer_size 1024k;
    server_names_hash_bucket_size 256;

    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_comp_level 2;
    gzip_proxied     expired no-cache no-store private auth;
    gzip_types text/plain text/css text/xml application/x-javascript application/json;
    gzip_vary on;

    fastcgi_temp_path /dev/shm/nginx_tmp;
    fastcgi_cache_path /dev/shm/nginx_cache levels=1:2 keys_zone=card_cache:20m inactive=5m max_size=1024m;
    fastcgi_cache_key "$request_method$host$request_uri";
    fastcgi_cache_min_uses 1;
    fastcgi_cache_methods GET HEAD POST;
    fastcgi_cache_bypass $cookie_nocache $arg_nocache;
    fastcgi_no_cache $cookie_nocache $arg_nocache;
    fastcgi_cache_use_stale error timeout http_500 http_404;
}
  • server_tokens off用来关闭nginx版本号显示。
  • include 用来设定文件的mime类型,类型在配置文件mime.types中定义。
  • default_type 设定默认类型为二进制,即当文件类型未定义时使用。
  • log_format 用于记录日志参数及格式,此处声明为main,用于access_log的记录。
  • add_header 增加自定义响应header头,header名为自定义,header值为主机名。

sendfile

  • zero-copy机制高效传输
  • 将tcp_nopush和tcp_nodelay两个指令设置为on即数据包积累到一定量时,尽快发送。

keepalive

  • client到nginx的长连接
    • keepalive_timeout 设置keep-alive客户端连接在服务器端保持开启的超时值。
    • keepalive_requests 设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100。
  • nginx到后端server的长连接(反向代理)

nginx
http {
upstream BACKEND {
server 192.168.0.1;
keepalive 300;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
  • upstream流配置keepalive,指定每个nginx worker到后端的最大连接数。
  • location 配置proxy_http_version 1.1(http1.1才支持keepalive), proxy_set_header Connection “”(清空客户端设置,即忽略client与nginx的连接方式)。

超时

  • send_timeout 指定向客户端传输数据的超时时间。
  • client_body_timeout 设定客户端与服务器建立连接后发送Request Body的超时时间,如果客户端在此期间没有发送任何内容,那么Nginx将返回Http 408错误(Request Time Out)。
  • client_header_timeout 设定客户端与服务器建立连接后发送Request Header的超时时间,如果在此期间没有发送数据,则同client_body_timeout一样返回HTTP408。

map

  • 是变量设置的映射表,映射表由两列组成,匹配模式和对应的值,匹配模式可以使正则。这里将clientip的值是从http_x_forwarded_for通过映射规则获取,从而获取到客户端真实的IP,而不是代理服务器IP(也可以使用nginx realip模块实现)。
  • “” 匹配当http_x_forwarded_for为空字符串时(第一次经过代理服务器)
  • default 在没有匹配到任何规则时执行(非第一次经过代理服务器)

proxy

  • proxy_ignore_client_abort 默认是关闭的,即请求过程中如果客户端端主动关闭请求或者客户端网络断掉,那么Nginx会记录499。
  • proxy_connect_timeout 定义了连接代理服务器的超时时间,一般情况下,这个值不超过75s。
  • proxy_read_timeout 定义了与代理服务器获读超时时间。
  • proxy_send_timeout 定义了与代理服务器写超时间时间。
  • proxy_buffer_size 用来响应头的缓冲区,一般4k就够了。
  • proxy_buffers 设置用来接收响应的缓冲区的数量和大小。
  • proxy_busy_buffers_size 设定高负荷下的缓冲区大小,建议为proxy_buffers中单个缓冲区大小的2倍。
  • proxy_max_temp_file_size和proxy_temp_file_write_size 指定临时文件的一次写入临时文件的大小及响应内容大于proxy_buffers指定的缓冲区时, 写入硬盘的临时文件的大小。

openfile

  • open_file_cache 缓存将最近使用的文件描述符和相关元数据(如修改时间,大小等)存储在缓存中,这里为1,000个元素定义了一个缓存,到期时间为20s。
  • open_file_cache_valid 定义时间段(以秒为单位),之后将重新验证open_file_cache中的元素。
  • open_file_cache_min_uses 将在非活动时间段之后从高速缓存中清除元素。 此指令可用于配置最小访问次数以将元素标记为活动使用。

client buffer

  • client_header_buffer_size和large_client_header_buffers 设置用于读取客户端请求头的缓冲区大小,先根据client_header_buffer_size配置的值分配一个buffer,如果分配的buffer无法容纳 request_line/request_header,那么就会再次根据large_client_header_buffers配置的参数分配large_buffer,如果large_buffer还是无法容纳,那么就会返回414(处理request_line)/400(处理request_header)错误。
  • client_max_body_size 设置nginx允许接收的客户端请求内容的最大值,及客户端请求Header头信息中设置的Content-Lenth大最大值。如果超出该指令设置的最大值,nginx将返回“Request Entity Too Large”的错误信息(HTTP的413错误码)
  • client_body_buffer_size 允许客户端请求的最大单个文件字节数。
  • server_names_hash_max_size 当配置多个server时需要开启。

Gzip

  • gzip on,开启Gzip,gzip可以帮助Nginx减少大量的网络传输工作。
  • gzip_min_length 设置允许压缩的页面最小字节数,页面字节数从header头中的Content-Length中进行获取。
  • gzip_buffers 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。
  • gzip_comp_level gzip压缩比,1 压缩比最小处理速度最快,9 压缩比最大但耗CPU,超过2时,压缩率提升已经不明显。
  • gzip_types 匹配MIME类型进行压缩,(无论是否指定)”text/html” 类型总是会被压缩的。
  • gzip_vary 是否发送Header头Vary: Accept-Encoding响应头字段,通知接收方响应使用了gzip压缩。
  • gzip_proxied 根据某些请求和应答来决定是否在对代理请求的应答启用压缩。

fastcgi cache

  • fastcgi_temp_path 缓存文件的临时目录。
  • fastcgi_cache_path 用于设置缓存文件的存放路径。
    • levels:指定了该缓存空间有两层hash目录,设置缓存目录层数
    • keys_zone为这个缓存区起名为zone_name,20m指代缓存空间为20MB
    • inactive=5m 代表如果缓存文件5分钟内没有被访问,则删除
    • max_size代表最大缓存size
  • fastcgi_cache_methods 指定缓存的HTTP method。
  • fastcgi_cache_min_uses URL经过多少次访问被缓存。
  • fastcgi_cache_key 缓存的key名。
  • fastcgi_cache_use_stale 针对错误码的缓存。
  • fastcgi_no_cache和fastcgi_cache_bypass,通过set变量值控制指定参数的0/1,来控制缓存的使用,因为并不是所有情况下都需要缓存。

server模块

server {
    listen 80;
    root /home/git/www/;
    server_name xstudio.me yueqian.sinaapp.com;

    access_log /home/git/logs/access_log main;
    error_log /home/git/logs/error_log error;

    if ($uri !~ "^/(?:crossdomain.xml|favicon.ico|static/.*|robots.txt)$") {
        rewrite  ".*" /index.php last;
    }

    location ~* ^.+.(jpg|jpeg|gif|png|bmp|css|js)$ {
        access_log off;
        expires 1d;
        break;
    }

    location ~ .php$ {
        set $script_uri "";
        if ( $request_uri ~* "([^?]*)?" ) {
                set $script_uri $1;
        }
        fastcgi_param SCRIPT_URL $script_uri;
        fastcgi_pass 127.0.0.1:9001;
        include fastcgi_params;
    }
}
  • listen 监听的服务端口 后边加default_server指定默认虚拟主机。
  • server_name 用来指定IP地址或者域名。
  • root 表示在这整个server虚拟主机内,全部的root web根目录,区别于location下root。

正则

  • ~ 为区分大小写的匹配。
  • ~* 不区分大小写的匹配(匹配firefox的正则同时匹配FireFox)。
  • !~ 不匹配的。
  • ^~ 标识符后面跟一个字符串,将在这个字符串匹配后停止进行正则表达式的匹配。
  • = 表示精确的查找地址。

Rewrite

  • last :相当于Apache里德(L)标记,表示完成rewrite。
  • break;本条规则匹配完成后,终止匹配,不再匹配后面的规则。
  • redirect:返回302临时重定向。
  • permanent:返回301永久重定向。

文件缓存

  • expires 控制 HTTP 应答中的“ Expires ”和“ Cache-Control ”的头标
    • time:可以使用正数或负数。“Expires”头标的值将通过当前系统时间加上设定time值来设定。
    • time值还控制”Cache-Control”的值:
    • 负数表示no-cache
    • 正数或零表示max-age=time

FPM

  • fastcgi_param 设置fastcgi接收的参数,最终传递给PHP,SCRIPT_URL为url path。
  • fastcgi_pass fastcgi的转发地址。

HTTPS

http {
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    server {
        listen              443 ssl;
        server_name         www.example.com;
        keepalive_timeout   30;

        ssl_certificate     www.example.com.crt;
        ssl_certificate_key www.example.com.key; 

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

        ssl_prefer_server_ciphers on;
        ssl_dhparam /etc/ssl/certs/dhparam.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-Xss-Protection 1;
        #...
  • ssl_session_timeout : 客户端可以重用会话缓存中ssl参数的过期时间
  • ssl_session_cache 设置ssl/tls会话缓存的类型和大小,nginx工作进程共享ssl会话缓存。
  • ssl_certificate证书其实是个公钥,它会被发送到连接服务器的每个客户端,ssl_certificate_key私钥是用来解密的,所以它的权限要得到保护但nginx的主进程能够读取。
  • add_header Strict-Transport-Security,使用 HSTS 策略强制浏览器使用 HTTPS 连接
    • max-age:设置单位时间内強制使用 HTTPS 连接
    • includeSubDomains:可选,所有子域同时生效
    • preload:可选,非规范值,用于定义使用『HSTS 预加载列表』
    • always:可选,保证所有响应都发送此响应头,包括各种內置错误响应
  • HTTP/HTTPS混合配置
  • nginx
    server {
    listen 80;
    listen 443 ssl;
    server_name www.example.com;
    ssl_certificate www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    #…
    }

upstream模块

在多个应用实例间做负载均衡是一个被广泛使用的技术,用于优化资源效率,最大化吞吐量,减少延迟和容错。nginx可以作为一个非常高效的HTTP(7层)负载均衡器来分发请求到多个应用服务器,并提高web应用的性能,可扩展性和可靠性。
nginx支持以下负载均衡机制(或者方法):
– round-robin/轮询: 到应用服务器的请求以round-robin/轮询的方式被分发
– least-connected/最少连接:下一个请求将被分派到活动连接数量最少的服务器
– ip-hash/IP散列: 使用hash算法来决定下一个请求要选择哪个服务器(基于客户端IP地址)

默认轮询(加权)

http {
    upstream myapp1 {
        server srv1.example.com weight=3;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://myapp1;
        }
    }
}

在这个配置中,每5个新请求将会如下的在应用实例中分派: 3个请求分派去srv1,一个去srv2,另外一个去srv3.

最小连接数

当某些请求需要更长时间来完成时,最少连接可以更公平的控制应用实例上的负载。

upstream myapp1 {
    least_conn;
    server srv1.example.com  weight=3;
    server srv2.example.com;
    server srv3.example.com;
}

IP Hash

请注意,在轮询和最少连接负载均衡方法中,每个客户端的后续请求被分派到不同的服务器。对于同一个客户端没有任何方式保证发送给同一个服务器。如果需要将一个客户端绑定给某个特定的应用服务器,那么可以使用ip-hash负载均衡机制。

upstream myapp1 {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

FPM配置

global全局配置

[global]
pid = /home/git/php/var/run/php-fpm.pid
error_log = /home/git/logs/php-fpm.log
log_level = notice
rlimit_files = 65535
events.mechanism = epoll
daemonize = yes
process_control_timeout = 10
include=/home/git/php/etc/php-fpm.d/*.conf
  • pid主进程pid文件
  • error_log 错误日志文件
  • log_level 错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice.
  • daemonize 后台执行FPM
  • rlimit_files 设置文件打开描述符的rlimit限制。
  • events.mechanism 使用处理event事件的机制,可用以下选项:select、pool、epoll、kqueue (*BSD)、port (Solaris)。 默认值:不设置(自动检测)。
  • process_control_timeout 设置子进程接受主进程复用信号的超时时间,默认为0时,FPM无法真正实现平滑重启。具体看上一篇文章《详解nginx及FPM平滑重启》。
  • include 用于包含一个或多个配置文件

进程池配置

通过监听不同的端口可以定义多个不同的子进程池,进程池被用与记录和统计,对于fpm能够处理进程池数目的多少并没有限制,其中$pool变量可以在任何指令中使用,他将会替代相应的进程池名字。

[www]
user=git
group=git
listen = 127.0.0.1:9001
listen.allowed_clients = 127.0.0.1

pm = dynamic
pm.max_children = 100
pm.start_servers = 60
pm.min_spare_servers = 30
pm.max_spare_servers = 100
pm.max_requests = 500

slowlog = /home/git/logs/slow_log
request_slowlog_timeout = 10
catch_workers_output = yes

php_admin_value[open_basedir] = "./:/home/git/$pool/:/tmp/xhprof/"

env[SRV_SERVER_ROOT]   = /home/git/$pool/
env[SRV_DEVELOP_LEVEL] = 4
  • user和group指定worker运行的用户及组
  • listen指定监听的IP和端口
  • listen.allowwd_clients 设置允许连接到 FastCGI 的服务器 IPV4 地址,多个地址用’,’分隔,为空则允许任何地址发来链接请求。
  • pm 设置进程管理器如何管理子进程。可用值:static,ondemand,dynamic。必须设置。
    • static – 子进程的数量是固定的(pm.max_children)。
    • ondemand – 进程在有需求时才产生(当请求时才启动。与 dynamic 相反,在服务启动时 pm.start_servers 就启动了。
    • dynamic – 子进程的数量在下面配置的基础上动态设置。
  • pm.max_children pm 设置为 static 时表示创建的子进程的数量,pm 设置为 dynamic 时表示最大可创建的子进程的数量。
  • pm.start_servers 设置启动时创建的子进程数目。
  • pm.min_spare_servers 设置空闲服务进程的最低数目。
  • pm.max_spare_servers 设置空闲服务进程的最大数目。
  • pm.max_requests 最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新派生一个新的。这个配置的主要目的是避免php解释器或程序引用的第三方库造成的内存泄露。
  • request_slowlog_timeout 当一个请求超过该设置的时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中。
  • slowlog 慢日志文件。
  • catch_workers_output 重定向运行过程中的stdout和stderr到主要的错误日志文件中. 如果没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null。
  • php_admin_value 设定PHP配置值,且不会被php ini_set覆盖掉,open_basedir 定义php有权限读写的目录,$pool的值为进程池名字(www)。
  • env 设定环境变量,例如:根目录、开发/测试环境区分等,PHP可以通过$_SERVER读取。