nginx配置技巧汇总

本文记录了一些nginx作为反向代理和文件服务器的配置技巧和解决方案,原文会持续更新

Nginx作为文件服务

避免浏览器自动播放文件

有时对于图片、视频,浏览器会视能力,自动为用户显示或播放。这主要是由于Web服务器在返回文件本身数据的同时,返回了一些特殊的MIME类型,比如:image/jpeg(JPEG图像),application/pdf(PDF文档),video/mpeg(MPEG动画)。这些MIMIE类型实际上是告诉浏览器,文件数据到底是什么,这样浏览器就能更好的为用户展示数据。现在像图片、pdf、甚至是视频基本都是可以直接在浏览器中展示和播放的。但是有时,我们需要浏览器为用户下载文件而不是直接播放,而Nginx在默认配置下,会根据文件的后缀来匹配相应的MIME类型,并写入Response header,导致浏览器播放文件而不是下载,这时需要通过配置让Nginx返回的MIME类型为下面这个类型:

application/octet-stream

这个类型会让浏览器认为响应是普通的文件流,并提示用户下载文件。可以通过在Nginx的配置文件中做如下配置达到这样的目的:

location /download/ {
    types        { }
    default_type application/octet-stream;
}

这样当Url路径中包含/download/时,MIME类型会被重置为application/octet-stream。另外,nginx自带的MIME类型映射表保存在conf/mime.types中。

文件上传大小限制放开

有的时候后端的Web-Server提供文件上传的服务,但是如果前端使用Nginx做反向代理时,会出现文件无法上传的问题,这可能是由于Ngxin默认对客户端请求的body的限制。因为,默认情况下Nginx对客户端请求的大小限制是1m,而上传的文件往往超过1m。可以通过修改如下配置项,来放宽这个限制:

client_max_body_size 10m;

将这个值设置为0,可以取消这个限制。这个配置项可以用在httpserverlocation配置节中。详见client_max_body_size

下载文件重命名

通常情况下,为了保证用户上传的文件在服务器的文件系统中不至于重名,一般会将文件名修改成guid后保存,并在数据库中保持guid与文件名的映射。此时,如果使用Nginx来提供对这些用户文件的下载功能的话,文件下载到用户浏览器,会以文件的guid名作为文件名,这显然是用户不想看到的。可以考虑用这个方案。
假设我们有一个文件的原始文件名为test.txt,对应的guid文件名是21EC2020-3AEA-1069-A2DD-08002B30309D.txt,文件的虚拟路径是/download/

使用服务器端编程语言,在输出的html中使用如下链接提供文件的下载:

<a href="/download/21EC2020-3AEA-1069-A2DD-08002B30309D.txt?n=test.txt" target='_blank'>下载test.txt</a>

可以看到,将原始文件名以QueryString的方式带在请求中,这样可以在Nginx端,利用$arg_name变量来取到这个QueryString的值,从而重写response header:

add_header Content-Disposition "attachment; filename=$arg_n";

这会在response header中加入如下键值:

Content-Disposition: "attachment; filename=test.txt";

经测试,无论是IE还是Chrome都可以支持这个header。

关于Content-Disposition,详见这里

关于Nginx的标准http模块的嵌入变量,详见这里

Nginx作为反向代理

一个IP多个域名

如果只有一个公网IP,但是网站功能需要划分为多个不同的子网站或者子域名,可以用Nginx来搭建反向代理来“复用”IP资源。假设有如下几个域名都是abc.com这个主域的:

www.abc.com
image.abc.com
video.abc.com

1. 首先在DNS出注册这3个域名同时指向同一个IP,Nginx作为前端的web服务器,让所有访问这个IP地址80端口的请求全部指向Nginx
2. 然后,配置Nginx,根据域名将请求转发转发给内网的上游服务器,例如下面的配置:

    server {
        listen 80;
        server_name www.abc.com;
        location / {
                proxy_pass http://192.168.1.100;
        }
     }

    server {
        listen 80;
        server_name image.abc.com;
        location / {
                alias /var/www/image;
        }
     }

    server {
        listen 80;
        server_name video.abc.com;
        location / {
                proxy_pass http://192.168.1.100:8081/video;
        }
     }

