深入理解Apache虚拟主机

环境:

Rehat 9
Apache 2.0.54
IP:192.168.0.111

概论:

访问web服务,本质上看(从协议上)是访问某个IP的主机上的某个端口(默认是80)

通常需要通过访问不同的域名或者端口实现对不同网站的访问(具体到服务器里就是不同目录),这个时候就需要设置虚拟主机(VirtualHost).通常分为这样3种:

基于域名,基于端口,基于IP,以及它们的混合

准备工作:

在apache的主目录/var/www/html/下分别建立四个文件夹s1,s2,s3,s4,在里面建立不同网站的主页文件,如s1下面建立index.html内容为this is s1, s2下面建立index.html内容为this is s2,依此类推.

配置server的bind服务,为它添加2个域名s1.domain1.com, s2.domain1.com,.(其实是完全按我之前写的DNS设置的文章配的,只做了小小的改动而已)将客户机的DNS指向server,在客户机上ping 这2个域名都能返回正确的IP.

另外注意一点:每次修改配置文件httpd.conf之后apache要重新启动,才能使配置生效

下面是每种虚拟主机的配置过程

1.基于域名

这是一种最通用的情况,已经给服务器设置了多个域名,然后希望访问不同的域名来访问不同的网站文件.

修改httpd.conf的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Use name-based virtual hosting.  
#
NameVirtualHost * 表示在apache监听的所有IP和所有端口(此时只有80)上做多域名虚拟主机
<VirtualHost *>
ServerAdmin
DocumentRoot /var/www/html/s1
ServerName s1.domain1.com
ErrorLog logs/ error_log
TransferLog logs/ access_log
</VirtualHost>

<VirtualHost *>
ServerAdmin
DocumentRoot /var/www/html/s2
ServerName s2.domain1.com
ErrorLog logs/error_log
TransferLog logs/access_log
</VirtualHost>

测试虚拟主机配置

1
2
3
4
5
6
7
8
[root@server1 conf]# ../bin/httpd -S  
VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:
*:* is a NameVirtualHost
default server s1.domain1.com (/usr/local/apache2/conf/httpd.conf:1066)
port * namevhost s1.domain1.com (/usr/local/apache2/conf/httpd.conf:1066)
port * namevhost s2.domain1.com (/usr/local/apache2/conf/httpd.conf:1075)
Syntax OK

说明语法没问题,然后在测试机上访问这两个域名:s1.domain1.com和s2.domain1.com可以看到各自的页面

问题:如果以IP访问,看到的是什么?

是s1的页面,注意上面的这段话

1
2
*:* is a NameVirtualHost  
default server s1.domain1.com

意思十分明显,我们在*:*(apache监听的任意IP任意端口上)做了多域名虚拟主机,而s1.domain1.com是这个缺省的服务器.也就是说访问*:*,除非访问的是我们设置的域名,否则默认会转向到s1.domain1.com.

2.基于端口

通过访问同一个IP(或者域名)的不同端口来访问到不同的文件

httpd.conf做如下修改

增加监听口

1
2
Listen 80  
Listen 81

将之前做的多域名虚拟主机去掉(因为此时是用IP加端口来访问的),即

1
2
3
# Use name-based virtual hosting.  
#
# NameVirtualHost * 注释掉这句话

以下是虚拟主机配置(注意下面的配置部分我并没用ServerName字段)

1
2
3
4
5
6
7
8
9
10
11
12
13
<VirtualHost *:80>  
ServerAdmin
DocumentRoot /var/www/html/s1
ErrorLog logs/error_log
TransferLog logs/ access_log
</VirtualHost>

<VirtualHost *:81>
ServerAdmin
DocumentRoot /var/www/html/s2
ErrorLog logs/ error_log
TransferLog logs/ access_log
</VirtualHost>

验证虚拟主机配置

1
2
3
4
5
6
[root@server1 conf]# ../bin/httpd -S  
VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:
*:80 192.168.0.111 (/usr/local/apache2/conf/httpd.conf:1065)
*:81 192.168.0.111 (/usr/local/apache2/conf/httpd.conf:1074)
Syntax OK

