1. 程式人生 > >nginx的配置、虛擬主機、負載均衡和反向代理--02

nginx的配置、虛擬主機、負載均衡和反向代理--02

基於域名的虛擬主機

假設我們在本地開發有3個專案,分別在hosts裡對映到本地的127.0.0.1上:

127.0.0.1 www.iyangyi.com iyangyi.com
127.0.0.1 api.iyangyi.com
127.0.0.1 admin.iyangyi.com

有這樣3個專案,分別對應於web根目錄下的3個資料夾,我們用域名對應資料夾名字,這樣子好記:

/Users/yangyi/www/www.iyangyi.com/
/Users/yangyi/www/api.iyangyi.com/
/Users/yangyi/www/admin.iyangyi.com/

每個目錄下都有一個index.php檔案,都是簡單的輸入自己的域名。

下面我們就來搭建這3個域名的虛擬主機,很顯然,我們要新建3個server來完成。
為了看起來簡潔好看,我們使用require來包含外面的3個server在nginx.conf中,這樣就清晰了很多。不會使得這個nginx.conf內容太多:

main
events   {
  ....
}
http        {
  ....
  include vhost/www.iyangyi.conf;
  include vhost/api.iyangyi.conf;
  include vhost/admin.iyangyi.conf;
  #或者用 *.conf  包含
  # include vhost/*.conf
}

既然每一個conf都是一個server,第一篇文章中已經學習了一個完整的server寫的了。下面就開始:

配置: www.iyangyi.com

# www.iyangyi.conf
server {
    listen 80;
    server_name www.iyangyi.com iyangyi.com;
    root /Users/yangyi/www/www.iyangyi.com/;
    index index.php index.html index.htm;
    access_log /usr/local/var/log/nginx/www.iyangyi.access
.log main; error_log /usr/local/var/log/nginx/www.iyangyi.error.log error; location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; } }

配置: api.iyangyi.com

# api.iyangyi.conf
server {
    listen 80;
    server_name api.iyangyi.com;
    root /Users/yangyi/www/api.iyangyi.com/;
    index index.php index.html index.htm;
    access_log /usr/local/var/log/nginx/api.iyangyi.access.log main;
    error_log /usr/local/var/log/nginx/api.iyangyi.error.log error;
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000; 
        fastcgi_index  index.php;
        include        fastcgi.conf;
    }
}

配置: admin.iyangyi.com

# admin.iyangyi.conf
server {
    listen 80;
    server_name admin.iyangyi.com;
    root /Users/yangyi/www/admin.iyangyi.com/;
    index index.php index.html index.htm;
    access_log /usr/local/var/log/nginx/admin.iyangyi.access.log main;
    error_log /usr/local/var/log/nginx/admin.iyangyi.error.log error;
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000; 
        fastcgi_index  index.php;
        include        fastcgi.conf;
    }
}

這樣3個很精簡的虛擬域名就搭建好了。重啟下nginx,然後開啟瀏覽器訪問一下這3個域名,就能看到對應的域名內容了。

反向代理

正向代理

在說啥啥反向代理之前,先說下什麼是代理或者正向代理。

正向代理也就是代理,他的工作原理就像一個跳板,簡單的說,我訪問不了google.com,但是我能訪問一個代理伺服器A,
A能訪問google.com,於是我先連上代理伺服器A,告訴他我需要google.com的內容,A就去取回來,然後返回給我。
從網站的角度,只在代理伺服器來取內容的時候有一次記錄,有時候並不知道是使用者的請求,也隱藏了使用者的資料,這取決於代理告不告訴網站。

結論就是,
**正向代理是一個位於客戶端和原始伺服器(origin server)之間的伺服器。
為了從原始伺服器取得內容,客戶端向代理髮送一個請求並指定目標(原始伺服器),然後代理向原始伺服器轉交請求並將獲得的內容返回給客戶端**。

正向代理的實際例子如VPN這類的代理工具。

反向代理

舉個例子,比如我想訪問 http://www.test.com/readme,但www.test.com上並不存在readme頁面,於是他是偷偷從另外一臺伺服器上取回來,然後作為自己的內容返回使用者,但使用者並不知情。
這裡所提到的 www.test.com 這個域名對應的伺服器就設定了反向代理功能。

