什么是虚拟主机

    "虚拟主机"是指在一个机器上运行多个网站(比如:www.company1.com  和 www.company2.com  )。如果每个网站拥有不同的IP地址,则虚拟主机可以是"基于IP"的;如果只有一个IP地址,也可以是"基于主机名"的,其实现对最终用户是透明的。物理服务器只有一个,而且web服务器也只有一个,却可以服务多个不同的站点。

    如果要调试你的虚拟主机配置,你会发现Apache的 -S 命令行开关很有用。比如:

[root@localhost ~]# httpd -S
 

虚拟主机类型支持

        同IP,同端口,不同域名,虽然被解析到同一个IP上,但是通过http头的 host 能够区分

      不同IP, 同端口,每个虚拟主机使用不同的IP地址(但是IP地址有点贵)

  • 基于端口的虚拟主机

           同IP,不同端口,但是困扰的是浏览器默认的端口都是80

  

    基于IP的虚拟主机使用连接的IP地址来决定相应的虚拟主机。这样,你就需要为每个虚拟主机分配一个独立的IP地址。而基于域名的虚拟主机是根据客户端提交的HTTP头中标识主机名的部分决定的。使用这种技术,很多虚拟主机可以共享同一个IP地址。

    基于域名的虚拟主机相对比较简单,因为你只需要配置你的DNS服务器将每个主机名映射到正确的IP地址,然后配置Apache HTTP服务器,令其辨识不同的主机名就可以了。基于域名的服务器也可以缓解IP地址不足的问题。所以,如果没有特殊原因使你必须使用基于IP的虚拟主机,您最好还是使用基于域名的虚拟主机。

    基于IP的虚拟主机使用连接的IP地址来决定相应的虚拟主机。这样,你就需要为每个虚拟主机分配一个独立的IP地址。而基于域名的虚拟主机是根据客户端提交的HTTP头中标识主机名的部分决定的。使用这种技术,很多虚拟主机可以共享同一个IP地址。

    我们知道,TCP/IP网络通信都是建立在 socket <IP:PORT> 之上的。<IP:PORT>就能唯一标识网络上的哪个主机哪个进程。当我们通过浏览器访问:http://www.baidu.com  的时候,首先DNS会把www.baidu.com这个域名解析为IP地址,然后才能在网络中路由。既然域名都会被解析为IP地址,那么怎么实现基于域名的虚拟主机呢?

       仅仅是TCP/IP是无法做到的,但是HTTP协议却能够实现这一点。在http协议首部,定义了一个 host 参数,通过它就可以区分究竟访问的哪个虚拟主机。httpd 可以根据 host 区分到底访问的是哪一个虚拟主机。

wKiom1cJ8Pbg8-f4AAA4KxN78OA703.png

 

《HTTP权威指南》

  缺失主机信息是原始HTTP规范的疏忽,它错误的假设每个web服务器上只托管了一个网站。在这个期间涌现了以下4种技术来解决这个问题。

(1)通过URL路径进行虚拟主机托管

在url中添加专门的路径部分,以便服务器判断是哪个网站。

(2)通过端口号进行主机托管

为每个站点分配一个端口号。

(3)通过IP地址进行主机托管

为不同的虚拟站点分配专门的ip地址,把这些地址绑定到一台单独的机器上。

(4)通过Host首部进行主机托管

通过Htpp/1.0增强版和HTTP/1.1正式版定义Host请求首部来携带网站名称。

 

  • 通过URL路径进行虚拟主机托管

通过分配不同的url路径,把共享服务器上的虚拟站点隔离开。

如:

http://www.xx123.com/zyt/index.html

http://www.xx356.com/icrt/index.html

    当请求到达服务时,其中并没有主机信息,但是服务器可以通过路径来区分它们。请求zyt的网址是:GET /zyt/index.html,请求icrt的网址是: GET /icrt/index.html 这不是一个好办法/zyt 和/icrt是多余的,更坏的是http://www.xx123.com和http://www.xx356.com不能用了。

