# 大批量虛擬主機的動態配置
本文檔描述如何使用Apache有效的架設大批量虛擬主機。
## 動機
如果你的配置文件`httpd.conf`中包含類似下面的許多`<VirtualHost>`段,并且其中的內容都大致相同的話,你應該會對這里所講的技術感興趣。比如:
```
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>
```
最基本的思想是用動態的機制來實現所有這些靜態的`<VirtualHost>`配置段。這樣做有許多優點:
1. 配置文件變小,使得Apache可以更快的啟動,同時消耗更少的內存。
2. 添加一個虛擬主機,應該只是簡單的在文件系統中創建合適的目錄,以及配置相關的DNS信息,且無需重新啟動Apache 。
主要的缺點是你無法針對每個虛擬主機使用不同的日志文件。然而,如果真的在配置有大量虛擬主機的服務器上記錄不同的日志文件的話,很有可能會達到操作系統所允許的最大文件描述符的數量。更好的辦法是把日志寫到管道或者先入先出的棧,并啟用其他的進程來分揀所得到的日志信息(同時也可以做一些歷史紀錄的統計等等)。
## 概述
一個虛擬主機由兩部分來定義:一個是它的IP地址,還有一個是HTTP的"`Host:`"請求頭。動態大量虛擬主機的技術,是基于自動在所要返回的文件路徑中插入相關信息的想法實現的。使用`mod_vhost_alias`可以很容易的實現,但如果你的Apache版本低于1.3.6 ,則你必須使用`mod_rewrite` 。兩者在默認情況下都不啟用;要使用他們,必須在配置和編譯Apache的階段啟用。
我們需要做很多"偽裝",才能使動態虛擬主機看起來像普通主機。最重要的一點是Apache使用虛擬主機名(ServerName)來生成自引用(self-referential)URL等信息。這是用`ServerName`指令來配置的,并且可以通過環境變量`SERVER_NAME`傳遞給CGI腳本。運行時實際使用的值是由`UseCanonicalName`指令的設置來控制的。當 `UseCanonicalName Off` 時,虛擬主機名(ServerName)取自請求中的"`Host:`"頭。當 `UseCanonicalName DNS` 時,則通過DNS反解析虛擬主機的IP地址得到主機名。以前的做法是基于名稱的動態虛擬主機,現在常用基于IP地址的虛擬主機。如果Apache無法判斷虛擬主機名,則可能是沒有"`Host:`"頭或是DNS解析失敗,這樣種情況下,Apache將使用配置`ServerName`時所填寫的主機名。
另一件需要"偽裝"的事情是文檔根目錄(由`DocumentRoot`配置并可以通過`DOCUMENT_ROOT`環境變量為CGI腳本所使用)。在通常的配置方式下,這些設置信息由核心(core)模塊在將URI映射到文件系統的時候使用,但是如果使用動態虛擬主機配置,這些信息將由另外一個使用不同于核心(core)模塊將URI映射到文件系統的方式的模塊(`mod_vhost_alias`或`mod_rewrite`)使用。這兩個模塊都不負責設置`DOCUMENT_ROOT`環境變量,所以如果CGI或SSI程序使用了`DOCUMENT_ROOT`環境變量,那么將得到錯誤的值。
## 簡單的動態虛擬主機
這是`httpd.conf`文件中,完成和上文[動機](#calibre_link-660)部分所提到的虛擬主機一樣效果的配置方法,但這里采用了`mod_vhost_alias`模塊:
```
# 從"Host:"頭中取得主機名
UseCanonicalName Off
# 這種日志格式可以從第一個字段中提取出主機名
LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
CustomLog logs/access_log vcommon
# 在返回請求的文件名路徑中包含主機名
VirtualDocumentRoot /www/hosts/%0/docs
VirtualScriptAlias /www/hosts/%0/cgi-bin
```
將 `UseCanonicalName Off` 的配置改為 `UseCanonicalName DNS` 即可實現基于IP地址的虛擬主機。而在文件路徑中所要插入的服務器名則通過虛擬主機的IP地址解析得到。
## 一個實際的個人主頁系統
這里對上面的系統作了一點調整,便可作為ISP的個人主頁服務器。我們使用了略微復雜的方法,從主機名(ServerName)中提取子字符串,并插入到文件路徑中。在這個例子中`www.user.isp.com`的文檔將在`/home/user/`中定位。并對所有虛擬主機使用單個`cgi-bin`目錄。
```
# 所有之前的準備事項和上面一樣,然后在文件路徑中包含主機名
VirtualDocumentRoot /www/hosts/%2/docs
# 單個cgi-bin目錄
ScriptAlias /cgi-bin/ /www/std-cgi/
```
更復雜的關于`VirtualDocumentRoot`的設置,可以查閱`mod_vhost_alias`文檔。
## 在同一個服務器上架設多個主機的虛擬系統
更復雜的設置,應該使用Apache的`<VirtualHost>`容器來管理各種虛擬主機配置的作用域。例如,你可以用一個IP地址來給個人主頁客戶使用,同時用下面的配置提供給商業客戶使用。自然的,這兩者通過運用`<VirtualHost>`結合到一起。
```
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>
```
## 更為有效的基于IP地址的虛擬主機
在[第一個例子](#calibre_link-661)中說過,轉為基于IP地址的虛擬主機設置很容易做到。但不幸的是,那種做法并不高效,因為這樣會在每次處理請求時,需要查詢DNS。通過在文件系統中包含IP地址的做法可以避免這樣的問題。這樣一來,免去了和主機名的關聯,在日志記錄中也一樣可以用IP來分離不同日志。Apache將不會為了確定主機名(ServerName)而去做DNS查詢。
```
# 從IP地址反解析得到主機名
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
```
## 使用老版本的Apache
上面的例子基于`mod_vhost_alias` ,但它是在版本1.3.6之后才出現的。如果你的版本比較老,可以通過使用`mod_rewrite`來達到相同的目的,如下所示。但只能是基于"Host:"頭方式的虛擬主機。
此外還須注意日志方面的問題。Apache1.3.6是第一個支持"`%V`"日志格式指令的版本,在版本1.3.0-1.3.3中,"`%v`"選項做和"`%V`"一樣的事情;而在版本1.3.4中沒有等價指令。在所有的這些版本中,指令`UseCanonicalName`可以出現在`.htaccess`文件中,這意味著客戶的設置可能會導致日志記錄紊亂。所以最好的做法是使用"`%{Host}i`"指令,它可以直接記錄"`Host:`"頭;注意,這樣可能在末尾包含"`:port`",而使用"`%V`"則不會這樣。
## 使用`mod_rewrite`實現簡單的動態虛擬主機
這里的例子摘自`httpd.conf` ,效果等同于[第一個例子](#calibre_link-661)中的情況。前半部分和上面的例子大致相似,只是為了向后兼容`mod_rewrite`作了適當修改;后半部分配置`mod_rewrite`來做實際的工作。
有些特別的地方需要注意:默認情況下,`mod_rewrite`在所有其他URI轉換模塊(`mod_alias`等)之前運行,所以如果使用這些模塊的話,`mod_rewrite`必須作相應的調整。同時,我們還要為每個動態虛擬主機變些戲法,使之等效于`ScriptAlias`
```
# 從"Host:"頭獲取主機名
UseCanonicalName Off
# 可分揀的日志
LogFormat "%{Host}i %h %l %u %t \"%r\" %s %b" vcommon
CustomLog logs/access_log vcommon
<Directory /www/hosts>
# 這里需要ExecCGI ,因為我們不能強制CGI以與ScriptAlias相同的方式執行
Options FollowSymLinks ExecCGI
</Directory>
# 接下來是關鍵部分
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
## 現在處理CGI(我們需要強制使用一個MIME類型)
RewriteCond %{REQUEST_URI} ^/cgi-bin/
RewriteRule ^/(.*)$ /www/hosts/${lowercase:%{SERVER_NAME}}/cgi-bin/$1 [T=application/x-httpd-cgi]
# ok 了!
```
## 使用`mod_rewrite`的個人主頁系統
這里的配置完成和[第二個例子](#calibre_link-662)相同的工作。
```
RewriteEngine on
RewriteMap lowercase int:tolower
# 允許CGI工作
RewriteCond %{REQUEST_URI} !^/cgi-bin/
# 檢查hostname正確與否,之后才能使RewriteRule起作用
RewriteCond ${lowercase:%{SERVER_NAME}} ^www\.[a-z-]+\.isp\.com$
# 將虛擬主機名字連接到URI的開頭
# [C]表明本次重寫的結果將在下一個rewrite規則中使用
RewriteRule ^(.+) ${lowercase:%{SERVER_NAME}}$1 [C]
# 現在創建實際的文件名
RewriteRule ^www\.([a-z-]+)\.isp\.com/(.*) /home/$1/$2
# 定義全局CGI目錄
ScriptAlias /cgi-bin/ /www/std-cgi/
```
## 使用獨立的虛擬主機配置文件
這樣的布局利用了`mod_rewrite`的高級特性,在獨立的虛擬主機配置文件中轉換。如此可以更為靈活,但需要較為復雜的設置。
`vhost.map`文件包含了類似下面的內容:
```
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
```
- Apache HTTP Server Version 2.2 文檔 [最后更新:2006年3月21日]
- 版本說明
- 從1.3升級到2.0
- 從2.0升級到2.2
- Apache 2.2 新特性概述
- Apache 2.0 新特性概述
- The Apache License, Version 2.0
- 參考手冊
- 編譯與安裝
- 啟動Apache
- 停止和重啟
- 配置文件
- 配置段(容器)
- 緩沖指南
- 服務器全局配置
- 日志文件
- 從URL到文件系統的映射
- 安全方面的提示
- 動態共享對象(DSO)支持
- 內容協商
- 自定義錯誤響應
- 地址和端口的綁定(Binding)
- 多路處理模塊
- Apache的環境變量
- Apache處理器的使用
- 過濾器(Filter)
- suEXEC支持
- 性能方面的提示
- URL重寫指南
- Apache虛擬主機文檔
- 基于主機名的虛擬主機
- 基于IP地址的虛擬主機
- 大批量虛擬主機的動態配置
- 虛擬主機示例
- 深入研究虛擬主機的匹配
- 文件描述符限制
- 關于DNS和Apache
- 常見問題
- 經常問到的問題
- Apache的SSL/TLS加密
- SSL/TLS高強度加密:緒論
- SSL/TLS高強度加密:兼容性
- SSL/TLS高強度加密:如何...?
- SSL/TLS Strong Encryption: FAQ
- 如何.../指南
- 認證、授權、訪問控制
- CGI動態頁面
- 服務器端包含入門
- .htaccess文件
- 用戶網站目錄
- 針對特定平臺的說明
- 在Microsoft Windows中使用Apache
- 在Microsoft Windows上編譯Apache
- Using Apache With Novell NetWare
- Running a High-Performance Web Server on HPUX
- The Apache EBCDIC Port
- 服務器和支持程序
- httpd - Apache超文本傳輸協議服務器
- ab - Apache HTTP服務器性能測試工具
- apachectl - Apache HTTP服務器控制接口
- apxs - Apache 擴展工具
- configure - 配置源代碼樹
- dbmmanage - 管理DBM格式的用戶認證文件
- htcacheclean - 清理磁盤緩沖區
- htdbm - 操作DBM密碼數據庫
- htdigest - 管理用于摘要認證的用戶文件
- httxt2dbm - 生成RewriteMap指令使用的dbm文件
- htpasswd - 管理用于基本認證的用戶文件
- logresolve - 解析Apache日志中的IP地址為主機名
- rotatelogs - 滾動Apache日志的管道日志程序
- suexec - 在執行外部程序之前切換用戶
- 其他程序
- 雜項文檔
- 與Apache相關的標準
- Apache模塊
- 描述模塊的術語
- 描述指令的術語
- Apache核心(Core)特性
- Apache MPM 公共指令
- Apache MPM beos
- Apache MPM event
- Apache MPM netware
- Apache MPM os2
- Apache MPM prefork
- Apache MPM winnt
- Apache MPM worker
- Apache模塊 mod_actions
- Apache模塊 mod_alias
- Apache模塊 mod_asis
- Apache模塊 mod_auth_basic
- Apache模塊 mod_auth_digest
- Apache模塊 mod_authn_alias
- Apache模塊 mod_authn_anon
- Apache模塊 mod_authn_dbd
- Apache模塊 mod_authn_dbm
- Apache模塊 mod_authn_default
- Apache模塊 mod_authn_file
- Apache模塊 mod_authnz_ldap
- Apache模塊 mod_authz_dbm
- Apache模塊 mod_authz_default
- Apache模塊 mod_authz_groupfile
- Apache模塊 mod_authz_host
- Apache模塊 mod_authz_owner
- Apache模塊 mod_authz_user
- Apache模塊 mod_autoindex
- Apache模塊 mod_cache
- Apache模塊 mod_cern_meta
- Apache模塊 mod_cgi
- Apache模塊 mod_cgid
- Apache模塊 mod_charset_lite
- Apache模塊 mod_dav
- Apache模塊 mod_dav_fs
- Apache模塊 mod_dav_lock
- Apache模塊 mod_dbd
- Apache模塊 mod_deflate
- Apache模塊 mod_dir
- Apache模塊 mod_disk_cache
- Apache模塊 mod_dumpio
- Apache模塊 mod_echo
- Apache模塊 mod_env
- Apache模塊 mod_example
- Apache模塊 mod_expires
- Apache模塊 mod_ext_filter
- Apache模塊 mod_file_cache
- Apache模塊 mod_filter
- Apache模塊 mod_headers
- Apache模塊 mod_ident
- Apache模塊 mod_imagemap
- Apache模塊 mod_include
- Apache模塊 mod_info
- Apache模塊 mod_isapi
- Apache模塊 mod_ldap
- Apache模塊 mod_log_config
- Apache模塊 mod_log_forensic
- Apache模塊 mod_logio
- Apache模塊 mod_mem_cache
- Apache模塊 mod_mime
- Apache模塊 mod_mime_magic
- Apache模塊 mod_negotiation
- Apache模塊 mod_nw_ssl
- Apache模塊 mod_proxy
- Apache模塊 mod_proxy_ajp
- Apache模塊 mod_proxy_balancer
- Apache模塊 mod_proxy_connect
- Apache模塊 mod_proxy_ftp
- Apache模塊 mod_proxy_http
- Apache模塊 mod_rewrite
- Apache模塊 mod_setenvif
- Apache模塊 mod_so
- Apache模塊 mod_speling
- Apache模塊 mod_ssl
- Apache模塊 mod_status
- Apache模塊 mod_suexec
- Apache模塊 mod_unique_id
- Apache模塊 mod_userdir
- Apache模塊 mod_usertrack
- Apache模塊 mod_version
- Apache模塊 mod_vhost_alias
- Developer Documentation for Apache 2.0
- Apache 1.3 API notes
- Debugging Memory Allocation in APR
- Documenting Apache 2.0
- Apache 2.0 Hook Functions
- Converting Modules from Apache 1.3 to Apache 2.0
- Request Processing in Apache 2.0
- How filters work in Apache 2.0
- Apache 2.0 Thread Safety Issues
- 詞匯和索引
- 詞匯表
- 指令索引
- 指令速查
- 模塊索引
- 站點導航