Apache HTTP Server 版本2.2
本文檔描述如何使用Apache有效的架設(shè)大批量虛擬主機(jī)。
如果你的配置文件httpd.conf
中包含類似下面的許多<VirtualHost>
段,并且其中的內(nèi)容都大致相同的話,你應(yīng)該會對這里所講的技術(shù)感興趣。比如:
NameVirtualHost 111.22.33.44
<VirtualHost 111.22.33.44>
ServerName www.customer-1.com
DocumentRoot /www/hosts/www.customer-1.com/docs
ScriptAlias /cgi-bin/ /www/hosts/www.customer-1.com/cgi-bin
</VirtualHost>
<VirtualHost 111.22.33.44>
ServerName www.customer-2.com
DocumentRoot /www/hosts/www.customer-2.com/docs
ScriptAlias /cgi-bin/ /www/hosts/www.customer-2.com/cgi-bin
</VirtualHost>
# 等 等 等 。。。
<VirtualHost 111.22.33.44>
ServerName www.customer-N.com
DocumentRoot /www/hosts/www.customer-N.com/docs
ScriptAlias /cgi-bin/ /www/hosts/www.customer-N.com/cgi-bin
</VirtualHost>
最基本的思想是用動態(tài)的機(jī)制來實(shí)現(xiàn)所有這些靜態(tài)的<VirtualHost>
配置段。這樣做有許多優(yōu)點(diǎn):
主要的缺點(diǎn)是你無法針對每個虛擬主機(jī)使用不同的日志文件。然而,如果真的在配置有大量虛擬主機(jī)的服務(wù)器上記錄不同的日志文件的話,很有可能會達(dá)到操作系統(tǒng)所允許的最大文件描述符的數(shù)量。更好的辦法是把日志寫到管道或者先入先出的棧,并啟用其他的進(jìn)程來分揀所得到的日志信息(同時也可以做一些歷史紀(jì)錄的統(tǒng)計(jì)等等)。
一個虛擬主機(jī)由兩部分來定義:一個是它的IP地址,還有一個是HTTP的"Host:
"請求頭。動態(tài)大量虛擬主機(jī)的技術(shù),是基于自動在所要返回的文件路徑中插入相關(guān)信息的想法實(shí)現(xiàn)的。使用mod_vhost_alias
可以很容易的實(shí)現(xiàn),但如果你的Apache版本低于1.3.6 ,則你必須使用mod_rewrite
。兩者在默認(rèn)情況下都不啟用;要使用他們,必須在配置和編譯Apache的階段啟用。
我們需要做很多"偽裝",才能使動態(tài)虛擬主機(jī)看起來像普通主機(jī)。最重要的一點(diǎn)是Apache使用虛擬主機(jī)名(ServerName)來生成自引用(self-referential)URL等信息。這是用ServerName
指令來配置的,并且可以通過環(huán)境變量SERVER_NAME
傳遞給CGI腳本。運(yùn)行時實(shí)際使用的值是由UseCanonicalName
指令的設(shè)置來控制的。當(dāng) UseCanonicalName Off
時,虛擬主機(jī)名(ServerName)取自請求中的"Host:
"頭。當(dāng) UseCanonicalName DNS
時,則通過DNS反解析虛擬主機(jī)的IP地址得到主機(jī)名。以前的做法是基于名稱的動態(tài)虛擬主機(jī),現(xiàn)在常用基于IP地址的虛擬主機(jī)。如果Apache無法判斷虛擬主機(jī)名,則可能是沒有"Host:
"頭或是DNS解析失敗,這樣種情況下,Apache將使用配置ServerName
時所填寫的主機(jī)名。
另一件需要"偽裝"的事情是文檔根目錄(由DocumentRoot
配置并可以通過DOCUMENT_ROOT
環(huán)境變量為CGI腳本所使用)。在通常的配置方式下,這些設(shè)置信息由核心(core)模塊在將URI映射到文件系統(tǒng)的時候使用,但是如果使用動態(tài)虛擬主機(jī)配置,這些信息將由另外一個使用不同于核心(core)模塊將URI映射到文件系統(tǒng)的方式的模塊(mod_vhost_alias
或mod_rewrite
)使用。這兩個模塊都不負(fù)責(zé)設(shè)置DOCUMENT_ROOT
環(huán)境變量,所以如果CGI或SSI程序使用了DOCUMENT_ROOT
環(huán)境變量,那么將得到錯誤的值。
這是httpd.conf
文件中,完成和上文動機(jī)部分所提到的虛擬主機(jī)一樣效果的配置方法,但這里采用了mod_vhost_alias
模塊:
# 從"Host:"頭中取得主機(jī)名
UseCanonicalName Off
# 這種日志格式可以從第一個字段中提取出主機(jī)名
LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
CustomLog logs/access_log vcommon
# 在返回請求的文件名路徑中包含主機(jī)名
VirtualDocumentRoot /www/hosts/%0/docs
VirtualScriptAlias /www/hosts/%0/cgi-bin
將 UseCanonicalName Off
的配置改為 UseCanonicalName DNS
即可實(shí)現(xiàn)基于IP地址的虛擬主機(jī)。而在文件路徑中所要插入的服務(wù)器名則通過虛擬主機(jī)的IP地址解析得到。
這里對上面的系統(tǒng)作了一點(diǎn)調(diào)整,便可作為ISP的個人主頁服務(wù)器。我們使用了略微復(fù)雜的方法,從主機(jī)名(ServerName)中提取子字符串,并插入到文件路徑中。在這個例子中www.user.isp.com
的文檔將在/home/user/
中定位。并對所有虛擬主機(jī)使用單個cgi-bin
目錄。
# 所有之前的準(zhǔn)備事項(xiàng)和上面一樣,然后在文件路徑中包含主機(jī)名
VirtualDocumentRoot /www/hosts/%2/docs
# 單個cgi-bin目錄
ScriptAlias /cgi-bin/ /www/std-cgi/
更復(fù)雜的關(guān)于VirtualDocumentRoot
的設(shè)置,可以查閱mod_vhost_alias
文檔。
更復(fù)雜的設(shè)置,應(yīng)該使用Apache的<VirtualHost>
容器來管理各種虛擬主機(jī)配置的作用域。例如,你可以用一個IP地址來給個人主頁客戶使用,同時用下面的配置提供給商業(yè)客戶使用。自然的,這兩者通過運(yùn)用<VirtualHost>
結(jié)合到一起。
UseCanonicalName Off
LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
<Directory /www/commercial>
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /www/homepages>
Options FollowSymLinks
AllowOverride None
</Directory>
<VirtualHost 111.22.33.44>
ServerName www.commercial.isp.com
CustomLog logs/access_log.commercial vcommon
VirtualDocumentRoot /www/commercial/%0/docs
VirtualScriptAlias /www/commercial/%0/cgi-bin
</VirtualHost>
<VirtualHost 111.22.33.45>
ServerName www.homepages.isp.com
CustomLog logs/access_log.homepages vcommon
VirtualDocumentRoot /www/homepages/%0/docs
ScriptAlias /cgi-bin/ /www/std-cgi/
</VirtualHost>
在第一個例子中說過,轉(zhuǎn)為基于IP地址的虛擬主機(jī)設(shè)置很容易做到。但不幸的是,那種做法并不高效,因?yàn)檫@樣會在每次處理請求時,需要查詢DNS。通過在文件系統(tǒng)中包含IP地址的做法可以避免這樣的問題。這樣一來,免去了和主機(jī)名的關(guān)聯(lián),在日志記錄中也一樣可以用IP來分離不同日志。Apache將不會為了確定主機(jī)名(ServerName)而去做DNS查詢。
# 從IP地址反解析得到主機(jī)名
UseCanonicalName DNS
# 在日志中包含IP地址,便于以后分揀
LogFormat "%A %h %l %u %t \"%r\" %s %b" vcommon
CustomLog logs/access_log vcommon
# 在文件路徑中包含IP地址
VirtualDocumentRootIP /www/hosts/%0/docs
VirtualScriptAliasIP /www/hosts/%0/cgi-bin
上面的例子基于mod_vhost_alias
,但它是在版本1.3.6之后才出現(xiàn)的。如果你的版本比較老,可以通過使用mod_rewrite
來達(dá)到相同的目的,如下所示。但只能是基于"Host:"頭方式的虛擬主機(jī)。
此外還須注意日志方面的問題。Apache1.3.6是第一個支持"%V
"日志格式指令的版本,在版本1.3.0-1.3.3中,"%v
"選項(xiàng)做和"%V
"一樣的事情;而在版本1.3.4中沒有等價(jià)指令。在所有的這些版本中,指令UseCanonicalName
可以出現(xiàn)在.htaccess
文件中,這意味著客戶的設(shè)置可能會導(dǎo)致日志記錄紊亂。所以最好的做法是使用"%{Host}i
"指令,它可以直接記錄"Host:
"頭;注意,這樣可能在末尾包含":port
",而使用"%V
"則不會這樣。
mod_rewrite
實(shí)現(xiàn)簡單的動態(tài)虛擬主機(jī)這里的例子摘自httpd.conf
,效果等同于第一個例子中的情況。前半部分和上面的例子大致相似,只是為了向后兼容mod_rewrite
作了適當(dāng)修改;后半部分配置mod_rewrite
來做實(shí)際的工作。
有些特別的地方需要注意:默認(rèn)情況下,mod_rewrite
在所有其他URI轉(zhuǎn)換模塊(mod_alias
等)之前運(yùn)行,所以如果使用這些模塊的話,mod_rewrite
必須作相應(yīng)的調(diào)整。同時,我們還要為每個動態(tài)虛擬主機(jī)變些戲法,使之等效于ScriptAlias
# 從"Host:"頭獲取主機(jī)名
UseCanonicalName Off
# 可分揀的日志
LogFormat "%{Host}i %h %l %u %t \"%r\" %s %b" vcommon
CustomLog logs/access_log vcommon
<Directory /www/hosts>
# 這里需要ExecCGI ,因?yàn)槲覀儾荒軓?qiáng)制CGI以與ScriptAlias相同的方式執(zhí)行
Options FollowSymLinks ExecCGI
</Directory>
# 接下來是關(guān)鍵部分
RewriteEngine On
# 來自"Host:"頭的ServerName ,可能大小寫混雜
RewriteMap lowercase int:tolower
## 首先處理普通文檔
# 允許變名/icons/起作用,其他變名類同
RewriteCond %{REQUEST_URI} !^/icons/
# 允許CGI
RewriteCond %{REQUEST_URI} !^/cgi-bin/
# 開始"變戲法"
RewriteRule ^/(.*)$ /www/hosts/${lowercase:%{SERVER_NAME}}/docs/$1
## 現(xiàn)在處理CGI(我們需要強(qiáng)制使用一個MIME類型)
RewriteCond %{REQUEST_URI} ^/cgi-bin/
RewriteRule ^/(.*)$ /www/hosts/${lowercase:%{SERVER_NAME}}/cgi-bin/$1 [T=application/x-httpd-cgi]
# ok 了!
mod_rewrite
的個人主頁系統(tǒng)這里的配置完成和第二個例子相同的工作。
RewriteEngine on
RewriteMap lowercase int:tolower
# 允許CGI工作
RewriteCond %{REQUEST_URI} !^/cgi-bin/
# 檢查hostname正確與否,之后才能使RewriteRule起作用
RewriteCond ${lowercase:%{SERVER_NAME}} ^www\.[a-z-]+\.isp\.com$
# 將虛擬主機(jī)名字連接到URI的開頭
# [C]表明本次重寫的結(jié)果將在下一個rewrite規(guī)則中使用
RewriteRule ^(.+) ${lowercase:%{SERVER_NAME}}$1 [C]
# 現(xiàn)在創(chuàng)建實(shí)際的文件名
RewriteRule ^www\.([a-z-]+)\.isp\.com/(.*) /home/$1/$2
# 定義全局CGI目錄
ScriptAlias /cgi-bin/ /www/std-cgi/
這樣的布局利用了mod_rewrite
的高級特性,在獨(dú)立的虛擬主機(jī)配置文件中轉(zhuǎn)換。如此可以更為靈活,但需要較為復(fù)雜的設(shè)置。
vhost.map
文件包含了類似下面的內(nèi)容:
www.customer-1.com /www/customers/1
www.customer-2.com /www/customers/2
# ...
www.customer-N.com /www/customers/N
http.conf
包含了:
RewriteEngine on
RewriteMap lowercase int:tolower
# 定義映射文件
RewriteMap vhost txt:/www/conf/vhost.map
# 和上面的例子一樣,處理別名
RewriteCond %{REQUEST_URI} !^/icons/
RewriteCond %{REQUEST_URI} !^/cgi-bin/
RewriteCond ${lowercase:%{SERVER_NAME}} ^(.+)$
# 這里做基于文件的重新映射
RewriteCond ${vhost:%1} ^(/.*)$
RewriteRule ^/(.*)$ %1/docs/$1
RewriteCond %{REQUEST_URI} ^/cgi-bin/
RewriteCond ${lowercase:%{SERVER_NAME}} ^(.+)$
RewriteCond ${vhost:%1} ^(/.*)$
RewriteRule ^/(.*)$ %1/cgi-bin/$1