这种方案在实际中很少会用到。

  • 通过端口号进行主机托管

    可在在web服务器上为不同网站指定不同的端口号,不再使用80端口,而是采用其它端口号。这个解决方案也有问题:终端用户不乐意在url中指定非标准的端口号。如http://www.xx123.com:89

  • 通过IP地址进行主机托管

    通过ip地址进行主机托管,是一个更常用、更好的办法。为每个虚拟网站分配一个或者多个唯一的ip地址。所有虚拟网站的ip地址都绑定到同一个共享服务器上。服务器可以查询http连接的目的ip地址,并以此来判断客户端的目标网站。

    如:把ip:209.173.34.3分配给www.xxx123.com,把ip:209.172.34.2分配给www.xx356.com,把这两个ip地址都绑定到同一物理服务器上。

    但是这样做也会带来一些麻烦:首先在计算机系统上能够绑定的虚拟ip地址通常是有限制的,其次ip地址是稀缺资源,最后托管都通过复制服务器来增加容量时,ip地址短缺的问题就更加严重了。尽管有这样的缺陷,但是它仍然得到广泛的应用。

  • 通过Host首部进行主机托管

    为了避免过度的地址消耗和虚拟ip地址的限制,我们希望在虚拟站点间共享同一个ip地址,且仍然能够区分站点。为了解决这个问题,浏览器来服务器的实现都扩展了HTTP,把原始主机名给了服务器。不过,浏览器不能只发送完整的URL,因为这会使许多只能接收路径的服务器无法工作。替代的方法是,把主机名和端口号放在所请求的Host扩展首部中传送。

绝大多数现代浏览器和服务器都支持Host首部,但仍然有一些客户端以及网络机器人不支持它。

 

