Ubuntu Docker 配置 Tomcat 和 Nginx 使用 HTTPS 访问

安装 Docker

使用脚本自动安装

curl -fsSL get.docker.com -o get-docker.sh
sudo sh get-docker.sh --mirror Aliyun

更改镜像地址

  • 修改或新建 /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://registry.docker-cn.com"
  ]
}

启动 Docker

sudo systemctl daemon-reload
sudo systemctl enable docker
sudo systemctl start docker

配置 Tomcat

启动 Tomcat 容器

docker pull tomcat
docker run --name tomcat -d -p 8080:8080 tomcat

修改 Tomcat Manager 应用

docker exec -it tomcat /bin/bash
  • 修改 webapps/manager/META-INF/content.xml,允许需要的IP访问,这里运行所有的IP访问
<Context antiResourceLocking="false" privileged="true" >
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="^.*$" />
  <Manager sessionAttributeValueClassNameFilter="java.lang.(?:Boolean|Integer|Long|Number|String)|org.apache.catalina.filters.CsrfPreventionFilter$LruCache(?:$1)?|java.util.(?:Linked)?HashMap"/>
</Context>

配置 Tomcat 用户

  • 修改 conf/tomcat-user.xml,添加用户
<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/>

配置 Nginx

配置目录

  • 新建目录 /home/ubuntu/hellowood/dev/nginx/conf, /home/ubuntu/hellowood/dev/nginx/log, /home/ubuntu/hellowood/dev/nginx/certs
  • 下载并解压相应的Nginx证书文件到 /home/ubuntu/hellowood/dev/nginx/conf

添加 Nginx 配置

  • nginx.conf
server {
      listen 80;
      listen 443 ssl;
      server_name hellowood.com.cn;
      ssl_certificate /etc/nginx/certs/hellowood.com.cn_bundle.crt;
      ssl_certificate_key /etc/nginx/certs/hellowood.com.cn.key;
      location / {
        proxy_pass http://tomcat:8080;
      }
}

http://tomcat:8080: 将所有请求都转发到 tomcat 容器的 8080端口

启动 Nginx 容器

docker pull nginx 
docker run --name nginx -d -p 80:80 -p 443:443 
  --link tomcat:tomcat 
  -v /home/ubuntu/hellowood/dev/nginx/conf:/etc/nginx/conf.d  
  -v /home/ubuntu/hellowood/dev/nginx/log:/var/log/nginx  
  -v /home/ubuntu/hellowood/dev/nginx/certs:/etc/nginx/certs nginx

此时,访问相应的域名:http://hellowood.com.cn和https://hellowood.com.cn会显示Tomcat 的首页,配置完成

tomcat简单优化

一、内存优化

1、修改tomcat路径bin目录下的catalina.sh

cat >> catalina.sh << END
JAVA_OPTS='-Xms20480m -Xmx20480m -Xss256k -XX:PermSize=256M -XX:MaxNewSize=256m -XX:MaxPermSize=256m'
END

参数:

-Xms  jvm初始化堆内存大小
-Xmx  jvm堆的最大内存   #堆内存建议设为服务器内存的60%~80%
-Xss  线程栈大小       #一般设置为256k

-XX:PermSize     jvm非堆区初始内存分配大小
-XX:MaxPermSize  jvm非堆区最大内存 #非堆内存不可回收,看项目大小而定

2、重启tomcat验证:jmap -heap 12345(tomcat PID)

二、配置优化

修改server.xml添加参数

maxThreads        客户请求最大线程数 
minSpareThreads   初始化时创建的 socket 线程数 
maxSpareThreads   连接器的最大空闲 socket 线程数 
enableLookups     若设为true, 则支持域名解析,可把 ip 地址解析为主机名 
redirectPort      在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口 
acceptAccount     监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads) 
connectionTimeout 连接超时 
minProcessors     服务器创建时的最小处理线程数 
maxProcessors     服务器同时最大处理线程数 
URIEncoding URL   统一编码

生产中配置:

<Connector executor="tomcatThreadPool"
           port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           minSpareThreads="150" 
           maxSpareThreads="500"
           enableLookups="false" 
           disableUploadTimeout="true" 
           acceptCount="1000"   
           maxThreads="1000" 
           maxProcessors="1000" 
           minProcessors="5"
           useURIValidationHack="false"
           compression="on" 
           compressionMinSize="2048"
           compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
           uriencoding="utf-8"
           redirectPort="8443" />