結論就是,反向代理正好相反,對於客戶端而言它就像是原始伺服器,並且客戶端不需要進行任何特別的設定。
客戶端向反向代理的名稱空間(name-space)中的內容傳送普通請求,接著反向代理將判斷向何處(原始伺服器)轉交請求,並將獲得的內容返回給客戶端,就像這些內容原本就是它自己的一樣。

正向代理和反向代理的基本概念算是理清楚了,那我們就來用nginx來配置一個反向代理。

nginx 使用反向代理,主要是使用location模組下的proxy_pass選項。

來個最簡單的。當我訪問 mac 上的nginx 的 centos.iyangyi.com 的內容時候, 就反向代理到虛擬機器centos上的 apache 192.168.33.10 的index.html頁面。

192.168.33.10 中的html 是很簡單的一句輸出:

centos apache2 index.html

在hosts裡新加上這個域名。

#vi /etc/hosts 
127.0.0.1 centos.iyangyi.com

在vhost目錄中新建一個conf server

#centos.iyangyi.conf
server {
    listen 80;
    server_name centos.iyangyi.com;
    access_log /usr/local/var/log/nginx/centos.iyangyi.access.log main;
    error_log /usr/local/var/log/nginx/centos.iyangyi.error.log error;
    location / {
        proxy_pass http://192.168.33.10;
    }
}

重啟下nginx:

sudo nginx -s reload

開啟瀏覽器,就可以看到頁面輸出了:

centos apache2 index.html

當然。proxy 還有其他的引數,比如:proxy_set_header 用來設定header頭部資訊引數轉發等,等用了可以仔細看看。

負載均衡

別被這個名字給嚇住了,以為是什麼很牛逼的東西的。其實不然。也很簡單。

先簡單說下負載均衡是幹嘛的?舉個例子:我們的小網站,剛開始就一臺nginx伺服器,後來,隨著業務量增大,使用者增多,一臺伺服器已經不夠用了,我們就又多加了幾臺伺服器。
那麼這幾臺伺服器如何排程?如何均勻的提供訪問?這就是負載均衡。

負載均衡的好處是可以叢集多臺機器一起工作,並且對外的IP 和 域名是一樣的,外界看起來就好像一臺機器一樣。

nginx 也剛好提供了強大而又簡單的負載均衡功能。

在第一節中,我詳細講了nginx的負載均衡模組upstream,負載均衡呢,主要是用這個模組。

我們先用vagrant搭建一個centos虛擬機器叢集(附vagrantfile檔案)

Vagrant.configure(2) do |config|
  config.vm.define :web1 do |web1|
    web1.vm.provider "virtualbox" do |v|
          v.customize ["modifyvm", :id, "--name", "web1", "--memory", "128"]
    end
    web1.vm.box = "centos65"
    web1.vm.hostname = "web1"
    web1.vm.network :private_network, ip: "192.168.33.11"
  end
  config.vm.define :web2 do |web2|
    web2.vm.provider "virtualbox" do |v|
          v.customize ["modifyvm", :id, "--name", "web2", "--memory", "128"]
    end
    web2.vm.box = "centos65"
    web2.vm.hostname = "web2"
    web2.vm.network :private_network, ip: "192.168.33.12"
  end
  config.vm.define :web3 do |web3|
    web3.vm.provider "virtualbox" do |v|
          v.customize ["modifyvm", :id, "--name", "web3", "--memory", "128"]
    end
    web3.vm.box = "centos65"
    web3.vm.hostname = "web3"
    web3.vm.network :private_network, ip: "192.168.33.13"
  end
end

分別配置靜態ip並且取名字:

web1 192.168.33.11
web2 192.168.33.12
web3 192.168.33.13

然後,我們分別啟動vagrant ssh web1,web2,web3,並且分別用yum簡單安裝好apache。並在www目錄裡面新建index.html,分別輸出一句簡單的web1、web2、web3。

好,這個時候,我們在瀏覽器裡輸入對應的IP就會顯示對應的web*名字了。

ok,我們再來配置mac下的nginx,前面說過,主要是用nginx的upstream,我接下來要完成的需求就是當我訪問upstram.iyangyi.com時,會自動的負載均衡到這3個伺服器上去。

先在hosts里加上