配置虚拟主机

    对于Apache而言,主机有两类(中心主机和虚拟主机):不能同时使用,如果要定义虚拟主机,那么必须为现存的主机也创建一个 <VirtualHost>

  1. 中心主机(Mainhost)    未配置虚拟主机时

  2. 虚拟主机(Vhost)          如果配置了中心主机,同时也想配置虚拟主机,那么把中心主机也配置虚拟主机

     

  • 取消中心主机(Mainhost)

    一、如果你想在现有的web服务器上增加虚拟主机,你必须也为现存的主机建造一个<VirtualHost>定义块。这个虚拟主机中ServerNameDocumentRoot所包含的内容应该与全局的ServerNameDocumentRoot保持一致。还要把这个虚拟主机放在配置文件的最前面,来让它扮演默认主机的角色。

    二、如果不想使用中心主机,那么可以直接在httpd.conf配置文件中注释掉中心主机的 DocumentRoot即可。

    wKioL1U-YROiUtWDAADPYonkN3o285.jpg

     

  • 虚拟主机的配置文件

    可以直接在主配置文件httpd.conf中直接配置,但是这样不利于维护,建议在/etc/httpd/conf.d/建立 *.conf 配置文件。前提是在httpd.conf中配置 Include conf.d/*.conf 

 

    我们通过<VirtualHost>指令来定义虚拟主机,ServerNmae和DocumentRoot是一个虚拟主机最基本的两个配置,其他可以在中心主机配置的指令,基本都可以应用于虚拟主机。

<VirtualHost IP:PORT>
    ServerName
    DocumentRoot "/path/to/"
</VirutalHost>
 

 

基于域名的虚拟主机(推荐使用该方式)

   注意,在您的Apache服务器配置中创建一个基于域名虚拟主机并不会自动在您的DNS中对主机名做相应的更新。您必须自己在DNS中添加域名来指向您的IP地址。否则别人是无法访问您的web站点的。也可以在hosts文件中添加这一条目进行测试,但是这种方法仅适用于那些有这些hosts条目的机器来使用。 

  为了使用基于域名的虚拟主机,你必须指定服务器IP地址(和可能的端口)来使主机接受请求,这个可以用 NameVirtualHost 指令来进行配置。如果服务器上所有的IP地址都会用到,你可以用"*"作为NameVirtualHost的参数。如果你打算使用多端口(如运行SSL)你必须在参数中指定一个端口号,比如"*:80"。请注意,在NameVirtualHost指令中指定IP地址并不会使服务器自动侦听那个IP地址。请参阅设置Apache使用的地址和端口一章获取更多详情。另外,这里设定的IP地址必须对应服务器上的一个网络接口。

 这样,当一个请求到达的时候,服务器会首先检查它是否使用了一个能和NameVirtualHost相匹配的IP地址。如果能够匹配,它就会查找每个与这个IP地址相对应的<VirtualHost>段,并尝试找出一个与请求的主机名相同的ServerNameServerAlias配置项。如果找到了,它就会使用这个服务器。否则,将使用符合这个IP地址的第一个列出的虚拟主机。

  综上所述,第一个列出的虚拟主机充当了默认虚拟主机的角色。当一个IP地址与NameVirtualHost指令中的配置相符的时候,主服务器中的DocumentRoot将永远不会被用到。所以,如果你想创建一段特殊的配置用于处理不对应任何一个虚拟主机的请求的话,你只要简单的把这段配置放到<VirtualHost>段中,并把它放到配置文件的最前面就可以了。

需要注意的是:apache2.2 需要用 NameVirtualHost 指令配置, apache2.4 不需要

  下一步就是为每个虚拟主机建立<VirtualHost>段。<VirtualHost>的参数与NameVirtualHost的参数必须是一样的(比如说,一个IP地址或"*"代表的所有地址)。在每个<VirtualHost>段中,至少要有一个ServerName指令来指定伺服哪个主机和一个DocumentRoot指令来说明这个主机的内容位于文件系统的什么地方。

 

以下试验中,请注意 iptables, SELinux 的设置;

# 修改配置文件
[root@localhost ~]# vi /etc/httpd/conf.d/virtual.conf 
NameVirtualHost *:80        # apache 2.2 必须

<VirtualHost *:80>
        ServerName www.a.com
        DocumentRoot "/www/a.com"
</VirtualHost>

<VirtualHost *:80>
        ServerName www.b.org
        DocumentRoot "/www/b.org"
</VirtualHost>

# 创建web目录
[root@localhost ~]# mkdir -pv /www/a.com /www/b.org
mkdir: created directory `/www'
mkdir: created directory `/www/a.com' 
mkdir: created directory `/www/b.org'

# 编辑index.html
[root@localhost ~]# echo '<h1>www.a.com</h1>' > /www/a.com/index.html
[root@localhost ~]# echo '<h1>www.b.org</h1>' > /www/b.org/index.html

# 检查虚拟主机配置
[root@localhost ~]# httpd -S
httpd: apr_sockaddr_info_get() failed for skype
httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:
*:80                   is a NameVirtualHost
         default server www.a.com (/etc/httpd/conf.d/virtual.conf:3)
         port 80 namevhost www.a.com (/etc/httpd/conf.d/virtual.conf:3)
         port 80 namevhost www.b.org (/etc/httpd/conf.d/virtual.conf:8)

# 重新载入配置文件 
[root@localhost ~]# service httpd reload

# 我们只能通过域名进行访问,如果直接使用IP,那么则会返回默认的虚拟主机。
# 1 可以修改客户机的host文件, win: C:\Windows\System32\drivers\etc
# 2 也可以搭建内部的DNS服务器进行解析

# 编辑hosts文件,有些机器的hosts可能被隐藏了
192.168.11.101		www.a.com
192.168.11.101		www.b.org

C:\Users\yy>ping www.a.com
正在 Ping www.a.com [192.168.11.101] 具有 32 字节的数据:
来自 192.168.11.101 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.11.101 的回复: 字节=32 时间<1ms TTL=64

C:\Users\yy>ping www.b.org
正在 Ping www.b.org [192.168.11.101] 具有 32 字节的数
来自 192.168.11.101 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.11.101 的回复: 字节=32 时间<1ms TTL=64

# OK, 我们看到
# 通过浏览器访问, 注意首先要注释掉中心主机哈
 

wKiom1PXeDiwK8V7AABMUHOdsF0797.jpg

wKioL1PXeVLTITq4AABNWoZi4tA887.jpg

    

  最后,你可以把其他一些指令放入<VirtualHost>段中,以更好的配置一个虚拟主机。大部分指令都可以放入这些<VirtualHost>段中以改变相应虚拟主机配置。如果您想了解一个特定的指令是否可以这样运用,请参见指令的作用域主服务器(main server)范围内的配置指令(在所有<VirtualHost>配置段之外的指令)仅在它们没有被虚拟主机的配置覆盖时才起作用。

  这样,当一个请求到达的时候,服务器会首先检查它是否使用了一个能和NameVirtualHost相匹配的IP地址。如果能够匹配,它就会查找每个与这个IP地址相对应的<VirtualHost>段,并尝试找出一个与请求的主机名相同的ServerNameServerAlias配置项。如果找到了,它就会使用这个服务器。否则,将使用符合这个IP地址的第一个列出的虚拟主机

  综上所述,第一个列出的虚拟主机充当了默认虚拟主机的角色。当一个IP地址与NameVirtualHost指令中的配置相符的时候,主服务器中的DocumentRoot永远不会被用到。所以,如果你想创建一段特殊的配置用于处理不对应任何一个虚拟主机的请求的话,你只要简单的把这段配置放到<VirtualHost>段中,并把它放到配置文件的最前面就可以了。

配置实例框架:

注意
在您的Apache服务器配置中创建一个虚拟主机并不会自动在您的DNS中对主机名做相应更新。您必须自己在DNS中添加域名来指向您的IP地址。否则别人是无法看到您的web站点的。您可以在您的hosts文件中添加这一条目来进行测试,但这种方法仅适用于那些有这些hosts文件的机器来使用。

# 确保Apache在监听80端口
Listen 80

# 为虚拟主机在所有IP地址上监听
NameVirtualHost    *:80    # apache2.2需要此指令启动基于域名的虚拟主机

# vhost for www.example.com
<VirtualHost *:80>
    ServerName       www.example.com
    DocumentRoot     "/www/example1"
    ErrorLog         "/www/example1/logs/error_log"
    CustomLog        "/www/example1/logs/access_log"
    # 修饰目录访问属性
    <Directory />
            Options FollowSymLinks
            AllowOverride None
            Order allow,deny
            Allow from all
    </Directory>
    
    # 您可以在这里添加其他指令
    
</VirtualHost>


# vhost for www.example.org
<VirtualHost *:80>
    ServerName        www.example.org
    DocumentRoot      "/www/example2"
    
    
    # 您可以在这里添加其他指令
    
</VirtualHost>
 

  因为 www.example.com  首先出现在配置文件中,所以它拥有最高优先级,可以认为是默认或主服务器。这意味着如果一个请求不能与某个ServerName指令相匹配,它将会由第一个<VirtualHost>段所伺服。

 

基于IP的虚拟主机

     这样的服务器中每个基于IP的虚拟主机必须拥有不同的IP地址。可以通过配备多个真实的物理网络接口来达到这一要求,也可以使用几乎所有流行的操作系统都支持的虚拟界面来达到这一要求(详情请参见您的系统文档,这种功能一般被称作"IP别名",一般用"ifconfig"命令来进行设置)。

   基于IP的虚拟主机应用也不多,以为IP地址是稀缺资源,而且价格昂贵。

 有两种配置方法来使apache支持多主机:为每个虚拟主机运行不同的httpd守护进程;或者用同一个守护进程来支持所有虚拟主机。

以下情况使用多个守护进程:

  • 出于安全的考虑,比如说公司甲不希望公司乙的任何人能用除web以外的方式访问到他们的数据。在这种情况下,

  • 您需要启动两个守护进程。每个进程都使用不同的UserGroupListen,ServerRoot设置。

  • 您能够为机器上的每个IP地址提供内存和文件描述符需求。您只能Listen一个"通配符型"地址或一个特定的地址。

  • 所以不管出于什么原因,如果您需要侦听一个特定的地址,您就必须同时侦听所有特定的地址。(尽管可以让一

  • httpd侦听N-1个地址,而让另一个侦听剩下的地址)

以下情况使用单一守护进程:

  • httpd的配置可以为多个虚拟主机共享而不引起麻烦。

  • 机器要接受大量的访问请求,从而多启动一个守护进程会导致性能大幅度降低。

1、设置多个守护进程

  为每个虚拟主机创建一个不同的httpd安装。每次安装都在配置文件中使用Listen指令指定守护进程伺服的IP地址(或虚拟主机)。比如:

Listen www.smallco.com:80
 

2、配置拥有多个虚拟主机的单一守护进程

  在这种情况下,单一的httpd将伺服所有对主服务器和虚拟主机的请求。而配置文件中的VirtualHost指令将为每个虚拟主机配置不同的ServerAdminServerNameDocumentRootErrorLog,TransferLogCustomLog 。

[root@localhost ~]# vi /etc/httpd/conf.d/virtual.conf 
<VirtualHost 192.168.11.101:80>
        serverName www.a.com
        DocumentRoot "/www/a.com"
</VirtualHost>

<VirtualHost 192.168.11.102:80>
        serverName www.b.org
        DocumentRoot "/www/b.org"
</VirtualHost>

# 检查虚拟主机配置
[root@localhost ~]# httpd -S

# 重新读取配置文件
[root@localhost ~]# service httpd reload

# 编辑index.html
[root@localhost ~]# echo '<h1>192.168.11.101</h1>' > /www/a.com/index.html
[root@localhost ~]# echo '<h1>192.168.11.102</h1>' > /www/b.org/index.html

# 如何测试呢?我们通过添加网卡别名来达成
[root@localhost ~]# ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:0C:29:FB:65:36  
        inet addr:192.168.11.101  Bcast:192.168.11.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fefb:6536/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:168196 errors:0 dropped:0 overruns:0 frame:0
          TX packets:44037 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:197148682 (188.0 MiB)  TX bytes:3998845 (3.8 MiB)
          Interrupt:19 Base address:0x2080 

[root@localhost ~]# ifconfig eth1:0 192.168.11.102/24 up    # 添加网卡别名
 

wKioL1PXfUnBvdlCAABZ51OP9lI797.jpg

wKiom1PXfDDAMLq8AABaVmHjbN4145.jpg

 

基于端口的虚拟主机

    基于端口的虚拟主机在生产环境用的不多,因为客户端浏览器默认都是80端口,客户端并不知道我们配置端口是什么。除了通过链接地址进行跳转的情况以及公司内部使用。

    必须配置httpd监听端口: Listen PORT(极为重要)

[root@localhost ~]# vi /etc/httpd/conf.d/virtual.conf
NameVirtualHost *:80

<VirtualHost 192.168.11.101:80>
	serverName www.a.com
	DocumentRoot "/www/a.com"
</VirtualHost>

<VirtualHost 192.168.11.101:8080>
	serverName www.b.org
	DocumentRoot "/www/b.org"
</VirtualHost>

# 修改主配置文件,修改监听端口(非常重要)
# 这样httpd才能向linux内核(BIND)注册端口,然后TCP/IP协议栈才知道把包传给哪个应用程序。
[root@localhost ~]# vi /etc/httpd/conf/httpd.conf 
Listen 80
Listen 8080

[root@localhost ~]#  httpd -t
[root@localhost ~]# service httpd restart    # 由于修改了端口,需要restart

[root@localhost ~]# echo '<h1>192.168.11.101: 80</h1>' > /www/a.com/index.html
[root@localhost ~]# echo '<h1>192.168.11.101: 8080</h1>' > /www/b.org/index.html
 

如果开启了防火墙,请检查防火墙设置,添加其端口的入站规则。

wKiom1PXfrSwzvf9AABfchb0PEw343.jpg

wKioL1PXf87QcYNuAABqPds34tY037.jpg

 

总结

定义一个站点,至少需要提供如下信息:

  • DocumentRoot

  • ServerName

  • IP

  • PORT

 

如果测试无法正常访问, 请检查iptables防火墙规则,以及SELinux设置。

更多详细信息, 请参考Apache官方网站。