三、优化网卡驱动

$ cat >> /etc/sysctl.cnf << END
net.core.netdev_max_backlog = 32768
net.core.somaxconn = 32768 
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 65536

END

$ sysctl -p

tcpdump只抓取HTTP报文头部

因为要做一个需求,我需要调研现网请求http头部的大小,都有什么字段,shell脚本代码如下所示

#! /bin/bash

s_512=0
s_512_1k=0
s_1k_2k=0
s_2k_4k=0
s_4k_8k=0
s_8k=0

idx=0

while true
do

   if (($idx >= 10000));then
       break
   fi

   tcpdump "tcp[20:2]=0x4854" -i eth0 -nn  -A -c 1 | sed "s/.*HTTP/HTTP/g" > tmp_http_header.log
   cat tmp_http_header.log >> http_header.log
   #notice ^M CTRL-V Shift-M
   #cat -v
   #sed -n '/HTTP/,/^^M/p' tmp_http_header.log
   #  ^M is special character
   sed -i -e 's/^^M$/vaynedu_test_http/g' tmp_http_header.log
   header_num=`cat tmp_http_header.log | grep  -A100 "HTTP" | grep -m1 -B100  "vaynedu_test_http" | grep -v "vaynedu_test_http" | wc -c `
  # echo $header_num

   if (($header_num < 512));then
       let s_512++
   elif (($header_num < 1024));then
       let s_512_1k++
   elif (($header_num < 2048));then
       let s_1k_2K++
       echo "vaynedu_1k_2k $header_num" >> http_header.log
   elif (($header_num < 4096));then
       let s_2k_4k++
       echo "vaynedu_2k_4k $header_num" >> http_header.log
   elif (($header_num < 8192));then
       let s_4k_8k++
       echo "vaynedu_4k_8k $header_num" >> http_header.log
   else
       let s_8k++
       echo "vaynedu_8k    $header_num" >> http_header.log
   fi

   let idx++
done

>count_result.txt
echo "http header size:"  >> count_result.txt
echo "[0, 512) : $s_512" >> count_result.txt
echo "(512,1k] : $s_512_1k" >> count_result.txt
echo "(1k, 2k] : $s_1k_2k"  >> count_result.txt
echo "(2k, 4k] : $s_2k_4k"  >> count_result.txt
echo "(4k, 8k] : $s_4k_8k"  >> count_result.txt
echo "(8k, ++] : $s_8k"  >> count_result.txt

注意注意^M是特殊字符是特殊字符

抓取1w个http请求,并且将这些头部保存下来,统计1w请求http头部大小在哪个区间,下面效果展示

未分类

未分类

Ubuntu 16.04 LTS ssh安装和配置

第一步:获取root权限

sudo -i

根据提示输入密码

第二步:更新源列表

sudo apt-get update

第三步:安装ssh

sudo apt-get install openssh-server

第四步:配置ssh

gedit /etc/ssh/ssh_config

注释掉 PermitRootLogin without-password
加入 PermitRootLogin yes

第五步:启动ssh服务

service ssh start

温馨提示:ssh默认端口号为22

CentOS监控ssh免密登录

ssh免密登录在带来方便的同时也带来一些问题,那就是不知道什么时间什么人利用ssh免密通道登录服务器了,为此我们需要在sshd的配置文件里设置好详细日志,以便日后回溯。

在CentOS里,sshd的日志文件是保存在/var/log/secure里的,如果不进行特殊设置的话,它只记录一些最简单的信息,比如什么时间哪个账号被人用免密登录的方式登录了,如果这个账号的authorized_keys里有很多key的话,这样的log没有办法告诉你客户到底是用哪个key登录的。

所以,我们需要在sshd的配置文件/etc/ssh/sshd_config文件里找到以下项LogLevel并把它改成LogLevel VERBOSE,这一项的缺省值是INFO,不够详细,所以我们需要把它改成啰嗦模式,这样可以记录更多信息。

改成VERBOSE并重启sshd(service sshd restart)后,我们会在日志文件里看到类似于这样的记录:

Apr 1 10:37:06 hostname sshd[5903]: Found matching RSA key: 83:67:b5:c7:bb:17:4d:06:ca:dc:8b:ca:85:cc:0c:b1

但这样的信息明显不同于我们在authorized_keys里存放的信息,那该怎么办呢?实际上,在sshd的日志文件里存储的只是我们authorized_keys的指纹信息fingerprint,不是真正的key,必须从authorized_keys反算出fingerprint来,才能做对比:

ssh-keygen -E md5 -lf /home/someuser/.ssh/authorized_keys

但是这样依然很麻烦,有没有办法直接告诉我日志里到底是谁登录的呢?为此我们还需要引入一个用Perl写的小程序点击预览。(原作者写的略有问题,在新版的CentOS里必须要求附加md5参数,为此我做了一些小的修改):

#!/usr/bin/perl -w
use strict;
use diagnostics;
use File::Temp;

# Matches Fingerprints from sshd logs (sshd on loglevel VERBOSE) against
# authorized_keys for the respective user.

die "Please specify input file!n" unless ($ARGV[0]);

my $fingerprints;

my $nav = File::Navigate->new($ARGV[0]);
# Store publickey login events
my @lines = @{$nav->find(qr/sshd[d+]: Accepted publickey for .+ from .+ port d+/)};

# Process all publickey login events
foreach(@lines){
    $nav->cursor($_);
    my $line = $nav->get();
    $line =~ /^(.{15}).+sshd[(d+)]: Accepted publickey for (.+) from (.+) port (d+)/;
    my $date = $1;
    my $pid  = $2;
    my $user = $3;
    my $ip   = $4;
    my $port = $5;
    my $fp   = "unknown"; # (yet)
    # Seek backwards to find matching fingerprint line
    my $sought = 0;
    while ((my $seekline = $nav->getprev()) and ($sought++ < 1000)){
        if ($seekline =~ /sshd[$pid]: Found matching .+ key: (.+)/){
            $fp = $1;
            last;
        }elsif($line =~ /sshd[$pid]: Connection from $ip port $port/){
            last;
        }
    }
    my $key = get_key($fp, $user);
    print ""$date";"$user";"$fp";"$key"n";
}

sub get_key{
    my $fp   = shift;
    $fp = "MD5:" . $fp;
    my $user = shift;

    # See if FP is cached
    if ($fingerprints->{$user}){
        if ($fingerprints->{$user}->{$fp}){
            return $fingerprints->{$user}->{$fp};
        }else{
            return "No matching key found.";
        }
    }

    # Else, generate fingerprints from users authorized_keys
    print STDERR "------> Reading keys for user $usern";
    my $home = (getpwnam($user))[7];
    open my $fh_in, "<$home/.ssh/authorized_keys" or warn "No such file: $home/.ssh/authorized_keysn";
    while (<$fh_in>){
        chomp;
        next unless (/^ssh-/);
        my $out_fh = File::Temp->new();
        print $out_fh "$_n";
        close $out_fh;
        my $fp_raw = `ssh-keygen -E md5 -lf $out_fh`;
        # Second field of output has the fingerpring
        my $fp = (split /s+/, $fp_raw)[1];
        $fingerprints->{$user}->{$fp} = $_;
    }
    if ($fingerprints->{$user}->{$fp}){
        return $fingerprints->{$user}->{$fp};
    }else{
        return "No matching key found.";
    }
}

package File::Navigate;
use strict;
use warnings;

=head1 NAME

File::Navigate - Navigate freely inside a text file

=head1 DESCRIPTION

The module is a glorified wrapper for tell() and seek().

It aims to simplify the creation of logfile analysis tools by
providing a facility to jump around freely inside the contents
of large files without creating the need to slurp excessive
amounts of data.

=head1 SYNOPSIS

  use File::Navigate;
  my $nav = File::Navigate->new('/var/log/messages');

  # Read what's below the "cursor":
  my $first = $nav->get;

  # Advance the cursor before reading:
  my $second = $nav->getnext;
  my $third  = $nav->getnext;

  # Advance the cursor by hand:
  $nav->next;
  my $fourth = $nav->get;

  # Position the cursor onto an arbitrary line:
  $nav->cursor(10);
  my $tenth  = $nav->get;

  # Reverse the cursor one line backward:
  $nav->prev;
  my $ninth  = $nav->get;

  # Reverse the cursor before reading:
  my $eigth  = $nav->getprev;

  # Read an arbitrary line:
  my $sixth  = $nav->get(6);

