利用Linux架构高稳定、高可用、高效率的负载均衡(Load balancer)系统
引言:
Linux在网络方面具有很强大的功能。可以用Linux架构Firewall(防火墙), IDS(入侵检测),Cache&Proxy,大型的mail 系统,
甚至高端的路由器等,无所不能。本文介绍Linux在负载均衡方面的应用和具体的实现。本文的前半部分主要是一些理论的说明及所用
组件的简要介绍,后半部分是具体的实现,包括软件的安装、配置和启动。本文的内容是本人实验得来的,难免有疏漏,请各位谅解!
本文的目标:
本文的目标是实现一个由两台Linux Load balancer(LLB)和三台web服务器组成一个web服务器群,这个服务器群对用户来说就好象
是一台服务器。LLB对三台web服务器进行负载均衡。并且当web服务器出现故障时LLB会自动将出现故障的web服务器从服务器群中剔除,
当web服务器的故障修复后LLB会自动将web服务器加入到服务器群中来。其中两台LLB一台为主(Master LLB),一台为附(Slave LLB),
当Master LLB出现故障以后,Slave LLB会自动接管Master LLB的所有工作。系统结构如图所示。
client
________|__________
| |
| |
Master LLB Slave LLB
| |
|_________________|
|
HUB
____________|_______________
| | |
| | |
webserver1 webserver2 webserver3
理论的说明及所用组件的简要介绍:
我们这里的所实现的负载均衡是以 项目的 ipvsadm 为原型,配合以其他的一些开源软件来实现的。
在这里,我们需要四个组件:
· ipchains
· ipvsadm, ()
· keepalived, (
· heartbeat , ()
ipchains
ipchains是Linux下很有名的Firewall软件,ipchains在Linux的内核中对进出Linux的数据进行控制,包括数据包的过滤和伪装等。
在Linux的内核中有三条标准控制数据包去向的链:input , forward , output 。input是对进入Linux的数据包进行控制,forward是
对路由的数据包进行控制,output是对出Linux的数据包进行控制。网上有不少关于ipchains的文章,在Linux的2.4.0版本的内核中用
的是iptable。在我们的这个方案中ipchains的作用是将用户的请求数据交给ipvsadm来处理,并由ipvsadm决定用户的请求数据最终由
哪台web服务器来相应。大家也可参考IPCHAINS-HOWTO。在此不多说。
ipvsadm()
ipvsadm是由中国的年轻黑客维护的,他是从ipportfw发展而来的。ipvsadm是在Linux的内核中实现的,他在Linux的内核中监测需
要路由的IP数据包,ipvsadm根据用户设置的条件对数据包进行相应的操作。了解ipchains的用户知道,在Linux的内核中有三条控制数
据包去向的链:input , forward , output,ipvsadm是在forward的过程中对数据包进行操作的。ipvsadm的作用是为用户选择合适的
web服务器。LLB在选择服务器时有四种不同的规则,这四种规则用于选择哪台服务器处理用户的请求。这四种规则是:Round-Robin (RR)、
Weighted Round-Robin (WRR)、 Least-Connection (LC)、 Weighted Least-Connection (WLC)。这四种规则各有自己的适应环境。
Round-Robin:
如果您的LLB选用的是这种算法,她会将数据包均匀的分发给各台服务器,他把所有的服务器放在相等的地位上,而不会实际的去考
虑各台服务器的差异,如响应时间、session数等!例如您有ABC三台服务器,那么LLB分发数据包的顺序是......ABCABCABC.....
Round-Robin算法的好处是简单、占用系统资源少,缺点是无法检测哪台服务器有更高的响应速度、更少的连接,所以他非常适合服
务器性能相当的环境。
Weighted Round-Robin
这种规则适用于用户扩展系统时,因为这是集群中的服务器的性能会有较大的差别,为每一个服务器定义一个参数是必要的。
这是一种带参数的Round-Robin的算法,参数的名字叫Weighed。您可以根据您的服务器的处理能力来为每一台服务器分配一个
Weighted值,值越高其优先程度越高,默认值是1。例如:你有三台服务器,分别为A:486、B:奔腾、C:奔腾2,你可以为他们分配
Weighted值为:1、2、3,则按照Weighted Round-Robin的算法处理数据包的服务器的顺序是:CCCBBBA
Round-Robin可以说是Weighted Round-Robin的一种特殊情况,既所有的服务器有相同的Weighted值。
Least-Connection
这是一种动态算法,LLB将根据每台服务器当前的连接数目来转发数据包,具有最少连接的服务器将处理下一个请求。这一种算法能很好
的分配各种流量,对于突发的请求或大量的请求能够做出比较平滑的处理,不会产生将请求的数据发往同一台服务器的情况。
Weighted Least-Connection
这种算法是Least-Connection Scheduling的一种扩展,她为每一台服务器分配一个weighted值,然后根据这个值和每台服务器当前状态
下的连接数来决定由谁来处理用户的请求。可以举一个例子来说明她的工作原理:
假设有n台服务器,每一台服务器的weighted值为Wi ( i=1,2. . . n) ,session为Ci (i=1,...n), ALL-CONNECTIONS 是所有服务器
的session和,既C1+C2+....+Cn.,那么按照下面的算法,服务器j将处理下一个请求:
( Cj/ALL-CONNECTIONS )/Wj=min { (Ci/ALL-CONNECTIONS)/Wi } ( i=1,..,n)
也可以简化为:
Cj/Wj = min { Ci/Wi } (i=1,..,n)
我们可以对这四种算法做一个比较,Round-Robin和Weighted Round-Robin是静态的方法,对于服务器的当前状态不能做出很好的估计,
因此数据的分配并不是最合理。
但在服务器的性能相当时Round-Robin却是最好的选择。Least-Connection 和Weighted Least-Connection是动态的方法,可以对服务器
的当前状态做出比较合理的估计,这样在平衡各台服务器时,可以做出比较优化的选择。
Keepalived
keepalived是一个类似于layer3, 4 & 5交换机制的软件,也就是我们平时说的第3层、第4层和第5层交换。Keepalived的作用是检测web
服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器
工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web
服务器。
Layer3,4&5工作在IP/TCP协议栈的IP层,TCP层,及应用层,原理分别如下:
Layer3:Keepalived使用Layer3的方式工作式时,Keepalived会定期向服务器群中的服务器发送一个ICMP的数据包
(既我们平时用的Ping程序),如果发现某台服务的IP地址没有激活,Keepalived便报告这台服务器失效,并将它从服务器群中剔除,
这种情况的典型例子是某台服务器被非法关机。Layer3的方式是以服务器的IP地址是否有效作为服务器工作正常与否的标准。在本文中
将采用这种方式。
Layer4:如果您理解了Layer3的方式,Layer4就容易了。Layer4主要以TCP端口的状态来决定服务器工作正常与否。如web server的服务
端口一般是80,如果Keepalived检测到80端口没有启动,则Keepalived将把这台服务器从服务器群中剔除。
Layer5:Layer5就是工作在具体的应用层了,比Layer3,Layer4要复杂一点,在网络上占用的带宽也要大一些。Keepalived将根据用户的
设定检查服务器程序的运行是否正常,如果与用户的设定不相符,则Keepalived将把服务器从服务器群中剔除。
Heartbeat
heartbeat在前面我们简要的说明了一下,两台LLB可以互为备份,这个工作就是由heartbeat来完成的。Heartbeat的中文是
“心跳检测”。Slave LLB利用heartbeat来检测Master LLB的当前状态,当Master LLB不能工作时(如:down机)
Slave LLB通过heartbeat来接管Master LLB的所有工作,这个接管过程在10秒以内完成,对用户来说没有什么察觉。
Heartbeat中包含一个IP take over(IP 地址接管)的功能,此功能是通过ARP欺骗的手段来完成的。
实现
1,硬件配置:我本人的实验环境是5台PC机,两台作为LLB,并且在两台LLB上各装两块网卡。另外三台做web服务器 。一台HUB.
2,LLB上的软件配置:
· OS:RedHat Linux 6.2
· Linux Kernel:linux-2.2.17
· ipvsadm: IPVS-0.9.16
· ipchains: ipchains 1.3.9
· Keepalived: keepalived-0.2.7
· Heartbeat:heartbeat-0.4.9
3,软件的安装:
· 安装ipvs-0.9.16-2.2.17.tar.gz
#tar zxvf ipvs-0.9.16-2.2.17.tar.gz
#cd /usr/src/linux
#cat /ipvs-0.9.16-2.2.17.patch | patch -p1
在这一步完成后你必须重新编译Linux的内核,并且确定下面的选项被编译到新的内核中!
Kernel Compile Options:
Code maturity level options ---[*] Prompt for development and/or incomplete code/drivers
Networking options ---[*] Network firewalls
....[*] IP: firewalling
....[*] IP: masquerading
....[*] IP: masquerading virtual server support
(12) IP masquerading table size (the Nth power of 2)
<*> IPVS: round-robin scheduling
<*> IPVS: weighted round-robin scheduling
<*> IPVS: least-connection scheduling
<*> IPVS: weighted least-connection scheduling
....[*] IP: aliasing support
内核编译完后用新的内核重新启动系统。
然后执行下面的命令:
#cd //ipvsadm/
#make
#make install
至此,ipvsadm安装完毕!
· 安装 keepalived-0.2.7.tar.gz,执行下面的命令:
#tar keepalived-0.2.7.tar.gz
#cd keepalived-0.2.7
#make
#make install
· 安装heartbeat-0.4.9.tar.gz
#tar zxvf heartbeat-0.4.9.tar.gz
#cd heartbeat-0.4.9
#make
#make install
4,配置:
IP地址的配置:
用户IP地址:192.168.2.21
两台LLB各有两块网卡:eth0,eth1,
Master LLB eth0: 192.168.2.1
Master LLB eth1: 10.10.10.1
Slave LLB eth0: 192.168.2.2
Slave LLB eth1: 10.10.10.2
Server-1 : 10.10.10.11
Server-2 : 10.10.10.12
Server-3 : 10.10.10.13
另外还有两个特殊的IP地址是最需要注意的,一个是整个集群对外统一使用的IP地址,我们称它为VIP(Virtual IP) ,我们
他设为:192.168.2.11,这个IP地址应该出现在您的DNS中,如:我们配置的这个集群提供的是web服务,则在DNS中应该有这样
的记录:
--> 192.168.2.11
在LLB启动后VIP会运行在LLB的eth0:0上;
另一个特殊的IP是运行在LLB的eth1:0上,这个IP是三台服务器的默认网关,我们称它为V-gate (Virtual gate)。
所有的IP地址的子网掩码全采用255.255.255.0。VIP与V-gate在后面进行配置。到目前为止,网络中应该是一个这样的环境:
client
192.168.2.21
________|__________
| |
| |
Master LLB Slave LLB
eth0:192.168.2.1 eth0:192.168.2.2
eth1:10.10.10.10.1 eth1:10.10.10.2
| |
|_________________|
|
HUB
____________|_______________
| | |
| | |
webserver1 webserver2 webserver3
10.10.10.11 10.10.10.12 10.10.10.13
配置文件:
在LLB中,ipchains与ipvsadm是由keepalived来启动的,keepalived是由heartbeat来启动。Keepalived有两个配置文件:
/etc/keepalived/keepalived.conf和/etc/lvs.conf
/etc/lvs.conf内容如下:
#Configuration file for portFW NAT
#/etc/lvs.conf
#Port forwarding
ipvsadm -A -t 192.168.2.11:80 -s rr
#nat
ipchains -A forward -j MASQ -p tcp -s 10.10.10.0/24 80 -d 0.0.0.0/0
解释:
ipvsadm -A -t 192.168.2.11:80 -s rr
这句的意思是说192.168.2.11是一个集群地址(VIP),并且是针对80端口的,也就是说是一个web集群。在这个集群中采用
Round-Robin(rr)的算法。
ipchains -A forward -j MASQ -p tcp -s 10.10.10.0/24 80 -d 0.0.0.0/0
这句的作用是在服务器给用户做出回应时,将IP地址进行伪装,由10.10.10.11, 10.10.10.12, 10.10.10.13伪
装成192.168.2.11。
/etc/keepalived/keepalived.conf内容如下:
# Configuration File for keepalived
#begin www server config.......
virtual_server 192.168.2.11 80{
lb_algo rr
lb_kind NAT
protocol TCP
real_server 10.10.10.11 80 {
weight 1
ICMP_CHECK
}
real_server 10.10.10.12 80 {
weight 1
ICMP_CHECK
}
real_server 10.10.10.13 80 {
weight 1
ICMP_CHECK
}
}
#end of Fweb config!
解释:在这个文件里对三台服务器进行具体的说明,并且三台服务器有相同的Weight值。Keepalived以Layer3的方式(ICMP_CHECK)工作,
既定期检查服务器是否存在。
heartbeat 有三个配置文件:/etc/ha.d/ha.cf;/etc/ha.d/haresources;/etc/ha.d/ authkeys
/etc/ha.d/ha.cf内容如下:
#
# keepalive: how many seconds between heartbeats
#
keepalive 2
logfile /var/log/ha-log
#
# deadtime: seconds-to-declare-host-dead
#
deadtime 10
# hopfudge maximum hop count minus number of nodes in config
hopfudge 1
#
# What UDP port to use for udp or ppp-udp communication?
#
udpport 1001
# What interfaces to heartbeat over?
udp eth0
#
# Facility to use for syslog()/logger (alternative to log/debugfile)
#
logfacility local0
#
# Tell what machines are in the cluster
# node nodename ... -- must match uname -n
node Master #Master是Master LLB的机器名,必须与uname –n 相一致
node Slave #Slave是Slave LLB的机器名,必须与uname –n 相一致
/etc/ha.d/haresources内容如下:
Master IPaddr::10.10.10.9/24/eth1 IPaddr::192.168.2.11/24/eth0 keepalived
其中10.10.10.9是V-gate的值,192.168.2.11是VIP的值,这两个特殊的IP地址就是在这里设置的。并且说明了Master是主用的LLB,
Keepalived是需要heartbeat启动的程序。Keepalived是在安装Keepalived时自动生成的一个可执行文件,heartbeat会
在/etc/ha.d/resource.d目录下寻找keepalived,所以您需要将可执行的keepalived文件复制到/etc/ha.d/resource.d/目录下。
/etc/ha.d/ authkeys内容如下:
auth 1
1 sha1 cluster
这个文件不是很重要,但一定要有。在这里说明了LLB之间的一种AUTH机制。详细的说明请参考:
至此,所有的安装配置全部完成。一共有五个配置文件:
· /etc/keepalived/keepalived.conf
· /etc/lvs.conf
· /etc/ha.d/ha.cf
· /etc/ha.d/haresources
· /etc/ha.d/ authkeys
请您在仔细检查一下,而且确定这五个文件都安装在了Master LLB与Slave LLB上。下一步就可以启动了。
启动系统:
[root@master heartbeat]#heartbeat start
[root@slave heartbeat ]# heartbeat start
如果没有什么问题,系统已经启动了,以下是我的Master LLB上的一些启动的记录:
heartbeat的/var/log/ha-log如下:
heartbeat: 2001/05/29_10:31:37 info: **************************
heartbeat: 2001/05/29_10:31:37 info: Configuration validated. Starting heartbeat 0.4.9
heartbeat: 2001/05/29_10:31:37 info: heartbeat: version 0.4.9
heartbeat: 2001/05/29_10:31:37 info: Heartbeat generation: 20
heartbeat: 2001/05/29_10:31:37 info: Creating FIFO /var/run/heartbeat-fifo.
heartbeat: 2001/05/29_10:31:37 notice: UDP heartbeat started on port 1001 interface eth0
heartbeat: 2001/05/29_10:31:37 info: Local status now set to: 'up'
heartbeat: 2001/05/29_10:31:37 info: Heartbeat restart on node master
heartbeat: 2001/05/29_10:31:38 info: Link master:eth0 up.
heartbeat: 2001/05/29_10:31:38 info: Running /etc/ha.d/rc.d/ifstat ifstat
heartbeat: 2001/05/29_10:32:08 WARN: node slave: is dead
heartbeat: 2001/05/29_10:32:08 info: Local status now set to: 'active'
heartbeat: 2001/05/29_10:32:08 info: Node master: status up
heartbeat: 2001/05/29_10:32:08 info: Running /etc/ha.d/rc.d/status status
heartbeat: 2001/05/29_10:32:08 info: Running /etc/ha.d/rc.d/status status
heartbeat: 2001/05/29_10:32:08 info: mach_down takeover complete.
heartbeat: 2001/05/29_10:32:08 info: Running /etc/ha.d/resource.d/IPaddr 192.168.2.11/24/e
th0 status
heartbeat: 2001/05/29_10:32:08 info: Node master: status active
heartbeat: 2001/05/29_10:32:08 info: Resource acquisition completed.
heartbeat: 2001/05/29_10:32:08 info: Running /etc/ha.d/rc.d/status status
heartbeat: 2001/05/29_10:32:08 info: Running /etc/ha.d/rc.d/ip-request ip-request
heartbeat: 2001/05/29_10:32:18 info: Running /etc/ha.d/resource.d/IPaddr 192.168.2.11/24/e
th0 status
heartbeat: 2001/05/29_10:32:18 info: Acquiring resource group: master IPaddr::192.168.2.11/24
/eth0 IPaddr::10.10.10.9/24/eth1 keepalived
heartbeat: 2001/05/29_10:32:18 info: Running /etc/ha.d/resource.d/IPaddr 192.168.2.11/24/e
th0 start
heartbeat: 2001/05/29_10:32:18 info: ifconfig eth0:0 192.168.2.11 netmask 255.255.255.0 br
oadcast 192.168.2.255
heartbeat: 2001/05/29_10:32:18 info: Sending Gratuitous Arp for 192.168.2.11 on eth0:0 [et
h0]
heartbeat: 2001/05/29_10:32:18 info: Running /etc/ha.d/resource.d/IPaddr 10.10.10.9/24/
eth1 start
heartbeat: 2001/05/29_10:32:19 info: ifconfig eth1:0 10.10.10.9 netmask 255.255.255.0
broadcast 192.168.2.255
heartbeat: 2001/05/29_10:32:19 info: Sending Gratuitous Arp for 10.10.10.9 on eth1:0 [e
th1]
heartbeat: 2001/05/29_10:32:19 info: Running /etc/ha.d/resource.d/keepalived
keepalived 的keepalived.log文件如下:
/etc/keepalived/log/keepalived.log
[24/05/01 - 09:39:11] keepalived[709]: Starting keepalived daemon
[24/05/01 - 09:39:11] keepalived[710]: Using LVS dynamic data representation :
[24/05/01 - 09:39:11] keepalived[710]: ------< Global definitions >------
[24/05/01 - 09:39:11] keepalived[710]: LVS ID =
[24/05/01 - 09:39:11] keepalived[710]: Delay loop = , Smtp server =
[24/05/01 - 09:39:11] keepalived[710]: Email notification from =
[24/05/01 - 09:39:11] keepalived[710]: ------< LVS Topology >------
[24/05/01 - 09:39:11] keepalived[710]: VS IP = 192.168.2.11, PORT = 80
[24/05/01 - 09:39:11] keepalived[710]: -> lb_algo = rr, lb_kind = NAT, persistence = , pr
otocol = TCP
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.11, PORT = 80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.12, PORT = 80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.13, PORT = 80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
我们将一台服务器的网线从网络上拿掉:(keepalived.log文件如下:注意最后两行)
24/05/01 - 09:39:11] keepalived[709]: Starting keepalived daemon
[24/05/01 - 09:39:11] keepalived[710]: Using LVS dynamic data representation :
[24/05/01 - 09:39:11] keepalived[710]: ------< Global definitions >------
[24/05/01 - 09:39:11] keepalived[710]: LVS ID =
[24/05/01 - 09:39:11] keepalived[710]: Delay loop = , Smtp server =
[24/05/01 - 09:39:11] keepalived[710]: Email notification from =
[24/05/01 - 09:39:11] keepalived[710]: ------< LVS Topology >------
[24/05/01 - 09:39:11] keepalived[710]: VS IP = 192.168.2.11, PORT=80
[24/05/01 - 09:39:11] keepalived[710]: -> lb_algo = rr, lb_kind = NAT, persistence = , pr
otocol = TCP
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.11, PORT=80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.12, PORT=80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.13, PORT=80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:48:56] keepalived[710]: ICMP check failed to 10.10.10.13.
[24/05/01 - 09:48:56] keepalived[710]: Removing service [10.10.10.13:80] from VS [192.168
.2.11:80]
再将网线插上去后配置文件如下:(keepalived.log文件如下:注意最后两行)
[24/05/01 - 09:39:11] keepalived[709]: Starting keepalived daemon
[24/05/01 - 09:39:11] keepalived[710]: Using LVS dynamic data representation :
[24/05/01 - 09:39:11] keepalived[710]: ------< Global definitions >------
[24/05/01 - 09:39:11] keepalived[710]: LVS ID =
[24/05/01 - 09:39:11] keepalived[710]: Delay loop = , Smtp server =
[24/05/01 - 09:39:11] keepalived[710]: Email notification from =
[24/05/01 - 09:39:11] keepalived[710]: ------< LVS Topology >------
[24/05/01 - 09:39:11] keepalived[710]: VS IP = 192.168.2.11, PORT=80
[24/05/01 - 09:39:11] keepalived[710]: -> lb_algo = rr, lb_kind = NAT, persistence = , pr
otocol = TCP
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.11, PORT=80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.12, PORT=80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:39:11] keepalived[710]: -> SVR IP = 10.10.10.13, PORT=80, WEIGHT = 1
[24/05/01 - 09:39:11] keepalived[710]: -> Keepalive method = ICMP_CHECK
[24/05/01 - 09:48:56] keepalived[710]: ICMP check failed to 10.10.10.13.
[24/05/01 - 09:48:56] keepalived[710]: Removing service [10.10.10.13:80] from VS [192.168
.2.11:80]
[24/05/01 - 09:51:23] keepalived[710]: ICMP check succeed to 10.10.10.13.
[24/05/01 - 09:51:23] keepalived[710]: Adding service [10.10.10.13:80] to VS [192.168.2.11 80]
总结:本文实现的是一种本地的负载均衡(Local Load balancer),既LLB和服务器都位于同一个LAN中。利用LINUX也可以
实现全球负载均衡(Global Load balancer),既服务器可以位于世界的任何角落。Global Load balancer可以利用BGP协议来实
现,BGP是边界网关协议,他将Internet划分为不同的自治域(AS),利用AS可以实现服务器的定位。Global Load balancer 的
实现与本文的Local Load balancer 的实现方式是完全不同的。本人正在做这方面的实验和研究,有兴趣的读者可以来信交流,
我的信箱是:connili@sina.com
参考文献:
IPCHAINS-HOWTO
IPMASQADM-HOWTO
LVS-HOWTO
IPVSADM(8)
IPCHAINS(8)
http://keepalived.sourceforge.net/documentation.html