127.0.0.1 upstream.iyangyi.com

好,按照慣例,新建一個upstream.iyangyi.conf的server配置檔案。

基於 weight 權重的負載

先來一個最簡單的,weight權重的:

upstream webservers{
    server 192.168.33.11 weight=10;
    server 192.168.33.12 weight=10;
    server 192.168.33.13 weight=10;
}
server {
    listen 80;
    server_name upstream.iyangyi.com;
    access_log /usr/local/var/log/nginx/upstream.iyangyi.access.log main;
    error_log /usr/local/var/log/nginx/upstream.iyangyi.error.log error;
    location / {
        proxy_pass http://webservers;
        proxy_set_header  X-Real-IP  $remote_addr;
    }
}

重啟nginx nginx -s reload,開啟瀏覽器輸入upstream.iyangyi.com,不斷重新整理下,就能看到變化顯示web1,web2,web3。說明我們的負載均衡起作用了。

我們再開啟web[1-3]的apache的訪問日誌:

sudo vi /var/log/httpd/access_log
192.168.33.1 - - [12/May/2015:10:47:02 +0000] "GET / HTTP/1.0" 200 5 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36"

上面也顯示出了ip為這臺mac的地址。說明負載均衡已經生效。

我們再來繼續看幾個引數 : max_fails和fail_timeout

max_fails : 允許請求失敗的次數,預設為1。當超過最大次數時,返回proxy_next_upstream 模組定義的錯誤。

fail_timeout : 在經歷了max_fails次失敗後,暫停服務的時間。max_fails可以和fail_timeout一起使用,進行健康狀態檢查。

server 192.168.33.11 weight=1 max_fails=2 fail_timeout=30s;

所以這2個一起搭配使用,表示:當失敗2次的時候,就停止使30秒

好,我們來繼續做實驗,將web1的httpd服務停掉,然後我們加上max_fails可以和fail_timeout。

sudo /usr/sbin/apachectl stop
upstream webservers{
    server 192.168.33.11 weight=10 max_fails=2 fail_timeout=30s;
    server 192.168.33.12 weight=10 max_fails=2 fail_timeout=30s;
    server 192.168.33.13 weight=10 max_fails=2 fail_timeout=30s;
}

重啟nginx,然後重新整理upstream.iyangyi.com,就能看到變化顯示,只有web2,web3,web1沒有了。

看下日誌,顯示web1 掛了:

2015/05/14 15:15:56 [error] 2381#0: *93 kevent() reported that connect() failed (61: Connection refused) while connecting to upstream, client: 127.0.0.1, server: upstream.iyangyi.com, request: "GET / HTTP/1.1", upstream: "http://192.168.33.11:80/", host: "upstream.iyangyi.com"

我們再來繼續看剩下幾個引數 : down和backup

down 表示這臺機器暫時不參與負載均衡。相當於註釋掉了。

backup 表示這臺機器是備用機器,是其他的機器不能用的時候,這臺機器才會被使用,俗稱備胎 O__O "…

我們繼續來做實驗,改一下,先把web1改成down,然後將web3改成backup:

upstream webservers{
    server 192.168.33.11 down;
    server 192.168.33.12 weight=10 max_fails=2 fail_timeout=30s;
    server 192.168.33.13 backup;
}

重啟下nginx,然後重新整理下,不管怎麼重新整理,都顯示是web2。

接下來,我們將web2 的服務停掉:

sudo /usr/sbin/apachectl stop

然後,我們再重新整理下網頁,看下備胎web3是不是被啟用了:果然,頁面上輸出了web3。

基於 ip_hash 的負載

這種分配方式,每個請求按訪問IP的hash結果分配,這樣來自同一個IP的訪客固定訪問一個後端伺服器,有效解決了動態網頁存在的session共享問題。

動手看怎麼操作:

upstream webservers{
    ip_hash;
    server 192.168.33.11 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.33.12 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.33.13 down;
}

重啟nginx,我們重新整理,發現,再怎麼刷,都是web1, 是固定的了。

我們講web2的權重該大一點:

upstream webservers{
    ip_hash;
    server 192.168.33.11 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.33.12 weight=2 max_fails=2 fail_timeout=30s;
    server 192.168.33.13 down;
}

這樣就會永遠是web2了。