=cut

our @ISA       = qw(Exporter);
our @EXPORT_OK = qw();
our $VERSION   = '1.0';

=head1 CLASS METHODS

=head2 I<new()>

Open the file and create an index of the lines inside of it.

  my $mapper = File::Navigate->new($filename);

=cut

sub new($){
    my $class = shift;
    my $file;
    unless ($file = shift){
        die "No file specifiedn";
    }
    unless (-e $file){
        die "File not found: $filen";
    }
    unless (-r $file){
        die "File not readable: $filen";
    }
    my $self = {};
       $self->{'cursor'}         = 1;
       $self->{'lineindex'}      = {};
       $self->{'lineindex'}->{1} = 0;
    open my $fh, "$file"
        or die "Can't open $file: $!n";
    while (<$fh>){
        my $thisline = $.;
        my $nextline = $thisline + 1;
        $self->{'lineindex'}->{$nextline} = tell $fh;
    }
    $self->{'length'} = scalar(keys %{$self->{'lineindex'}}) - 1 ;
    $self->{'fh'} = $fh;
    bless $self;
}

=head1 OBJECT METHODS

=head2 I<count()>

Returns the number of lines in the file ("wc -l")

  my $lines = $nav->count;

=cut

sub length(){
    my $self = shift;
    return $self->{'length'};
}

=head2 I<cursor()>

Returns the current cursor position and/or sets the cursor.

  my $cursor = $nav->cursor();   # Query cursor position.
  my $cursor = $nav->cursor(10); # Set cursor to line 10

=cut

sub cursor($){
    my $self = shift;
    if (my $goto = shift){
        $self->{'cursor'} = $goto;
    }
    return $self->{'cursor'};
}

=head2 I<get()>

Gets the line at the cursor position or at the given position.

  my $line = $nav->get();   # Get line at cursor
  my $line = $nav->get(10); # Get line 10

=cut

sub get($){
    my $self = shift;
    my $fh   = $self->{'fh'};

    my $getline;
    $getline = $self->{'cursor'} unless ($getline = shift);

    if ($getline < 1){
        warn "WARNING: Seek before first line.";
        return undef;
    }elsif($getline > $self->{'length'}){
        warn "WARNING: Seek beyond last line.";
        return undef;
    }
    seek ($fh, $self->{'lineindex'}->{$getline}, 0);
    my $gotline = <$fh>;
    chomp $gotline;
    return $gotline;
}

=head2 I<next()>

Advance the cursor position by one line. Returns the new cursor position.
Returns I<undef> if the cursor is already on the last line.

  my $newcursor = $nav->next();

=cut

sub next(){
    my $self = shift;
    if ($self->{'cursor'} == $self->{'length'}){
        return undef;
    }
    $self->{'cursor'}++;
    return $self->{'cursor'};
}

=head2 I<prev()>

Reverse the cursor position by one line. Returns the new cursor position.
Returns I<undef> if the cursor is already on line 1.

  my $newcursor = $nav->prev();

=cut

sub prev(){
    my $self = shift;
    if ($self->{'cursor'} == 1){
        return undef;
    }
    $self->{'cursor'}--;
    return $self->{'cursor'};
}

=head2 I<getnext()>

Advance to the next line and return it.
Returns I<undef> if the cursor is already on the last line.

  my $newcursor = $nav->getnext();

=cut

sub getnext(){
    my $self = shift;
    $self->next or return undef;
    return $self->get;
}

=head2 I<getprev()>

Reverse to the previous line and return it:
Returns I<undef> if the cursor is already on line 1.

  my $newcursor = $nav->getprev();

=cut

sub getprev(){
    my $self = shift;
    $self->prev or return undef;
    return $self->get;
}

=head2 I<find()>

Find lines containing given regex. Returns array with line numbers.

  my @lines = @{$nav->find(qr/foo/)};

=cut

sub find($){
    my $self = shift;
    my $regex = shift;

    my @results;
    for (my $lineno = 1; $lineno <= $self->{'length'}; $lineno++){
        my $line = $self->get($lineno);
            if ($line =~ $regex){
            push @results, $lineno;
        }
    }
    return @results;
}