在上述配置中,将三个域名分发给了不同的模块处理:

  1. www.abc.com 分发给上游的http://192.168.1.100服务器处理
  2. image.abc.com 则直接映射到了Nginx本机的一个目录
  3. video.abc.com 分发给上游的http://192.168.1.100:8081/video服务器处理(video是上游web-server的某虚拟目录)

上游服务器超时

Nginx作为反向代理的时候,如果上游服务器处理时间过长的话,有时会返回504网关超时,从nginx的错误日志看出如果是upstream timed out,就表示是上游服务器处理时间过长,Nginx认为服务超时。Nginx在请求上游服务器时默认的超时时间为1分钟,可以通过调整proxy_read_timeout属性增加这个超时时间

proxy_read_timeout  180s;

解决liunx的Slab占用比较高的有关问题

liunx的Slab占用比较高的问题

最近经常报内存超过80%的阀值.

统计了下内存实际使用只有1.6G,希望能帮我们分析下为什么内存的使用率显示使用了3178M.

然后统计 rss列的内存总数是1,680M 远小于显示使用的3G.请问那部分内存被什么程序占用了?
确认应用进程占用内存不多,可以安装工具atop,或者执行命令cat /proc/meminfo |grep -i slab看看slab占用的内存是否较多。
SLAB是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,您可以看看哪些应用进程的slab占用的内存比较多,是否这些应用需要频繁的请求和释放内存,比如进行一些小文件的读写。如果都是应用的正常使用,可以考虑升级服务器内存,如果内存不足影响业务,需要临时释放一下slab占用的内存,
可以参考以下步骤: #echo 2 > /proc/sys/vm/drop_caches
等内存回收完毕后再 #echo 0 > /proc/sys/vm/drop_caches
其中drop_caches的4个值有如下含义:
0:不做任何处理,由系统自己管理 1:清空pagecache 2:清空dentries和inodes 3:清空pagecache、dentries和inodes
————————————————–

问题描述

Linux服务器内存使用量超过阈值,触发报警。

问题排查

首先,通过free命令观察系统的内存使用情况,显示如下:

  1. total       used       free     shared    buffers     cached
  2. Mem:      24675796   24587144      88652          0     357012    1612488
  3. -/+ buffers/cache:   22617644    2058152
  4. Swap:      2096472     108224    1988248

其中,可以看出内存总量为24675796KB,已使用22617644KB,只剩余2058152KB。

然后,接着通过top命令,shift + M按内存排序后,观察系统中使用内存最大的进程情况,发现只占用了18GB内存,其他进程均很小,可忽略。

因此,还有将近4GB内存(22617644KB-18GB,约4GB)用到什么地方了呢?

进一步,通过cat /proc/meminfo发现,其中有将近4GB(3688732 KB)的Slab内存:

  1. ……
  2. Mapped:          25212 kB
  3. Slab:          3688732 kB
  4. PageTables:      43524 kB
  5. ……

Slab是用于存放内核数据结构缓存,再通过slabtop命令查看这部分内存的使用情况:

  1. OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
  2. 13926348 13926348 100%    0.21K 773686       18   3494744K dentry_cache
  3. 334040 262056  78%    0.09K   8351       40     33404K buffer_head
  4. 151040 150537  99%    0.74K  30208        5    120832K ext3_inode_cache

发现其中大部分(大约3.5GB)都是用于了dentry_cache。

问题解决