我們試著把web2服務關掉,再重新整理,就會輸出web1了,已經切換過來了。

注意 
ip_hash 模式下,最好不要設定weight引數,因為你設定了,就相當於手動設定了,將會導致很多的流量分配不均勻。

ip_hash模式下, backup引數不可用,加了會報錯,為啥呢?因為,本身我們的訪問就是固定的了,其實,備用已經不管什麼作用了。

頁面快取

頁面快取也是日常web 開發中很重要的一個環節,對於一些頁面,我們可以將其靜態化,儲存起來,下次請求時候,直接走快取,而不用去請求反相代理伺服器甚至資料庫服務了。從而減輕伺服器壓力。

nginx 也提供了簡單而強大的下重定向,反向代理的快取功能,只需要簡單配置下,就能將指定的一個頁面快取起來。
它的原理也很簡單,就是匹配當前訪問的url, hash加密後,去指定的快取目錄找,看有沒有,有的話就說明匹配到快取了。

好。現在開始學習!

我們先來看一下一個簡單的頁面快取的配置:

http {
  proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=cache_zone:10m inactive=1d max_size=100m;
  upstream myproject {
    .....
  }
  server  {
    ....
    location ~* \.php$ {
        proxy_cache cache_zone; #keys_zone的名字
        proxy_cache_key $host$uri$is_args$args; #快取規則
        proxy_cache_valid any 1d;
        proxy_pass http://127.0.0.1:8080;
    }
  }
  ....
}

下面我們來一步一步說。用到的配置引數,主要是proxy_*字首的很多配置。

首先需要在http中加入proxy_cache_path 它用來制定快取的目錄以及快取目錄深度制定等。它的格式如下:

proxy_cache_path path [levels=number] keys_zone=zone_name:zone_size [inactive=time] [max_size=size]; 
  1. path是用來指定 快取在磁碟的路徑地址。比如:/data/nginx/cache。那以後生存的快取檔案就會存在這個目錄下。

  2. levels用來指定快取資料夾的級數,可以是:levels=1, levels=1:1, levels=1:2, levels=1:2:3 可以使用任意的1位或2位數字作為目錄結構分割符,如 X, X:X,或 X:X:X 例如: 2, 2:2, 1:1:2,但是最多隻能是三級目錄。

那這個裡面的數字是什麼意思呢。表示取hash值的個數。比如:現在根據請求地址localhost/index.php?a=4 用md5進行雜湊,得到e0bd86606797639426a92306b1b98ad9

levels=1:2 表示建立2級目錄,把hash最後1位(9)拿出建一個目錄,然後再把9前面的2位(ad)拿來建一個目錄, 那麼快取檔案的路徑就是/data/nginx/cache/9/d/e0bd86606797639426a92306b1b98ad9

以此類推:levels=1:1:2表示建立3級目錄,把hash最後1位(9)拿出建一個目錄,然後再把9前面的1位(d)建一個目錄, 最後把d前面的2位(8a)拿出來建一個目錄 那麼快取檔案的路徑就是/data/nginx/cache/9/d/8a/e0bd86606797639426a92306b1b98ad9

  1. keys_zone 所有活動的key和元資料儲存在共享的記憶體池中,這個區域用keys_zone引數指定。one指的是共享池的名稱,10m指的是共享池的大小。

注意每一個定義的記憶體池必須是不重複的路徑,例如:

proxy_cache_path  /data/nginx/cache/one  levels=1      keys_zone=one:10m;
proxy_cache_path  /data/nginx/cache/two  levels=2:2    keys_zone=two:100m;
proxy_cache_path  /data/nginx/cache/three  levels=1:1:2  keys_zone=three:1000m;
  1. inactive 表示指定的時間內快取的資料沒有被請求則被刪除,預設inactive為10分鐘。inactive=1d 1小時。inactive=30m30分鐘。

  2. max_size 表示單個檔案最大不超過的大小。它被用來刪除不活動的快取和控制快取大小,當目前快取的值超出max_size指定的值之後,超過其大小後最少使用資料(LRU替換演算法)將被刪除。max_size=10g表示當快取池超過10g就會清除不常用的快取檔案。

  3. clean_time 表示每間隔自動清除的時間。clean_time=1m 1分鐘清除一次快取