在客户端通过访问IP:80和IP:81看到不同页面

混合实验

看到这里我们就可以做一个稍微复杂的实验,在不同的端口上做不同域名的访问.例如

在80上访问s1.domain1.com和s2.domain1.com
在81上访问s3.domain2.com和s4.domain2.com

当然这样访问是要带端口号的,上面因为是80默认端口所以不需要写端口号

访问这四个域名+端口分别访问到不同的页面.

配置过程

首先在bind上增加两个域名指向服务器. s3.domain2.com,s4.domain2.com,增加之后记得要重启bind.

然后修改httpd.conf文件

增加监听口

Listen 80
Listen 81

虚拟主机设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Use name-based virtual hosting.  
#
NameVirtualHost *:80
NameVirtualHost *:81
表示要在本机的所有IP的80和81上面做多域名(NameVirtualHost:即为名称虚拟主机的意思)
<VirtualHost *:80>
ServerAdmin
DocumentRoot /var/www/html/s1
ServerName s1.domain1.com
ErrorLog logs/ error_log
TransferLog logs/ access_log
</VirtualHost>

<VirtualHost *:80>
ServerAdmin
DocumentRoot /var/www/html/s2
ServerName s2.domain1.com
ErrorLog logs/ error_log
TransferLog logs/access_log
</VirtualHost>


<VirtualHost *:81>
ServerAdmin
DocumentRoot /var/www/html/s3
ServerName s3.domain2.com
ErrorLog logs/error_log
TransferLog logs/access_log
</VirtualHost>

<VirtualHost *:81>
ServerAdmin
DocumentRoot /var/www/html/s4
ServerName s4.domain2.com
ErrorLog logs/error_log
TransferLog logs/access_log
</VirtualHost>

检查虚拟主机设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@server1 conf]# /usr/local/apache2/bin/httpd -S  
VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:
*:83 192.168.0.111 (/usr/local/apache2/conf/httpd.conf:1098)
*:81 is a NameVirtualHost
default server s3.domain2.com (/usr/local/apache2/conf/httpd.conf:1082)
这句话表示这是访问81端口默认访问到s3上(后面会看到效果)
port 81 namevhost s3.domain2.com (/usr/local/apache2/conf/httpd.conf:1082)
port 81 namevhost s4.domain2.com (/usr/local/apache2/conf/httpd.conf:1090)
*:80 is a NameVirtualHost
default server s1.domain1.com (/usr/local/apache2/conf/httpd.conf:1065)
这句话表示这是访问80端口默认访问到s1上(后面会看到效果)
port 80 namevhost s1.domain1.com (/usr/local/apache2/conf/httpd.conf:1065)
port 80 namevhost s2.domain1.com (/usr/local/apache2/conf/httpd.conf:1073)
Syntax OK

重新启动服务/usr/local/apache2/apachectl restart
访问测试页面:

http://s1.domain1.com/
http://s2.domain1.com/ 注意这两个是默认端口80
http://s3.domain2.com:81/
http://s4.domain2.com:81/ 这里是通过不同域名访问81

注意如果后两个不带端口,如直接访问http://s4.domain2.com 看到的会是s1的页面,因为从80端口访问默认匹配到第一个虚拟主机了,上面说了s1是80上的默认主机

同样的,如果用80端口上设的域名访问81端口, http://s1.domain1.com:81/,则会看到s3的页面.因为s3是81上的默认主机.

3.基于IP的虚拟主机

将不同的网站挂在不同的IP上,访问不同的IP,所看到的是不同网站.因为一般服务器没那么多公网IP,而且大家一般都是用域名访问的.所以这个基本没用.但是可以用来测试和学习.

适用环境:server上没有配置域名,只能用IP访问.

为了实验效果,我们先来停掉DNS服务.

首先为服务器增加一个IP,192.168.0.112

1
2
3
4
5
6
7
8
[root@server1 network-scripts]# cp ifcfg-eth0 ifcfg-eth0:0  
[root@server1 network-scripts]# vi ifcfg-eth0:0
DEVICE=eth0:0 //设备名称
ONBOOT=yes //随系统启动
BOOTPROTO=static
IPADDR=192.168.0.112 //新设的IP
NETMASK=255.255.255.0
GATEWAY=192.168.0.1