1. 修改/proc/sys/vm/drop_caches,释放Slab占用的cache内存空间(参考drop_caches的官方文档):

  1. Writing to this will cause the kernel to drop clean caches, dentries and inodes from memory, causing that memory to become free.
  2. To free pagecache:
  3. * echo 1 > /proc/sys/vm/drop_caches
  4. To free dentries and inodes:
  5. * echo 2 > /proc/sys/vm/drop_caches
  6. To free pagecache, dentries and inodes:
  7. * echo 3 > /proc/sys/vm/drop_caches
  8. As this is a non-destructive operation, and dirty objects are notfreeable, the user should run “sync” first in order to make sure allcached objects are freed.
  9. This tunable was added in 2.6.16.

2. 方法1需要用户具有root权限,如果不是root,但有sudo权限,可以通过sysctl命令进行设置:

  1. $sync
  2. $sudo sysctl -w vm.drop_caches=3
  3. $sudo sysctl -w vm.drop_caches=0 #recovery drop_caches

操作后可以通过sudo sysctl -a | grep drop_caches查看是否生效。

3. 修改/proc/sys/vm/vfs_cache_pressure,调整清理inode/dentry caches的优先级(默认为100),LinuxInsight中有相关的解释:

  1. At the default value of vfs_cache_pressure = 100 the kernel will attempt to reclaim dentries and inodes at a “fair” rate with respect to pagecache and swapcache reclaim. Decreasing vfs_cache_pressure causes the kernel to prefer to retain dentry and inode caches. Increasing vfs_cache_pressure beyond 100 causes the kernel to prefer to reclaim dentries and inodes.

具体的设置方法,可以参考方法1或者方法2均可。

参考资料

  • https://www.kernel.org/doc/Documentation/sysctl/vm.txt
  • http://major.io/2008/12/03/reducing-inode-and-dentry-caches-to-keep-oom-killer-at-bay/
  • http://linux-mm.org/Drop_Caches

以下记录的是进一步排查的进展情况。

更深层次的原因

上文排查到Linux系统中有大量的dentry_cache占用内存,为什么会有如此多的dentry_cache呢?

1. 首先,弄清楚dentry_cache的概念及作用:目录项高速缓存,是Linux为了提高目录项对象的处理效率而设计的;它记录了目录项到inode的映射关系。因此,当应用程序发起stat系统调用时,就会创建对应的dentry_cache项(更进一步,如果每次stat的文件都是不存在的文件,那么总是会有大量新的dentry_cache项被创建)。

2. 当前服务器是storm集群的节点,首先想到了storm相关的工作进程,strace一下storm的worker进程发现其中有非常频繁的stat系统调用发生,而且stat的文件总是新的文件名:

sudo strace -fp <pid> -e trace=stat

3. 进一步观察到storm的worker进程会在本地目录下频繁的创建、打开、关闭、删除心跳文件,每秒钟一个新的文件名:

sudo strace -fp <pid> -e trace=open,stat,close,unlink

以上就是系统中为何有如此多的dentry_cache的原因所在。

一个奇怪的现象

通过观察/proc/meminfo发现,slab内存分为两部分:

SReclaimable // 可回收的slab
SUnreclaim // 不可回收的slab

当时服务器的现状是:slab部分占用的内存,大部分显示的都是SReclaimable,也就是说可以被回收的。

但是通过slabtop观察到slab内存中最主要的部分(dentry_cache)的OBJS几乎都是ACTIVE的,显示100%处于被使用状态。

OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
13926348 13926348 100%    0.21K 773686       18   3494744K dentry_cache
334040 262056  78%    0.09K   8351       40     33404K buffer_head
151040 150537  99%    0.74K  30208        5    120832K ext3_inode_cache