sub DESTROY(){
    my $self = shift;
    close $self->{'fh'};
}

=head1 EXAMPLE

I<tac>, the opposite of I<cat>, in Perl using File::Navigate:

  #!/usr/bin/perl -w
  use strict;
  use File::Navigate;

  foreach my $file (reverse(@ARGV)){
          my $nav = File::Navigate->new($file);
          # Force cursor beyond last line
          $nav->cursor($nav->length()+1);
          print $nav->get()."n" while $nav->prev();
  }

=head1 BUGS

Seems to lack proper error handling.

=head1 LIMITATIONS

Works only on plain text files. Sockets, STDIO etc. are not supported.

=head1 PREREQUISITES

Tested on Perl 5.6.1.

=head1 STATUS

Mostly harmless.

=head1 AUTHOR

Martin Schmitt <mas at scsy dot de>

=cut

1;

为此我们给它起名叫match-ssh-keys,赋予它可执行权限(chmod +x match-ssh-keys),然后把它搬到/usr/local/bin里(mv match-ssh-keys /usr/local/bin/),这样我们以后再想查谁通过sshd免密登录过服务器就方便了,我们只需要执行:

match-ssh-keys /var/log/secure

就可以了。

解决内网ssh连接速度很慢的解决方案

服务器相互ping是延时小,但ssh -p 端口号 内网IP 时发现很慢,好长时间才返回。把服务器改按以下方法修改相应配置,再ssh相互连接发现很快:

# vi /etc/ssh/sshd_conf
UseDNS no

GSSAPIAuthentication no

保存退出

#service sshd restart

使用inotify-tools与rsync构建实时备份系统

使用inotifywait监控文件变动

inotifywait是 inotify-tools 包中提供的一个工具,它使用 inotify API 来监控文件/目录中的变动情况。

在archlinux上,我们可以使用下面命令来安装

sudo pacman -S --noconfirm inotify-tools

平时 inotifywait 会挂起在那里,直到文件/目录发生了要引起关注的事件后,它会退出并输出事件发生的场所、事件的名称以及引起事件的文件(当事件发生在目录上时才会输出).

inotifywait 最常用的选项有两个,一个是 -r 一个是 -e ,其中:

-r
表示递归监控子目录中文件发生的事件

-e
指定要监控的事件列表。对于备份系统来说,只需要监控 modify、create和delete三种事件就行了。

比如,我们运行

inotifywait -r -e modify,create,delete /tmp

表示监控 /tmp 目录及其子目录中文件修改、文件创建和文件删除三种事件。

这时程序一直在挂起状态

[lujun9972@X61 ~]$ inotifywait -r -e modify,create,delete /tmp
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

这时在 /tmp 目录下新建一个文件

touch /tmp/newFile

则 inotifywait 进程退出,并输出如下信息

/tmp/ CREATE newFile

使用rsync同步变动

rsync是一款快速增量备份工具。它的具有以下几个特点使得它很适合用作做备份的工具:

  • 增量备份,只会传输修改过的内容
  • 可以在传输过程中实时解压缩,减少带宽消耗
  • 可以保持原来文件的权限、事件、软硬链接
  • 即支持本机复制,也支持远程复制

rsync常用法为:

rsync -avz --delete  src/ foo:/data

其中

-a
表示archive mode,即备份目录下的所有内容(包括子目录中的内容),并且保持软链接、文件属性、文件修改事件、文件的所有者和宿主信息不变,并且同步字符/块设备以及命名socket和fifo等特殊文件。

-v
表示输出备份的详细信息

-z
表示传输时进行压缩

--delete
删除备份目的地里src中没有的文件

src/
表示要备份的是src目录下的所有内容,注意这里最后的 / 不能去掉,否则会把src目录本身备份过去

foo:/data
表示备份的目的地是foo主机下的 /data/ 目录

整合起来

接下来我们只需要用个 while 死循环把两个工具整合起来就行了,非常简单

#!/bin/bash