好。說完了這個很重要的引數。我們再來說在server模組裡的幾個配置引數:

proxy_cache 用來指定用哪個keys_zone的名字,也就是用哪個目錄下的快取。上面我們指定了三個one, two,three 。比如,我現在想用one 這個快取目錄 : proxy_cache one

proxy_cache_key 這個其實蠻重要的,它用來指定生成hash的url地址的格式。他會根據這個key對映成一個hash值,然後存入到本地檔案。
proxy_cache_key hosturi表示無論後面跟的什麼引數,都會訪問一個檔案,不會再生成新的檔案。
而如果proxy_cache_key isargsargs,那麼傳入的引數 localhost/index.php?a=4 與localhost/index.php?a=44將對映成兩個不同hash值的檔案。

proxy_cache_key 預設是 “schemehostrequesturi"hosturiis_args$args 一個完整的url路徑。

proxy_cache_valid 它是用來為不同的http響應狀態碼設定不同的快取時間,

proxy_cache_valid  200 302  10m;
proxy_cache_valid  404      1m;

表示為http status code 為200和302的設定快取時間為10分鐘,404程式碼快取1分鐘。
如果只定義時間:

proxy_cache_valid 5m;

那麼只對程式碼為200, 301和302的code進行快取。
同樣可以使用any引數任何相響應:

proxy_cache_valid  200 302 10m;
proxy_cache_valid  301 1h;
proxy_cache_valid  any 1m; #所有的狀態都快取1小時

好。快取的基本一些配置講完了。也大致知道了怎麼使用這些引數。

現在開始實戰!我們啟動一臺vagrant linux 機器 web1 (192.168.33.11) 用作遠端代理機器,就不搞複雜的負載均衡了。

先在Mac本地加一個域名cache.iyangyi.com, 然後按照上面的配置在vhost 下新建一個proxy_cache.iyangyi.conf 檔案:

proxy_cache_path /usr/local/var/cache levels=1:2 keys_zone=cache_zone:10m inactive=1d max_size=100m;
server  {
    listen 80;
    server_name cache.iyangyi.com;
    access_log /usr/local/var/log/nginx/cache.iyangyi.access.log main;
    error_log /usr/local/var/log/nginx/cache.iyangyi.error.log error;
    add_header X-Via $server_addr;
    add_header X-Cache $upstream_cache_status;
    location / {
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_cache cache_zone;
        proxy_cache_key $host$uri$is_args$args;
        proxy_cache_valid 200 304 1m;
        proxy_pass http://192.168.33.11;
    }
}

當然快取資料夾 /usr/local/var/cache得提前新建好。然後重啟nginx。

192.168.33.11 是apache伺服器,在index.html頁面就寫了一個web1。

我們開啟瀏覽器訪問 cache.iyangyi.com 。就能看到web1了。

開啟稽核元素或者firebug。看network網路請求選項,我們可以看到,Response Headers,在這裡我們可以看到:

X-Cache:MISS
X-Via:127.0.0.1
X-cache 為 MISS 表示未命中,請求被傳送到後端。y因為是第一次訪問,沒有快取,所以肯定是未命中。我們再重新整理下,就發現其變成了HIT, 表示命中。它還有其他幾種狀態:

MISS 未命中,請求被傳送到後端
HIT 快取命中
EXPIRED 快取已經過期請求被傳送到後端
UPDATING 正在更新快取,將使用舊的應答
STALE 後端將得到過期的應答
BYPASS 快取被繞過了
我們再去看看快取資料夾 /usr/local/var/cache裡面是否有了檔案:

cache git:(master) cd a/1313 git:(master) ls
5bd1af99bcb0db45c8bd601d9ee9e13a
➜  13 git:(master) pwd
/usr/local/var/cache/a/13

已經生成了快取檔案。

我們在url 後面隨便加一個什麼引數,看會不會新生成一個快取資料夾及檔案: http://cache.iyangyi.com/?w=ww55。因為我們使用的生成規則是全部url轉換(proxy_cache_key hosturiisargsargs;)

檢視 X-cache 為 MISS,再重新整理 ,變成HIT。再去看一下快取資料夾 /usr/local/var/cache。

  ~cache git:(master) ls
  4 a

果然又生成了一個4資料夾。