为什么显示可回收的,但是又处于ACTIVE状态呢?求Linux内核达人看到后热心解释下:(

会不会由于是ACTIVE状态,导致dcache没有被自动回收释放掉呢?

让系统自动回收dcache

上一小节,我们已经提到,服务器上大部分的slab内存是SReclaimable可回收状态的,那么,我们能不能交给操作系统让他在某个时机自动触发回收操作呢?答案是肯定的。

查了一些关于Linux dcache的相关资料,发现操作系统会在到了内存临界阈值后,触发kswapd内核进程工作才进行释放,这个阈值的计算方法如下:

1. 首先,grep low /proc/zoneinfo,得到如下结果:

        low      1
        low      380
        low      12067

2. 将以上3列加起来,乘以4KB,就是这个阈值,通过这个方法计算后发现当前服务器的回收阈值只有48MB,因此很难看到这一现象,实际中可能等不到回收,操作系统就会hang住没响应了。

3. 可以通过以下方法调大这个阈值:将vm.extra_free_kbytes设置为vm.min_free_kbytes和一样大,则/proc/zoneinfo中对应的low阈值就会增大一倍,同时high阈值也会随之增长,以此类推。

$ sudo sysctl -a | grep free_kbytes       
vm.min_free_kbytes = 39847
vm.extra_free_kbytes = 0
$ sudo sysctl -w vm.extra_free_kbytes=836787  ######1GB

4. 举个例子,当low阈值被设置为1GB的时候,当系统free的内存小于1GB时,观察到kswapd进程开始工作(进程状态从Sleeping变为Running),同时dcache开始被系统回收,直到系统free的内存介于low阈值和high阈值之间,停止回收。

原文链接:http://www.cnblogs.com/panfeng412/p/drop-caches-under-linux-system.html

find -mtime -newermt 使用心得

最近需要在linux下,某目录中筛出所有mtime < 今天的文件。于是有了这篇博文。

find {path} -mtime {days}
本能地使用find -mtime +1 命令。使用后发现,筛出的一直是修改时间为48小时以外的文件。经过研究man find,亲自试验,得到了如下结论。
find -mtime -1
find -mtime 1
find -mtime +1
find在修改时间处使用的是排除法,上述三个出现的文件不会有交集。
-1是24小时之内操作过的;1是24小时之外,48小时之内操作过的;+1是48小时之外操作过的.
如果有文件同时符合两个或三个,优先出现在第一个,不会出现在第二个或第三个。比如有文件从三天前到今天一直在append,那么只会出现在find -mtime -1里。
基于此,find -mtime +1只能得到48小时之外的文件,不符合我的要求。

find {path} -newermt {time}
其实find -newermt的真正形式是find -newerXY {variable},旨在找到一些X属性比variable的Y属性更早的文件。其中X指代find的目标文件属性,Y代表参照属性。X可选a,c,m;Y可选a,c,m,t。acm意义分别为atime(访问时间),ctime(改变时间),mtime(修改时间)。t代表客观绝对时间,只作为参照属性存在,格式为yyyy-MM-dd hh:mm:ss。
由于我想要找到除了今天修改的文件之外的文件,只需要
1. 筛出mtime为今天的文件:find {path} -newermt `date +%F` -type f
2. 进行反选,在find中加入 !
所以得到的最终命令为
find {path} ! -newermt \`date +%F\` -exec {order} {} \;
最后一组{}表示exec之前筛到的文件。

结论:find -mtime更适合筛选以小时为单位(相对时间)的时间区间。对于以绝对时间(如自然日)为单位的时间区间,更适合使用find {path} (!) -newermt -type f
———————
作者:ligeforrent
来源:CSDN
原文:https://blog.csdn.net/ligeforrent/article/details/75352245
版权声明:本文为博主原创文章,转载请附上博文链接!

SSL3_GET_SERVER_CERTIFICATE 错误解决办法(SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed)

requests模块之前一直正常的,某一天开始对https的请求都抛错误了:
requests.exceptions.SSLError: [Errno 1] _ssl.c:504: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
完整的:

/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 68, in get
    return request('get', url, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 50, in request
    response = session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 464, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 576, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 431, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [Errno 1] _ssl.c:504: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

试过:

sudo apt-get install ca-certificates
sudo update-ca-certificates

没效果,
最后试了下面的起效的:

sudo pip uninstall -y certifi
sudo pip install certifi==2015.04.28

当然,还是有警告:

/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning

不够,至少可以先用着了。原因估计就是高版本(2016.2.8)的certifi对证书验证更为严格了。

为什么nohup在终端退出之后进程会退出

因为在nohup执行之后,该终端必须以exit方式正常退出,若直接退出,则有些主机会认为你异常退出而关闭你的进程。

 

 

 

进程与终端–终端关闭后,后台进程也终止

 

问题:
ssh登录服务器,并开启一个后台进程,然后自己去做其他事情了。等过了一会儿,当再来查看时,发现开启的后台进程莫名其妙的挂掉了。

原因:
无论进程是在后台还是在前台,它都与启动它的终端实例紧密绑定。当终端关闭时,它通常向绑定到终端的所有进程(前台、后台进程)发送SIGHUP信号。 这表示进程终止,因为它们的控制终端将很快不可用。
本例中,ssh登录终端,超过一定时间没有操作后,连接断开,终端关闭,与终端相关的所有进程(包括前台和后台进程)都将终止。

验证:
开启一个后台进程

代码如下:

terminal_process.c
#include <unistd.h>

int main()
{
while(1)
{
sleep(10);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
编译、后台运行

$ gcc terminal_process.c -o terminal_process
$ ./terminal_process &
1
2
3
查看前后台进程、终端

$ ps -o pid,ppid,pgrp,session,tpgid,comm,tty
PID PPID PGRP SESS TPGID COMMAND TT
15907 15906 15907 15907 16145 bash pts/1
15946 15907 15946 15907 16145 terminal_proces pts/1
16145 15907 16145 15907 16145 ps pts/1
1
2
3
4
5
其中,

pgrp 进程组ID
tpgid 控制进程组ID
tty 控制终端号
15097是会话首进程,会话ID为其ID,此时会话首进程属于后台进程组。
terminal_proces进程是15946,其父进程为bash(会话首进程15907)。
16145拥有控制终端,所以是前台进程组。
15907、15946都是后台进程组。
过一段时间,没有操作终端,ssh登录终端自动断开。
再查看进程,可以看到刚才开启的进程已经终止。

另外,本地终端强制关闭,也有类似的问题。

解决办法:
终端开启一个后台进程后,例如termal_process,然后终端下输入命令exit正常退出.

$ ./terminal_process &
$ ps -ef | grep terminal_process
lanyang 16946 16907 0 10:18 pts/1 00:00:00 ./terminal_process

$ exit
1
2
3
4
5
登录另一个终端查看

$ ps -ef | grep terminal_process
lanyang 16946 1 0 10:18 ? 00:00:00 ./terminal_process
1
2
terminal_process的父进程已经变成了init进程,不会出现刚才的问题。

除了这个方法,还有nohup,disown等其他,有兴趣可以自行搜索查看。

[FTP] Pure-FTPd SSL/TLS 配置方法

一、准备 & 安装
启用 Pure-FTPd SSL/TLS 连接方式在安装时需要检查以下两项:
1、系统中是否已经安装了 openssl 和 openssl-devel 包?
2、在编译 Pure-FTPd 的时候需要加载 –with-tls

二、证书制作
在成功安装好 Pure-FTPd 后,我们需要制作一张 SSL 证书供 Pure-FTPd 使用。

编译安装后的 Pure-FTPd 默认的证书存储位置为:/etc/ssl/private/pure-ftpd.pem,该文件会在 Pure-FTPd 启动时自动加载,若证书不存在或路径错误则 Pure-FTPd 启动失败。可以检查 /var/log/message 日志,其中记录了 Pure-FTPd 启动时的报错信息。

下面,我们开始制作证书:(以下操作需要 root 权限)
# mkdir -p /etc/ssl/private
# openssl req -x509 -nodes -newkey rsa:1024 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem
(按照提示输入证书的相关信息)
# chmod 600 /etc/ssl/private/*.pem

至此,证书就制作完成了。

三、配置 Pure-FTPd
在 Pure-FTPd 配置文件 pure-ftpd.conf 中配置所需使用的登录方式。
TLS        0|1|2
其中:
0:禁用 SSL/TLS 登录模式;
1:同时支持普通模式登录和 SSL/TLS 模式登录;
2:仅支持有效的 SSL/TLS 登录模式(推荐!)。

Jenkins 为每个job配置相应的jdk版本

 

在使用jenkins构建项目时会遇到不同的job需要配置不同版本的jdk。例如job1里构建project A 时需要的jdk版本是1.7的,但是在job2里面构建project B时,却需要的jdk版本是1.8的。或者是同一个job需要在构建时根据情况指定不同的jdk版本等情况。此时就需要为job指定使用的jdk版本,这个需求可以通过配置jenkins Global Tool Configuration 和JDK Parameter Plugin实现。
方法如下说明:

先在jenkins 上的manage Jenkins ——->Manage Plugins—–>Available里面搜索JDK Parameter Plugin安装。
在Manage Jenkins——->Global Tool Configuration——>JDK部分,点击Add JDK:

配置不同版本的jdk:
填写name(可以随便取名,主要用来标识,会出现在后续job中jdk的版本选择中),然后勾选I agree to the Java SE Development Kit Licence Agreement。

这里是通过jenkins每次去网站上自动下载,此时若是第一次配置jdk,还会要求你输入java.sun.com网站上的账号。根据提示填写就好(没有就注册一个)。

新建一个job,此时在job 的config界面会有jdk选项,供你选择jdk的版本。下拉框中的选项是之前在Global Tool Configuration中配置的jdk name

若是需要把设置jdk 版本作为参数化构建的参数,此时可以在job 的config 界面中添加参数:

填写参数的名称,选择默认的jdk版本,selectable jdks 是在参数化构建时的可选项,下拉菜单中是之前Global Tool Configuration中配置的jdk name。

该配置完成后,参数化构建job时会是下图所示的样子:

ok~为不同的job指定不同版本的jdk完成构建就完成啦,不得不感慨jenkins 的强大之处,可以集成各种各样的插件~
———————
作者:MaggieTian77
来源:CSDN
原文:https://blog.csdn.net/maggietian77/article/details/80831259
版权声明:本文为博主原创文章,转载请附上博文链接!

-bash: locate: command not found

部分版本的linux系统使用locate快速查找某文件路径会报以下错误:

-bash: locate: command not found

其原因是没有安装mlocate这个包

安装:yum  -y install mlocate

安装完再尝试用locate定位内容,发现依然不好使,报了新的错误:

locate: can not stat () `/var/lib/mlocate/mlocate.db’: No such file or directory

原因是安装完后没有更新库

更新库:updatedb

———————
作者:搁浅记忆
来源:CSDN
原文:https://blog.csdn.net/huangyuhuangyu/article/details/80347250
版权声明:本文为博主原创文章,转载请附上博文链接!

解决zabbix可用性为灰色状态

zabbix添加主机后,获取不到zabbix agent状态,并且图标为灰色,而在zabbix-server端使用zabbix_get可以正常获取到数据。在正常情况下应为绿色或红色。

问题原因,添加主机时没有添加监控项,或链接至模板,后触发zabbix可用性。

原因为:在添加zabbix主机时,虽然通过agent获取到监控对应主机信息,但是,由于并没有配置监控主机的任何指标(item),所以,ZBX仍然是灰色的,也就是说,此时只是将被监控的agent端加入了zabbix的监控范围,但是并没有对它进行任何实际的监控。需要配置任意监控项才可激活zabbix支持的监控方式。

解决zabbix可用性为灰色状态