if [[ $# -ne 2 ]];then
    cat<<EOF
Usage $(basename $0) source_dir [host:]dest_dir
EOF
    exit 0
fi

source_dir=$1
dest_dir=$2
while :
do
    inotifywait -r -e modify,create,delete ${source_dir} && rsync -avz ${source_dir}/ ${dest_dir} --delete
done

这里有必要说明的是,虽然用 inotifywait 能探测出文件具体做了什么改动,但实际上我们根本不需要知道具体的改变是什么。

我们只需要知道有所改变了,然后具体改变了什么由 rsync 来自己处理就行了。

解决nginx反向代理缓存不起作用的问题

昨天尝试用nginx搭建nuget镜像服务器,镜像服务器需要两个功能:1)反向代理;2)内容缓存。

用nginx做反向代理,配置非常简单,只需在/etc/nginx/nginx.conf中添加一个包含proxy_pass的server设置:

server {
    listen       80;
    listen       [::]:80;
    server_name  镜像服务器主机名;
    location / {
        proxy_pass http://www.nuget.org;
    }
}

而添加缓存功能,配置稍微复杂些。

首先要创建一个用于存放缓存文件的文件夹,比如这里用 /data/nuget-cache 。

mkdir /data/nuget-cache

然后在nginx.conf的http设置部分添加proxy_cache_path设置:

proxy_cache_path /data/nuget-cache levels=1:2 keys_zone=nuget-cache:20m max_size=50g inactive=168h;
  • keys_zone指的是缓存空间名称。

  • max_size指的是缓存文件可以占用的最大空间。

  • inactive指的是如果一个缓存文件多长时间不被访问,就会被删除。

接着在server设置部分添加proxy_cache与proxy_cache_valid设置:

server {
    listen       80;
    listen       [::]:80;
    server_name  镜像服务器主机名;
    location / {
        proxy_pass http://www.nuget.org;
        proxy_cache nuget-cache;
        proxy_cache_valid 168h;
    }
}
  • proxy_cache设置的就是proxy_cache_path中keys_zone的值。

  • proxy_cache_valid设置的是缓存过期时间,比如这里168小时过期。

这样设置后运行nginx,发现只有少部分内容被缓存,大部分内容不能被缓存,比如下面的URL的响应内容就不能被缓存:

http://www.myget.org/F/aspnetvnext/api/v2/FindPackagesById%28%29?id=%27System.Linq%27

查看http响应头(http headers),发现了下面2个http header:

Cache-Control: private
Set-Cookie: ai_session=ad829b6c509946098fa7f8e32fada661|2015-06-24T03:52:38.2032109+00:00|2015-06-24T03:52:38.4219541
+00:00; expires=Wed, 24-Jun-2015 04:22:38 GMT; path=/

问题就是它们引起的,需要在nginx中通过proxy_ignore_headers设置忽略它们,设置方法如下:

server {
    listen       80;
    listen       [::]:80;
    server_name  镜像服务器主机名;
    location / {
        proxy_pass http://www.nuget.org;
        proxy_cache nuget-cache;
        proxy_cache_valid 168h;
        proxy_ignore_headers Set-Cookie Cache-Control;
        proxy_hide_header Cache-Control;
        proxy_hide_header Set-Cookie;
    }
}

修改 Nginx 服务器 WordPress 上传文件大小限制

默认情况下 WordPress 上传文件限制为 2M,如果有上传较大文件的需要,我们需要将上传文件的大小上限调大。

修改 PHP 配置文件

编辑 PHP 配置文件 php.ini ,查找以下字段:

$ sudo vi /etc/php.ini

post_max_size=8M 
upload_max_filesize=2M 

其中,post_max_size 参数表示 POST 数据所允许的最大大小,一般要设置的比upload_max_filesize大;upload_max_filesize 参数表示默认上传文件大小。

将其改为自己需要的值,例如:

post_max_size=80M
upload_max_filesize=50M

修改 Nginx 配置文件

修改当前网站的 Nginx 配置文件,这个文件一般在 /etc/nginx/conf.d 目录下,后缀名为 .conf,在 http{} 字段中添加以下内容:

client_max_body_size 50M;

完成上述修改后,重启 PHP 与 Nginx 使修改生效:

sudo service nginx restart
sudo service php-fpm restart

完成后 WordPress 上传文件的大小限制就变成 50M 了!

Nginx 使用 GeoIP 模块区分用户地区

检查 GeoIP 是否安装