激活这个设备

1
[root@server1 network-scripts]# ifdown eth0;ifup eth0  

查看当前ip设置

1
2
3
4
5
6
[root@server1 network-scripts]# ifconfig  
eth0 Link encap:Ethernet HWaddr 00:0A:EB:XX:XX:XX
inet addr:192.168.0.111 Bcast:192.168.0.255 Mask:255.255.255.0

eth0:0 Link encap:Ethernet HWaddr 00:0A:EB:XX:XX:XX
inet addr:192.168.0.112 Bcast:192.168.0.255 Mask:255.255.255.0

ping这个112确实启用成功.

这样增加IP的工作就完成了,下面对httpd.conf做修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Use name-based virtual hosting.  
#
#NameVirtualHost * 去掉基于多域名的主机

<VirtualHost 192.168.0.111>
ServerAdmin
DocumentRoot /var/www/html/s1
# ServerName s1.domain1.com
ErrorLog logs/ error_log
TransferLog logs/ access_log
</VirtualHost>

<VirtualHost 192.168.0.112>
ServerAdmin
DocumentRoot /var/www/html/s2
# ServerName ss2.domain1.com
ErrorLog logs/ error_log
TransferLog logs/ access_log
</VirtualHost>

注意上面红色的部分,由于此时没用多域名所以我将ServerName字段去掉了(在多端口的时候这样做是可以的).但是出现了下面的错误,可以看到它是把IP作为域名给解析了.

1
2
3
4
5
6
7
8
"httpd.conf" 1157L, 38072C written  
[root@server1 conf]# ../bin/httpd -S
[Mon Jul 30 14:09:06 2007] [error] (EAI 2)Name or service not known: Failed to resolve server name for 192.168.0.112 (check DNS) -- or specify an explicit ServerName
[Mon Jul 30 14:09:06 2007] [error] (EAI 2)Name or service not known: Failed to resolve server name for 192.168.0.111 (check DNS) -- or specify an explicit ServerName
VirtualHost configuration:
192.168.0.111:* bogus_host_without_reverse_dns (/usr/local/apache2/conf/httpd.conf:1065)
192.168.0.112:* bogus_host_without_reverse_dns (/usr/local/apache2/conf/httpd.conf:1074)
Syntax OK

下面我们将ServerName字段加上去,后面的名称我们随意写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<VirtualHost 192.168.0.111>  
ServerAdmin
DocumentRoot /var/www/html/s1
ServerName s1
ErrorLog logs/dbpi_web-error_log
TransferLog logs/dbpi_web-access_log
</VirtualHost>

<VirtualHost 192.168.0.112>
ServerAdmin
DocumentRoot /var/www/html/s2
ServerName s2
ErrorLog logs/dbpi_web-error_log
TransferLog logs/dbpi_web-access_log
</VirtualHost>

然后再来检查虚拟主机配置

1
2
3
4
5
[root@server1 conf]# ../bin/httpd -S  
VirtualHost configuration:
192.168.0.111:* s1 (/usr/local/apache2/conf/httpd.conf:1065)
192.168.0.112:* s2 (/usr/local/apache2/conf/httpd.conf:1075)
Syntax OK

在客户机上测试访问192.168.0.111和192.168.0.112成功.

个人觉得这应该是一处bug,因为在这种情况下并不需要域名访问,采用的是完全以实际IP进行访问.相应的ServerName这个字段应该是没有意义的(正如多端口时我们这样做,完全没问题).事实也表明了我们任意起名字也是可以通过的.

最后说一个Apache常常会碰到的问题

启动apache时会有警告

httpd: Could not determine the server’s fully qualified domain name, using 127.0.0.1 for ServerName

还是能够正常的启动和访问,只是有这个问题.

解决:

1
#ServerName [www.example.com:80](http://www.example.com/)

将这句话修改如下

ServerName 192.168.0.111 当然根据你的实际情况修改为本机的IP或者域名