首先需要确认当前安装的 Nginx 是否安装了 GeoIP 模块

$ nginx -V
nginx version: nginx/1.12.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --user=nginx --group=nginx --with-http_geoip_module --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-mail --with-mail_ssl_module --with-file-aio

如果版本信息中包含 –with-http_geoip_module,则说明已经支持该模块,如果不支持请往下看

安装 GeoIP

首先安装依赖

$ yum -y install zlib zlib-devel

安装 GeoIP

$ wget http://geolite.maxmind.com/download/geoip/api/c/GeoIP.tar.gz
$ tar -zxvf GeoIP.tar.gz
$ cd GeoIP-1.4.8
$ ./configure
$ make
$ make install

使用ldconfig将库索引到系统中

$ echo '/usr/local/lib' > /etc/ld.so.conf.d/geoip.conf
$ ldconfig

检查库是否加载成功

$ ldconfig -v | grep GeoIP
libGeoIPUpdate.so.0 -> libGeoIPUpdate.so.0.0.0
libGeoIP.so.1 -> libGeoIP.so.1.4.8
libGeoIPUpdate.so.0 -> libGeoIPUpdate.so.0.0.0
libGeoIP.so.1 -> libGeoIP.so.1.5.0

将 GeoIP 模块编译到 Nginx 中

根据你当前 Nginx 的安装参数带上 –with-http_geoip_module 重新编译

$ ./configure --user=nginx --group=nginx 
    --with-http_geoip_module 
    --with-http_ssl_module 
    --with-http_realip_module 
    --with-http_addition_module 
    --with-http_sub_module 
    --with-http_dav_module 
    --with-http_flv_module 
    --with-http_mp4_module 
    --with-http_gunzip_module 
    --with-http_gzip_static_module 
    --with-http_random_index_module 
    --with-http_secure_link_module 
    --with-http_stub_status_module 
    --with-mail 
    --with-mail_ssl_module 
    --with-file-aio
$ make && make install

或者重新安装

$ wget https://nginx.org/download/nginx-1.12.2.tar.gz
$ tar zxvf nginx-1.12.2.tar.gz
$ cd nginx-1.12.2
$ ./configure --user=nginx --group=nginx 
    --with-http_geoip_module 
    --with-http_ssl_module 
    --with-http_realip_module 
    --with-http_addition_module 
    --with-http_sub_module 
    --with-http_dav_module 
    --with-http_flv_module 
    --with-http_mp4_module 
    --with-http_gunzip_module 
    --with-http_gzip_static_module 
    --with-http_random_index_module 
    --with-http_secure_link_module 
    --with-http_stub_status_module 
    --with-mail 
    --with-mail_ssl_module 
    --with-file-aio
$ make && make install

使用 GeoIP

首先查看本地是否已有 GeoIP 数据库

$ cd /usr/local/share/GeoIP
$ ll
-rw-r--r--. 1 root root  1183408 Mar 31 06:00 GeoIP.dat
-rw-r--r--. 1 root root 20539238 Mar 27 05:05 GeoLiteCity.dat

如果没有这两个库,则手动下载

wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gzip GeoLiteCity.dat.gz
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
gzip GeoIP.dat.gz

将库地址配置到 nginx.conf 中这个位置

http{
    ...
    geoip_country /usr/local/share/GeoIP/GeoIP.dat;
    geoip_city /usr/local/share/GeoIP/GeoLiteCity.dat;
    server {
        location / {
            root /www;
            if( $geo_country_code = CN ){
                root /www/zh;
            }
        }
    }
}

其他参数

  • $geoip_country_code; – 两个字母的国家代码,如:”RU”, “US”。
  • $geoip_country_code3; – 三个字母的国家代码,如:”RUS”, “USA”。
  • $geoip_country_name; – 国家的完整名称,如:”Russian Federation”, “United States”。
  • $geoip_region – 地区的名称(类似于省,地区,州,行政区,联邦土地等),如:”30”。 30代码就是广州的意思
  • $geoip_city – 城市名称,如”Guangzhou”, “ShangHai”(如果可用)。
  • $geoip_postal_code – 邮政编码。
  • $geoip_city_continent_code。
  • $geoip_latitude – 所在维度。
  • $geoip_longitude – 所在经度。