墨少离 - 个人小站,分享一些资源以及心得~ - Windows/Linux 2022-03-18T13:04:32+08:00 Typecho https://www.msl.la/feed/atom/category/WindowsAndLinux/ <![CDATA[使用curl命令查看请求响应时间方法]]> https://www.msl.la/archives/502/ 2022-03-18T13:04:32+08:00 2022-03-18T13:04:32+08:00 墨少离 https://www.msl.la/ curl命令查看请求响应时间

# curl -o /dev/null -s -w %{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}"\n" "http://www.36nu.com"
0.014::0.015::0.018::0.019::1516256.00

-o:把curl 返回的html、js 写到垃圾回收站[ /dev/null]
-s:去掉所有状态
-w:按照后面的格式写出rt
time_namelookup:DNS 解析域名www.36nu.com的时间
time_commect:client和server端建立TCP 连接的时间
time_starttransfer:从client发出请求;到web的server 响应第一个字节的时间
time_total:client发出请求;到web的server发送会所有的相应数据的时间
speed_download:下周速度 单位 byte/s

上面这条命令及返回结果可以这么理解:
0.014: DNS 服务器解析www.36nu.com 的时间单位是s
0.015: client发出请求,到c/s 建立TCP 的时间;里面包括DNS解析的时间
0.018: client发出请求;到s响应发出第一个字节开始的时间;包括前面的2个时间
0.019: client发出请求;到s把响应的数据全部发送给client;并关闭connect的时间
1516256.00 :下载数据的速度

建立TCP连接到server返回client第一个字节的时间:0.018s - 0.015s = 0.003s
server把响应数据发送给client的时间:0.019s - 0.018 = 0.01s

curl 命令提供了 -w 参数,这个参数在 manpage 是这样解释的:

 -w, --write-out <format>
              Make curl display information on stdout after a completed transfer. The format is a string that may contain plain text mixed with any number of variables. The  format
              can  be  specified  as  a literal "string", or you can have curl read the format from a file with "@filename" and to tell curl to read the format from stdin you write
              "@-".

              The variables present in the output format will be substituted by the value or text that curl thinks fit, as described below. All variables are specified  as  %{vari‐
              able_name} and to output a normal % you just write them as %%. You can output a newline by using \n, a carriage return with \r and a tab space with \t.

它能够按照指定的格式打印某些信息,里面可以使用某些特定的变量,而且支持 \n、\t和 \r转义字符。提供的变量很多,比如 status_code、local_port、size_download 等等,这篇文章我们只关注和请求时间有关的变量(以 time_ 开头的变量)。

先往文本文件 curl-format.txt 写入下面的内容:

➜  ~ cat curl-format.txt
    time_namelookup:  %{time_namelookup}\n
       time_connect:  %{time_connect}\n
    time_appconnect:  %{time_appconnect}\n
      time_redirect:  %{time_redirect}\n
   time_pretransfer:  %{time_pretransfer}\n
 time_starttransfer:  %{time_starttransfer}\n
                    ----------\n
         time_total:  %{time_total}\n

那么这些变量都是什么意思呢?我解释一下:

  • time_namelookup:DNS 域名解析的时候,就是把 https://zhihu.com 转换成 ip 地址的过程
  • time_connect:TCP 连接建立的时间,就是三次握手的时间
  • time_appconnectSSL/SSH 等上层协议建立连接的时间,比如 connect/handshake 的时间
  • time_redirect:从开始到最后一个请求事务的时间
  • time_pretransfer:从请求开始到响应开始传输的时间
  • time_starttransfer:从请求开始到第一个字节将要传输的时间
  • time_total:这次请求花费的全部时间

我们先看看一个简单的请求,没有重定向,也没有 SSL 协议的时间:

➜  ~ curl -w "@curl-format.txt" -o /dev/null -s -L "http://cizixs.com"
    time_namelookup:  0.012
       time_connect:  0.227
    time_appconnect:  0.000
      time_redirect:  0.000
   time_pretransfer:  0.227
 time_starttransfer:  0.443
                    ----------
         time_total:  0.867

可以看到这次请求各个步骤的时间都打印出来了,每个数字的单位都是秒(seconds),这样可以分析哪一步比较耗时,方便定位问题。这个命令各个参数的意义:

  • -w:从文件中读取要打印信息的格式
  • -o /dev/null:把响应的内容丢弃,因为我们这里并不关心它,只关心请求的耗时
  • -s:不要打印进度条

从这个输出,我们可以算出各个步骤的时间:

  • DNS 查询:12ms
  • TCP 连接时间:pretransfter(227) - namelookup(12) = 215ms
  • 服务器处理时间:starttransfter(443) - pretransfer(227) = 216ms
  • 内容传输时间:total(867) - starttransfer(443) = 424ms

来个比较复杂的,访问某度首页,带有中间有重定向和 SSL 协议:

➜  ~ curl -w "@curl-format.txt" -o /dev/null -s -L "https://baidu.com"
    time_namelookup:  0.012
       time_connect:  0.018
    time_appconnect:  0.328
      time_redirect:  0.356
   time_pretransfer:  0.018
 time_starttransfer:  0.027
                    ----------
         time_total:  0.384

可以看到 time_appconnect 和 time_redirect 都不是 0 了,其中 SSL 协议处理时间为 328-18=310ms。而且 pretransfer 和 starttransfer 的时间都缩短了,这是重定向之后请求的时间。

参考资料
Timing Details With cURL
转自:zzhongcy

]]>
<![CDATA[nginx反向代理时保持长连接]]> https://www.msl.la/archives/468/ 2021-10-25T04:48:00+08:00 2021-10-25T04:48:00+08:00 墨少离 https://www.msl.la/ 【场景描述】

HTTP1.1之后,HTTP协议支持持久连接,也就是长连接,优点在于在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。

如果我们使用了nginx去作为反向代理或者负载均衡,从客户端过来的长连接请求就会被转换成短连接发送给服务器端。

为了支持长连接,我们需要在nginx服务器上做一些配置。

【要求】

使用nginx时,想要做到长连接,我们必须做到以下两点:

  1. 从client到nginx是长连接
  2. 从nginx到server是长连接

对于客户端而言,nginx其实扮演着server的角色,反之,之于server,nginx就是一个client。

【保持和 Client 的长连接】

我们要想做到Client与Nginx之间保持长连接,需要:

  1. Client发送过来的请求携带"keep-alive"header。
  2. Nginx设置支持keep-alive

【HTTP配置】

默认况下,nginx已经开启了对client连接的 keepalive 支持。对于特殊场景,可以调整相关参数。

http {

    keepalive_timeout 120s;      #客户端链接超时时间。为0的时候禁用长连接。
    keepalive_requests 10000;    #在一个长连接上可以服务的最大请求数目。
    
                                 #当达到最大请求数目且所有已有请求结束后,连接被关闭。
    
                                 #默认值为100
    }

大多数情况下,keepalive_requests = 100也够用,但是对于 QPS 较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。
QPS=10000 时,客户端每秒发送 10000 个请求 (通常建立有多个长连接),每个连接只能最多跑 100 次请求,意味着平均每秒钟就会有 100 个长连接因此被 nginx 关闭。
同样意味着为了保持 QPS,客户端不得不每秒中重新新建 100 个连接。
因此,如果用netstat命令看客户端机器,就会发现有大量的TIME_WAIT的socket连接 (即使此时keep alive已经在 Client 和 NGINX 之间生效)。

【保持和Server的长连接】

想让Nginx和Server之间维持长连接,最朴素的设置如下:

http {
    upstream backend {
      server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
      server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;
      keepalive 300; // 这个很重要!
    }   
    server {
      listen 8080 default_server;
      server_name "";
      location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;  # 设置http版本为1.1
        proxy_set_header Connection "";      # 设置Connection为长连接(默认为no)
      }
    }
}

【upstream配置】

upstream中,有一个参数特别的重要,就是keepalive。
这个参数和之前http里面的 keepalive_timeout 不一样。
这个参数的含义是,连接池里面最大的空闲连接数量。

不理解?没关系,我们来举个例子:

场景:

有一个HTTP服务,作为upstream服务器接收请求,响应时间为100毫秒。
要求性能达到10000 QPS,我们需要在nginx与upstream服务器之间建立大概1000条HTTP请求。(1000/0.1s=10000)

最优情况:

假设请求非常的均匀平稳,每一个请求都是100ms,请求结束会被马上放入连接池并置为idle(空闲)状态。
我们以0.1s为单位:

  1. 我们现在keepalive的值设置为10,每0.1s钟有1000个连接
  2. 第0.1s的时候,我们一共有1000个请求收到并释放
  3. 第0.2s的时候,我们又来了1000个请求,在0.2s结束的时候释放
    请求和应答都比较均匀,0.1s释放的连接正好够用,不需要建立新连接,且连接池中没有idle状态的连接。

第一种情况:

应答非常平稳,但是请求不平稳的时候

  1. 第0.3s的时候,我们只有500个请求收到,有500个请求因为网络延迟等原因没有进来这个时候,Nginx检测到连接池中有500个idle状态的连接,就直接关闭了(500-10)个连接
  2. 第0.4s的时候,我们收到了1500个请求,但是现在池里面只有(500+10)个连接,所以Nginx不得不重新建立了(1500-510)个连接。
    如果在第4步的时候,没有关闭那490个连接的话,只需要重新建立500个连接。

第二种情况:

请求非常平稳,但是应答不平稳的时候

  1. 第0.3s的时候,我们一共有1500个请求收到但是池里面只有1000个连接,这个时候,Nginx又创建了500个连接,一共1500个连接
  2. 第0.3s的时候,第0.3s的连接全部被释放,我们收到了500个请求
    Nginx检测到池里面有1000个idle状态的连接,所以不得不释放了(1000-10)个连接

造成连接数量反复震荡的一个推手,就是这个keepalive 这个最大空闲连接数。
上面的两种情况说的都是 keepalive 设置的不合理导致Nginx有多次释放与创建连接的过程,造成资源浪费。
keepalive 这个参数设置一定要小心,尤其是对于 QPS 要求比较高或者网络环境不稳定的场景,一般根据 QPS 值和 平均响应时间能大致推算出需要的长连接数量。
然后将keepalive设置为长连接数量的10%到30%。

【location配置】

http {

    server {
        location / {   
        proxy_pass http://backend;
        proxy_http_version 1.1;              # 设置http版本为1.1
        proxy_set_header Connection "";      # 设置Connection为长连接(默认为no)
    
    }
    
    }
    
}

HTTP 协议中对长连接的支持是从 1.1 版本之后才有的,因此最好通过 proxy_http_version 指令设置为 1.1。
HTTP1.0不支持keepalive特性,当没有使用HTTP1.1的时候,后端服务会返回101错误,然后断开连接。
而 "Connection" header 可以选择被清理,这样即便是 Client 和 Nginx 之间是短连接,Nginx 和 upstream 之间也是可以开启长连接的。

【另外一种高级方式】

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }
    upstream backend {
        server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
        server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;
        keepalive 300;
    }   
    
    server {
    listen 8080 default_server;
    server_name "";    
    location / {    
    proxy_pass http://backend;
    proxy_connect_timeout 15;       #与upstream server的连接超时时间(没有单位,最大不可以超过75s)
    proxy_read_timeout 60s;         #nginx会等待多长时间来获得请求的响应
    proxy_send_timeout 12s;         #发送请求给upstream服务器的超时时间   
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    }
    
    }
    
}

http里面的map的作用是:

让转发到代理服务器的 "Connection" 头字段的值,取决于客户端请求头的 "Upgrade" 字段值。
如果 $http_upgrade没有匹配,那 "Connection" 头字段的值会是upgrade。
如果 $http_upgrade为空字符串的话,那 "Connection" 头字段的值会是 close。

【补充】

NGINX支持WebSocket。
对于NGINX将升级请求从客户端发送到后台服务器,必须明确设置Upgrade和Connection标题。
这也算是上面情况所非常常用的场景。
HTTP的Upgrade协议头机制用于将连接从HTTP连接升级到WebSocket连接,Upgrade机制使用了Upgrade协议头和Connection协议头。
为了让Nginx可以将来自客户端的Upgrade请求发送到后端服务器,Upgrade和Connection的头信息必须被显式的设置。

【注意】

在nginx的配置文件中,如果当前模块中没有proxy_set_header的设置,则会从上级别继承配置。
继承顺序为:http, server, location。
如果在下一层使用proxy_set_header修改了header的值,则所有的header值都可能会发生变化,之前继承的所有配置将会被丢弃。
所以,尽量在同一个地方进行proxy_set_header,否则可能会有别的问题。

【参考】

Nginx中文官方文档: http://www.nginx.cn/doc/
测试参考文档: https://www.lijiaocn.com/问题/2019/05/08/nginx-ingress-keep-alive-not-work.html
keep-alive参考文档: https://wglee.org/2018/12/02/nginx-keepalive/

]]>
<![CDATA[将用户名设置在 Git 中和全局修改用户名和email]]> https://www.msl.la/archives/465/ 2021-09-29T14:15:00+08:00 2021-09-29T14:15:00+08:00 墨少离 https://www.msl.la/ Git uses a username to associate commits with an identity. The Git username is not the same as your GitHub username.

You can change the name that is associated with your Git commits using the git config command. The new name you set will be visible in any future commits you push to GitHub from the command line. If you'd like to keep your real name private, you can use any text as your Git username.

Changing the name associated with your Git commits using git config will only affect future commits and will not change the name used for past commits.

Setting your Git username for every repository on your computer

  1. Open Git Bash.
  2. Set a Git username:

    $ git config --global user.name "Mona Lisa"
    $ git config --global user.email “xxx@email.com”
  3. Confirm that you have set the Git username correctly:

    $ git config --global user.name
    > Mona Lisa
    $ git config --global user.email
    > xxx@email.com

Setting your Git username for a single repository

  1. Open Git Bash.
  2. Change the current working directory to the local repository where you want to configure the name that is associated with your Git commits.
  3. Set a Git username:

    $ git config user.name "Mona Lisa"
  4. Confirm that you have set the Git username correctly:

    $ git config user.name
    > Mona Lisa
    $ git config user.email
    > xxx@email.com

修改的用户名和邮箱

查看配置

git config --list
git config --global --replace-all user.name “你的用户名”
git config --global --replace-all user.email “你的邮箱”

原文地址:https://docs.github.com/en/get-started/getting-started-with-git/setting-your-username-in-git#

]]>
<![CDATA[使用 openssl 命令行构建 CA 及证书]]> https://www.msl.la/archives/462/ 2021-09-19T02:25:00+08:00 2021-09-19T02:25:00+08:00 墨少离 https://www.msl.la/ 这是一篇快速指南,使用 OpenSSL 来生成 CA (证书授权中心certificate authority)、 中级 CAintermediate CA 和末端证书end certificate。包括 OCSP、CRL 和 CA 颁发者Issuer信息、具体颁发和失效日期。

我们将设置我们自己的根 CAroot CA,然后使用根 CA 生成一个示例的中级 CA,并使用中级 CA 签发最终用户证书。

使用 openssl 命令行构建 CA 及证书

根 CA

为根 CA 创建一个目录,并进入:

mkdir -p ~/SSLCA/root/
cd ~/SSLCA/root/

生成根 CA 的 8192 位长的 RSA 密钥:

openssl genrsa -out rootca.key 8192

输出类似如下:

Generating RSA private key, 8192 bit long modulus
.........++
....................................................................................................................++
e is 65537 (0x10001)

如果要用密码保护这个密钥,在命令行添加选项 -aes256

创建 SHA-256 自签名的根 CA 证书 ca.crt;你需要为你的根 CA 提供识别信息:

openssl req -sha256 -new -x509 -days 1826 -key rootca.key -out rootca.crt

输出类似如下:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Chaoyang dist.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Msl.LA
Organizational Unit Name (eg, section) []:Msl.LA CA
Common Name (e.g. server FQDN or YOUR name) []:Msl.LA Root CA
Email Address []:ca@msl.la

创建几个文件, 用于该 CA 存储其序列号:

touch certindex
echo 1000 > certserial
echo 1000 > crlnumber

创建 CA 的配置文件,该文件包含 CRL 和 OCSP 终端的存根。

# vim ca.conf
[ ca ]
default_ca = myca
[ crl_ext ]
issuerAltName=issuer:copy 
authorityKeyIdentifier=keyid:always
[ myca ]
dir = ./
new_certs_dir = $dir
unique_subject = no
certificate = $dir/rootca.crt
database = $dir/certindex
private_key = $dir/rootca.key
serial = $dir/certserial
default_days = 730
default_md = sha1
policy = myca_policy
x509_extensions = myca_extensions
crlnumber = $dir/crlnumber
default_crl_days = 730
[ myca_policy ]
commonName = supplied
stateOrProvinceName = supplied
countryName = optional
emailAddress = optional
organizationName = supplied
organizationalUnitName = optional
[ myca_extensions ]
basicConstraints = critical,CA:TRUE
keyUsage = critical,any
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign
extendedKeyUsage = serverAuth
crlDistributionPoints = @crl_section
subjectAltName  = @alt_names
authorityInfoAccess = @ocsp_section
[ v3_ca ]
basicConstraints = critical,CA:TRUE,pathlen:0
keyUsage = critical,any
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign
extendedKeyUsage = serverAuth
crlDistributionPoints = @crl_section
subjectAltName  = @alt_names
authorityInfoAccess = @ocsp_section
[ alt_names ]
DNS.0 = Msl.LA Root CA
DNS.1 = Msl.LA CA Root

[crl_section]
URI.0 = http://pki.msl.la/rootca.crl
URI.1 = http://pki2.msl.la/rootca.crl
[ ocsp_section ]
caIssuers;URI.0 = http://pki.msl.la/rootca.crt
caIssuers;URI.1 = http://pki2.msl.la/rootca.crt
OCSP;URI.0 = http://pki.msl.la/ocsp/
OCSP;URI.1 = http://pki2.msl.la/ocsp/

如果你要设置一个特定的证书起止时间,添加下述内容到 [myca]

# format: YYYYMMDDHHMMSS
default_enddate = 20191222035911
default_startdate = 20181222035911

创建1号中级 CA

生成中级 CA 的私钥

openssl genrsa -out intermediate1.key 4096

生成其 CSR:

openssl req -new -sha256 -key intermediate1.key -out intermediate1.csr

输出类似如下:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Chaoyang dist.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Msl.LA
Organizational Unit Name (eg, section) []:Msl.LA CA
Common Name (e.g. server FQDN or YOUR name) []:Msl.LA Intermediate CA
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

请确保中级 CA 的主题名(CN,Common Name)和根 CA 的不同。

使用根 CA 为你创建的中级 CA 的 CSR 签名:

openssl ca -batch -config ca.conf -notext -in intermediate1.csr -out intermediate1.crt

输出类似如下:

Using configuration from ca.conf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'CN'
stateOrProvinceName   :ASN.1 12:'Beijing'
localityName          :ASN.1 12:'chaoyang dist.'
organizationName      :ASN.1 12:'Msl.LA'
organizationalUnitName:ASN.1 12:'Msl.LA CA'
commonName            :ASN.1 12:'Msl.LA Intermediate CA'
Certificate is to be certified until Mar 30 15:07:43 2017 GMT (730 days)
Write out database with 1 new entries
Data Base Updated

生成 CRL (包括 PEM 和 DER 两种格式):

openssl ca -config ca.conf -gencrl -keyfile rootca.key -cert rootca.crt -out rootca.crl.pem
openssl crl -inform PEM -in rootca.crl.pem -outform DER -out rootca.crl

每次使用该 CA 签名证书后都需要生成 CRL。

如果需要的话,你可以撤销revoke这个中级证书:

openssl ca -config ca.conf -revoke intermediate1.crt -keyfile rootca.key -cert rootca.crt

配置1号中级 CA
给该中级 CA 创建新目录,并进入:

mkdir ~/SSLCA/intermediate1/
cd ~/SSLCA/intermediate1/

从根 CA 那边复制这个中级 CA 的证书和私钥:

cp ../root/intermediate1.key ./
cp ../root/intermediate1.crt ./

创建索引文件:

touch certindex
echo 1000 > certserial
echo 1000 > crlnumber

创建一个新的 ca.conf :

# vim ca.conf
[ ca ]
default_ca = myca
[ crl_ext ]
issuerAltName=issuer:copy 
authorityKeyIdentifier=keyid:always
[ myca ]
dir = ./
new_certs_dir = $dir
unique_subject = no
certificate = $dir/intermediate1.crt
database = $dir/certindex
private_key = $dir/intermediate1.key
serial = $dir/certserial
default_days = 365
default_md = sha1
policy = myca_policy
x509_extensions = myca_extensions
crlnumber = $dir/crlnumber
default_crl_days = 365
[ myca_policy ]
commonName = supplied
stateOrProvinceName = supplied
countryName = optional
emailAddress = optional
organizationName = supplied
organizationalUnitName = optional
[ myca_extensions ]
basicConstraints = critical,CA:FALSE
keyUsage = critical,any
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
crlDistributionPoints = @crl_section
subjectAltName  = @alt_names
authorityInfoAccess = @ocsp_section
[ alt_names ]
DNS.0 = Msl.LA Intermidiate CA 1
DNS.1 = Msl.LA CA Intermidiate 1
[ crl_section ]
URI.0 = http://pki.msl.la/intermediate1.crl
URI.1 = http://pki2.msl.la/intermediate1.crl
[ ocsp_section ]
caIssuers;URI.0 = http://pki.msl.la/intermediate1.crt
caIssuers;URI.1 = http://pki2.msl.la/intermediate1.crt
OCSP;URI.0 = http://pki.msl.la/ocsp/
OCSP;URI.1 = http://pki2.msl.la/ocsp/

修改 [alt_names] 小节为你所需的替代主题名Subject Alternative names。如果不需要就删除引入它的 subjectAltName = @alt_names 行。

如果你需要指定起止时间,添加如下行到 [myca] 中。

# format: YYYYMMDDHHMMSS
default_enddate = 20191222035911
default_startdate = 20181222035911

生成一个空的 CRL (包括 PEM 和 DER 两种格式):

openssl ca -config ca.conf -gencrl -keyfile intermediate1.key -cert intermediate1.crt -out intermediate1.crl.pem
openssl crl -inform PEM -in intermediate1.crl.pem -outform DER -out intermediate1.crl

创建最终用户证书

我们使用新的中级 CA 来生成最终用户的证书。为每个你需要用此 CA 签名的最终用户证书重复这些步骤。

mkdir ~/enduser-certs
cd ~/enduser-certs

生成最终用户的私钥:

openssl genrsa -out enduser-example.com.key 4096

生成最终用户的 CSR:

openssl req -new -sha256 -key enduser-example.com.key -out enduser-example.com.csr

输出类似如下:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Shanghai
Locality Name (eg, city) []:Xuhui dist.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Inc
Organizational Unit Name (eg, section) []:IT Dept
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

用1号中级 CA 签名最终用户的证书:

cd ~/SSLCA/intermediate1
openssl ca -batch -config ca.conf -notext -in ~/enduser-certs/enduser-example.com.csr -out ~/enduser-certs/enduser-example.com.crt

输出类似如下:

Using configuration from ca.conf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'CN'
stateOrProvinceName   :ASN.1 12:'Shanghai'
localityName          :ASN.1 12:'Xuhui dist.'
organizationName      :ASN.1 12:'Example Inc'
organizationalUnitName:ASN.1 12:'IT Dept'
commonName            :ASN.1 12:'example.com'
Certificate is to be certified until Mar 30 15:18:26 2016 GMT (365 days)
Write out database with 1 new entries
Data Base Updated

生成 CRL (包括 PEM 和 DER 两种格式):

cd ~/SSLCA/intermediate1/
openssl ca -config ca.conf -gencrl -keyfile intermediate1.key -cert intermediate1.crt -out intermediate1.crl.pem
openssl crl -inform PEM -in intermediate1.crl.pem -outform DER -out intermediate1.crl

每次使用该 CA 签名证书后都需要生成 CRL。

如果需要的话,你可以撤销revoke这个最终用户证书:

cd ~/SSLCA/intermediate1/
openssl ca -config ca.conf -revoke ~/enduser-certs/enduser-example.com.crt -keyfile intermediate1.key -cert intermediate1.crt

输出类似如下:

Using configuration from ca.conf
Revoking Certificate 1000.
Data Base Updated

将根证书和中级证书连接起来创建证书链文件:

cat ../root/rootca.crt intermediate1.crt > ~/enduser-certs/enduser-example.com.chain

将这些文件发送给最终用户:

enduser-example.com.crt
enduser-example.com.key
enduser-example.com.chain

你也可以让最终用户提供他们中级的 CSR 文件,而只发回给他们 这个 .crt 文件。不要从服务器上删除它们,否则就不能撤销了。

校验证书

你可以通过如下命令使用证书链来验证最终用户证书:

cd ~/enduser-certs
openssl verify -CAfile enduser-example.com.chain enduser-example.com.crt 
enduser-example.com.crt: OK

你也可以用 CRL 来校验它。首先将 PEM CRL 连接到证书链文件:

cd ~/SSLCA/intermediate1
cat ../root/rootca.crt intermediate1.crt intermediate1.crl.pem > ~/enduser-certs/enduser-example.com.crl.chain

校验证书:

cd ~/enduser-certs
openssl verify -crl_check -CAfile enduser-example.com.crl.chain enduser-example.com.crt

如果该证书未撤销,输出如下:

enduser-example.com.crt: OK

如果撤销了,输出如下:

enduser-example.com.crt: CN = example.com, ST = Beijing, C = CN, O = Example Inc, OU = IT Dept
error 23 at 0 depth lookup:certificate revoked

原文:https://raymii.org/s/tutorials/OpenSSL_command_line_Root_and_Intermediate_CA_including_OCSP_CRL%20and_revocation.html
作者: Remy van Elst
译文:LCTT https://linux.cn/article-6498-1.html
译者: wxy

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

]]>
<![CDATA[Haproxy基础知识 -运维小结]]> https://www.msl.la/archives/352/ 2021-06-15T19:01:00+08:00 2021-06-15T19:01:00+08:00 墨少离 https://www.msl.la/ 开源软件负载均衡器

现在常用的三大开源软件负载均衡器分别是Nginx、LVS、Haproxy。 在之前的文章中已经对比了这三个负载均衡软件, 下面根据自己的理解和使用经验, 再简单说下这三个负载均衡软件各自特点:
LVS负载均衡的特点
1) 抗负载能力强。抗负载能力强、性能高,能达到F5硬件的60%;对内存和cpu资源消耗比较低
2) 工作在网络4层,通过vrrp协议转发(仅作分发之用),具体的流量linux内核处理,因此没有流量的产生。
3) 稳定性、可靠性好,自身有完美的热备方案;(如:LVS+Keepalived)
4) 应用范围比较广,可以对所有应用做负载均衡;
5) 不支持正则处理,不能做动静分离。
6) 支持负载均衡算法:rr(轮循)、wrr(带权轮循)、lc(最小连接)、wlc(权重最小连接)
7) 配置 复杂,对网络依赖比较大,稳定性很高。

Ngnix负载均衡的特点
1) 工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构;
2) Nginx对网络的依赖比较小,理论上能ping通就就能进行负载功能;
3) Nginx安装和配置比较简单,测试起来比较方便;
4) 也可以承担高的负载压力且稳定,一般能支撑超过1万次的并发;
5) 对后端服务器的健康检查,只支持通过端口来检测,不支持通过url来检测。
6) Nginx对请求的异步处理可以帮助节点服务器减轻负载;
7) Nginx仅能支持http、https和Email协议,这样就在适用范围较小。
8) 不支持Session的直接保持,但能通过ip_hash来解决。、对Big request header的支持不是很好,
9) 支持负载均衡算法:Round-robin(轮循)、Weight-round-robin(带权轮循)、Ip-hash(Ip哈希)
10) Nginx还能做Web服务器即Cache功能。

HAProxy负载均衡的特点
1) 支持两种代理模式:TCP(四层)和HTTP(七层),支持虚拟主机;
2) 能够补充Nginx的一些缺点比如Session的保持,Cookie的引导等工作
3) 支持url检测后端的服务器出问题的检测会有很好的帮助。
4) 更多负载均衡策略比如:动态加权轮循(Dynamic Round Robin),加权源地址哈希(Weighted Source Hash),加权URL哈希和加权参数哈希(Weighted Parameter Hash)已经实现
5) 单纯从效率上来讲HAProxy更会比Nginx有更出色的负载均衡速度。
6) HAProxy可以对Mysql进行负载均衡,对后端的DB节点进行检测和负载均衡。
9) 支持负载均衡算法:Round-robin(轮循)、Weight-round-robin(带权轮循)、source(原地址保持)、RI(请求URL)、rdp-cookie(根据cookie)
10) 不能做Web服务器即Cache。

这三大主流软件负载均衡器适用业务场景
1) 网站建设初期,可以选用Nigix/HAproxy作为反向代理负载均衡(或者流量不大都可以不选用负载均衡),因为其配置简单,性能也能满足一般的业务场景。如果考虑到负载均衡器是有单点问题,可以采用Nginx+Keepalived/HAproxy+Keepalived避免负载均衡器自身的单点问题。
2) 网站并发达到一定程度之后,为了提高稳定性和转发效率,可以使用LVS、毕竟LVS比Nginx/HAproxy要更稳定,转发效率也更高。不过维护LVS对维护人员的要求也会更高,投入成本也更大。

需要注意的是
Niginx与Haproxy比较: Niginx支持七层、用户量最大,稳定性比较可靠。Haproxy支持四层和七层,支持更多的负载均衡算法,支持session保存等。具体选型看使用场景,目前来说Haproxy由于弥补了一些Niginx的缺点致使其用户量也不断在提升。

衡量负载均衡器好坏的几个重要因素
1) 会话率 :单位时间内的处理的请求数
2) 会话并发能力:并发处理能力
3) 数据率:处理数据能力
经过官方测试统计,haproxy 单位时间处理的最大请求数为20000个,可以同时维护40000-50000个并发连接,最大数据处理能力为10Gbps。综合上述,haproxy是性能优越的负载均衡、反向代理服务器。

Haproxy简介

HAProxy是法国人Willy Tarreau开发的一款可应对客户端10000以上的同时连接的高性能的TCP和HTTP负载均衡器。由于其丰富强大的功能在国内备受推崇,是目前主流的负载均衡器。Haproxy是一个开源的高性能的反向代理或者说是负载均衡服务软件之一,它支持双机热备、虚拟主机、基于TCP和HTTP应用代理等功能。其配置简单,而且拥有很好的对服务器节点的健康检查功能(相当于keepalived健康检查),当其代理的后端服务器出现故障时,Haproxy会自动的将该故障服务器摘除,当服务器的故障恢复后haproxy还会自动重新添加回服务器主机。

Haproxy实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户空间(User-Space)实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么必须对其进行优化以使每个CPU时间片(Cycle)做更多的工作。

Haproxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。haproxy运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进当前架构中,同时可以保护web服务器不被暴露到网络上。

Haproxy软件引入了frontend,backend的功能,frontend(acl规则匹配)可以根据任意HTTP请求头做规则匹配,然后把请求定向到相关的backend(server pools等待前端把请求转过来的服务器组)。通过frontend和backend,可以很容易的实现Haproxy的7层负载均衡代理功能。

Haproxy是一种高效、可靠、免费的高可用及负载均衡解决方案,非常适合于高负载站点的七层数据请求。客户端通过Haproxy代理服务器获得站点页面,而代理服务器收到客户请求后根据负载均衡的规则将请求数据转发给后端真实服务器。
同一客户端访问服务器,Haproxy保持回话的三种方案:
1) Haproxy将客户端ip进行Hash计算并保存,由此确保相同IP访问时被转发到同一真实服务器上。
2) Haproxy依靠真实服务器发送给客户端的cookie信息进行回话保持。
3) Haproxy保存真实服务器的session及服务器标识,实现会话保持功能。

Haproxy工作原理

Haproxy提供高可用性、负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。Haproxy特别适用于那些负载特别大的web站点,这些站点通常又需要会话保持或七层处理。Haproxy运行在时下的硬件上,完全可以支持数以万计的并发连接,并且它的运行模式使得它可以很简单安全的整合进您当前的架构中,同时可以保护的web服务器不被暴露到网络上。

Haproxy实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户端(User-Space)实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以使每个CPU时间片(Cycle)做更多的工作。

Haproxy的优点

1)免费开源,稳定性也是非常好。单Haproxy也跑得不错,稳定性可以与硬件级的F5相媲美。
2)根据官方文档,Haproxy可以跑满10Gbps,这个数值作为软件级负载均衡器是相当惊人的。
3)Haproxy支持连接拒绝:因为维护一个连接的打开的开销是很低的,有时我们很需要限制攻击蠕虫(attack bots),也就是说限制它们的连接打开从而限制它们的危害。这个已经为一个陷于小型DDoS攻击的网站开发了而且已经拯救了很多站点,这个优点也是其它负载均衡器没有的。
4)Haproxy支持全透明代理(已具备硬件防火墙的典型特点):可以用客户端IP地址或者任何其他地址来连接后端服务器。这个特性仅在Linux 2.4/2.6内核打了tcp proxy补丁后才可以使用。这个特性也使得为某特殊服务器处理部分流量同时又不修改服务器的地址成为可能。
5)Haproxy现在多用于线上的Mysql集群环境,常用于它作为MySQL(读)负载均衡。
6)自带强大的监控服务器状态的页面,实际环境中我们结合Nagios进行邮件或短信报警。
7)Haproxy支持虚拟主机,许多朋友说它不支持虚拟主机是错误的,但是通过测试可知,Haproxy是支持虚拟主机的。

Haproxy主要工作位置

2021-06-15T10:28:17.png

  • Haproxy支持http反向代理
  • Haproxy支持动态程序的反向代理
  • Haproxy支持基于数据库的反向代理

Haproxy 相关术语

访问控制列表(ACL)
在负载均衡当中,ACL用于测试某些状况,并根据测试结果执行某些操作(例如选定一套服务器或者屏蔽某条请求), 如下ACL示例

acl url_blog path_beg /blog

如果用户请求的路径以/blog开头,则匹配这条ACL。举例来说,http://yourdomain.com/blog/blog-entry-1请求即符合这一条件。要了解更为具体的AC使用方法,请参阅HAProxy配置指南

Backend

所谓backend是指一组服务器,负责接收各转发请求。Backend在HAProxy配置中的backend部分进行定义。一般来讲,backend的定义主要包括:

  • 使用哪种负载均衡算法
  • 一套服务器与端口列表

一条backend能够容纳一套或者多套服务器——总体而言向backend中添加更多服务器将能够将负载扩散至更多服务器,从而增加潜在负载容量。这种方式也能够提升可靠性,从而应对部分后端服务器发生故障的况。

下面来看一套双backend配置示例,分别为web-backend与blog-backend,其各自包含两套Web服务器,且监听端口80:

backend web-backend
  balance roundrobin
  server web1 web1.test.com:80 check
  server web2 web2.test.com:80 check
 
backend blog-backend
  balance roundrobin
  mode http
  server blog1 blog1.test.com:80 check
  server blog2 blog2.test.com:80 check

其中:
.balance roundrobin行指定负载均衡算法, 具体细节请参阅负载均衡算法部分
.mode http则指定所使用的7层代理机制,具体请参阅负载均衡类型部分。
.结尾处的check选项指定在这些后端服务器上执行运行状态检查。

Frontend

一条frontend负责定义请求如何被转发至backend。Frontend在HAProxy配置中的frontend部分进行定义。其定义由以下几部分组成:
.一组IP地址与一个端口(例如10.1.1.7:80, *:443,等等)
.ACL
.use_backend规则,其负责根据当前ACL条件定义使用哪个backend,而default_backend规则处理一切其它情况
我们可以将同一frontend配置至多种不同网络流量类型

可以将同一frontend配置至多种不同网络流量类型!

下面了解负载均衡的基本类型。

无负载均衡
简单的无负载均衡Web应用环境, 用户会直接接入Web服务器,即test.com且其中不存在负载均衡机制。如果单一Web服务器发生故障,用户将无法接入该服务器。另外,如果多位用户同时访问该服务器,且其无法处理该负载,则可能出现响应缓慢或者无法接入的情况。

四层负载均衡
最为简单的负载均衡方式,将网络流量引导至多台服务器以使用四层(即传输层)负载均衡。这种方式会根据IP范围与端口进行用户流量转发(如果有请求指向http://test.com/anything,则该流量将被转发至backend,并由其处理全部指向test.com在端口80上的请求)

frontend web_80                            
   mode tcp
   bind :80
   default_backend web-backend
 
backend web-backend
   mode tcp
   server 172.16.60.164 172.16.60.164:80 weight 1 check inter 1s rise 2 fall 2
   server 172.16.60.165 172.16.60.165:80 weight 1 check inter 1s rise 2 fall 2

用户访问负载均衡器,后者将用户请求转发至后端服务器的web-backend组。被选定的后端服务器将直接响应用户请求。总体而言,web-backend中的全部服务器都应当拥有同样的内容, 否则用户可能会遭遇内容不一致问题。请注意后端两个Web服务器都接入同样的数据库服务器。
七层负载均衡
另一种更为复杂的负载均衡方式,网络流量使用7层(即应用层)负载均衡。使用7层意味着负载均衡器能够根据用户的请求内容将请求转发至不同后端服务器。这种方式允许大家在同一域名及端口上运行多套Web应用服务器。下图为一套简单的7层负载均衡示例在本示例中,如果用户向test.com/blog发送请求,则会被转发至blog后端,其包含一组运行有同一blog应用的服务器。其它请求则会被转发至web-backend,其负责运行其它应用。本示例中的两套backend皆使用同样的数据库服务器。下面来看本示例中frontend配置片段

frontend http
   bind *:80
   mode http
 
  acl url_blog path_beg /blog
  use_backend blog-backend if url_blog
 
  default_backend web-backend
 
backend web-backend
    mode http
    balance roundrobin
    cookie SERVERID insert indirect nocache
    option httpclose
    option forwardfor
    server web01 172.16.60.150:80 weight 1 cookie 3 check inter 2000 rise 2 fall 5
    server web01 172.16.60.151:80 weight 1 cookie 3 check inter 2000 rise 2 fall 5

这部分片段配置一套名为http的frontend,其负责处理端口80上的所有输入流量。
.acl url_blog path_beg 这里的/blog表示匹配那些用户请求路径以/blog开头的请求。
.use_backend blog-backend if url_blog 这里采用该ACL将流量代理至blog-backend。
.default_backend web-backend 这里指定全部其它流量被转发至web-backend。

负载均衡算法
负载均衡算法用于检测后端中的哪套服务器被负载均衡机制选定进行请求响应。HAProxy提供多种算法选项。除了负载均衡算法之外,我们还能够为服务器分配一个weight参数以指定该服务器被选定的频率。由于HAProxy提供多种负载均衡算法,因此我们下面只提及其中的一部分。来看几种常用算法:

roundrobin
Round Robin会对服务器进行轮流选定,亦为HAProxy的默认算法。即客户端每访问一次, 请求就跳转打到后端不同的节点机器上.

leastconn
选定连接数量最少的服务器——建议在周期较长的会话中使用这一算法。同一后端中的服务器亦会以轮循方式进行轮流选定。

source
其根据源IP(即用户IP地址)进行服务器选定。通过这种方式,我们可以确定同一用户接入同一服务器。

粘性会话
部分应用要求用户始终接入同一后端服务器,这一点需要利用粘性会话实现,其要求在backend中使用appsession参数。

运行状态检查
HAProxy利用运行状态检查来检测后端服务器是否可用于处理请求。通过这种方式,我们不必以手动方式撤下不可用的后端服务器。默认运行状态检查会与目标服务器建立一条TCP连接,即检查后端服务器是否在监听所配置的IP地址与端口。

如果某套服务器未能通过运行状态检查,并因此无法响应请求,则其会在后端中被自动禁用——意味着流量将不再被转发到该服务器处,直到其重新恢复正常。如果后端中的全部服务器皆发生故障,则服务将不再可用,直到至少一套后端服务器重新恢复正常。

对于特定后端类型,例如特定状态下的数据库服务器,默认运行状态检查可能无法正确识别其正常与否。
其它解决方案
如果大家认为HAProxy可能太过复杂,那么以下解决方案同样值得考虑:
.Linux虚拟服务器(LVS) - 一套简单且快速的4层负载均衡器,多数Linux发行版都内置有这套方案。
.Nginx - 一套快速且可靠的Web服务器,亦可用于代理及负载均衡。Nginx通常与HAProxy相结合以实现缓存及压缩功能。

Haproxy 使用细节梳理

以上简单介绍了Haproxy, 下面来整体梳理下Haproxy的使用:

Haproxy 程序环境

安装:yum install haproxy -y
主程序: /usr/sbin/haproxy
配置文件: /etc/haproxy/haproxy.cfg
主配置文件主要结构:1) global 全局配置段 ;  2) proxies代理配置段

一. global 全局配置段

1.1) log:全局日志配置
默认发往本机的日志服务器;
1) 系统默认:log 127.0.0.1 local2 //表示发往远端可添加:log 远端IP local2
2) 修改本机日志配置文件

[root@localhost ~]# vim  /etc/rsyslog.conf
$ModLoad imudp          #取消注释
$UDPServerRun 514    #取消注释
local2.*                                                /var/log/haproxy.log  #新增

3) 远端日志配置(配合前端log配置)

[root@localhost ~]# vim  /etc/rsyslog.conf
local2.*    /var/log/haproxy.log                 #新增

1.2) 性能调整(依据生产环境适当调整)
.maxconn <number>:设定每个haproxy进程所能接受的最大并发连接数
.maxconnrate <number>:设置每个进程每秒种所能建立的最大连接数量
.maxsessrate <number>:设置每个进程每秒种所能建立的最大会话数量
.maxsslconn <number>: 每进程支持SSL的最大连接数量
.spread-checks <0..50, in percent>: 健康检测延迟时长比建议2-5之间

二. proxies 代理配置段结构

1) defaults:为frontend, backend, listen提供默认配置;
2) fronted:前端,指定接收客户端连接侦听套接字设置;
3) backend:后端,指定将连接请求转发至后端服务器的相关设置;
4) listen:同时拥有前端和后端,适用于一对一环境;

三. 简单haproxy代理案例

实验拓扑:
2021-06-15T10:34:37.png

3.1) 方法一
配置 /etc/haproxy/haproxy.cfg, 将frontend段、backend段注释,新建如下配置

[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
............
............
frontend http *:80                #均衡器端采用http 协议,监听80端口
      default_backend websrvs   #引用后端自定义服务器组名websrvs
backend websrvs                   #设定默认后端,自定义名字为websrvs
      balance roundrobin        #轮询方式为加权轮询
      server web1 192.168.43.61:80 check    #设定后端服务器IP ,并引入健康检查
      server web2 192.168.43.63:80 check    #设定后端服务器IP ,并引入健康检查
 
 
 
[root@localhost ~]# service haproxy start

在浏览器访问http://172.18.43.62 刷新即可看到轮询效果, 即访问结果为192.168.43.61和192.168.43.63的web端口(80端口)展示的内容, 每次刷新, 访问结果都不一样, 即交替展示两个机器的web内容.

3.2) 方法二
上面两段配置也可以配置在一段实现相同功能,配置如下

[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
............
............
listen http
   bind :80
   balance roundrobin
   server web1 192.168.43.61:80 check
   server web2 192.168.43.63:80 check
 
[root@localhost ~]# service haproxy start

配置参数 bind:指定一个或多个前端侦听地址和端口
语法: bind [<address>]:<port_range> [, ...] [param*]
示例如下:

listen http_proxy
bind :80,:443
bind 172.16.60.10:10080,172.16.60.10:10443

option选项说明

option httpclose

haproxy会针对客户端的第一条请求的返回添加cookie并返回给客户端,客户端发送后续请求时会发送此cookie到HAProxy,HAProxy会针对此cookie分发到上次处理此请求的服务器上,如果服务器不能忽略此cookie值会影响处理结果。如果避免这种情况配置此选项,防止产生多余的cookie信息。"option httpclose" 和 "no option httpclose" 是两个对立设置, 分别表示启用或禁止消极的HTTP连接关闭

默认的,客户端与服务端的通讯,HAProxy只做分析、日志和分析每个连接的第一个request。如果设置了 "option httpclose" , 则会检查双向的http头是否有"Connection: close",如果没有则自动添加,使每个客户端或服务端在每次传输后,都主动关闭TCP连接,使HTTP传输处于HTTP close模式下。任何 "Connection" 头如果不是"close",都会被移除。

很少会有服务器不正确的忽略掉头,即使收到"Connection: close"也不关闭连接,否则就是不兼容HTTP 1.0浏览器标准。 如果发生这种情况,可以使用"option forceclose",在服务端响应后主动关闭请求连接。选项 "forceclose"还可以及早释放服务连接,而不必等到客户端的应答确认。

这个选项可以设置在frontend或backend上,只要其上可以建立连接。如果同时设置了"option forceclose",那么它比"httpclose"优先。如果同时设置了 "option http-server-close",则会实现"option forceclose"的效果。

option forceclose

启用response后的主动关闭连接 (no option forceclose 表示禁止)

有的HTTP服务器收到"option httpclose"设置的"Connection: close",也不会关闭连接,如果客户端也不关闭,连接会 一直打开,直到超时。这会造成服务器上同一时段内的大量连接,日志中也会显示较高的全局会话时间。

此时,可以使用 "option forceclose",当完成响应时,立即关闭对外的服务通道。该选项隐式打开httpclose选项。需要注意,该选项允许解析完整的request 和 response,所以可以很快关闭至服务器的连接,比httpclose更早释放一些资源。

如果同时启用了"option http-pretend-keepalive",虽然会禁止发送 "Connection: close"头,但是依然会在整个response被接收后,关闭连接。

option http-server-close

启用关闭服务端的HTTP连接 (no option http-server-close 表示禁止)

默认的,客户端与服务端通讯,haproxy 只是分析、记日志,并处理每个连接的第一个请求。该选项设置server端为HTTP 连接关闭模式,并支持客户端为HTTP keep-alive的pipelining模式。提供了最低的客户端延迟和最快的服务端会话重用,以节省服务资源,类似"option forceclose"。还允许无keepalive能力的服务端在keep-alive模式下为客户端提供服务,但是需要符合 RFC2616。需要注意的是,有些服务器遇到"Connection: close" 时并不总是执行关闭,那么keep-alive 则无法使用,解决方法是启用 "option http-pretend-keepalive".

目前,日志无法指明请求是否来自同一会话;日志中的接收日期为前一个请求的结束;请求时间为新请求的等待时间;keep-alive ### request time
存活请求时间为超时时间,绑定 "timeout http-keep-alive" 或 "timeout http-request"的设置。

这个操作可以设置在frontend或backend上,只要其上可以建立连接。需要注意的是,这个选项可以与 "option httpclose"结合, 但 "option httpclose"优先级更高,结合后基本实现了forceclose的效果。

option http-pretend-keepalive

(表示http-假装-长连接, 即haproxy 与服务器是否是 keepalive 的) (如果不开启该配置, 则为no option http-pretend-keepalive )

当声明了 "option http-server-close" 或 "option forceclose", haproxy会在给server的request头中添加 "Connection: close" 。然而有些服务器看到这个头,会返回未知长度的response,并自动避免chunked encoding,其实这是不对的。它会阻止haproxy保持客户端长连接,还会使客户端或缓存接收了未完成的响应,却认为响应结束了。

设置 "option http-pretend-keepalive", haproxy会在服务器端保持长连接,服务端则不会出现前面的问题。当 haproxy 获取了完整的response, 才会以类似forceclose的方式关闭服务端。这样客户端得到一个普通的响应,连接也在服务端被正常关闭。

建议不将其设为默认值,因为大部分服务器会在发送完最后一个包之后更高效的关闭连接,并释放缓存,而且网络上的数据包也会略微降低整体的峰值性能。但是启用该选项,haproxy会略微少做一些工作。所以如果haproxy在整个架构中是个瓶颈,可以启用该操作,以节省CPU。

这个选项可以设置在frontend或backend上,只要其上可以建立连接。这个选项可以与 "option httpclose"结合, 使服务端keepalive,客户端close,但并不建议这样做。

option forwardfor

如果服务器上的应用程序想记录发起请求的客户端的IP地址,需要在HAProxy上配置此选项,这样haproxy会把客户端的IP信息发送给服务器,在HTTP请求中添加"X-Forwarded-For"字段。

option originalto

如果服务器上的应用程序想记录发起请求的原目的IP地址,需要在HAProxy上配置此选项,这样haproxy会添加"X-Original-To"字段。

option dontlognull

(即记录空连接)
保证haproxy不记录上级负载均衡发送过来的用于检测状态没有数据的心跳包。

其他option相关设置:

option abortonclose 丢弃由于客户端等待时间过长而关闭连接但仍在haproxy等待队列中的请求
option accept-invalid-http-request 接受无效的http请求,建议关闭(开启可能有安全隐患)
option accept-invalid-http-response 接受无效的response ,建议关闭
option allbackups 应该是后备服务器,如果正常的后端无法使用,就使用这些后备的设备,balance方式还是用原来的,没有优先的选择,常用来提供错误的页面
option checkcache 分析后端response,阻止可缓存的cookie,它对response 进行严格检查,包括"Cache-control", "Pragma" and "Set-cookie" ,查看在客户端代理那边保存是否有风险,如果这个允许的话,符全以下条件 的response 将被允许,其它的将被阻止。

option clitcpka 是否允许客户端发送tcp keepalive 包,这个和http 的keepalive 没有关系
option contstats 允许连续的流量统计更新
option dontlog-normal 开启正常连接的日志
option forceclose 允许关闭session 在后端把response 发送后
option forwardfor [ except <network> ] [ header <name> ] 允许在request 中加入X-Forwarded-For header 发往server
option http-pretend-keepalive 定义是否haproxy要宣布同server keepalive
option http-server-close 是否开启在server 端 connection closing
option http-use-proxy-header 用non-standard Proxy-Connection 替换 connection

option httpchk <method> <uri> <version> 允许用http协议检查server 的健康
option httplog [ clf ] 定制日志格式
option http_proxy 开启http 代理模式,只有最基本的代理功能
option ignore-persist { if | unless } <condition> 在某条件下拒绝持续连接,适用于对静态文件的负载均衡
option independant-streams 启用双向超时处理,如socket 的read 和write
option log-health-checks 记录健康检查日志
option log-separate-errors 对非完全成功的连接改变日志记录等级
option logasap 大传输大文件时可以提前记录日志
option mysql-check mysql 健康检查
option nolinger 清除肮脏连接后开成的tcp 状态及占用的资源,不过并不是强列要求你用这个选项,当然如果你有thousands of FIN_WAIT1 sessions on your system ,那肯定得用了
**option originalto [ except <network> ] [ header <name> ] 允许在requests中加入X-Original-To header 发往server
option persist 强制将http请求发往已经down 掉的server
option redispatch 是否允许重新分配在session 失败后, 即serverId对应的服务器挂掉后,强制定向到其他健康的服务器!
option smtpchk smtp 检查;
option socket-stats 允许对单个socket进行统计;
option srvtcpka 是否允许向server 发送keepalive;
option tcpka 是否允许向server和client发送keepalive;
option tcplog 允许记录tcp 连接的状态和时间;
option transparent 允许客户端透明代理;
rate-limit sessions <rate> 设置frontend 每秒处理的连接的上限,如果到达上限就停止建立新的connection;

mode http 所处理的类别 (#7层 http;4层tcp);
mode tcp 所处理的类别 (#7层 http;4层tcp);
stats hide-version 隐藏统计页面上的HAproxy版本信息;

四. 代理配置段常见配置参数

4.1) Balance相关

balance:后端服务器组内的服务器调度算法
语法: balance <algorithm> [ <arguments> ]

        balance url_param `<param>` [check_post]

调度算法:
① roundrobin:基于权重轮询,动态算法, 支持权重的运行时调整,支持慢启动;每个后端backend中最多支持4095个server; haproxy把请求轮流的转发到每一个服务器上,依据每台服务器的权重,此权重会动态调整。这是最常见的默认调度算法的配置。即客户端每一次访问, 都跳转到后端不同的服务器上.
② static-rr:基于权重轮询,静态算法,不支持权重的运行时调整及慢启动;后端主机数量无上限;
③ leastconn:加权最少连接,动态算法,最少连接的后端服务器优先分配接收新连接,相同连接时轮询,推荐在较长会话的场景使用,例如MySQL、 LDAP等,不适合http;
④ first:根据服务器在列表中的位置,自上而下进行调度;前面服务器的连接数达到上限,新请求才会分配给下一台服务器;
⑤ hdr(name):对于每个http请求,此处由<name>指定的http首部将会被取出做hash计算; 并由服务器总权重相除以后派发至某挑出的服务器; 无有效值的会被轮询调度;
例:hdr(host) hdr(Cookie)

hash类算法:
需配合参数:hash-type <method> <function> <modifier>
method:

 map-based:除权取余法,哈希数据结构是静态数组;
 consistent:一致性哈希,哈希数据结构是一棵树;

⑥ source:源地址hash,新连接先按权重分配,后续连接按source分配请求; 如果想让haproxy按照客户端的IP地址进行负载均衡策略,即同一IP地址的所有请求都发送到同一服务器时,需要配置此选项。
⑦ uri:对URI的左半部分或整个uri做hash计算,并除以服务器总权重取模,以后派发至某挑出的服务器,适用于后端缓存服务器;
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
左半部分: /<path>;<params>
整个uri: /<path>;<params>?<query>#<frag>
⑧ url_param:对用户请求的uri听<params>部分中的参数的值(通常为用户ID)作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个Backend Server;

Haproxy中balance的算法总结,见下表
2021-06-15T10:39:37.png

4.2) server:定义后端主机相关选项

语法:
server <name> <address>[:[port]] [param*]

`<name>`:服务器在haproxy上的自定义名称;出现在日志及警告信息
`<address>`:服务器地址,支持使用主机名
[:[port]]:端口映射;省略时,表示同bind中绑定的端口

[param*]:server后可加的参数
weight <weight>:权重,默认为1
maxconn <maxconn>:当前server的最大并发连接数
backlog <backlog>:当server的连接数达到上限后的后援队列长度
backup:设定当前server为备用服务器
check:对当前server做健康状态检测,只用于四层检测

注意: httpchk, “smtpchk”, “mysql-check”, “pgsql-check” and “sslhello-chk” 用于定义应用层检测方法

     addr :检测时使用的IP地址(检测的不一定是vip)
     port :针对此端口进行检测
     inter `<delay>`:连续两次检测之间的时间间隔,默认单位为毫秒,默认为2000ms
     rise `<count>`:连续多少次检测结果为“成功”才标记服务器为可用;默认为2
     fall `<count>`:连续多少次检测结果为“失败”才标记服务器为不可用;默认为3

disabled:标记为不可用
redir <prefix>:将发往此server的所有GET和HEAD类的请求重定向至指定的URL
cookie <value>:为当前server指定cookie值,实现基于cookie的会话黏性。

基于cookie的session sticky的实现

frontend http
   bind 172.18.43.62:80              
   default_backend websrvs  
 
backend websrvs                  
   balance roundrobin
   cookie SRV insert nocache
   #自定义会话添加cookie信息SRV,insert:插入 nocache:不缓存增加保密性    
   server web1 192.168.43.61:80 check   weight 2 cookie srv1 #自定义该server添加cookie信息为srv1
   server web2 192.168.43.63:80 check   maxconn 5000 cookie srv2 #自定义该server添加cookie信息为srv2
   server sorroyserver 192.168.43.62:80 backup

4.3) 实现图形化配置页面

stats enable 添加这条参数后会在web页面启用统计页;在frontend段添加stats enable ,重启haproxy。浏览器访问172.18.43.62/haproxy?stats即可。
2021-06-15T10:40:28.png
相关参数:
① stats refresh <delay> 设定自动刷新时间间隔
若服务器出现故障,默认手动刷新才能才能看到状态的变化,可设置自动刷新

例:添加stats refresh 2s

② stats uri <prefix> 自定义stats page uri
默认为/haproxy?stats 可自定义uri

例:stats uri  /hastas

重启后访问http://172.18.43.62/hastas即可
③ stats hide-version
如上图所示界面会显示haproxy版本信息,若想隐藏版本加上此参数即可
④ stats auth <user>:<passwd> 认证时的账号和密码,可使用多次。如多个用户定义多行即可,例:

stats auth ha1:centos1
stats auth ha2:centos2

⑤ stats realm <realm> 认证时浏览器弹出对话的提示信息

例:stats realm "haproxy info"

⑥ stats admin { if | unless } <cond> 启用stats page中的管理功能

例:stats  admin  if TRUE

将stats分离出单独语句块,采用内网的6666端口提高访问安全性示例

listen admin
    bind 192.168.43.62:6666
    stats enable
    stats uri /status
    stats realm "haproxy info"
    stats auth ha1:centos
    stats hide-version
    stats refresh 5s
    stats admin if TRUE

访问http://192.168.43.62:6666/status
2021-06-15T10:41:20.png

4.4) default中定义的内容

可以在frontend、backend、listen中分别进行定义,如没有指定将默认default中设置,如:

maxconn `<conns>`:为指定的frontend定义其最大并发连接数;默认为3000
mode { tcp|http|health } 定义haproxy的工作模式(默认http)
    tcp:基于layer4实现代理;可代理mysql, pgsql, ssh,ssl等协议,https时使用此模式,默认模式
    http:仅当代理协议为http时使用,centos实际默认模式
    health:工作为健康状态检查的响应模式,当连接请求到达时回应“OK”后即断开连接,较少使用

TCP模式的健康状态检测示例

listen ssh
    bind :22022
    balance leastconn
    mode tcp
    server sshsrv1 172.16.100.6:22 check
    server sshsrv2 172.16.100.7:22 check

4.5) forwardfor配置

语法 :option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
在由haproxy发往后端主机的请求报文中添加“X-ForwardedFor”首部,其值为前端客户端的地址;用于向后端主发送真实的客户端IP

[ except `<network>` ]:请求报请来自此处指定的网络时不予添加此首部,如haproxy自身所在网络
[ header `<name>` ]:使用自定义的首部名称,而非“XForwarded-For”
[ if-none ] 如果没有首部才添加首部,如果有使用默认值

默认defaults中有一条配置option forwardfor except 127.0.0.0/8
若想后端日志记录真实请求客户端IP,需更改后端两台服务器配置文件

[root@localhost ~]# vim /etc/httpd/conf/httpd.conf
LogFormat "%{x-forwarded-for}i %l %u %t \"%r\" %>`s %b \"%{Referer}i\" \"%{User-Agent}i\"" haformat
 
#添加日志格式
CustomLog logs/access_log haformat #修改记录日志

重载客户端httpd服务,浏览器刷新请求。在客户端观看日志即可看到真实请求服务器

[root@localhost ~]# tail -f /var/log/httpd/access_log

4.6) 自定义错误页面

① errorfile `<code>` `<file>`   自定义错误页
        `<code>`: HTTP status code.  支持200, 400, 403, 408, 500, 502, 503, 504.
        `<file>`:错误页文件路径
    例:errorfile 400 /etc/haproxy/errorfiles/400badreq.http
② rrorloc `<code>` `<url>`    相当于errorloc302 `<code>` `<url>`,利用302重定向至指URL
    例:errorloc 503 http://www.magedu.com/error_pages/503.html

### 4.7) 修改报文首部
① reqadd `<string>` [{if | unless} `<cond>`]    在请求报文尾部添加指定首部(用于web端区分从哪个调度器发来请求)
例:在frontend中添加  reqadd  X-via:\  haproxy1
后端修改httpd.conf中logformat添加%{X-via}i
再次访问日志就会看到是通过哪个调度器调度到本机
② rspadd `<string>` [{if | unless} `<cond>`]  在响应报文尾部添加指定首部 (用于haproxy之前的调度器查看用于排错)
示例: rspadd X-Via:\ HAPorxy
③ reqdel `<search>` [{if | unless} `<cond>`]
reqidel `<search>` [{if | unless} `<cond>`] (ignore case) 不分大小写
从请求报文中删除匹配正则表达式的首部
④ rspdel `<search>` [{if | unless} `<cond>`]
rspidel `<search>` [{if | unless} `<cond>`] (ignore case) 不分大小写
从响应报文中删除匹配正则表达式的首部
示例: rspidel Server.*  用于隐藏web服务器版本信息
rspadd Server:\ Apache 15.1 结合上例可伪造server信息

### 4.8) 定义连接超时
① timeout client `<timeout>`    客户端最长空闲连接超时时长 默认单位是毫秒
② timeout server `<timeout>`   后端服务器最长空闲连接超时时长
③ timeout http-keep-alive `<timeout>`    持久连接的持久时长
④ timeout http-request `<timeout>`     一次完整的HTTP请求的最大等待时长
⑤ timeout connect `<timeout>`     成功连接后端服务器的最大等待时长
⑥ timeout client-fin `<timeout>`   客户端半连接的空闲时长
⑦ timeout server-fin `<timeout>`     后端服务器半连接的空闲时长

五. ACL灵活转发详解
acl:访问控制列表(ACL)的使用提供了一个灵活的解决方案来执行内容交换,并且通常基于从请求中提取的内容、响应或任何环境状态进行决策,是haproxy的重要特色。

5.1) 语法

acl <aclname> <criterion> [flags] [operator] [<value>]...
<aclname>: ACL名称,可使用字母 数字 : . - _区分字符大小写
<criterion>: 比较的标准和条件

    dst 目标IP
    dst_port 目标PORT
    src 源IP
    src_port 源PORT
    示例: acl invalid_src src 172.16.100.200

<flags>

    -i 不区分大小写
    -m 使用指定的pattern匹配方法
    -n 不做DNS解析
    -u 强制每个ACL必须唯一ID,否则多个同名ACL或关系
    --   强制flag结束. 当字符串和某个flag相似时使用

[operator]

匹配整数值: eq、 ge、 gt、 le、 lt
匹配字符串:
    - exact match (-m str) :字符串必须完全匹配模式
    - substring match (-m sub) :在提取的字符串中查找如果包含, ACL将匹配
    - prefix match (-m beg) :在提取的字符串首部中查找模式,如果其中任何一个被发现, ACL将匹配
    - suffix match (-m end) :将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则ACL进行匹配
    - subdir match (-m dir) :查看提取出来的用斜线分隔(“/”)的字符串, 如果其中任何一个匹配,则ACL进行匹配
    - domain match (-m dom) :查找提取的用点(“.”)分隔字符串,如果其中任何一个匹配,则ACL进行匹配

<value>的类型:

- boolean
- integer or integer range
- IP address / network
- string (exact, substring, suffix, prefix, subdir,domain)
- regular expression
- hex block

5.2) 匹配条件<criterion>进阶

(1)base : string
返回第一个主机头和请求的路径部分的连接,该请求从第一个斜杠开始,并在问号之前结束,对虚拟主机有用;
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

base : exact string match      #字符串匹配
base_beg : prefix match       #前缀匹配
base_dir : subdir match            #目录匹配
base_dom : domain match       #域名匹配
base_end : suffix match       #后缀匹配
base_len : length match      #长度
base_reg : regex match        #正则
base_sub : substring match       #字符串

(2)path : string
提取请求的URL路径,该路径从第一个斜杠开始,并在问号之前结束(无主机部分);
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

path : exact string match
path_beg : prefix match
path_dir : subdir match
path_dom : domain match
path_end : suffix match
path_len : length match
path_reg : regex match
path_sub : substring match

例:acl adminpath path_beg -i /admin
acl imagefile path_end .jpg .png .bmp

(3)url : string
提取请求中的URL。 一个典型的应用是具有预取能力的缓存,以及需要从数据库聚合多个信息并将它们保存在缓存中的网页门户入口

url : exact string match
url_beg : prefix match
url_dir : subdir match
url_dom : domain match
url_end : suffix match
url_len : length match
url_reg : regex match
url_sub : substring match

(4)req.hdr([<name>[,<occ>]]) : string
提取在一个HTTP请求报文的首部

hdr([`<name>`[,`<occ>`]]) : exact string match
hdr_beg([`<name>`[,`<occ>`]]) : prefix match
hdr_dir([`<name>`[,`<occ>`]]) : subdir match
hdr_dom([`<name>`[,`<occ>`]]) : domain match
hdr_end([`<name>`[,`<occ>`]]) : suffix match
hdr_len([`<name>`[,`<occ>`]]) : length match
hdr_reg([`<name>`[,`<occ>`]]) : regex match
hdr_sub([`<name>`[,`<occ>`]]) : substring match

(5)status : integer
返回在响应报文中的状态码

5.3) acl作为条件时的逻辑关系

- 与:隐式(默认)使用
- 或:使用“or” 或 “||”表示
- 否定:使用“!“ 表示

同一个组名可定义多次
示例:

if invalid_src invalid_port       #与关系
if invalid_src || invalid_port    #或
if ! invalid_src                        #非

5.4) 预定义ACL(部分)

2021-06-15T10:42:58.png

5.5) 配置block拒绝访问

语法:block { if | unless } <condition> 阻止7层请求if/unless一个条件匹配
例1

frontend http
   acl deny_src src 172.18.0.108
   #自定义源地址为172.18.0.108的acl组deny_src
   acl deny_port dst_port 80:100
   #自定义目标端口为80到100的acl组deny_port
   block if deny_src || deny_port
   #如果属于deny_src组或deny_port组则拒绝访问
   block unless deny_src #如果不属于acl组deny_src则拒绝访问

例2(阻止curl访问)

acl bad_curl hdr_sub(User-Agent) -i curl
block if bad_curl

5.6) 指定backend组

语法:use_backend <backend> [{if | unless} <condition>]
当if/unless一个基于ACL的条件匹配时切换指定backend(可实现动静分离)
例1

acl imagefile path_end .jpg .png .bmp
acl appfile path_end .php
use_backend websrvs1 if imagefile
use_backend websrvs2 if appfile

例2
2021-06-15T10:44:07.png
例3

acl webhost hdr(host) web.kevin.com
acl apphost hdr(host) app.kevin.com
use_backend websrvs if webhost
use_backend appsrvs if apphost

5.7) http 7层访问控制

语法:http-request { allow | deny |add-header <name> <fmt>|set-header <name> <fmt> } [ { if | unless }<condition> ]
对7层请求的访问控制

5.8) tcp4层连接控制

语法:tcp-request connection {accept|reject} [{if | unless} <condition>]
根据第4层条件对传入连接执行操作
例子

listen ssh
bind :22022
balance leastconn
acl invalid_src src 172.16.200.2
tcp-request connection reject if invalid_src
mode tcp
server sshsrv1 172.16.100.6:22 check
server sshsrv2 172.16.100.7:22 check backup

5.9) 配置HAProxy支持https协议

① 支持ssl会话;
bind *:443 ssl crt /PATH/TO/SOME_PEM_FILE
crt 后证
为实验方便自签证书方法:
在etc/pki/tls/certs/目录下执行make命令可获得同时包含证书和所有私钥的证书

[root@localhost ~]# cd /etc/pki/tls/certs/
[root@localhost ~]# make /etc/haproxy/haproxy.pem

证书和私钥分离的情况可通过重定向获得:cat demo.crt demo.key >` demo.pem
② 把80端口的请求重向定443
bind *:80
redirect scheme https if !{ ssl_fc } #注意花括号里面有空格
③ 向后端传递用户请求的协议和端口(frontend或backend)
http_request set-header X-Forwarded-Port %[dst_port]
http_request add-header X-Forwared-Proto https if { ssl_fc }
例子
2021-06-15T10:45:43.png
测试
2021-06-15T10:45:55.png
向后端传递用户请求
2021-06-15T10:46:08.png
修改后端httpd.conf中日志格式即可
2021-06-15T10:46:19.png

六. 基于ACL的动静分离示例

frontend web *:80
           acl url_static path_beg -i /static /images /javascript /stylesheets
           acl url_static path_end -i .jpg .gif .png .css .js .html .txt .htm
           use_backend staticsrvs if url_static
           default_backend appsrvs
backend staticsrvs
           balance roundrobin
           server stcsrv1 172.16.100.6:80 check
backend appsrvs
           balance roundrobin
           server app1 172.16.100.7:80 check
           server app1 172.16.100.7:8080 check
listen stats
          bind :9091
          stats enable
          stats auth admin:admin
          stats admin if TRUE

分享一个小案例

如下的一个haproxy代理需求:

1) 域名跳转
客户端访问http://beijing.test.com 时,要把请求分发到172.16.51.171:8080、172.16.51.174:8080、172.16.51.178:8080,这三台服务器上。
客户端访问http://anhui.test.com时,要把请求分发到web01.test.com这台服务器上。

2) IP地址跳转
客户端访问http://172.16.51.171时,要把请求分发到172.16.51.174:80、172.16.51.178:80这两台服务器上。同时还要求客户端每一次访问,都跳转到不同的服务器上。

2.3) 端口跳转
客户端访问http://beijing.test.com:8090http://anhui.test.com:8090这两个地址时,要把请求分发到172.16.51.174:8090、172.16.51.178:8090这两台服务器上。

2.4) 默认跳转
如果客户端访问的不是beijing.test.com与172.16.51.171,这两个地址的话,要把请求全部分发到172.16.51.178:8080上。

2.5) 多ACL匹配
如果客户端的IP是172.16.51.140,同时访问的是http://172.16.51.171时,要把请求分发到www.test2.com上。

proxy.cfg配置内容如下:

[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
global
   log 127.0.0.1 local0
   log 127.0.0.1 local1 notice
   maxconn 4096
   uid 1005
   gid 1005
   daemon
  
defaults
   log global
   mode http
   option httplog
   option dontlognull
   retries 3
   option redispatch
   maxconn 2000
   contimeout 5000
   clitimeout 50000
   srvtimeout 50000
  
listen admin_stats
   bind 172.16.51.171:1080
   mode http
   option httplog
   maxconn 10
   stats refresh 30s
   stats uri /stats
   stats auth admin:admi
   stats hide-version
  
frontend weblb
   bind *:80
   acl is_beijing hdr_beg(host) beijing.test.com
   acl is_anhui hdr_beg(host) anhui.test.com
   acl is_171 hdr_beg(host) 172.16.51.171
   acl is_ip src 172.16.51.140
   use_backend acl if is_171 is_ip
   use_backend beijingserver if is_beijing
   use_backend anhui if is_anhui
   use_backend 171server if is_171
   default_backend backend_default
  
backend beijingserver
   balance source
   server web1 172.16.51.171:8080 maxconn 1024 weight 3 check inter 2000 rise 2 fall 3
   server web2 172.16.51.174:8080 maxconn 1024 weight 3 check inter 2000 rise 2 fall 3
   server web3 172.16.51.178:8080 maxconn 1024 weight 3 check inter 2000 rise 2 fall 3
  
backend 171server
   balance roundrobin
   server beijing1 172.16.51.174:80 check
   server beijing2 172.16.51.178:80 check
  
backend anhui
   server web1 web01.test.com:80 weight 3 check inter 2000 rise 2 fall 3
 
backend acl
   balance source
   server web1 www.test2.com:80 maxconn 1024 weight 3 check inter 2000 rise 2 fall 3
  
backend backend_default
   server web1 172.16.51.178:8080 weight 3 check inter 2000 rise 2 fall 3
  
listen 8090
   bind 0.0.0.0:8090
   mode http
   balance roundrobin
   server web1 172.16.51.174:8090 maxconn 1024 weight 5 check inter 2000 rise 2 fall 3
   server web2 172.16.51.178:8090 maxconn 1024 weight 3 check inter 2000 rise 2 fall 3

补充下ACL规则的一些说明
ACL(Access Control List)访问控制列表,HAProxy中的ACL的匹配条件和控制条件有许多种,功能很强大,可以通过源地址、源端口、目标地址、目标端口、请求的资源类型、请求的主机等。

haproxy的ACL用于实现基于请求报文的首部、响应报文的内容或其它的环境状态信息来做出转发决策,这大大增强了其配置弹性。其配置法则通常分为两 步,首先去定义ACL,即定义一个测试条件,而后在条件得到满足时执行某特定的动作,如阻止请求或转发至某特定的后端。
定义ACL的语法格式:acl <aclname> <criterion> [flags] [operator] [<value>]

aclname:自定义acl的名称,必填项,只能是大小写、数字、'-'、'_'、'.'、':' , 也就是说这个是定义ACL名称,区分字符大小写,且其只能包含大小写字母、数字、-(连接线)、_(下划线)、.(点号)和:(冒号);haproxy中,acl可以重名,这可以把多个测试条件定义为一个共同的acl;
criterion:表示检查那些数据或内容,例如USERAGENT这个首部的值. 测试标准,即对什么信息发起测试;测试方式可以由[flags]指定的标志进行调整;而有些测试标准也可以需要为其在<value>之前指定一个操作符[operator];
    四个最为常用的criterion为:
     src:ip 源IP
     src_port:integer 源端口
     dst:ip 目标IP
     dst_port:integer 目标端口
flags:定义控制条件,例如是否区分字符大小写等,flags的可选参数如下:
     -i 忽略字符大小写
     -m 启用特定的匹配方式,一般不用
     -n 禁止DNS反向解析
     -u 不允许aclname重复,默认是可以重名的,当两个acl的名称相同时,运算为或机制。

        --    标志符的强制结束标记,在模式中的字符串像标记符时使用;

operator:判断匹配条件,与<criterion>相比较的条件
    若匹配整数值:eq,ge,gt,le,lt
    若匹配字符串:
value: 访问控制的具体内容或值。这是acl测试条件支持的值。value的类型如下:
    boolean:布尔值
    integer or integer range:整数或整数范围. 如1024:65535表示从1024至65535;仅支持使用正整数(如果出现类似小数的标识,其为通常为版本测试),且支持使用的操作符有5个,分别为eq、ge、gt、le和lt;
    IP address/network:网络地址
    string(exact, substring, suffix, prefix, subdir, domain):字符串. 支持使用“-i”以忽略字符大小写,支持使用“\”进行转义;如果在模式首部出现了-i,可以在其之前使用“--”标志位;
    regular expression:正则表达式
    hex block

同一个acl中可以指定多个测试条件,这些测试条件需要由逻辑操作符指定其关系。条件间的组合测试关系有三种:“与”(默认即为与操作)、“或”(使用“||”操作符)以及“非”(使用“!”操作符);
常用的测试标准(criteria):
1) be_sess_rate(backend) <integer>
用于测试指定的backend上会话创建的速率(即每秒创建的会话数)是否满足指定的条件;常用于在指定backend上的会话速率过高时将用户请求转发至另外的backend,或用于阻止攻击行为。例如:

 backend dynamic
      mode http
      acl being_scanned be_sess_rate gt 50
      redirect location /error_pages/denied.html if being_scanned

2) fe_sess_rate(frontend) <integer>
用于测试指定的frontend(或当前frontend)上的会话创建速率是否满足指定的条件;常用于为frontend指定一个合理的会话创建速率的 上限以防止服务被滥用。例如下面的例子限定入站邮件速率不能大于50封/秒,所有在此指定范围之外的请求都将被延时50毫秒。

  frontend mail
      bind :25
      mode tcp
      maxconn 500
      acl too_fast fe_sess_rate ge 50
      tcp-request inspect-delay 50ms
      tcp-request content accept if ! too_fast
      tcp-request content accept if WAIT_END

3) hdr(header) <string>
用于测试请求报文中的所有首部或指定首部是否满足指定的条件;指定首部时,其名称不区分大小写,且在括号“()”中不能有任何多余的空白字符。测试服务器 端的响应报文时可以使用shdr()。例如下面的例子用于测试首部Connection的值是否为close。

      hdr(Connection) -i close

4) method <string>
测试HTTP请求报文中使用的方法。
5) path_beg <string>
用于测试请求的URL是否以<string>指定的模式开头。下面的例子用于测试URL是否以/static、/iilannis、/javascript或/stylesheets头。

    acl url_static path_beg -i /static /iilannis /javascript /stylesheets
                                 #url 目录

6) path_end <string>
用于测试请求的URL是否以<string>指定的模式结尾。例如,下面的例子用户测试URL是否以jpg、gif、png、css或js结尾。

   acl url_static path_end -i .jpg .gif .png .css .js
                                #url 结尾文件

7) hdr_beg <string>
用于测试请求报文的指定首部的开头部分是否符合<string>指定的模式。例如,下面的例子用记测试请求是否为提供静态内容的主机img、video、download或ftp。

   acl host_static hdr_beg(host) -i img. video. download. ftp.

8) hdr_end <string>
用于测试请求报文的指定首部的结尾部分是否符合<string>指定的模式。例如,下面的例子用记测试请求是否为
9) hdr_reg <string>
正则匹配

   acl bbs hdr_reg(host) -i ^(bbs.test.com|shequ.test.com|forum)
        use_backend bbs_pool if bbs or bbs_path #注意 "or"

Haproxy 会话保持 (三种方式)

Haproxy保持客户端session会话同步的三种方式梳理如下:

============第一种方式: 源地址hash(用户IP识别)==========
haroxy 将用户IP经过hash计算后 指定到固定的真实服务器上(类似于nginx 的IP hash 指令)。
缺陷: 当后端一台服务器挂了以后会造成部分session丢失。

配置示例:.

backend SOURCE_srv
  mode   http
  balance  source
  server app-node1  172.16.60.179:80  check port 80 inter 3000  rise 3  fall 3
  server app-node2  172.16.60.191:80  check port 80 inter 3000  rise 3  fall 3
  server app-node3  172.16.60.135:80  check port 80 inter 3000  rise 3  fall 3

===========第二种方式: cookie 识别 ===========
haproxy 将WEB服务端返回给客户端的cookie中插入haproxy中特定的字符串(或添加前缀)在后端的服务器cookie id。

配置指令例举: cookie SESSION_COOKIE insert indirect nocache
用firebug可以观察到用户的请求头的cookie里 有类似" Cookie jsessionid=0bc588656ca05ecf7588c65f9be214f5;SESSION_COOKIE=app1"
其中SESSION_COOKIE=app1就是haproxy添加的内容。

配置示例:

backend COOKIE_srv
   mode    http
   cookie SERVERID insert indirectnocache
   server app-node1 172.16.60.179:80 check  port 80 cookie  a  inter 3000  rise 3  fall 3
   server app-node2 172.16.60.191:80 check  port 80 cookie  b  inter 3000  rise 3  fall 3
   server app-node3 172.16.60.135:80 check  port 80 cookie  c  inter 3000  rise 3  fall 3

cookie参数说明:
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]

特别注意: rewrite | insert | prefix 这三个参数相互排斥,只能选一!
rewrite: 表示cookie由服务器生成并且haproxy会在其值中注入该服务器的标识符;此关键字不能在HTTP隧道模式下工作。

insert: 表示如果客户端没有cookie信息且有权限访问服务器时,持久性cookie必须通过haproxy穿插在服务器的响应报文中。当服务器收到相同名称的cookie并且没有“preserve(保存)”选项时,将会移除之前已存的cookie信息。因此,insert可视作"rewrite"的升级版。cookie信息仅仅作为会话cookie且不会存到客户端的磁盘上。默认除非加了“indirect(间接)”选项,否则服务器端会看到客户端端发送的cookie信息。由于缓存的影响,最好加上“nocache”或“postonly”选项。

prefix: 表示不依赖专用的cookie做持久性,而是依赖现成的。用在某些特殊的场景,如客户端不支持一个以上的cookie和应用程序需求它。每当服务器建立一个名为<name>的cookie时,它将以服务器的标识符和分隔符为前缀。来自于客户端的请求报文中的前缀将会被删除以便服务器端能识别出它所发出的cookie,由于请求和响应报文都被修改过,所以此模式不能工作在隧道模式中。且不能和“indirect”共用,否则服务器端更新的cookie将不会被发到客户端.

indirect: 指定此选项时,将不会向客户端发送服务器已经处理过请求的且可用的cookie信息。如果服务器设置这样一个cookie本身,它将被删除,除非“保存”选项也设置。在“插入”模式下,将从发送给服务器的请求中删除cookie,使持久机制从应用程序的角度完全透明。

nocache: 当客户端和haproxy间存在缓存时,使用此选项和insert搭配最好,以便确保如果一个cookie需要被插入时,可被缓存的响应会被标记成不可缓存。这很重要,举个例子:如果所有的持久cookie被添加到一个可缓存的主页上,之后所有的客户将从外部高速缓存读取页面并将共享相同的持久性cookie,会造成服务器阻塞。

在LB服务器上配置好HAProxy后,LB服务器将接受用户的所有请求。如果一个用户请求不包含任何cookie,那这个请求将被HAProxy转发到一台可用的WEB服务器。可能是webA,webB,webC或webD。然后HAProxy将把处理这个请求的WEB服务器的cookie值插入到请求响应中。如SERVERID=A。当这个客户端再次访问并在HTTP请求头中带有SERVERID=A,HAProxy将会把它的请求直接转发给webA处理。在请求到达webA之前,cookie将被移除,webA将不会看到这个cookie。如果webA不可用,对应的请求将被转发到其他可用的WEB服务器,相应的cookie值也将被重新设置。
==============第三种方式: 基于session识别 ============
haproxy 将后端服务器产生的session和后端服务器标识存在haproxy中的一张表里。客户端请求时先查询这张表。
配置参数:appsession JSESSIONID len 64 timeout 5h request-learn

配置示例:

backend APPSESSION_srv
    mode    http
    appsession JSESSIONID len 64 timeout 5h request-learn
    server REALsrv_70     172.16.60.70:80 cookie 11 check inter 1500 rise 3 fall 3 weight 1
    server REALsrv_120   172.16.60.120:80 cookie 12 check inter 1500 rise 3 fall 3 weight 1

注意使用事项
1) 如果客户端使用HTTP1.1(keep-alive),只有第一个响应才会被插入cookie,并且HAProxy只会分析每个session的第一个请求。在使用cookie的插入模式下,可能不会造成什么问题,因为HAProxy会立即在第一个响应中插入cookie,同一个session中的所有请求将被转发到同一台服务器上。但是,到达服务器端的请求中的cookie不会被删除,所以这要求后端服务器对来历不明的cookie不作敏感处理。如果不想引起其他的问题,可以使用"option httpclose"参数关掉keep-alive

2) 由于一些原因,一些客户端不能识别多个cookie,并且后端应用已经设置了cookie,如果HAProxy再对响应插入cookie后,这些客户端可能不能识别cookie。这种情况下,可以使用cookie的prefix模式。

3) LB服务器成为整个架构中的瓶颈,如果LB不可用,那么客户端的所有请求都得不到响应。可以使用Keepalived配置HAProxy解决LB单点故障的问题。
4) 在以上配置中后端服务器是看不到客户端的真实IP地址的; 可以使用 forwardfor 参数,它能够在客户端的请求头中增加 X-Forwarded-For 字段; 同时需要配置使用 httpclose 参数,确保每个请求都被重写,而不只是第一个请求被重写。即使用配置下面两个参数来实现.

option httpclose
option forwardfor

后端Realserver的WEB服务器上的Nginx的日志格式需要增加$http_x_forwarded_for 变量

log_format  main  ‘$remote_addr - $remote_user [$time_local] "$request" ‘
         ‘$status $body_bytes_sent "$http_referer" ‘
        ‘"$http_user_agent" "$http_x_forwarded_for"‘;

5) 在有些情况下,一些用户会将他们的浏览器的cookie功能关闭。这时候可能没法访问WEB服务器的内容。这种情况下,可以使用HAProxy的 source 负载均衡算法替代 roundroubin算法。source 算法可以确保来自同一个IP的所有请求都到达同一台WEB服务器,但是前提是,可用的服务器的数量保持不变。

6) 不要在一个小型网络和代理服务器后面使用source 算法,因为请求分发会不太公平,但是在大型内部网络或互联网上使用source算法,转发效率很好。对于那些具有动态IP地址的客户端,只要它们能够接收cookie,那么请求转发就不会受到影响,因为HAProxy对cookie的处理具有较高优先级。

===========================================================================================
以上总结的指令和参数都是为了设置或定义ACL匹配条件的,即ACL仅仅只是将匹配条件进行分类归纳而不进行处理。若要将定义好的ACL规则做处理,需下面参数来设置:

1) 当符合指定的条件时使用特定的backend,<backend>表示设置的backend名,if和unless为判断条件,<condition>为比较的对象,可以是ACL规则,要注意的是在if和unless后面可以接两个ACL,默认表示两个ACL同时满足时才use_backend执行。格式如下

use_backend `<backend>` [{if | unless} `<condition>`] #Switch to a specific backend if/unless an ACL-based condition is matched.

例如

acl invalid_src src 192.18.29.101  #acl匹配条件为源地址为192.18.29.101,acl名为invalid_src
block if invalid_src                        #阻断满足名为invalid_src的acl匹配条件
errorfile 403 /etc/fstab                  #并定义错误页

3) 配置七层的请求访问控制,与block阻塞不同,http-request更灵活,可做黑白名单控制。只能用在mode http中.

http-request { allow | deny } [ { if | unless } `<condition>` ]

4) 配置四层的请求访问控制

tcp-request connection {accept|reject}  [{if | unless} `<condition>`]

例如:

listen ssh
bind :22022
balance leastconn
acl invalid_src src 172.16.200.2         #定义acl匹配规则
tcp-request connection reject if invalid_src  #在四层拒绝满足名为invalid_src的acl匹配规则
mode tcp
server sshsrv1 172.16.100.6:22 check
server sshsrv2 172.16.100.7:22 check       

=======ACL示例======
要注意到的是acl关键字可用在frontend、listen、backend中,不能用在default中。

例1:设置HAProxy状态页,只允许指定IP访问,设置如下

listen stats #定义名称
        bind *:9099 #监听在9099端口
        acl sta src 192.168.29.1 #匹配名为sta且源IP地址为192.168.29.1的ACL规则
        block if ! sta #阻断不匹配sta规则的所有条件,!为非
        stats enable #启用stats页
        stats uri /myhaproxy?admin #自定义stats页面uri
        stats realm "Hello World"
        stats auth admin:admin #设置状态页登录账号和密码

说明只有来源为192.168.29.1的机器才能访问这个haproxy的stats状态, 访问地址假设为http://192.168.29.101:9099/myhaproxy?admin

例2:不适用block,使用http-request来达到例1的效果。
在没有设置访问规则限制时,用IP为192.168.29.104的主机访问stats主页
2021-06-15T10:54:35.png
提示错误401,只需输入账号密码即可访问,方法为

curl --basic -u admin:admin http://192.168.29.101:9099/myhaproxy?admin

说明IP为29.104的主机可以访问,并无限制。
添加限制后

listen stats      #定义名称
        bind *:9099    #监听在9099端口
        acl sta src 192.168.29.1     #匹配名为sta且源IP地址为192.168.29.1的ACL规则
        http-request deny unless sta      #拒绝除匹配sta规则以外的规则访问
        stats enable      #启用stats页
        stats uri /myhaproxy?admin     #自定义stats页面uri
        stats realm "Hello World"
        stats auth admin:admin     #设置状态页登录账号和密码

再用IP为29.104的主机访问效果如下
2021-06-15T10:55:19.png
例3: 七层规则匹配
七层ACL规则匹配中,常用的参数是path,它用来做URL规则匹配,path包括以下参数:
path : 精确匹配
path_beg : 匹配字符串开头的所有内容
path_dir : 子路径匹配
path_dom : 域名匹配
path_end : 匹配字符串结尾的所有内容
path_len : 字符串长度匹配
path_reg : 正则表达式匹配
path_sub : 域名子串匹配

下面来举一个使用path的具体例子,选两台主机,安装Nginx并用Nginx虚拟为4台主机,两台用来处理图片请求,两台用来处理文本请求,利用HAProxy负载均衡的ACL控制机制实现,结构图如下:
2021-06-15T10:55:39.png
1) 在192.168.29.102上添加监听8080端口的虚拟主机(安装的是Nginx),修改Nginx配置,在主配置中添加如下信息

server {
        listen       80 default_server;
        listen       [::]:8080 default_server;
        server_name  _;
        root         /usr/share/nginx/html/test; #路径要修改,尽量不使用默认,尽量模拟为两台不同物理机的效果
 
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
 
        location / {
        }
 
        error_page 404 /404.html;
            location = /40x.html {
        }
 
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

2) 在80端口的主机root目录下创建文件static.txt,内容为static 1;在8080端口主机root目录下创建文件static.txt,内容为static 2。重启Nginx服务,并确保能正常访问,如下图
2021-06-15T10:56:09.png
2021-06-15T10:56:14.png
3) 用同样的方法在192.168.29.103上创建分别监听在80和8080端口的虚拟主机,并在根目录下创建图片文件,确保能正常访问,如下图:
2021-06-15T10:56:27.png
2021-06-15T10:56:37.png
4) 在主机192.168.29.101上配置HAProxy

frontend myweb
        bind *:80
        acl image path_end .png #匹配以.png结尾的path规则
        acl txt path_end .txt #匹配以.txt结尾的path规则
        use_backend imagesv if image #将规则image负载均衡到服务器组imagesv
        use_backend txtsv if txt  #将规则txt负载均衡至服务器组txtsv
        default_backend app #默认组,无匹配时负载均衡至此
 
backend imagesv
        balance roundrobin
        server image1 192.168.29.103:80 check
        server image2 192.168.29.103:8080 check
 
backend txtsv
        balance roundrobin
        server txt1 192.168.29.102:80 check
        server txt2 192.168.29.102:8080 check
 
backend app
        balance leastconn
        cookie server insert  nocache
        server app1 192.168.29.102:80 check cookie svr1
        server app2 192.168.29.103:80 check cookie svr2

配置完成后重启HAProxy,分别访问192.168.29.101/image.png和192.168.29.101/static.txt,并刷新,会发现内容会不停变化,如下图


这说明ACL规则培植成功,HAProxy会根据请求的结尾来判断负载均衡规则。
简单总结: 上面举了3个很简单的HAProxy ACL访问控制的例子,虽然例子中的配置很粗糙,但也可以初步领略到HAProxy有着比较智能的负载均衡功能,在后面的博客中我会更深入的介绍HAProxy的细节配置与调优

================Haproxy ACL规则小例==============
1) 按请求的主机头(名)负载
[root@localhost ~]# cat haproxy.cfg

global
    log 127.0.0.1 local1
    maxconn 65000             #最大连接数
    chroot /usr/local/haproxy #安装目录
    uid 99                    #用户haproxy
    gid 99                    #组haproxy
    daemon                    #守护进程运行
    nbproc 1                  #进程数量
    pidfile /usr/local/haproxy/logs/haproxy.pid #haproxy pid
  
defaults
   log     global
   mode    http               #7层 http;4层tcp 
   option  httplog            #http 日志格式
   option  httpclose          #主动关闭http通道
   option  redispatch         #serverId对应的服务器挂掉后,强制定向到其他健康的服务器
   option  forwardfor
   option  dontlognull
   maxconn 50000              #最大连接数
   contimeout      5000       #连接超时(毫秒)
   clitimeout      50000      #客户端超时(毫秒)
   srvtimeout      50000      #服务器超时(毫秒)
  
   #errorfile 502 /usr/local/haproxy/html/maintain.html
   #errorfile 503 /usr/local/haproxy/html/maintain.html
   #errorfile 504 /usr/local/haproxy/html/maintain.html
   
  
frontend test.com             #定义前端服务器(haproxy)
        bind *:80             #监听地址
        acl web-client path_beg -i /vsphere-client
        acl bbs hdr_reg(host) -i ^(bbs.test.com|shequ.test.com|forum)
        acl monitor hdr_beg(host) -i monitor.test.com    #定义ACL名称,对应的请求的主机头是monitor.test.com 
        acl www hdr_beg(host) -i www.test.com
        use_backend  cache.test.com if static   
        use_backend  monitor.test.com if bbs or monitor
        use_backend  www.test.com if www
        use_backend  vsphere-client if web-client
  
        default_backend www.test.com  #指定默认的后端服务器
  
  
backend monitor.test.com              #定义后端服务器群(web server/apache/nginx/iis..)
        mode http
        option  forwardfor    #后端服务器(apache/nginx/iis/*),从Http Header中获得客户端IP
        balance leastconn     #负载均衡的方式,最小连接
        cookie SERVERID       #插入serverid到cookie中,serverid后面可以定义
        option  httpchk HEAD /check.html #用来做健康检查html文档
        #option httpchk HEAD /index.php HTTP/1.1\r\nHost:monitor.test.com #HTTP && Host
        server server1 10.0.100.70:80 cookie server1 check inter 2000 rise 3 fall 3 weight 3
        #服务器定义:
        #cookie server1表示serverid为server1;
        #check inter 2000 是检测心跳频率(check 默认 );
        #rise 3 表示 3次正确认为服务器可用;
        #fall 3 表示 3次失败认为服务器不可用;
        #weight 表示权重。
  
backend www.test.com
        mode http
        option  forwardfor
        balance roundrobin    #负载均衡的方式,轮询方式
        cookie SERVERID  
        option  httpchk HEAD /check.html 
        server server1 10.0.100.71:80 cookie server1 check inter 2000 rise 3 fall 3 weight 3
  
backend vsphere-client
        mode http
        option  forwardfor header ORIG_CLIENT_IP
        balance roundrobin
        server server1 10.0.100.81:80 redir https://192.168.57.81:443 check inter 2000 rise 3 fall 3 weight 3
  
backend cache.test.com
        option  forwardfor
        #balance uri len 15 #url hash
        balance roundrobin
        server server1 10.0.100.73:80  check inter 2000 rise 3 fall 3 weight 3
        server server2 10.0.100.75:80  check inter 2000 rise 3 fall 3 weight 3
  
listen admin_stat                   #status
    bind 0.0.0.0:8080               #监听端口
    mode http                       #http的7层模式
    stats refresh 30s               #统计页面自动刷新时间
    stats uri /haproxy_stats_url    #统计页面URL
    stats realm Haproxy\ Statistics #统计页面密码框上提示文本
    stats auth admin:admin          #统计页面用户名和密码设置
    stats hide-version              #隐藏统计页面上HAProxy的版本信息
    stats admin if TRUE             #手工启用/禁用,后端服务器

2) 其它ACL规则

###########acl 开始了############
acl bbs       hdr_reg(host) -i ^(bbs.test.com|forum.test.com)  #使用正则匹配
acl bbs_path  path_beg -i /bbs                #url 目录
acl youxi     path_beg -i /youxi              
acl static    path_end -i .html .css .js      #url 结尾文件
acl php       path_end -i .php 
acl jsp       path_end -i .jsp .do 
  
use_backend bbs_pool if bbs or bbs_path       #注意 "or" 
use_backend youxi_pool if youxi
use_backend static_pool if static 
use_backend php_pool if php
use_backend jsp_pool if jsp
default_backend www.test.com                
###########acl 结束了############

use_backend 参数
or 用于匹配多个acl 名称
default_backend 没有满足条件的时候使用默认的后端服务器

========HAProxy 七层层模型下强大的ACL规则配置示例========

global
    maxconn     20000
    #设定HAProxy进程可接受的最大并发数
    ulimit-n    41000
    #linux命令行选项,等同于上参数
    log         127.0.0.1 local0
    #全局的日志中配置,local0 是日志设备 info(err,warnig,minfo,debug)为日志级别,使用rsyslog
    uid         200
    gid         200
    #用户和组 ,可以用uid,gid代替
    chroot      /var/empty
    nbproc      1
    #HAProxy启动时可创建的进程数,配合daemon参数使用,默认只启动一个进程,该值应小于cpu核数。
    daemon
    #进程后台运行,(推荐模式)
  
defaults
    mode        http
    retries     3
    timeout     connect 10s
    timeout     client  20s
    timeout     server  30s
    timeout     check   5s
  
listen  admin_stats
    bind        *:9188
    mode        http
    log         global
    stats       refresh 30s
    stats       uri /haproxy-status
    stats       realm welcome login\ Haproxy
    stats       auth    admin:admin~!@
    stats       hide-version
    stats       admin if TRUE
  
frontend test-proxy
    bind        *:80
    mode        http
    log         global
    option      httplog
    option      forwardfor
    option      httpclose
    #HAProxy在完成一次请教请求连接后,将主动关闭该连接,对性能非常有帮助
    option      dontlognull
    option      nolinger
    option      http_proxy
    maxconn     8000
    timeout client  30s
  
    #layer3: Valid users
    #acl allow_host src 192.168.200.150/32
    acl allow_host src 192.168.10.0/24
    #acl allow_host src 0.0.0.0/24
    http-request deny if !allow_host
  
   #layer7: prevent private network relaying
   #acl forbidden_dst url_ip 192.168.0.0/24
   #acl forbidden_dst url_ip 172.16.0.0/12
    acl forbidden_dst url_ip 192.168.20.0/24
    http-request deny if forbidden_dst
    acl host_a hdr_reg(host) -i www.a.cn
    acl host_b hdr_dom(host) -i www.b.cn
    acl url_policy  url_sub -i buy_sid
  
    use_backend test-proxy-srv-a    if  host_a
    use_backend test-proxy-srv-b    if  host_b
    use_backend test-proxy-srv-c    if  url_policy
    default_backend test-proxy-srv-default
  
backend test-proxy-srv-default
    mode        http
    retries     2
    option      redispatch
    option      abortonclose
    option      nolinger
    option      http_proxy
   #option      httpchk GET /index.php
    balance     roundrobin
    cookie      SERVERID
    server web0 192.168.10.101:8999 cookie  server0 weight 4    check inter 2000 rise 2 fall 3
  
backend test-proxy-srv-a
    mode            http
    timeout connect 5s
    timeout server  5s
    retries         2
    option      redispatch
    #应用于cookie保持的环境
    option      abortonclose
    #自动结束长时间连接
    option          nolinger
    option          http_proxy
   #option      httpchk GET /index.php
    balance     roundrobin
    cookie      SERVERID
    server web1 192.168.10.101:8080 cookie  server1 weight 6 check inter 2000 rise 2 fall 3
    server web2 192.168.10.101:8888 cookie  server2 weight 6 check inter 2000 rise 2 fall 3
  
backend test-proxy-srv-b
    mode        http
    retries     2
    option      redispatch
    option      abortonclose
    option      nolinger
    option      http_proxy
   #option      httpchk GET /index.php
    balance     roundrobin
    cookie      SERVERID
    server web3 192.168.10.182:8080 cookie  server3 weight 8 check inter 2000 rise 2 fall 3
  
backend test-proxy-srv-c
    mode        http
    retries     2
    option      redispatch
    option      abortonclose
    option      nolinger
    option      http_proxy
   #option      httpchk GET /index.php
    balance     roundrobin
    cookie      SERVERID
    server web4 192.168.10.182:8888 cookie  server4 weight 6 check inter 2000 rise 2 fall 3
  
    #layer7: Only GET method is valid
    acl valid_method        method GET
    acl valid_method    method POST
    http-request deny if !valid_method
  
    #layer7: protect bad reply
    http-response deny if { res.hdr(content-type) audio/mp3 }

以上haproxy.cfg配置文件的acl控制主要是在frontend里设置:
acl host_a hdr_reg(host) -i ^(www.a.cn|a.cn)
acl host_b hdr_dom(host) -i www.b.cn
acl url_policy url_sub -i buy_sid

use_backend test-proxy-srv-a if host_a
use_backend test-proxy-srv-b if host_b
use_backend test-proxy-srv-c if url_policy
default_backend test-proxy-srv-default

第一个acl控制,当主机为www.a.cn或者a.cn时,规则返回TRUE,对应第一个use_backend 请求访问后端服务器池test-proxy-srv-a
第二个acl控制,当主机为www.b.cn时,规则返回TRUE,对应第二个use_backend请求访问后端服务器池test-proxy-srv-b
第三个acl控制,当请求url路径出现buy_sid 字符串时,规则返回TRUE,对应第三个use_backend请求访问后端服务器池test-proxy-srv-c
当所有规则均不为TRUE时,请求访问默认后端服务器池test-proxy-srv-default

当然acl还有更多的用法,如下:
acl url_static path_end .gif .png .jpg .css .js
acl host_www hdr_beg(host) -i www
acl host_static hdr_beg(host) -i img. video. download. ftp.

use_backend static if host_static || host_www url_static
use_backend www if host_www
default_backend server_cache

第一个acl,通过path_end参数定义请求的url以.gif,.png,.jpg,.css,.js结尾时返回TRUE
第二个acl,通过hdr_beg(host)参数定义如果客户端以www开头的域名发送请求时返回TRUE
第三个acl,也是通过hdr_beg(host)参数定义以img.,video.,download.,ftp.开头域名发送请求返回TRUE
第四行backend,定义了当请求需同时满足host_static与url_static时,或者满足host_www与url_static时请求将发往名为static 的backend
第五行backend,定义了当请求满足host_www规则时,请求发往名为www的backend
第六行backend,定义将用户请求默认调度到名为server-cache的backend

以上就是Haproxy的ACL常用控制用法,基本上可以满足日常负载需求。

========================设置haproxy负载均衡的最大并发连接数=======================

从三个方面来进行设置
a) 查看内核
[root@haproxy-test ~]# sysctl -a | grep file
fs.file-nr = 1024       0       386459
fs.file-max = 386459
 
b) 查看应用层面的需求
[root@haproxy-test ~]# vim /usr/local/haproxy/conf/haproxy.cfg
global                                                   #全局参数设置
        maxconn         65535                    #设置最大连接数
 
c) 更改系统层面
[root@haproxy-test ~]# vim /etc/security/limits.conf                #最后一行增加
*                -       nofile          65535

===========================下面分析一个haproxy的负载均衡配置=============================

global
   log 127.0.0.1 local3
   chroot /etc/haproxy
   pidfile /var/run/haproxy.pid
   user haproxy
   group haproxy
   daemon
   nbproc 1         #单进程
   tune.ssl.default-dh-param 2048
 
defaults
   log global
   mode http
   maxconn 20480    #最大连接数
   balance roundrobin
   retries 3
   option httplog
   option httpclose
   option forwardfor
   option dontlognull
   option redispatch
   timeout connect 5000
   timeout client 50000
   timeout server 50000
   timeout check 1000 #心跳检测超时
 
listen web_status
   bind 0.0.0.0:9088
   mode http
   option httplog
   option dontlognull
   option logasap
   option forwardfor
   option httpclose
   stats admin if TRUE         #手工启用/禁用,后端服务器(haproxy-1.4.9以后版本)
   stats refresh 15s
   stats uri /admin?stats
   stats realm "请输入用户名密码"
   stats auth admin:xxxxxx
   stats hide-version
 
frontend public
   bind 0.0.0.0:80
   bind 0.0.0.0:443 ssl crt /home/hemin/installs/haproxy/kevin.com.pem
   maxconn 20400
   acl secure dst_port 443
   capture request header Host len 50
   capture request header Connection len 20
   capture response header Connection len 20
 
   #定义域名
   acl wihtout_www_domain hdr_beg(host) kevin.com
   acl www_domain hdr_beg(host) www.kevin.com
   acl wiki_domain hdr_beg(host) wiki.kevin.com
   acl jira_domain hdr_beg(host) jira.kevin.com
   acl oms_domain hdr_beg(host) oms.kevin.com
   acl static_domain hdr_beg(host) static.kevin.com
   acl company_domain hdr_beg(host) company.kevin.com
   acl archive_domain hdr_beg(host) -i archive.kevin.com
   acl m_domain hdr_beg(host) -i m.kevin.com
   acl s_domain hdr_beg(host) -i s.kevin.com
   acl oms_domain hdr_beg(host) -i oms.kevin.com
   acl wx_domain hdr_beg(host) -i wx.kevin.com wxdev.kevin.com
 
   #限定OMS访问IP
   acl oms_ip_valid src 102.100.24.35
   acl oms_ip_valid src 101.18.124.19
  #acl pay_callback_ip_valid src 115.238.xx.xx 115.236.xx.xx
 
   #首页根路径(http://www.kevin.com/ )
   acl root path /
   acl aboutUs path_beg -i /aboutUs
   acl index path_beg -i /index
   acl login_page path_beg /login.htm
   acl register_page path_beg /register.htm
 
   #标列列,标详情
   acl product_no_secure path_beg /claims/info/show /claims/product/list /claims/product/detail
 
   #用户中心,需要走https
   acl user_resource path_beg /user
 
   #用户中心,查询用户信息时,不需要走https,并且走https时,有奖推广不正常
   acl userInfo path_beg /user/userInfo
 
   #活动页
   acl activity path_beg -i /prom/activity/recommend
 
   #投标面
   acl bid path_beg -i /claims/bid.htm
 
   #计算收益
   acl bid_getReceivable path_end -i /bid/getReceivable
 
   #新手指引
   acl newbieGuide path_beg -i /newbieGuide/newbieGuide
   acl favicon path_beg -i /favicon.ico
 
   #有奖推广
   acl invite path_beg -i /activity/invite
 
   #忘记密码页面
   acl forgetPwd path_beg -i /forgetPwd.htm
 
   #支付回调白名单控制
   acl pay_return path_beg /pay/return_url
   acl pay_notify path_beg /pay/notify_url
   acl pay_withDraw path_beg /pay/withDraw_notify_url
 
   #http-request deny if pay_return !pay_callback_ip_valid
   #http-request deny if pay_notify !pay_callback_ip_valid
   #http-request deny if pay_withDraw !pay_callback_ip_valid
    
    redirect prefix http://www.kevin.com if root secure www_domain
    redirect prefix http://www.kevin.com if invite secure www_domain
    redirect prefix http://www.kevin.com if index secure www_domain
    redirect prefix http://www.kevin.com if secure www_domain wihtout_www_domain
    redirect prefix https://www.kevin.com if login_page    !secure www_domain
    redirect prefix https://www.kevin.com if register_page !secure www_domain
    redirect prefix https://www.kevin.com if user_resource !secure !userInfo www_domain
    redirect prefix http://www.kevin.com if bid_getReceivable secure
    redirect prefix https://www.kevin.com if bid !secure www_domain
    redirect prefix https://static.kevin.com if favicon  www_domain
    redirect prefix https://www.kevin.com if forgetPwd !secure www_domain
    redirect prefix http://www.kevin.com if aboutUs secure www_domain
    redirect prefix http://www.kevin.com if activity secure www_domain
    redirect prefix http://www.kevin.com if newbieGuide secure www_domain
    redirect prefix http://www.kevin.com if product_no_secure secure www_domain
    redirect prefix https://company.kevin.com if company_domain !secure
    redirect prefix https://static.kevin.com if s_domain
    use_backend web_haproxy_group if www_domain
    use_backend web_haproxy_group if wihtout_www_domain
 
    #非www的acl
    acl index url_beg -i (/|/index.htm)
    acl login_page url_beg /login.htm
    acl register_page url_beg /register.htm
    acl static_resources path_beg -i /resources
 
    use_backend file_server if archive_domain
    use_backend static_server if static_resources static_domain
    use_backend static_server if static_domain
    use_backend company_server if company_domain
    use_backend wap_server if m_domain
    use_backend oms_server if oms_domain
    use_backend wxdev_server if wx_domain
    use_backend wiki_server if wiki_domain
    use_backend jira_server if jira_domain
    #errorloc 503 http://static.kevin.com/resources/error/503.html
    errorloc302 500 http://www.kevin.com/500.htm
    errorloc302 403 http://www.kevin.com
 
backend web_haproxy_group
    balance roundrobin
    server 17_8080 172.16.60.51:8080 check inter 1000 rise 3 fall 3
 
backend file_server
    server 18_80994 172.16.60.51:8094 check inter 1000 rise 3 fall 3
 
backend static_server
    balance roundrobin
    server 18_8084 172.16.60.51:8084 check inter 1000 rise 3 fall 3 maxconn 2000
 
backend company_server
    balance roundrobin
    server 18_8083 172.16.60.51:8083 check inter 1000 rise 3 fall 3
 
backend oms_server
     server 18_8093 172.16.60.51:9093 check inter 1000 rise 3 fall 3
 
backend wap_server
     server webdev 172.16.60.51:8081 check inter 1000 rise 3 fall 3
 
backend wxdev_server
     server wxdev 172.16.60.51:8088 check inter 1000 rise 1 fall 1
 
backend wiki_server
      server wiki 172.16.60.30:8090 check inter 1000 rise 3 fall 3
 
backend jira_server
      server jira 172.16.60.12:80 check inter 1000 rise 3 fall 3

==========================整体总结下Haproxy的参数说明============================

一. Haproxy 配置说明
haproxy的配置段有"global","defaults","listen","frontend"和"backend"等:
global: 配置中的参数为进程级别的参数,且通常与其运行的操作系统有关
defaults: 用于为所有其他配置段提供默认参数,这配置默认配置参数可由下一个"defaults"所重新设定
forntend: 用于定义一系列监听的套接字,这些套接字可以接受客户端请求并与子建立连接
backend: 用于定义一系列"后端"服务器,代理将会将对应客户端的请求转发至这些服务器
listen: 用于定义通过关联"前段"和"后端"一个完整的代理,通常只对TCP流量有用

所有代理的名称只能使用大写字母、小写字母、数字、-(中线)、_(下划线)、.(点号)和:(冒号)。此外,ACL名称会区分大小写.

时间格式一些包含了值得参数表示时间,如超时时长。
这些值一般都以毫秒为单位,但也可以使用其他的时间单位做后缀,如us(微妙,1/10000000秒),ms(毫秒,1/1000秒),s(秒),m(分钟),h(小时),d(天)

二. global 全局配置
chroot: 修改haproxy的工作目录至指定的目录,并在放弃权限之前执行chroot()操作,可以提升haproxy的安全级别,不过需要注意的是确保指定的目录为空目录且任何用户均不能有写权限;
daemon: 让haproxy以守护进程的方式工作于后台,其等同于"-D"选项的功能,当然,也可以在命令行中以"-db"选项将其禁用;
gid: 以指定的GID运行haproxy,建议使用专用于运行haproxy的GID,以避免因权限带来的风险;
group: 同gid,不过这里为指定的组名;
uid: 已指定的UID身份运行haproxy进程;
user: 同uid,但这里使用的为用户名;
log: 定义全局的syslog服务器,最多可以定义两个;
nbproc: 指定启动的haproxy进程个数,只能用于守护进程模式的haproxy;默认为止启动一个进程,鉴于调试困难等多方面的原因,一般只在但进程仅能打开少数文件描述符的场中才使用多进程模式;
pidfile: pid文件的存放位置;
ulimit-n:设定每个进程所能够打开的最大文件描述符,默认情况下其会自动进行计算,因此不建议修改此选项;
node: 定义当前节点的名称,用于HA场景中多haproxy进程共享同一个IP地址时;
description: 当前实例的描述信息;
maxconn: 设定每个haproxy进程所接受的最大并发连接数,其等同于命令行选项"-n","ulimit-n"自动计算的结果正式参照从参数设定的;
maxpipes: haproxy使用pipe完成基于内核的tcp报文重组,此选项用于设定每进程所允许使用的最大pipe个数,每个pipe会打开两个文件描述符,因此,"ulimit -n"自动计算的结果会根据需要调大此值,默认为maxcoon/4;
noepoll: 在linux系统上禁用epoll机制;
nokqueue: 在BSE系统上禁用kqueue机制;
nopoll: 禁用poll机制;
nosepoll: 在linux系统上禁用启发式epoll机制;
nosplice: 禁止在linux套接字上使用tcp重组,这会导致更多的recv/send调用,不过,在linux2.6.25-28系列的内核上,tcp重组功能有bug存在;
spread-checks<0..50,in percent>: 在haprorxy后端有着众多服务器的场景中,在紧缺是时间间隔后统一对中服务器进行健康状况检查可能会带来意外问题,此选项用于将检查的时间间隔长度上增加或减少一定的随机时长,为当前检查检测时间的%;
tune.bufsize: 设定buffer的大小,同样的内存条件下,较小的值可以让haproxy有能力接受更多的并发连接,较大的值了可以让某些应用程序使用较大的cookie信息,默认为16384,其可以在编译时修改,不过强烈建议使用默认值;
tune.chksize: 设定检查缓冲区的大小,单位为字节,更大的值有助于在较大的页面中完成基于字符串或模式的文本查找,但也会占用更多的系统资源,不建议修改;
tune.maxaccept: 设定haproxy进程内核调度运行时一次性可以接受的连接的个数,较大的值可以带来较大的吞吐量,默认为单进程模式下为100,多进程模式下为8,设定为-1可以禁止此限制,一般不建议修改;
tune.maxpollevents: 设定一次系统调用可以处理的事件最大数,默认值取决于OS,其至小于200时可介于带宽,但会略微增大网络延迟,但大于200时会降低延迟,但会稍稍增加网络带宽的占用;
tune.maxrewrite: 设定在首部重写或追加而预留的缓存空间,建议使用1024左右的大小,在需要更大的空间时,haproxy会自动增加其值;
tune.rcvbuf.client: 设定内核套接字中客户端接收缓存区的大小,单位为字节,强烈推荐使用默认值;
tune.rcvbuf.server: 设定内核套接字中服务器接收缓存区的大小,单位为字节,强烈推荐使用默认值;
tune.sndbuf.client: 设定内核套接字中客户端发送缓存区的大小,单位为字节,强烈推荐使用默认值;
tune.sndbuf.server: 设定内核套接字中服务器端发送缓存区的大小,单位为字节,强烈推荐使用默认值;
debug: 调试模式,输出启动信息到标准输出;
quiet: 安装模式,启动时无输出;

三. defaults 默认配置
defaults:用于为所有其他配置段提供默认参数,这配置默认配置参数可由下一个"defaults"所重新设定

1) balance
balance <algorithm> [<arguments>]
balance url_param <param> [check_post [<max_wait>]]
定义负载均衡算法,可用于"defaults"、"listen"和"backend"中。<algorithm>用于在负载均衡场景中挑选一个server,其仅用于持久信息不可用的条件下或需要将一个连接重新派发至另一个服务器时。支持的算法有:

roundrobin:基于权重进行轮询,在服务器的处理时间保持均匀分布时 ,这是最平衡、最公平的算法。此算法是动态的,这表示某权重可以在运行时进行调整,不过,在设计上,每个后端服务器仅能最多支持4128个连接; 即客户端请求每次都转发到不同的节点服务器上;

static-rr:基于权重进行轮询,与roundrobin类似,但是为静态方法,在运行时调整期后端权重不会生效,不过,其在后端服务器连接数上没有限制;

leastconn:新的连接强求笨哦派发至具有最少连接数目的后端服务器,在有这较长会话的场景中推荐使用此算法,如LDAP、SQL等。其并不太适合用于较短会话的应用层协议,如HTTP,此算法是动态的,可以在运行时调整其权重;

source:将请求的源地址进行hash运算,并有后端的服务器的权重总数相处后派发至某匹配的服务器,这可以使得同一个客户端IP的请求始终被派发至某特定的服务器,不过,当服务器权重总数发生变化时,如某服务器宕机或者添加新服务器,许多的请求可能会被派发至与此前请求不同的服务器,常用于负载均衡无cooki功能的基于TCP的协议,默认为动态,不过可以使用hash-type修改此特性;

uri:对URI的左半部分(“问号”标记之前的部分)或整个URI进行hash运算,并由服务器的总权重相除后派发至某匹配的服务器;这可以使得对同一个URI的请求总是派发至某匹配的服务器,除法服务器的权重总数发生了变化,此算法常用于代理缓存或反病毒代理以提高缓存的命中率,需要注意的是,此算法仅应用于HTTP后端服务器场景,其默认为静态算法,不过可以使用hash-type修改此特性;

url_param:通过<argument>为URL指定的参数在每个HTTP GET请求中将会被索引,日过找到了指定的参数且其通过等于号“=”被赋予了一个值,那么此值将被执行hash运算并被服务器的总权重相处后派发至某匹配的服务器,此算法可以通过追踪请求中的用户标识进而确保同一个用户的ID请求被发送同一个特定的服务器,除非服务器的总权重发生了变化;如果某请求中没有出现指定的参数或其没有有效值,则使用轮询算法对其想用请求进行调度,此算法默认为静态,不过可以使用hash-type修改此特性;

har(<name>):对于每个HTTP请求,通过<name>指定的HTTP首部将会被检索,如果对于那个的首部没有出现或其没有有效值,则使用轮询算法对响应请求进行调度,其有一个可选项“use_domain_only”可以指定检索类似host类的首部时仅计算域名部分以降低hash算法的运算量,此算法默认为静态,不过可以使用hash-type修改此特性;

2) bind
bind [<address>]:<port_range>[,.....]
bind [<address>]:<port_range>[,.....] interface <interface>
该指令仅能用于frontend和listen区段,用于定义一个或多个监听的套接字;
<address>: 可选项,其可以为主机名、IPV4地址、IPV6地址或:省略此选项、将其指定为或0.0.0.0时,将监听当前系统的所有IPv4地址;

<port_range>: 可以是一个特定的TCP端口,也可是一个端口范围(如6604-6610),代理服务器将通过制定的端口来接受客户端请求,需要注意的是,每组监听的套接字;

<address:prot>: 在同一个实例上只能使用一次,而且小于1024的端口需要有特定的权限的用户才能使用,这可能需要通过uid参数来定义;

<interface>:指定物理接口的名称,仅能在linux系统上使用,其不能使用接口别名,二进程使用物理端口名称,而且只有管理有权限指定绑定的物理端口;

3) mode
mode{ tcp|http|health }
设定实例的运行模式或协议,当实现内容交换时,前段和后端必须工作与统一中模式(一般说来时tcp模式),否则将无法启动实例;

tcp: 实例运行于纯tcp模式,在客户端和服务器端之间将建立一个全双工的连接,且不会对7层报文做任何类型的检查,此为默认模式,通常用于SSL、SSH、SMTP等应用;

http:实例运行于http模式,客户端请求在转发至后端服务器之前将被深度分析,所有不与RFC模式兼容的请求都会被拒绝;

health:实例运行于health模式,其对入站请求仅响应“OK”信息并关闭连接,且不会记录任何日志信息 ,此模式将用于相应外部组件的监控状态检测请求;目前来讲,此模式已经废弃,因为tcp或http模式中的monitor关键字可完成此类功能;

4) log
log global
log<address>`[[`]]
为每个实例启用事件和流量日志,因此可用于所有区段。每个实例最多可硬定义两个log参数,不过,如果使用了“log global”且“global”端定义了两个log参数时,多余的log参数将会倍忽略;
global:当前实例的日志系统参数同“global”段中的定义时,将使用此格式,每个实例仅能定义一个“log global”语句,且其没有额外的参数;

<address>:定义日志发往的位置,其格式之一可以为<ipv4_address:port>,其中prot为udp协议,默认为514,格式之二为Unix套接字文件路径,当需要留心chroot应用及用户读写权限;

<facility>: 可以为syslog系统的标准facility之一;

<level>: 定义日志级别,即输出信息过滤器,默认为所有信息,指定级别时,所有等于或高于此级别的日志信息将会被发送;

5) maxconn
maxconn <conns>
设定一个前段的最大并发连接数,因此,其不能用于backend区段,对于大型站点来说,可以尽可能提高此值以便让haproxy管理连接队列,从而便面无法应答用户请求。当然,此最大值不能超过“global”段中的定义。此外,需要留心的是,haproxy会为每个连接维持两个缓冲,每个缓存的大小为8KB,在加上其他的数据,每个连接将大约占用17KB的RAM空间,这意味着经过适当优化后 ,有着1GB的可用RAM空间时将维护40000-50000并发连接;

如果为<conns>指定了一个过大值,极端场景中,其最终所占据的空间可能会超过当前主机的可用内存,这可能会带来意想不到的结果,因此,将其设定一个可接受值放为明智绝对,其默认为2000;

6) default_backend
default_backend <backend>
在没有匹配的“use_backend”规则时为实例指定使用的默认后端,因此,其不可应用于backend区段,在“frontend”和“backend”之间进行内容交换时,通常使用“use-backend”定义其匹配规则,而没有被匹配到的请求将有此参数指定的后端接收;

<backend>:指定使用的后端名称;

7) server
server<name>`

`:port
在后端声明一个server,因此,不能用于defaults和frontend区段。

<name>: 为此服务器指定的内部名称,其将会出现在日志及警告信息中;如果设定了“http-send-server-name”,他还将会被添加至发往此服务器的请求首部中;

<adderss>:此服务器的IPv4地址,也支持使用可解析的主机名,只不过在启动时需要解析主机名至响应的IPV4地址;

<:port>:指定将连接请求所发往此服务器时的目标端口,其为可选项,为设定是,将使用客户端请求时的同一相同端口;

[param*]:为此服务器设定的一系列参数:其可以得参数非常多,下面仅说明几个常用的服务器或默认服务器参数:
backup:设定为备用服务器,仅在负载均衡场景中的其他server均不可以启用此server;
check:启动对此server执行监控状态检查,其可以借助于额外的其他参数完成更精细的设定,如:

   inter`<delay>`: 设定监控状态检查的时间间隔,单位为毫秒,默认为2000,也可以使用fastinter和downinter来根据服务器端专题优化此事件延迟;
   rise`<count>`:设定检查状态检查中,某离线的server从离线状态转换至正常状态需要成功检查的次数;
   fall`<count>`:设定检查状态检查中,某离线的server从正常状态转换至离线状态需要成功检查的次数;

cookie<value>:为指定server设定cookie值,此处指定的值将会在请求入站时被检查,第一次为此值挑选的server将会倍后续的请求所选中,其目的在于实现持久连接的功能;
maxconn<maxconn>:指定此服务器接受的最大并发连接数,如果发往此服务器的连接数目高于此处指定的值,其将被放置于请求队列,以等待其他连接被释放;
maxqueue<maxqueue>:通过观察服务器的通信状况来判断其健康状态,默认为禁用,其支持的类型有“layer 4”和“layer 7”,“layer 7”仅能用于http代理场景;
redir<prefix>:启用重定向功能,将发往此服务的GET和HEAD请求均以302状态码响应,需要注意的是,在prefix后面不能使用/,且不能使用相对地址,以避免造成循环,例如

server srv1 192..168.1.202:80 redir http://www.kevin.com check

weight<weight>: 权重,默认为1,最大值为256,0表示不参与负载均衡;

检查方法:

  option httpchk
  option httpchk`<uri>`
  option httpchk`<method>``<uri>`
  option httpchk`<method>``<uri>``<version>`:不能用于frontend端,例如:
backend https_relay
mode tcp
option httpchk OPTIONS * HTTP/1.1rnHost: www
server apache1 192.168.1.1:443 check port 80

8) capture request header
capture request header <name> len <length>
捕获并记录指定的请求首部最近一次出现时的第一个值,仅能用于“frontend”和“listen”区段,捕获的首部值使用花括号{}括起来后添加进日志中,如果需要捕获多个首部值,他们将以指定的次序出现在日志文件中,并以竖线“|”作为分隔符,不存在的首部记录为空字符串,最长需要捕获的首部包括在虚拟主机环境中使用的“host”、上传请求首部的“Content-length”、快速区别现实用户和网络机器人“User-agent”,已经代理环境中距离请求来源的“X-Forword-For”;

<name>: 要捕获的首部的名称,此名称不区分大小写,但建议与他们出现在首部中的格式相同,比如大写首字母,需要注意的是,记录在日志的是首部的值,而非首部名称;

<length>: 指定距离首部值时所记录的精确长度,超出的部分将会倍忽略;

可以捕获的请求首部的个数没有限制,但每个捕获最多能记录64个字符,为了保证同一个frontend中日志格式的统一性,首部捕获仅能在frontend中定义;

9) capture response header
capture response header <name> len <length>
捕获并记录响应首部。其格式和要点同捕获的请求首部响应.

10) stats enable
启用基于程序编译时默认设置的统计报告,不能用于"frontend"区段,只要没有额外的其他设定,他们就会使用如下的配置:

  • stats uri : /haproxy?stats
  • stats realm : "HAProxy Statistics"
  • stats auth : no authentication
  • stats scope : no restriction
    尽管"stats enable"一条就能够启用统计报告,但还是建议设定其他所有的参数,以避免其依赖默认设定而带来非预期后果,下面是一个配置示例:

    backend public_www
     server srv1 192.168.1.201:80
     stats enable
     stats hide-version
     stats scope   .
     stats uri     /admin?stats
     stats realm   Haproxy Statistics
     stats auth    admin1:AdMiN123
     stats auth    admin2:AdMiN321

    11) stats hide-version
    启用统计报告并隐藏HAProxy版本报告,不能用于"frontend"区域,默认情况下,统计页面会显示一些有用信息,包括HAProxy的版本号,然后,向所有人公开HAproxy的准确版本号是非常有危险的,因为他能够版主恶意用户快速定位版本的缺陷和漏洞,尽管"stats hide-version"一条就能够启用统计报告,但还是建议设定其他所有的参数,以避免其依赖默认设定而带来非预期后果.

12) stats realm
stats realm <realm>
启用统计报告并高精认证领域,不能用于"frontend"区域,haproxy在读取realm是会讲是做一个单词,因此,中间的空白字符都必须使用反斜线进行转移。此参数仅在与"stats auth"配置使用时有意义;

<realm>:实现HTTP基本认证时显示在浏览器中的领域名称,用于提示用户输入一个用户名和密码;

尽管"stats realm"一条就能够启用统计报告,但还是建议设定其他所有的参数,以避免其依赖默认设定而带来非预期.

13) stats scope
stats scope {<name>|"."}
启用统计报告并限定报告的区段,不能用于“frontend”区域,当指定此语句时,统计报告将仅显示其列举出区段的报告信息,所有其他区段的信息将被隐藏,如果需要显示多个区段的统计报告,此语句可以定义多次,需要注意的是,区段名称进程仅仅是以字符串比较的方式进行,他不会真检查指定的区段是否真正存在;

<name>:可以是一个“listen”、“frontend”或“backend”区段的名称,而“.”则表示stats scope语句所定义的当前区段;

尽管“stats scope”一条就能够启用统计报告,但还是建议设定其他所有的参数,以避免其依赖默认设定而带来非预期后果.

14) stats auth
stats auth <user>:<password>
启用带认证的统计报告功能并授权一个用户账号,不能用于"frontend"区域;

<user>:授权进行访问的用户名;

<password>:此用户的访问密码,明文格式;

此语句将给予默认设定启用统计功能报告,并仅允许其定义的用户访问,其也可以定义多次以手段多个用户账号,可以结合"stats realm"参数在提示用户认证是给出一个领域说明信息,在使用非法用户访问统计功能时,其将会响应一个"401 Forbidden"页面,其认证方式为HTTP Basic认证,密码传输会以明文方式进行,因此,配置文件中也使用存储明文方式存储以说明其非保密信息故此不能想用与其他关键性账号的密码。

尽管"stats auth"一条就能够启用统计报告,但还是建议设定其他所有的参数,以避免其依赖默认设定而带来非预期后果.

15) stats admin
atsts admin {if|unless}<cond>
在指定的条件满足时启用统计报告页面的管理级别功能,它允许通过web接口启用或禁用服务器,不过,基于安全的角度考虑,统计报告页面应该尽可能为只读的,此外,如果启用了HAproxy的多进程模式,启用此管理级别将会可能导致异常行为;

目前来说,POST请求方法被限制于仅能使用缓冲区减去保留之外的空间,因此,服务器列表不能过长,否则,此请求将无法正常工作,因此,建议一次仅调整少数几个服务器.

16) option httplog
option httplog [clf]
启用记录HTTP请求、会话状态和计时器的功能;

clf:使用CLF格式来代替HAproxy默认的HTTP格式,通常在使用仅支持CLF格式的特定日志分析器时才需要使用此格式;

默认情况下,日志输入格式非常简陋。因为其仅包括源地址、目标地址和实例名称、而"option httplog"参数将会使得日志变得丰富许多,其通常包括但不局限于HTTP请求、连接计时器、会话状态、连接数、捕获的首部及cookie、"frontend"、"backend"及服务器名称。当然也包括源地址和端口号等。

17) option logasap
no option logasap
启用或禁用提前将HTTP请求记入日志,不能用于“frontend”区段。

默认情况下,HTTP请求是在请求结束时进行记录以便能够将其整体输入时长和字节数记入日志,由此,传较大的对象时,其记入日志的市场可能会略有延迟,“option logasap”参数能够在服务器发送complete首部时及时记录日志,只不过,此时将不记录整体传输时长和字节数。此情形下,捕获“Content-Length”响应报文来记录的字节数是以一个较好的选择;

18) option forwardfor
option forwardfor except <network> [ if-none ]
允许在发往服务器的请求首部中插入"X-Forwarded-For"首部;

<network>:可选参数,当指定时,源地址为皮至此网络中的请求都禁用此功能;

<name>:可选参数,可使用一个自定义的首部,如"X-Cluster-Client-IP"来代替"X-Forwarded-For",有些独特的web服务器的确需要用一个独特的首部;

if-none: 仅在此首部不存在时才会将其添加至请求报文中;

HAproxy工作与反向代理模式,其发往服务器的请求中的客户端IP均为HAproxy主机的地址而非真正的客户端地址,这会使得服务器的日志记录不了真正的请求来源,"X-Forwarded-For"首部则可用于解决此问题,HAproxy可以向每个房网服务器的请求上添加此首部,并以客户端IP为其value;

需要注意的是: HAproxy工作与隧道模式,其仅检查每一个连接的第一个请求,仅第一个请求报文中被附加此首部,请确保同时使用"option httpclose"、"option forceclose"和"option http-server-close"几个option.

19) errorfile
errorfile <code> <file>
在用户请求不存在的页面时,返回一个页面给客户端而非有haproxy生成的错误代码,可用于所有段中;

<code>: 指定对HTTP的那些状态码发回指定的页面,这里可用的状态码有200、400、403、408、500、502、503和504;

<file>:指定用于响应的页面文件;

例如:

errorfile 400 /etc/haproxy/errorfiles/400badreq.http
errorfile 403 /etc/haproxy/errorfiles/403forbid.http
errorfile 503 /etc/haproxy/errorfiles/503sorry.http

20) errorloc和errorloc302
errorloc <code> <url>
errorloc302 <code> <url>
请求错误时,返回一个HTTP重定向至某URL的信息,可以用于所有端中;

<code>: 指定对HTTP的那些状态码发回指定的页面,这里可用的状态码有200、400、403、408、500、502、503和504;

<url>:Location首部中指定的页面位置的具体路径,可以是在当前服务器上的页面的相对路径,也可以使用绝对路径,需要注意的是,如果URI之神错误时禅师某特定状态码信息的话,有可能会导致循环定向;

需要留意的是: 这两个关键字都会返回302状态码,浙江使得客户端使用同样的HTTP方法获取指定的URL。对于非GET方法获取指定的URL,对于非GET方法的场景(如POST)来说会产生问题,因为返回客户端的URL是不允许使用GET意外的其他方法的,如果的确有这种问题,可以使用errorloc303来返回303状态码给客户端;

21) errorloc303
errorloc303 <code> <url>
<code>: 指定对HTTP的那些状态码发回指定的页面,这里可用的状态码有400、403、408、500、502、503和504;

<url>:Location首部中指定的页面位置的具体路径,可以是在当前服务器上的页面的相对路径,也可以使用绝对路径;

需要注意的是,如果URI之神错误时禅师某特定状态码信息的话,有可能会导致循环定向.

转自:https://www.cnblogs.com/kevingrace/p/6138150.html

]]> <![CDATA[linux负载均衡总结性说明(四层负载/七层负载)]]> https://www.msl.la/archives/348/ 2021-06-15T18:24:15+08:00 2021-06-15T18:24:15+08:00 墨少离 https://www.msl.la/ 在常规运维工作中,经常会运用到负载均衡服务。负载均衡分为四层负载和七层负载,那么这两者之间有什么不同?
废话不多说,详解如下:

一. 什么是负载均衡

1)负载均衡(Load Balance)建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。负载均衡有两方面的含义:首先,大量的并发访问或数据流量分担到多台节点设备上分别处理,减少用户等待响应的时间;其次,单个重负载的运算分担到多台节点设备上做并行处理,每个节点设备处理结束后,将结果汇总,返回给用户,系统处理能力得到大幅度提高。

2)简单来说就是:其一是将大量的并发处理转发给后端多个节点处理,减少工作响应时间;其二是将单个繁重的工作转发给后端多个节点处理,处理完再返回给负载均衡中心,再返回给用户。目前负载均衡技术大多数是用于提高诸如在Web服务器、FTP服务器和其它关键任务服务器上的Internet服务器程序的可用性和可伸缩性。

二. 负载均衡分类

1)二层负载均衡(mac)

根据OSI模型分的二层负载,一般是用虚拟mac地址方式,外部对虚拟MAC地址请求,负载均衡接收后分配后端实际的MAC地址响应.

2)三层负载均衡(ip)

一般采用虚拟IP地址方式,外部对虚拟的ip地址请求,负载均衡接收后分配后端实际的IP地址响应. (即一个ip对一个ip的转发, 端口全放开)

3)四层负载均衡(tcp

在三次负载均衡的基础上,即从第四层"传输层"开始, 使用"ip+port"接收请求,再转发到对应的机器。

4)七层负载均衡(http)

从第七层"应用层"开始, 根据虚拟的url或IP,主机名接收请求,再转向相应的处理服务器。

我们运维中最常见的四层和七层负载均衡,这里重点说下这两种负载均衡。
1)四层的负载均衡就是基于IP+端口的负载均衡:在三层负载均衡的基础上,通过发布三层的IP地址(VIP),然后加四层的端口号,来决定哪些流量需要做负载均衡,对需要处理的流量进行NAT处理,转发至后台服务器,并记录下这个TCP或者UDP的流量是由哪台服务器处理的,后续这个连接的所有流量都同样转发到同一台服务器处理。
对应的负载均衡器称为四层交换机(L4 switch),主要分析IP层及TCP/UDP层,实现四层负载均衡。此种负载均衡器不理解应用协议(如HTTP/FTP/MySQL等等)。

实现四层负载均衡的软件有:
F5:硬件负载均衡器,功能很好,但是成本很高。
lvs:重量级的四层负载软件
nginx:轻量级的四层负载软件,带缓存功能,正则表达式较灵活
haproxy:模拟四层转发,较灵活

2)七层的负载均衡就是基于虚拟的URL或主机IP的负载均衡:在四层负载均衡的基础上(没有四层是绝对不可能有七层的),再考虑应用层的特征,比如同一个Web服务器的负载均衡,除了根据VIP加80端口辨别是否需要处理的流量,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。举个例子,如果的Web服务器分成两组,一组是中文语言的,一组是英文语言的,那么七层负载均衡就可以当用户来访问你的域名时,自动辨别用户语言,然后选择对应的语言服务器组进行负载均衡处理。

对应的负载均衡器称为七层交换机(L7 switch),除了支持四层负载均衡以外,还有分析应用层的信息,如HTTP协议URI或Cookie信息,实现七层负载均衡。此种负载均衡器能理解应用协议。
实现七层负载均衡的软件有:
haproxy:天生负载均衡技能,全面支持七层代理,会话保持,标记,路径转移;
nginx:只在http协议和mail协议上功能比较好,性能与haproxy差不多;
apache:功能较差
Mysql proxy:功能尚可。

总的来说,一般是lvs做4层负载;nginx做7层负载(也能做4层负载, 通过stream模块);haproxy比较灵活,4层和7层负载均衡都能做

三. 四层和七层负载均衡之间的区别

1) 从技术原理上分析

所谓四层负载均衡,也就是主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。

以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。在某些部署况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。
2021-06-15T10:17:42.png
所谓七层负载均衡,也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。

以常见的TCP为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接受到客户端发送的真正应用层内容的报文,然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。负载均衡和前端的客户端以及后端的服务器会分别建立TCP连接。所以从这个技术原理上来看,七层负载均衡明显的对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。
2021-06-15T10:17:55.png
四层负载均衡在中间传输层执行,它处理消息的传递,但不考虑消息的内容。例如TCP是网络上Hypertext Transfer Protocol(HTTP)流量的第四层协议。在这一过程中,4层负载均衡会将网络数据包转发到上游服务器,但不会检查数据包的内容,只能通过检查TCP流中的前几个包来做出有限的路由决策。

七层负载均衡不同于四层负载均衡,它在高级应用层上执行,会处理每个消息的实际内容。HTTP是网络上网站流量的主要7层协议。七层负载均衡以比四层负载均衡更复杂的方式路由网络流量,尤其适用于基于TCP的流量(如HTTP)。七层负载均衡会终止网络流量并读取器中消息,它可以根据消息内容(如URL或cookie)做出负载均衡决策。随后,七层负载均衡与选定上有服务器建立新的TCP连接并将请求写入服务器。

简单来说,二者之间的区别

  • 七层负载均衡基本都是基于http协议的,适用于web服务器的负载均衡。(nginx)
  • 四层负载均衡主要是基于tcp协议报文,可以做任何基于tcp/ip协议的软件的负载均衡。(haproxy、LVS)
  • 两者主要区别在于利用的报文所在的层面是不同的,各有各的好处。
  • 七层应用负载的好处,是使得整个网络更”智能化“。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。当然这只是七层应用的一个小案例,从技术原理上,这种方式可以对客户端的请求和服务器的响应进行任意意义上的修改,极大的提升了应用系统在网络层的灵活性。很多在后台,例如Nginx或者Apache上部署的功能可以前移到负载均衡设备上,例如客户请求中的Header重写,服务器响应中的关键字过滤或者内容插入等功能。
  • 四层负载均衡主要是较为灵活,可以作为多种软件的负载均衡器。

举个例子形象的说明:四层负载均衡就像银行的自助排号机,每一个达到银行的客户根据排号机的顺序,选择对应的窗口接受服务;而七层负载均衡像银行大堂经理,先确认客户需要办理的业务,再安排排号。这样办理理财、存取款等业务的客户,会根据银行内部资源得到统一协调处理,加快客户业务办理流程。
2021-06-15T10:18:12.png
七层负载均衡的好处
七层负载均衡比基于数据包的四层负载均衡更占CPU,但很少会导致服务器性能下降。七层负载均衡可以让负载均衡器做出更明智的决策,并可以对内容进行优化和更改,如压缩、加密等等。七层负载均衡还可以利用buffering来卸载上游服务器的慢速连接,从而提高性能。

执行七层负载平衡的组件通常被称为反向代理服务器。

七层负载均衡示例
举个简单的例子,假设用户访问高流量网站,在会话期间,它可能会请求静态内容(例如图像或视频)、动态内容(例如新闻订阅源)或者交易信息(例如订单状态)等等。7层负载平衡允许负载均衡器根据请求本身中的消息(如内容类型)来路由请求。也就是说,我们可以将对图像或视频的请求路由到存储它的服务器,并进行高度优化以提供多媒体内容;可以将诸如折扣价之类的交易信息请求路由到负责管理定价的应用服务器。借助7层负载平衡,网络和应用程序架构师可以创建高度优化的服务器基础架构或应用交付网络,在保障可靠性的同时进行有效扩展。

简单总结
从上面的对比看来四层负载与七层负载最大的区别就是效率与功能的区别。四层负载架构设计比较简单,无需解析具体的消息内容,在网络吞吐量及处理能力上会相对比较高,而七层负载均衡的优势则体现在功能多,控制灵活强大。在具体业务架构设计时,使用七层负载或者四层负载还得根据具体的情况综合考虑。

负载均衡时的数据流都经过负载均衡器,如何解决负载均衡器成为瓶颈的问题?
通过修改tcp报文的源地址和目的地址,使从web服务器中返回的数据直接返回到客户端,这是七层负载均衡无法做到的,因为tcp三次握手建立在客户端与负载均衡服务器之间,http协议基于tcp协议,建立好tcp链接后才传送http报文,收到http报文说明负载均衡器和客户端已经建立了tcp连接,而web服务器和客户端的tcp链接都没建立,怎么回传数据给客户端呢。以上的办法会出现问题:所有集群里的主机都是内网ip,无法跟外界联系。
解决方案1:
如果能买到那么多外网Ip地址来用,然后在tcp链接要建立时负载均衡给真正的web服务器,让客户端和服务器建立tcp链接
解决方案2:
引用一句话:计算机所有的问题都可以通过建立一层虚拟层解决。
可以通过将所有服务器主机ip虚拟化成负载均衡服务器的ip,这样服务器集群的所有主机都可以访问外界网络,因为ip地址(网络层,三层)都是相同,所以只能通过第二层来分辨数据流向,修改数据链路层(二层)目的主机的MAC地址,使请求发到web服务器上,然后才真正建立起tcp连接,然后web服务器因为可以联网,所以可以直接返回数据给客户端

2)从应用场景的需求上分析

七层应用负载的好处,是使得整个网络更"智能化"。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。当然这只是七层应用的一个小案例,从技术原理上,这种方式可以对客户端的请求和服务器的响应进行任意意义上的修改,极大的提升了应用系统在网络层的灵活性。很多在后台,例如Nginx或者Apache上部署的功能可以前移到负载均衡设备上,例如客户请求中的Header重写,服务器响应中的关键字过滤或者内容插入等功能。

另外一个常常被提到功能就是安全性。网络中最常见的SYN Flood攻击,即黑客控制众多源客户端,使用虚假IP地址对同一目标发送SYN攻击,通常这种攻击会大量发送SYN报文,耗尽服务器上的相关资源,以达到Denial of Service(DoS)的目的。从技术原理上也可以看出,四层模式下这些SYN攻击都会被转发到后端的服务器上;而七层模式下这些SYN攻击自然在负载均衡设备上就截止,不会影响后台服务器的正常运营。另外负载均衡设备可以在七层层面设定多种策略,过滤特定报文,例如SQL Injection等应用层面的特定攻击手段,从应用层面进一步提高系统整体安全。

现在的七层负载均衡,主要还是着重于应用HTTP协议,所以其应用范围主要是众多的网站或者内部信息平台等基于B/S开发的系统。 4层负载均衡则对应其他TCP应用,例如基于C/S开发的ERP等系统。

3)七层应用需要考虑的问题

3.1) 是否真的必要。七层应用的确可以提高流量智能化,同时必不可免的带来设备配置复杂,负载均衡压力增高以及故障排查上的复杂性等问题。在设计系统时需要考虑四层七层同时应用的混杂情况。

3.2) 是否真的可以提高安全性。例如SYN Flood攻击,七层模式的确将这些流量从服务器屏蔽,但负载均衡设备本身要有强大的抗DDoS能力,否则即使服务器正常而作为中枢调度的负载均衡设备故障也会导致整个应用的崩溃。

3.3) 是否有足够的灵活度。七层应用的优势是可以让整个应用的流量智能化,但是负载均衡设备需要提供完善的七层功能,满足客户根据不同情况的基于应用的调度。最简单的一个考核就是能否取代后台Nginx或者Apache等服务器上的调度功能。能够提供一个七层应用开发接口的负载均衡设备,可以让客户根据需求任意设定功能,才真正有可能提供强大的灵活性和智能性。

4)总体对比

4.1) 智能性
七层负载均衡由于具备OIS七层的所有功能,所以在处理用户需求上能更加灵活,从理论上讲,七层模型能对用户的所有跟服务端的请求进行修改。例如对文件header添加信息,根据不同的文件类型进行分类转发。四层模型仅支持基于网络层的需求转发,不能修改用户请求的内容。

4.2) 安全性
七层负载均衡由于具有OSI模型的全部功能,能更容易抵御来自网络的攻击;四层模型从原理上讲,会直接将用户的请求转发给后端节点,无法直接抵御网络攻击。

4.3) 复杂度
四层模型一般比较简单的架构,容易管理,容易定位问题;七层模型架构比较复杂,通常也需要考虑结合四层模型的混用情况,出现问题定位比较复杂。

4.4) 效率比
四层模型基于更底层的设置,通常效率更高,但应用范围有限;七层模型需要更多的资源损耗,在理论上讲比四层模型有更强的功能,现在的实现更多是基于http应用。

四、负载均衡技术方案说明

1)软/硬件负载均衡

软件负载均衡解决方案是指在一台或多台服务器相应的操作系统上安装一个或多个附加软件来实现负载均衡,如DNS Load Balance,CheckPoint Firewall-1 ConnectControl,Keepalive+ipvs等,它的优点是基于特定环境,配置简单,使用灵活,成本低廉,可以满足一般的负载均衡需求。软件解决方案缺点也较多,因为每台服务器上安装额外的软件运行会消耗系统不定量的资源,越是功能强大的模块,消耗得越多,所以当连接请求特别大的时候,软件本身会成为服务器工作成败的一个关键;软件可扩展性并不是很好,受到操作系统的限制;由于操作系统本身的Bug,往往会引起安全问题。

硬件负载均衡解决方案是直接在服务器和外部网络间安装负载均衡设备,这种设备通常是一个独立于系统的硬件,我们称之为负载均衡器。由于专门的设备完成专门的任务,独立于操作系统,整体性能得到大量提高,加上多样化的负载均衡策略,智能化的流量管理,可达到最佳的负载均衡需求。负载均衡器有多种多样的形式,除了作为独立意义上的负载均衡器外,有些负载均衡器集成在交换设备中,置于服务器与Internet链接之间,有些则以两块网络适配器将这一功能集成到PC中,一块连接到Internet上,一块连接到后端服务器群的内部网络上。

软件负载均衡与硬件负载均衡的对比:
软件负载均衡的优点是需求环境明确,配置简单,操作灵活,成本低廉,效率不高,能满足普通的企业需求;缺点是依赖于系统,增加资源开销;软件的优劣决定环境的性能;系统的安全,软件的稳定性均会影响到整个环境的安全。
硬件负载均衡优点是独立于系统,整体性能大量提升,在功能、性能上优于软件方式;智能的流量管理,多种策略可选,能达到最佳的负载均衡效果;缺点是价格昂贵。

2)本地/全局负载均衡

负载均衡从其应用的地理结构上分为本地负载均衡(Local Load Balance)和全局负载均衡(Global Load Balance,也叫地域负载均衡),本地负载均衡是指对本地的服务器群做负载均衡,全局负载均衡是指对分别放置在不同的地理位置、有不同网络结构的服务器群间作负载均衡。

本地负载均衡能有效地解决数据流量过大、网络负荷过重的问题,并且不需花费昂贵开支购置性能卓越的服务器,充分利用现有设备,避免服务器单点故障造成数据流量的损失。其有灵活多样的均衡策略把数据流量合理地分配给服务器群内的服务器共同负担。即使是再给现有服务器扩充升级,也只是简单地增加一个新的服务器到服务群中,而不需改变现有网络结构、停止现有的服务。

全局负载均衡主要用于在一个多区域拥有自己服务器的站点,为了使全球用户只以一个IP地址或域名就能访问到离自己最近的服务器,从而获得最快的访问速度,也可用于子公司分散站点分布广的大公司通过Intranet(企业内部互联网)来达到资源统一合理分配的目的。

3)网络层次上的负载均衡

针对网络上负载过重的不同瓶颈所在,从网络的不同层次入手,我们可以采用相应的负载均衡技术来解决现有问题。
随着带宽增加,数据流量不断增大,网络核心部分的数据接口将面临瓶颈问题,原有的单一线路将很难满足需求,而且线路的升级又过于昂贵甚至难以实现,这时就可以考虑采用链路聚合(Trunking)技术。
链路聚合技术(第二层负载均衡)将多条物理链路当作一条单一的聚合逻辑链路使用,网络数据流量由聚合逻辑链路中所有物理链路共同承担,由此在逻辑上增大了链路的容量,使其能满足带宽增加的需求。
现代负载均衡技术通常操作于网络的第四层或第七层。第四层负载均衡将一个Internet上合法注册的IP地址映射为多个内部服务器的IP地址,对每次 TCP连接请求动态使用其中一个内部IP地址,达到负载均衡的目的。在第四层交换机中,此种均衡技术得到广泛的应用,一个目标地址是服务器群VIP(虚拟 IP,Virtual IP address)连接请求的数据包流经交换机,交换机根据源端和目的IP地址、TCP或UDP端口号和一定的负载均衡策略,在服务器IP和VIP间进行映射,选取服务器群中最好的服务器来处理连接请求。

七层负载均衡控制应用层服务的内容,提供了一种对访问流量的高层控制方式,适合对HTTP服务器群的应用。第七层负载均衡技术通过检查流经的HTTP报头,根据报头内的信息来执行负载均衡任务。

七层负载均衡优点表现在如下几个方面:
1)通过对HTTP报头的检查,可以检测出HTTP400、500和600系列的错误信息,因而能透明地将连接请求重新定向到另一台服务器,避免应用层故障。
2)可根据流经的数据类型(如判断数据包是图像文件、压缩文件或多媒体文件格式等),把数据流量引向相应内容的服务器来处理,增加系统性能。
3)能根据连接请求的类型,如是普通文本、图象等静态文档请求,还是asp、cgi等的动态文档请求,把相应的请求引向相应的服务器来处理,提高系统的性能及安全性。

七层负载均衡缺点表现在如下几个方面:
1)七层负载均衡受到其所支持的协议限制(一般只有HTTP),这样就限制了它应用的广泛性。
2)七层负载均衡检查HTTP报头会占用大量的系统资源,势必会影响到系统的性能,在大量连接请求的情况下,负载均衡设备自身容易成为网络整体性能的瓶颈。

五、负载均衡策略

在实际应用中,我们可能不想仅仅是把客户端的服务请求平均地分配给内部服务器,而不管服务器是否宕机。而是想使Pentium III服务器比Pentium II能接受更多的服务请求,一台处理服务请求较少的服务器能分配到更多的服务请求,出现故障的服务器将不再接受服务请求直至故障恢复等等。选择合适的负载均衡策略,使多个设备能很好的共同完成任务,消除或避免现有网络负载分布不均、数据流量拥挤反应时间长的瓶颈。在各负载均衡方式中,针对不同的应用需求,在OSI参考模型的第二、三、四、七层的负载均衡都有相应的负载均衡策略。

负载均衡策略的优劣及其实现的难易程度有两个关键因素:负载均衡算法;对网络系统状况的检测方式和能力。
负载均衡算法
1)轮循均衡(Round Robin):每一次来自网络的请求轮流分配给内部中的服务器,从1至N然后重新开始。此种均衡算法适合于服务器组中的所有服务器都有相同的软硬件配置并且平均服务请求相对均衡的情况。
2)权重轮循均衡(Weighted Round Robin):根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。例如:服务器A的权值被设计成1,B的权值是 3,C的权值是6,则服务器A、B、C将分别接受到10%、30%、60%的服务请求。此种均衡算法能确保高性能的服务器得到更多的使用率,避免低性能的服务器负载过重。
3)随机均衡(Random):把来自网络的请求随机分配给内部中的多个服务器。
4)权重随机均衡(Weighted Random):此种均衡算法类似于权重轮循算法,不过在处理请求分担时是个随机选择的过程。
5)响应速度均衡(Response Time):负载均衡设备对内部各服务器发出一个探测请求(例如Ping),然后根据内部中各服务器对探测请求的最快响应时间来决定哪一台服务器来响应客户端的服务请求。此种均衡算法能较好的反映服务器的当前运行状态,但这最快响应时间仅仅指的是负载均衡设备与服务器间的最快响应时间,而不是客户端与服务器间的最快响应时间。
6)最少连接数均衡(Least Connection):客户端的每一次请求服务在服务器停留的时间可能会有较大的差异,随着工作时间加长,如果采用简单的轮循或随机均衡算法,每一台服务器上的连接进程可能会产生极大的不同,并没有达到真正的负载均衡。最少连接数均衡算法对内部中需负载的每一台服务器都有一个数据记录,记录当前该服务器正在处理的连接数量,当有新的服务连接请求时,将把当前请求分配给连接数最少的服务器,使均衡更加符合实际情况,负载更加均衡。此种均衡算法适合长时处理的请求服务,如FTP。
7)处理能力均衡:此种均衡算法将把服务请求分配给内部中处理负荷(根据服务器CPU型号、CPU数量、内存大小及当前连接数等换算而成)最轻的服务器,由于考虑到了内部服务器的处理能力及当前网络运行状况,所以此种均衡算法相对来说更加精确,尤其适合运用到第七层(应用层)负载均衡的情况下。
8)DNS响应均衡(Flash DNS):在Internet上,无论是HTTP、FTP或是其它的服务请求,客户端一般都是通过域名解析来找到服务器确切的IP地址的。在此均衡算法下,分处在不同地理位置的负载均衡设备收到同一个客户端的域名解析请求,并在同一时间内把此域名解析成各自相对应服务器的IP地址(即与此负载均衡设备在同一位地理位置的服务器的IP地址)并返回给客户端,则客户端将以最先收到的域名解析IP地址来继续请求服务,而忽略其它的IP地址响应。在种均衡策略适合应用在全局负载均衡的情况下,对本地负载均衡是没有意义的。

转自:https://www.cnblogs.com/kevingrace/p/6137881.html

]]>
<![CDATA[LVS负载均衡-基础知识梳理]]> https://www.msl.la/archives/328/ 2021-06-15T18:14:11+08:00 2021-06-15T18:14:11+08:00 墨少离 https://www.msl.la/ 一. 集群的概念

服务器集群简称集群是一种服务器系统,它通过一组松散集成的服务器软件和/或硬件连接起来高度紧密地协作完成计算工作。在某种意义上,他们可以被看作是一台服务器。
集群系统中的单个服务器通常称为节点,通常通过局域网连接,但也有其它的可能连接方式。集群服务器通常用来改进单个服务器的计算速度和/或可靠性。一般况下集群
服务器比单个服务器,比如工作站或超级服务器性能价格比要高得多。集群就是一组独立的服务器,通过网络连接组合成一个组合来共同完一个任务。

说的直白点,集群就是一组相互独立的服务器,通过高速的网络组成一个服务器系统,每个集群节点都是运行其自己进程的一个独立服务器。对网络用户来讲,网站后
端就是一个单一的系统,协同起来向用户提供系统资源,系统服务。

二. 为什么要使用集群

1) 集群的特点

  • 高性能performance
    一些需要很强的运算处理能力比如天气预报,核试验等。这就不是几台服务器能够搞定的。这需要上千台一起来完成这个工作的。
  • 价格有效性
    通常一套系统集群架构,只需要几台或数十台服务器主机即可,与动则上百万的专用超级服务器具有更高的性价比。
  • 可伸缩性
    当服务器负载压力增长的时候,系统能够扩展来满足需求,且不降低服务质量。
  • 高可用性
    尽管部分硬件和软件发生故障,整个系统的服务必须是7*24小时运行的。

2) 集群的优势

  • 透明性
    如果一部分服务器宕机了业务不受影响,一般耦合度没有那么高,依赖关系没有那么高。比如NFS服务器宕机了其他就挂载不了了,这样依赖性太强。
  • 高性能
    访问量增加,能够轻松扩展。
  • 可管理性
    整个系统可能在物理上很大,但很容易管理。
  • 编程
    在集群系统上,容易开发应用程序,门户网站会要求这个。

3) 集群分类及不同分类的特点
计算机集群架构按照功能和结构一般分成以下几类:

  • 负载均衡集群(Loadbalancingclusters)简称LBC
  • 高可用性集群(High-availabilityclusters)简称HAC
  • 高性能计算集群(High-perfomanceclusters)简称HPC
  • 网格计算(Gridcomputing)

就集群分类而言, 网络上面一般认为是有三个,负载均衡和高可用集群式我们互联网行业常用的集群架构。

1) 负载均衡集群
负载均衡集群为企业提供了更为实用,性价比更高的系统架构解决方案。负载均衡集群把很多客户集中访问的请求负载压力可能尽可能平均的分摊到计算机集群中处理。
客户请求负载通常包括应用程度处理负载和网络流量负载。这样的系统非常适合向使用同一组应用程序为大量用户提供服务。每个节点都可以承担一定的访问请求负载压力,
并且可以实现访问请求在各节点之间动态分配,以实现负载均衡。

负载均衡运行时,一般通过一个或多个前端负载均衡器将客户访问请求分发到后端一组服务器上,从而达到整个系统的高性能和高可用性。这样集群有时也被称为服务器群。
一般高可用性集群和负载均衡集群会使用类似的技术,或同时具有高可用性与负载均衡的特点。

负载均衡集群的作用:
a)分担访问流量(负载均衡)
b)保持业务的连续性(高可用)

2) 高可用性集群
一般是指当集群中的任意一个节点失效的情况下,节点上的所有任务自动转移到其他正常的节点上,并且此过程不影响整个集群的运行,不影响业务的提供。类似是集群中运行着两个或两个以上的一样的节点,当某个主节点出现故障的时候,那么其他作为从 节点的节点就会接替主节点上面的任务。从节点可以接管主节点的资源(IP地址,架构身份等),此时用户不会发现提供服务的对象从主节点转移到从节点。
高可用性集群的作用:当一台机器宕机另一台进行接管。比较常用的高可用集群开源软件有:keepalive,heardbeat。

3) 高性能计算集群
高性能计算集群采用将计算任务分配到集群的不同计算节点儿提高计算能力,因而主要应用在科学计算领域。比较流行的HPC采用Linux操作系统和其它一些免费软件来完成并行运算。这一集群配置通常被称为Beowulf集群。这类集群通常运行特定的程序以发挥HPCcluster的并行能力。这类程序一般应用特定的运行库, 比如专为科学计算设计的MPI库。HPC集群特别适合于在计算中各计算节点之间发生大量数据通讯的计算作业,比如一个节点的中间结果或影响到其它节点计算结果的情况。

三. 负载均衡集群介绍

负载均衡集群是 Load Balance 集群, 是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端提供服务的一种方式。
负载均衡集群的作用:提供一种廉价、有效、透明的方法,来扩展网络设备和服务器的负载带宽、增加吞吐量,加强网络数据处理能力、提高网络的灵活性和可用性。简单来说,也就是:
1) 把单台计算机无法承受的大规模的并发访问或数据流量分担到多台节点设备上分别处理,减少用户等待响应的时间,提升用户体验。
2) 单个重负载的运算分担到多台节点设备上做并行处理,每个节点设备处理结束后,将结果汇总,返回给用户,系统处理能力得到大幅度提高。
3) 7*24小时的服务保证,任意一个或多个设备节点设备宕机,不能影响到业务。在负载均衡集群中,所有计算机节点都应该提供相同的服务,集群负载均衡获取所有对该服务的如站请求。

常用的负载均衡分为:
1) 开源软件负载均衡: Nginx, LVS, Haproxy (Nginx和Haproxy通常做七层负载均衡, LVS做四层负载均衡. 但是Nginx也可以通过stream模块做四层负载均衡, Haproxy也可以做四层负载均衡 ) ;
2) 商业的硬件负载均衡: 设备F5、Netscale ;

简单理解一下软件负载均衡:
1) 所谓分层的负载均衡,都是以网络的模型来说的。四层就是基于IP和端口的负载均衡,七层就是基于URL等应用信息的负载均衡。所以简单的说四层负载均衡就是通过IP和端口接收请求再分发至真实的服务器,七层是通过URL或主机名接收请求,然后分发至真实的服务器。
2) .而七层的实现也是在四层的基础上是实现的,没有四层就不可能有七层。在第七层上可以做许多事情,比如可以根据七层的浏览器类别区分是手机还是PC,将WEB服务器分为2组,手机登陆专门的移动端网站。
3) 对客户端来说,客户端好像是访问的同一台主机。其实为了有更好的用户体验,从智能DNS入手,根据客户端IP来源将域名解析到距离客户端最近的一台服务器或者访问最快速的一台服务器,但这些内容客户端都是感觉不到的,客户端感觉到的只能是访问网站很快。

四. LVS负载均衡集群说明

1) LVS是什么?
LVS是linux virtual server的简写linux虚拟服务器,是一个虚拟的服务器集群系统,可以在unix/linux平台下实现负载均衡集群功能。该项目在1998年5月由章文嵩博士组织成立。LVS是一种集群(Cluster)技术,采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器
的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序。

LVS集群采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服 务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序。

LVS在设计时需要考虑系统的透明性、可伸缩性、高可用性和易管理性。一般来说,LVS集群采用三层结构,其体系结构如图所示:
体系结构

负载均衡的原理很简单,就是当客户端发起请求时,请求直接发给Director Server(调度器),这时会根据设定的调度算法,将请求按照算法的规定智能的分发到真正的后台服务器。以达到将压力均摊。但是我们知道,http的连接时无状态的,假设这样一个场景,我登录某宝买东西,当我看上某款商品时,我将它加入购物车,但是我刷新了一下页面,这时由于负载均衡的原因,调度器又选了新的一台服务器为我提供服务,我刚才的购物车内容全都不见了,这样就会有十分差的用户体验。所以就还需要一个存储共享,这样就保证了用户请求的数据是一样的。所以LVS负载均衡分为三层架构(也就是LVS负载均衡主要组成部分):

第一层:负载调度器(load balancer/ Director),它是整个集群的总代理,它在有两个网卡,一个网卡面对访问网站的客户端,一个网卡面对整个集群的内部。负责将客户端的请求发送到一组服务器上执行,而客户也认为服务是来自这台主的。举个生动的例子,集群是个公司,负载调度器就是在外接揽生意,将接揽到的生意分发给后台的真正干活的真正的主机们。当然需要将活按照一定的算法分发下去,让大家都公平的干活。
第二层:服务器池(server pool/ Realserver),是一组真正执行客户请求的服务器,可以当做WEB服务器。就是上面例子中的小员工。
第三层:共享存储(shared storage),它为服务器池提供一个共享的存储区,这样很容易使得服务器池拥有相同的内容,提供相同的服务。一个公司得有一个后台账目吧,这才能协调。不然客户把钱付给了A,而换B接待客户,因为没有相同的账目。B说客户没付钱,那这样就不是客户体验度的问题了。
2021-06-15T10:04:37.png
2) LVS负载均衡集群特点

2.1) IP负载均衡与负载调度算法
IP负载均衡技术
负载均衡技术有很多实现方案,有基于DNS域名轮流解析的方法、有基于客户端调度访问的方法、有基于应用层系统负载的调度方法,还有基于IP地址的调度方法,在这些负载调度算法中,执行效率最高的是IP负载均衡技术。

LVS的IP负载均衡技术是通过IPVS模块来实现的,IPVS是LVS集群系统的核心软件,它的主要作用是:安装在Director Server上,同时在Director Server上虚拟出一个IP地址,用户必须通过这个虚拟的IP地址访问服务。这个虚拟IP一般称为LVS的VIP,即Virtual IP。访问的请求首先经过VIP到达负载调度器,然后由负载调度器从Real Server列表中选取一个服务节点响应用户的请求。当用户的请求到达负载调度器后,调度器如何将请求发送到提供服务的Real Server节点,而Real Server节点如何返回数据给用户,是IPVS实现的重点技术,IPVS实现负载均衡机制有三种,分别是NAT、TUN和DR(下面会详细介绍);

负载调度算法
负载调度器是根据各个服务器的负载情况,动态地选择一台Real Server响应用户请求,那么动态选择是如何实现呢,其实也就是我们这里要说的负载调度算法,根据不同的网络服务需求和服务器配置,IPVS实现了如下八种负载调度算法:rr、wrr、Wlc、Dh、SH、Lc、Lblc(下面会详细介绍);

2.2) 高可用性
LVS是一个基于内核级别的应用软件,因此具有很高的处理性能,后端服务器可运行任何支持TCP/IP的操作系统,包括Linux,各种Unix(如FreeBSD、Sun Solaris、HP Unix等),Mac/OS和Windows NT/2000等。负载调度器能够支持绝大多数的TCP和UDP协议.

2.3) 性能
LVS服务器集群系统具有良好的伸缩性,可支持几百万个并发连接。用LVS构架的负载均衡集群系统具有优秀的处理能力,每个服务节点的故障不会影响整个系统的正常使用,同时又实现负载的合理均衡,使应用具有超高负荷的服务能力,可支持上百万个并发连接请求。如配置百兆网卡,采用VS/TUN或VS/DR调度技术,整个集群系统的吞吐量可高达1Gbits/s;如配置千兆网卡,则系统的最大吞吐量可接近10Gbits/s。

2.4)高可靠性
LVS负载均衡集群软件已经在企业、学校等行业得到了很好的普及应用,国内外很多大型的、关键性的web站点也都采用了LVS集群软件,所以它的可靠性在实践中得到了很好的证实。有很多以LVS做的负载均衡系统,运行很长时间,从未做过重新启动。这些都说明了LVS的高稳定性和高可靠性。

2.5) 适用环境
LVS对前端Director Server目前仅支持Linux和FreeBSD系统,但是支持大多数的TCP和UDP协议,支持TCP协议的应用有:HTTP,HTTPS ,FTP,SMTP,,POP3,IMAP4,PROXY,LDAP,SSMTP等等。支持UDP协议的应用有:DNS,NTP,ICP,视频、音频流播放协议等。LVS对Real Server的操作系统没有任何限制,Real Server可运行在任何支持TCP/IP的操作系统上,包括Linux,各种Unix(如FreeBSD、Sun Solaris、HP Unix等),Mac/OS和Windows等。

2.6) 开源软件(软件许可证)
LVS集群软件是按GPL(GNU Public License)许可证发行的自由软件,因此,使用者可以得到软件的源代码,并且可以根据自己的需要进行各种修改,但是修改必须是以GPL方式发行。

3) LVS体系结构

LVS集群负载均衡器接受服务的所有入展客户端的请求,然后根据调度算法决定哪个集群节点来处理回复客户端的请求。LVS虚拟服务器的体系如下图所示,一组服务器通过高速的局域网或者地理分布的广域网相互连接,在这组服务器之前有一个负载调度器(load balance)。负载调度器负责将客户的请求调度到真实服务器上。这样这组服务器集群的结构对用户来说就是透明的。客户访问集群系统就如只是访问一台高性能,高可用的服务器一样。客户程序不受服务器集群的影响,不做任何修改。

就比如说:我们去饭店吃饭点菜,客户只要跟服务员点菜就行。并不需要知道具体他们是怎么分配工作的,所以他们内部对于我们来说是透明的。此时这个服务员就会按照一定的规则把他手上的活,分配到其他人员上去。这个服务员就是负载均衡器(LB)而后面这些真正做事的就是服务器集群。

LVS结构图如下:
2021-06-15T10:04:59.png

LVS基本工作过程:
2021-06-15T10:05:11.png
客户请发送向负载均衡服务器发送请求。负载均衡器接受客户的请求,然后先是根据LVS的调度算法(8种)来决定要将这个请求发送给哪个节点服务器。然后依据自己的工作模式(3种)来看应该如何把这些客户的请求如何发送给节点服务器,节点服务器又应该如何来把响应数据包发回给客户端。

LVS组成

lvs分为两个部分,分别是内核模块和lvs的管理工具。目前来说,centos6及其以上的内核版本已经包括了ipvs的相关模块了。
2021-06-15T10:05:26.png
从上面可知, 内核支持的ipvs模块, 上图中的rr,wrr,lc,wlc,lblc等等都是lvs中调度器的调度算法,根据不同的调度算法可以更好的分配服务,实现负载均衡。而ipvs(ip virtual server):一段代码工作在内核空间,实现调度。
2021-06-15T10:06:43.png

4) LVS的实现原理

lvs的原理其实就是利用了Iptables的功能。了解防火墙的都知道四表五链。防火墙不仅仅有放火的功能还有转发,地址伪装,限流等等功能。
2021-06-15T10:06:58.png
1) 首先,客户端向调度器(Director Server)发起一个请求,调度器将这个请求发送至内核
2) PREROUTING链首先会接收到用户请求,判断目标IP确定是本机IP,将数据包发往INPUT链。
3) 当请求达到INPUT链上,调度器判断报文中的目标端口来确定这个访问是不是要访问集群服务(因为还有可能只是ssh想单纯的远程登录主机这个主机),如果是访问的集群服务,那么就会强制修改这个包的目标IP
4) POSTROUTING链接收数据包后发现目标IP地址刚好是自己的后端服务器,那么此时通过选路,将数据包最终发送给后端的服务器

5) LVS的工作原理

LVS 的工作模式分为4中分别是 NAT,DR,TUN,FULL-NAT。其中做个比较,由于工作原理的关系的,NAT的配置最为简单,但是NAT对调度器的压力太大了,导致其效率最低,DR和TUN的工作原理差不多,但是DR中,所有主机必须处于同一个物理环境中,而在TUN中,所有主机可以分布在不同的位置,服务器一个在纽约,一个在深圳。最多应用的是FULL-NAT。
2021-06-15T10:07:27.png
其中的专业术语
DS:Director Server。指的是前端负载均衡器。
RS:Real Server。后端真实的工作服务器。
VIP:向外部直接面向用户请求,作为用户请求的目标的IP地址。
DIP:Director Server IP,主要用于和内部主机通讯的IP地址。
RIP:Real Server IP,后端服务器的IP地址。
CIP:Client IP,访问客户端的IP地址。

下面介绍LVS常用的三种负载均衡模式
1)NAT模式-网络地址转换 Virtualserver via Network address translation(VS/NAT)
这个是通过网络地址转换的方法来实现调度的。首先调度器(LB)接收到客户的请求数据包时(请求的目的IP为VIP),根据调度算法决定将请求发送给哪个后端的真实服务器(RS)。然后调度就把客户端发送的请求数据包的目标IP地址及端口改成后端真实服务器的IP地址(RIP),这样真实服务器(RS)就能够接收到客户的请求数据包了。真实服务器响应完请求后,查看默认路由(NAT模式下我们需要把RS的默认路由设置为LB服务器。)把响应后的数据包发送给LB,LB再接收到响应包后,把包的源地址改成虚拟地址(VIP)然后发送回给客户端。

VS/NAT是一种最简单的方式,所有的RealServer只需要将自己的网关指向Director即可。客户端可以是任意操作系统,但此方式下,一个Director能够带动的RealServer比较有限。在VS/NAT的方式下,Director也可以兼为一台RealServer。VS/NAT的体系结构如图所示。
2021-06-15T10:08:00.png
NAT工作模式下,调度过程IP包详细图:
2021-06-15T10:09:00.png
NAT模式的以上原理图简述:
1) 客户端请求数据,目标IP为VIP
2) 请求数据到达LB服务器,LB根据调度算法将目的地址修改为RIP地址及对应端口(此RIP地址是根据调度算法得出的。)并在连接HASH表中记录下这个连接。
3) 数据包从LB服务器到达RS服务器webserver,然后webserver进行响应。Webserver的网关必须是LB,然后将数据返回给LB服务器。
4) 收到RS的返回后的数据,根据连接HASH表修改源地址VIP&目标地址CIP,及对应端口80.然后数据就从LB出发到达客户端。
5) 客户端收到的就只能看到VIP\DIP信息。

NAT模式优缺点:
1) NAT技术将请求的报文和响应的报文都需要通过LB进行地址改写,因此网站访问量比较大的时候LB负载均衡调度器有比较大的瓶颈,一般要求最多之能10-20台节点。
2) 只需要在LB上配置一个公网IP地址就可以了。
3) 每台内部的节点服务器的网关地址必须是调度器LB的内网地址。
4) NAT模式支持对IP地址和端口进行转换。即用户请求的端口和真实服务器的端口可以不一致。

再看下面的NAT模式图
2021-06-15T10:09:15.png
客户发出请求,发送请求给链接调度器的VIP,调度器将请求报文中的目标Ip地址改为RIP。这样服务器RealServer将请求的内容发给调度器,调度器再将报文中的源IP地址改为VIP;
1) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP;
2) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链;
3) IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器IP,然后将数据包发至POSTROUTING链。 此时报文的源IP为CIP,目标IP为RIP
4) POSTROUTING链通过选路,将数据包发送给Real Server;
5) Real Server比对发现目标为自己的IP,开始构建响应报文发回给Director Server。 此时报文的源IP为RIP,目标IP为CIP;
6) Director Server在响应客户端前,此时会将源IP地址修改为自己的VIP地址,然后响应给客户端。 此时报文的源IP为VIP,目标IP为CIP;

NAT模式特点和注意事项:
1) 很好配置,原理简单易懂;
2) 由于调度器的工作量太大,很容易成为整个集群系统的瓶颈;
3) RS应该使用私有地址;
4) RS的网关的必须指向DIP;
5) RIP和DIP必须在同一网段内;
6) 请求和响应的报文都得经过Director;在高负载场景中,Director很可能成为系统性能瓶颈;
7) 支持端口映射;
8) RS可以使用任意支持集群服务的OS;

2)TUN模式-IP隧道模式 Virtual Server via IP Tunneling(VS/TUN)
IP隧道(IP tunneling)是将一个IP报文封装在另一个IP报文的技术,这可以使得目标为一个IP地址的数据报文能被封装和转发到另一个IP地址。IP隧道技术亦称为IP封装技术(IP encapsulation)。

IP隧道主要用于移动主机和虚拟私有网络(Virtual Private Network),在其中隧道都是静态建立的,隧道一端有一个IP地址,另一端也有唯一的IP地址。它的连接调度和管理与VS/NAT中的一样,只是它的报文转发方法不同。调度器根据各个服务器的负载情况,动态地选择一台服务器,将请求报文封装在另一个IP报文中,再将封装后的IP报文转发给选出的服务器; 服务器收到报文后,先将报文解封获得原来目标地址为 VIP 的报文,服务器发现VIP地址被配置在本地的IP隧道设备上,所以就处理这个请求,然后根据路由表将响应报文直接返回给客户。
2021-06-15T10:09:41.png
采用NAT模式时,由于请求和响应的报文必须通过调度器地址重写,当客户请求越来越多时,调度器处理能力将成为瓶颈。为了解决这个问题,调度器把请求的报文通过IP隧道转发到真实的服务器。真实的服务器将响应处理后的数据直接返回给客户端。这样调度器就只处理请求入站报文,由于一般网络服务应答数据比请求报文大很多,采用VS/TUN模式后,集群系统的最大吞吐量可以提高10倍。

VS/TUN的工作原理流程图如下所示,它和NAT模式不同的是,它在LB和RS之间的传输不用改写IP地址。而是把客户请求包封装在一个IP tunnel里面,然后发送给RS节点服务器,节点服务器接收到之后解开IP tunnel后,进行响应处理。并且直接把包通过自己的外网地址发送给客户不用经过LB服务器。
2021-06-15T10:09:54.png
TUN模式下的以上原理图过程简述:
1)客户请求数据包,目标地址VIP发送到LB上;
2)LB接收到客户请求包,进行IP Tunnel封装。即在原有的包头加上IP Tunnel的包头。然后发送出去;
3)RS节点机器根据IP Tunnel包头信息 (此时就又一种逻辑上的隐形隧道,只有LB和RS之间懂)收到请求包,然后解开IP Tunnel包头信息,得到客户的请求包并进行响应处理。
4)响应处理完毕之后,RS服务器使用自己的出公网的线路,将这个响应数据包发送给客户端。源IP地址还是VIP地址。(RS节点服务器需要在本地回环接口配置VIP);

其实TUN模式和下面的DR模式差不多,但是比DR多了一个隧道技术以支持realserver不在同一个物理环境中。就是realserver一个在北京,一个工作在上海。在原有的IP报文外再次封装多一层IP首部,内部IP首部(源地址为CIP,目标IIP为VIP),外层IP首部(源地址为DIP,目标IP为RIP. 再看下面的TUN模式图:
2021-06-15T10:10:12.png
1) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 。
2) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链;
3) IPVS比对数据包请求的服务是否为集群服务,若是,在请求报文的首部再次封装一层IP报文,封装源IP为为DIP,目标IP为RIP。然后发至POSTROUTING链。 此时源IP为DIP,目标IP为RIP;
4) POSTROUTING链根据最新封装的IP报文,将数据包发至RS(因为在外层封装多了一层IP首部,所以可以理解为此时通过隧道传输)。 此时源IP为DIP,目标IP为RIP;
5) RS接收到报文后发现是自己的IP地址,就将报文接收下来,拆除掉最外层的IP后,会发现里面还有一层IP首部,而且目标是自己的lo接口VIP,那么此时RS开始处理此请求,处理完成之后,通过lo接口送给eth0网卡,然后向外传递。 此时的源IP地址为VIP,目标IP为CIP;
6) 响应报文最终送达至客户端;

LVS-TUN (ip隧道) 模式特点和注意事项
1) RIP、VIP、DIP全是公网地址
2) RS的网关不会也不可能指向DIP
3) 不支持端口映射
4) RS的系统必须支持隧道

3)DR模式-直接路由模式 Virtual Server via Direct Routing(VS/DR)
DR模式也就是用直接路由技术实现虚拟服务器。它的连接调度和管理与VS/NAT和VS/TUN中的一样,但它的报文转发方法又有不同,VS/DR通过改写请求报文的MAC地址,将请求发送到Real Server,而Real Server将响应直接返回给客户,免去了VS/TUN中的IP隧道开销。这种方式是三种负载调度机制中性能最高最好的,但是必须要求Director Server与Real Server都有一块网卡连在同一物理网段上。

Director和RealServer必需在物理上有一个网卡通过不间断的局域网相连。 RealServer上绑定的VIP配置在各自Non-ARP的网络设备上(如lo或tunl),Director的VIP地址对外可见,而RealServer的VIP对外是不可见的。RealServer的地址即可以是内部地址,也可以是真实地址。
2021-06-15T10:10:29.png
DR模式是通过改写请求报文的目标MAC地址,将请求发给真实服务器的,而真实服务器响应后的处理结果直接返回给客户端用户。同TUN模式一样,DR模式可以极大的提高集群系统的伸缩性。而且DR模式没有IP隧道的开销,对集群中的真实服务器也没有必要必须支持IP隧道协议的要求。但是要求调度器LB与真实服务器RS都有一块网卡连接到同一物理网段上,必须在同一个局域网环境。

DR模式是互联网使用比较多的一种模式,DR模式原理图如下:
2021-06-15T10:10:48.png
DR模式以上原理过程简述:
VS/DR模式的工作流程图如上图所示,它的连接调度和管理与NAT和TUN中的一样,它的报文转发方法和前两种不同。DR模式将报文直接路由给目标真实服务器。在DR模式中,调度器根据各个真实服务器的负载情况,连接数多少等,动态地选择一台服务器,不修改目标IP地址和目标端口,也不封装IP报文,而是将请求报文的数据帧的目标MAC地址改为真实服务器的MAC地址。然后再将修改的数据帧在服务器组的局域网上发送。因为数据帧的MAC地址是真实服务器的MAC地址,并且又在同一个局域网。那么根据局域网的通讯原理,真实复位是一定能够收到由LB发出的数据包。真实服务器接收到请求数据包的时候,解开IP包头查看到的目标IP是VIP。(此时只有自己的IP符合目标IP才会接收进来,所以我们需要在本地的回环借口上面配置VIP。

另外: 由于网络接口都会进行ARP广播响应,但集群的其他机器都有这个VIP的lo接口,都响应就会冲突。所以我们需要把真实服务器的lo接口的ARP响应关闭掉。)然后真实服务器做成请求响应,之后根据自己的路由信息将这个响应数据包发送回给客户,并且源IP地址还是VIP。

其实整个DR模式都是停留在第二层的数据链路层, 直接修改MAC。实现报文的转发。再看下面的DR模式图:
2021-06-15T10:11:09.png
1) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP;
2) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链;
3) IPVS比对数据包请求的服务是否为集群服务,若是,将请求报文中的源MAC地址修改为DIP的MAC地址,将目标MAC地址修改RIP的MAC地址,然后将数据包发至POSTROUTING链。 此时的源IP和目的IP均未修改,仅修改了源MAC地址为DIP的MAC地址,目标MAC地址为RIP的MAC地址;
4) 由于DS和RS在同一个网络中,所以是通过二层来传输。POSTROUTING链检查目标MAC地址为RIP的MAC地址,那么此时数据包将会发至Real Server;
5) 响应报文最终送达至客户端;

LVS-DR模式特点和注意事项
1) 在前端路由器做静态地址路由绑定,将对于VIP的地址仅路由到Director Server
2) arptables:在arp的层次上实现在ARP解析时做防火墙规则,过滤RS响应ARP请求。修改RS上内核参数(arp_ignore和arp_announce)将RS上的VIP配置在网卡接口的别名上,并限制其不能响应对VIP地址解析请求。
3) RS可以使用私有地址;但也可以使用公网地址,此时可以直接通过互联网连入RS以实现配置、监控等;
4) RS的网关一定不能指向DIP;
5) RS跟Dirctory要在同一物理网络内(不能由路由器分隔);
6) 请求报文经过Directory,但响应报文一定不经过Director
7) 不支持端口映射;
8) RS可以使用大多数的操作系统;

DR模式小结:
1)通过在调度器LB上修改数据包的目的MAC地址实现转发。注意源地址仍然是CIP,目的地址仍然是VIP地址。
2)请求的报文经过调度器,而RS响应处理后的报文无需经过调度器LB,因此并发访问量大时使用效率很高(和NAT模式比)
3)因为DR模式是通过MAC地址改写机制实现转发,因此所有RS节点和调度器LB只能在一个局域网里面
4)RS主机需要绑定VIP地址在LO接口上(防止IP冲突),并且需要配置ARP机制。
5)RS节点的默认网关不需要配置成LB,而是直接配置为上级路由的网关,能让RS直接出网就可以。
6)由于DR模式的调度器仅做MAC地址的改写,所以调度器LB就不能改写目标端口,那么RS服务器就得使用和VIP相同的端口提供服务。

三种负载均衡方式简单比较:

1)NAT模式-网络地址转换
VS/NAT 的优点是服务器可以运行任何支持TCP/IP的操作系统,它只需要一个IP地址配置在调度器上,服务器组可以用私有的IP地址。缺点是它的伸缩能力有限,当服务器结点数目升到20时,调度器本身有可能成为系统的新瓶颈,因为在VS/NAT中请求和响应报文都需要通过负载调度器。如果负载调度器成为系统新的瓶颈,可以有三种方法解决这个问题:混合方法、VS/TUN和 VS/DR。在DNS混合集群系统中,有若干个VS/NAT负调度器,每个负载调度器带自己的服务器集群,同时这些负载调度器又通过RR-DNS组成简单的域名。但VS/TUN和VS/DR是提高系统吞吐量的更好方法。对于那些将IP地址或者端口号在报文数据中传送的网络服务,需要编写相应的应用模块来转换报文数据中的IP地址或者端口号。这会带来实现的工作量,同时应用模块检查报文的开销会降低系统的吞吐率。

2)TUN模式-IP隧道模式
在TUN 的集群系统中,负载调度器只将请求调度到不同的后端服务器,后端服务器将应答的数据直接返回给用户。这样负载调度器就可以处理大量的请求,它甚至可以调度百台以上的服务器(同等规模的服务器),而它不会成为系统的瓶颈。即使负载调度器只有100Mbps的全双工网卡,整个系统的最大吞吐量可超过 1Gbps。所以,VS/TUN可以极大地增加负载调度器调度的服务器数量。VS/TUN调度器可以调度上百台服务器,而它本身不会成为系统的瓶颈,可以用来构建高性能的超级服务器。VS/TUN技术对服务器有要求,即所有的服务器必须支持"IP Tunneling"或者"IP Encapsulation"协议。目前,VS/TUN的后端服务器主要运行Linux操作系统,我们没对其他操作系统进行测试。因为"IP Tunneling"正成为各个操作系统的标准协议,所以VS/TUN应该会适用运行其他操作系统的后端服务器。

3)DR模式
跟VS/TUN方法一样,VS/DR调度器只处理客户到服务器端的连接,响应数据可以直接从独立的网络路由返回给客户。这可以极大地提高LVS集群系统的伸缩性。跟VS/TUN相比,这种方法没有IP隧道的开销,但是要求负载调度器与实际服务器都有一块网卡连在同一物理网段上,服务器网络设备(或者设备别名)不作ARP响应,或者能将报文重定向(Redirect)到本地的Socket端口上。
2021-06-15T10:11:32.png

6) LVS负载均衡调度算法

VS的调度算法决定了如何在集群节点之间分布工作负荷。当director调度器收到来自客户端访问VIP的上的集群服务的入站请求时,director调度器必须决定哪个集群节点应该
处理请求。

Director调度器用的调度方法基本分为两类 (如下所列, LVS总共有10种调度算法, 常用的也就四种调度算法, 下面会说到):
静态调度算法:rr,wrr,dh,sh
动态调度算法:wlc,lc,lblc,lblcr, sed, nq

静态调度 (也就是固定调度算法)的4种算法:
rr(轮询)
轮询调度:这种是最简单的调度算法,就是将请求A一个,B一个,A一个,B一个 ...... 循环的发。就算A主机挂掉了,调度器还是会将请求发送到A。十分均衡。

wrr(权重, 即加权轮询)
加权轮询调度:这种算法是在rr基础上实现的,只不过加了权重,权重范围为1-100,假设A的服务器性能好,就给A的权重设置的高一点,设为2,而B主机是1。这样就实现A二个,B一个,A二个,B一个 ...... 循环的发。这样照顾到了服务器性能。

sh(源地址哈希)
源地址散列:主要是实现将此前的session(会话)绑定。将此前客户的源地址作为散列键,从静态的散列表中找出对应的服务器,只要目标服务器是没有超负荷的就将请求发送过去。就是说某客户访问过A,现在这个客户又来了,所以客户请求会被发送到服务过他的A主机。

dh(目的地址哈希)
目的地址散列:以目的地址为关键字查找一个静态hash表来获得需要的RS。以目标地址为标准挑选。 功能是和sh近似的,但应用场景不同; 举个dh调度算法的例子:假设1号客户访问了web集群的一个动态页面,调度器将请求转发个A服务器,A服务器的PHP将这个动态请求运行了一遍,生成了缓存并回应1号客户。这下2号客户也访问了这个动态页面,调度器应该将请求发给A。毕竟A已经跑过这段程序了,有缓存,对吧。所以这既是dh算法)

动态调度算法,动态算法与静态算法最大的区别就是动态算法考虑了服务器的压力。
活动链接(active):客户与服务器建立连接并且有数据传送
非活动链接(inactive):只是建立连接,没有数据传送,没有断开连接

动态调度的6种算法
lc(最少链接)
最少连接调度:这种算法是看A,和B的主机谁的连接少,请求就发给谁。
简单算法:active*256+inactive (谁小发给谁)

wlc(加权最少链接)LVS的理想算法
加权最少链接:这种算法就是比lc多了一个加权。
简单算法:( active*256+inactive )/weight (谁小就发给谁)

sed(最短期望延迟)
基于wlc算法,假设A,B的权重分别是1,2 。而A的链接数为1,B的链接数为2 。这样的话,用wlc算法得出的结果一样,而明显B的权重大,B的能力较强。用sed算法的话,就可以避免wlc出现的问题。
简单算法:(active+1)256/weight (活动的连接数+1)256/除以权重 谁小发给谁
A:(1+1)/1
B:(2+1)/2 (B小,交给B)

nq(不用排队)
基于sed算法:在sed的基础上,若谁的链接数为0,直接将请求发送给它!

LBLC(基于局部性的最少连接)类似于dh,目标地址hash
这个算法主要用于Cache集群系统,因为Cache集群的中客户请求报文的目标IP地址的变化,将相同的目标URL地址请求调度到同一台服务器,来提高服务器的访问的局部性和Cache命中率。从而调整整个集群的系统处理能力。但是,如果realserver的负载处于一半负载,就用最少链接算法,将请求发送给活动链接少的主机。

LBLCR(带复制的基于局部性的最少链接)
该算法首先是基于最少链接的,当一个新请求收到后,一定会将请求发给最少连接的那台主机的。但这样又破坏了cache命中率。但这个算法中,集群服务是cache共享的,假设A的PHP跑了一遍,得到缓存。但其他realserver可以去A那里拿缓存,这是种缓存复制机制。

负载调度器是根据各个服务器的负载情况,动态地选择一台Real Server响应用户请求,那么动态选择是如何实现呢,其实也就是这里要说的负载调度算法,根据不同的网络服务需求和服务器配置,IPVS实现了如上的十种负载调度算法,下面详细讲述LVS最常用的四种调度算法:

  • 轮叫调度(Round Robin)
    "轮叫"调度也叫1:1调度,调度器通过"轮叫"调度算法将外部用户请求按顺序1:1的分配到集群中的每个Real Server上,这种算法平等地对待每一台Real Server,而不管服务器
    上实际的负载状况和连接状态。
  • 加权轮叫调度(Weighted Round Robin)
    "加权轮叫"调度算法是根据Real Server的不同处理能力来调度访问请求。可以对每台Real Server设置不同的调度权值,对于性能相对较好的Real Server可以设置较高的权值,而对于处理能力较弱的Real Server,可以设置较低的权值,这样保证了处理能力强的服务器处理更多的访问流量。充分合理的利用了服务器资源。同时,调度器还可以自动查询Real Server的负载情况,并动态地调整其权值。
  • 最少链接调度(Least Connections)
    "最少连接"调度算法动态地将网络请求调度到已建立的链接数最少的服务器上。如果集群系统的真实服务器具有相近的系统性能,采用"最小连接"调度算法可以较好地均衡负载。
  • 加权最少链接调度(Weighted Least Connections)
    "加权最少链接调度"是"最少连接调度"的超集,每个服务节点可以用相应的权值表示其处理能力,而系统管理员可以动态的设置相应的权值,缺省权值为1,加权最小连接调度在分配新连接请求时尽可能使服务节点的已建立连接数和其权值成正比。
    2021-06-15T10:12:02.png

LVS调度算法的生产环境选型:
1)一般的网络服务,如http,nginxmysql等常用的LVS调度算法为:
a. 基本轮询调度rr
b. 加权最小连接调度wlc
c. 加权轮询调度wrc

2)基于局部性的最小连接lblc和带复制的给予局部性最小连接lblcr主要适用于web cache和DB cache;

3)源地址散列调度SH和目标地址散列调度DH可以结合使用在防火墙集群中,可以保证整个系统的出入口唯一;

其实对于LVS的理解,主要部分还是在于3种工作方式和8种调度算法,实际这些算法的适用范围很多,工作中最好参考内核中的连接调度算法的实现原理,然后根据具体的业务需求合理的选型。

LVS的 Session持久机制
1)session绑定:始终将同一个请求者的连接定向至同一个rs(第一次请求时仍由调度方法选择);没有容错能力,有损均衡效果;
2)session复制:在rs之间同步session,因此,每个RS持集群中所有的session;对于大规模集群环境不适用;
3)session共享或服务器机制:利用单独部署的服务器来统一管理session;

LVS使用中特别需要注意事项:
1) 关于时间同步:各节点间的时间偏差不大于1s,建议使用统一的ntp服务器进行更新时间;
2) DR模型中的VIP的MAC广播问题:
在DR模型中,由于每个节点均要配置VIP,因此存在VIP的MAC广播问题,在现在的linux内核中,都提供了相应kernel 参数对MAC广播进行管理,具体如下:

  arp_ignore: 定义接收到ARP请求时的响应级别;
      0:只要本地配置的有相应地址,就给予响应;
      1:仅在请求的目标地址配置在到达的接口上的时候,才给予响应;DR模型使用

 arp_announce:定义将自己地址向外通告时的通告级别;
     0:将本地任何接口上的任何地址向外通告;
     1:试图仅向目标网络通告与其网络匹配的地址;
     2:仅向与本地接口上地址匹配的网络进行通告;DR模型使用

五. LVS安装和简单管理 (ipvsadm)

LVS全称为Linux Virtual Server,工作在ISO模型中的第四层,由于其工作在第四层,因此与iptables类似,必须工作在内核空间上。因此lvs与iptables一样,是直接工作在内核中的,叫ipvs,主流linux发行版默认都已经集成了ipvs,因此用户只需安装一个管理工具ipvsadm即可, ipvsadm是LVS在应用层的管理命令,可以通过这个命令去管理LVS的配置。

1) 安装LVS

先安装依赖
[root@localhost ~]# yum install -y libnl* popt*
 
查看是否加载lvs模块
[root@localhost ~]# modprobe -l |grep ipvs
kernel/net/netfilter/ipvs/ip_vs.ko
kernel/net/netfilter/ipvs/ip_vs_rr.ko
kernel/net/netfilter/ipvs/ip_vs_wrr.ko
kernel/net/netfilter/ipvs/ip_vs_lc.ko
kernel/net/netfilter/ipvs/ip_vs_wlc.ko
kernel/net/netfilter/ipvs/ip_vs_lblc.ko
kernel/net/netfilter/ipvs/ip_vs_lblcr.ko
kernel/net/netfilter/ipvs/ip_vs_dh.ko
kernel/net/netfilter/ipvs/ip_vs_sh.ko
kernel/net/netfilter/ipvs/ip_vs_sed.ko
kernel/net/netfilter/ipvs/ip_vs_nq.ko
kernel/net/netfilter/ipvs/ip_vs_ftp.ko
kernel/net/netfilter/ipvs/ip_vs_pe_sip.ko
 
下载并安装LVS
[root@localhost ~]# cd /usr/local/src/
[root@localhost src]# unlink /usr/src/linux
[root@localhost src]# ln -s /usr/src/kernels/2.6.32-431.5.1.el6.x86_64/ /usr/src/linux
[root@localhost src]# wget http://www.linuxvirtualserver.org/software/kernel-2.6/ipvsadm-1.26.tar.gz
[root@localhost src]# tar -zvxf ipvsadm-1.26.tar.gz
[root@localhost src]# cd ipvsadm-1.26
[root@localhost ipvsadm-1.26]# make && make install
 
LVS安装完成,查看LVS集群
[root@localhost ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn 

2) ipvsadm基本命令说明

1) ipvsadm的基本用法:
# ipvsadm COMMAND [protocol] service address
               [scheduling-method] [persistence options]
 
# ipvsadm command [protocol] service address
               server-address [packet-forwarding-method] [weight options]
 
第一条命令用于向LVS系统中添加一个用于负载均衡的virtual server(VS);
第二条命令用来修改已经存在的VS的配置,service address用来指定涉及的虚拟服务即虚拟地址,server-address指定涉及的真实地址。
 
 
2) ipvsadm的帮助信息
[root@localhost ~]# ipvsadm --help
ipvsadm v1.26 2008/5/15 (compiled with popt and IPVS v1.2.1)
Usage:
  ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] [--pe persistence_engine]
  ipvsadm -D -t|u|f service-address
  ipvsadm -C
  ipvsadm -R
  ipvsadm -S [-n]
  ipvsadm -a|e -t|u|f service-address -r server-address [options]
  ipvsadm -d -t|u|f service-address -r server-address
  ipvsadm -L|l [options]
  ipvsadm -Z [-t|u|f service-address]
  ipvsadm --set tcp tcpfin udp
  ipvsadm --start-daemon state [--mcast-interface interface] [--syncid sid]
  ipvsadm --stop-daemon state
  ipvsadm -h
 
Commands:
Either long or short options are allowed.
  --add-service     -A        add virtual service with options
  --edit-service    -E        edit virtual service with options
  --delete-service  -D        delete virtual service
  --clear           -C        clear the whole table
  --restore         -R        restore rules from stdin
  --save            -S        save rules to stdout
  --add-server      -a        add real server with options
  --edit-server     -e        edit real server with options
  --delete-server   -d        delete real server
  --list            -L|-l     list the table
  --zero            -Z        zero counters in a service or all services
  --set tcp tcpfin udp        set connection timeout values
  --start-daemon              start connection sync daemon
  --stop-daemon               stop connection sync daemon
  --help            -h        display this help message
 
Options:
  --tcp-service  -t service-address   service-address is host[:port]
  --udp-service  -u service-address   service-address is host[:port]
  --fwmark-service  -f fwmark         fwmark is an integer greater than zero
  --ipv6         -6                   fwmark entry uses IPv6
  --scheduler    -s scheduler         one of rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq,
                                      the default scheduler is wlc.
  --pe            engine              alternate persistence engine may be sip,
                                      not set by default.
  --persistent   -p [timeout]         persistent service
  --netmask      -M netmask           persistent granularity mask
  --real-server  -r server-address    server-address is host (and port)
  --gatewaying   -g                   gatewaying (direct routing) (default)
  --ipip         -i                   ipip encapsulation (tunneling)
  --masquerading -m                   masquerading (NAT)
  --weight       -w weight            capacity of real server
  --u-threshold  -x uthreshold        upper threshold of connections
  --l-threshold  -y lthreshold        lower threshold of connections
  --mcast-interface interface         multicast interface for connection sync
  --syncid sid                        syncid for connection sync (default=255)
  --connection   -c                   output of current IPVS connections
  --timeout                           output of timeout (tcp tcpfin udp)
  --daemon                            output of daemon information
  --stats                             output of statistics information
  --rate                              output of rate information
  --exact                             expand numbers (display exact values)
  --thresholds                        output of thresholds information
  --persistent-conn                   output of persistent connection info
  --nosort                            disable sorting output of service/server entries
  --sort                              does nothing, for backwards compatibility
  --ops          -o                   one-packet scheduling
  --numeric      -n                   numeric output of addresses and ports
 
 
命令:
-A, --add-service: 添加一个集群服务. 即为ipvs虚拟服务器添加一个虚拟服务,也就是添加一个需要被负载均衡的虚拟地址。虚拟地址需要是ip地址,端口号,协议的形式。
-E, --edit-service: 修改一个虚拟服务。
-D, --delete-service: 删除一个虚拟服务。即删除指定的集群服务;
-C, --clear: 清除所有虚拟服务。
-R, --restore: 从标准输入获取ipvsadm命令。一般结合下边的-S使用。
-S, --save: 从标准输出输出虚拟服务器的规则。可以将虚拟服务器的规则保存,在以后通过-R直接读入,以实现自动化配置。
-a, --add-server: 为虚拟服务添加一个real server(RS)
-e, --edit-server: 修改RS
-d, --delete-server: 删除
-L, -l, --list: 列出虚拟服务表中的所有虚拟服务。可以指定地址。添加-c显示连接表。
-Z, --zero: 将所有数据相关的记录清零。这些记录一般用于调度策略。
--set tcp tcpfin udp: 修改协议的超时时间。
--start-daemon state: 设置虚拟服务器的备服务器,用来实现主备服务器冗余。(注:该功能只支持ipv4)
--stop-daemon: 停止备服务器。
-h, --help: 帮助。
 
参数:
以下参数可以接在上边的命令后边。
-t, --tcp-service service-address: 指定虚拟服务为tcp服务。service-address要是host[:port]的形式。端口是0表示任意端口。如果需要将端口设置为0,还需要加上-p选项(持久连接)。
-u, --udp-service service-address: 使用udp服务,其他同上。
-f, --fwmark-service integer: 用firewall mark取代虚拟地址来指定要被负载均衡的数据包,可以通过这个命令实现把不同地址、端口的虚拟地址整合成一个虚拟服务,可以让虚拟服务器同时截获处理去往多个不同地址的数据包。fwmark可以通过iptables命令指定。如果用在ipv6需要加上-6。
-s, --scheduler scheduling-method: 指定调度算法,默认是wlc。调度算法可以指定以下8种:rr(轮询),wrr(权重),lc(最后连接),wlc(权重),lblc(本地最后连接),lblcr(带复制的本地最后连接),dh(目的地址哈希),sh(源地址哈希),sed(最小期望延迟),nq(永不排队)
-p, --persistent [timeout]: 设置持久连接,这个模式可以使来自客户的多个请求被送到同一个真实服务器,通常用于ftp或者ssl中。
-M, --netmask netmask: 指定客户地址的子网掩码。用于将同属一个子网的客户的请求转发到相同服务器。
-r, --real-server server-address: 为虚拟服务指定数据可以转发到的真实服务器的地址。可以添加端口号。如果没有指定端口号,则等效于使用虚拟地址的端口号。
[packet-forwarding-method]: 此选项指定某个真实服务器所使用的数据转发模式。需要对每个真实服务器分别指定模式。
-g, --gatewaying: 使用网关(即直接路由),此模式是默认模式。
-i, --ipip: 使用ipip隧道模式。
-m, --masquerading: 使用NAT模式。
-w, --weight weight: 设置权重。权重是0~65535的整数。如果将某个真实服务器的权重设置为0,那么它不会收到新的连接,但是已有连接还会继续维持(这点和直接把某个真实服务器删除时不同的)。
-x, --u-threshold uthreshold: 设置一个服务器可以维持的连接上限。0~65535。设置为0表示没有上限。
-y, --l-threshold lthreshold: 设置一个服务器的连接下限。当服务器的连接数低于此值的时候服务器才可以重新接收连接。如果此值未设置,则当服务器的连接数连续三次低于uthreshold时服务器才可以接收到新的连接。(PS:笔者以为此设定可能是为了防止服务器在能否接收连接这两个状态上频繁变换)
--mcast-interface interface: 指定使用备服务器时候的广播接口。
--syncid syncid:指定syncid, 同样用于主备服务器的同步。
 
以下选项用于list命令:
-c, --connection: 列出当前的IPVS连接。
--timeout: 列出超时
--daemon:
--stats: 状态信息
--rate: 传输速率
--thresholds: 列出阈值
--persistent-conn: 坚持连接
--sor: 把列表排序。
--nosort: 不排序
-n, --numeric: 不对ip地址进行dns查询
--exact: 单位
-6: 如果fwmark用的是ipv6地址需要指定此选项。    
     
 
=========其他注意事项=========
如果使用IPv6地址,需要在地址两端加上"[]"。例如:ipvsadm -A -t [2001:db8::80]:80 -s rr
可以通过设置以下虚拟文件的值来防御DoS攻击:
/proc/sys/net/ipv4/vs/drop_entry
/proc/sys/net/ipv4/vs/drop_packet
/proc/sys/net/ipv4/vs/secure_tcp

3) ipvsadm 举例说明

一. LVS集群服务管理类举例
1) 添加:-A
# ipvsadm -A -t|u|f service-address [-s scheduler]
  
举例1: 添加集群
[root@lvs ~]# ipvsadm -A -t 172.16.60.111:80 -s wlc
  
2) 修改:-E
# ipvsadm -E -t|u|f service-address [-s scheduler]
  
举例2: 修改集群 (修改集群的调度算法)
[root@lvs ~]# ipvsadm -E -t 172.16.60.111:80 -s wrr
  
3) 删除:-D
# ipvsadm -D -t|u|f service-address
  
举例3: 删除集群
[root@lvs ~]# ipvsadm -D -t 172.16.60.111:80
  
  
二. 管理LVS集群中的RealServer举例
1) 添加RS : -a
# ipvsadm -a -t|u|f service-address -r server-address [-g|i|m] [-w weight]
  
举例1: 往VIP资源为172.16.60.111的集群服务里添加两个realserver
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.120 –g -w 5
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.130 –g -w 10
  
2) 修改RS : -e
# ipvsadm -e -t|u|f service-address -r server-address [-g|i|m] [-w weight]
  
举例2: 修改172.16.60.111集群服务里172.16.60.120这个realserver的权重为3
[root@lvs ~]# ipvsadm -e -t 172.16.60.111:80 -r 172.16.60.120 –g -w 3
  
3) 删除RS : -d
# ipvsadm -d -t|u|f service-address -r server-address
  
举例3: 删除172.16.60.111集群服务里172.16.60.120这个realserver
[root@lvs ~]# ipvsadm -d -t 172.16.60.111:80 -r 172.16.60.120
  
  
三. 管理LVS集群服务的查看
# ipvsadm -L|l [options]
   options可以为:
   -n:数字格式显示
   --stats 统计信息
   --rate:统计速率
   --timeout:显示tcp、tcpinfo、udp的会话超时时长
   -c:连接客户端数量
  
举例1: 查看lvs集群转发情况
[root@lvs ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port         Forward Weight ActiveConn InActConn
TCP  172.16.60.111:80 wlc persistent 600
  -> 172.16.60.205:80             Route   1      0          0       
  -> 172.16.60.206:80             Route   1      0          0
  
举例2: 查看lvs集群的连接状态
[root@lvs ~]# ipvsadm -l --stats
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port               Conns   InPkts  OutPkts  InBytes OutBytes
  -> RemoteAddress:Port
TCP  172.16.60.111                     4           6         0            308        0
  -> 172.16.60.205:80                  0           0         0            0          0
  -> 172.16.60.206:80                  4           6         0            308        0
  
说明:
Conns    (connections scheduled)  已经转发过的连接数
InPkts   (incoming packets)       入包个数
OutPkts  (outgoing packets)       出包个数
InBytes  (incoming bytes)         入流量(字节)
OutBytes (outgoing bytes)         出流量(字节)
  
举例3: 查看lvs集群的速率
[root@lvs ~]# ipvsadm -l --rate
Prot LocalAddress:Port              CPS    InPPS   OutPPS    InBPS   OutBPS
  -> RemoteAddress:Port
TCP  172.16.60.111                    0         0          0            0        0
  -> 172.16.60.205:80                 0         0          0            0        0
  -> 172.16.60.206:80                 0         0          0            0        0
  
说明:
CPS      (current connection rate)   每秒连接数
InPPS    (current in packet rate)    每秒的入包个数
OutPPS   (current out packet rate)   每秒的出包个数
InBPS    (current in byte rate)      每秒入流量(字节)
OutBPS   (current out byte rate)      每秒入流量(字节)
  
4) 清除计数器:
# ipvsadm -Z [-t|u|f service-address]
  
5) 清除规则 (删除所有集群服务), 该命令与iptables的-F功能类似,执行后会清除所有规则:
# ipvsadm -C
  
6) 保存规则:
# ipvsadm -S > /path/to/somefile
# ipvsadm-save > /path/to/somefile
# ipvsadm-restore < /path/to/somefile
  
========================================================================================
一.  使用NAT模式
1) 添加vip地址: 172.16.60.111
[root@lvs ~]# /sbin/ifconfig eth0:0 172.16.60.111 broadcast 172.16.60.111 netmask 255.255.255.255 up
[root@lvs ~]# /sbin/route add -host 172.16.60.111 dev eth0:0
[root@lvs ~]# /sbin/arping -I eth0 -c 5 -s 172.16.60.111 172.16.60.1 >/dev/null 2>&1
 
2) 比如添加地址为172.16.60.111:80的lvs集群服务,指定调度算法为轮转。
[root@lvs ~]# ipvsadm -A -t 172.16.60.111:80 -s rr
  
1) 添加真实服务器,指定传输模式为NAT
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.180:80 -m
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.181:80 -m
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.182:80 -m
  
NAT模式是lvs的三种模式中最简单的一种。此种模式下只需要保证调度服务器与真实服务器互通就可以运行。
  
二. 使用DR模式
1) 对于DR模式首先要配置真实服务器:
[root@rs-01 ~]# vim /etc/init.d/realserver
#!/bin/sh
VIP=172.16.60.111
. /etc/rc.d/init.d/functions
         
case "$1" in
# 禁用本地的ARP请求、绑定本地回环地址
start)
    /sbin/ifconfig lo down
    /sbin/ifconfig lo up
    echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
    echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
    /sbin/sysctl -p >/dev/null 2>&1
    /sbin/ifconfig lo:0 $VIP netmask 255.255.255.255 up
    /sbin/route add -host $VIP dev lo:0
    echo "LVS-DR real server starts successfully.\n"
    ;;
stop)
    /sbin/ifconfig lo:0 down
    /sbin/route del $VIP >/dev/null 2>&1
    echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
    echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "LVS-DR real server stopped.\n"
    ;;
status)
    isLoOn=`/sbin/ifconfig lo:0 | grep "$VIP"`
    isRoOn=`/bin/netstat -rn | grep "$VIP"`
    if [ "$isLoON" == "" -a "$isRoOn" == "" ]; then
        echo "LVS-DR real server has run yet."
    else
        echo "LVS-DR real server is running."
    fi
    exit 3
    ;;
*)
    echo "Usage: $0 {start|stop|status}"
    exit 1
esac
exit 0
  
  
在真实服务器上执行上面的脚本
[root@rs-01 ~]# chmod 755 /etc/init.d/realserver
[root@rs-01 ~]# /etc/init.d/realserver  start
  
上面脚本执行后, 真实服务器上就在lo:0设备上配置了vip地址, 可以使用"ifconfig"命令查看
  
2) 在LVS机器上接着添加ipvs规则:
 
先添加vip地址: 172.16.60.111
[root@lvs ~]# /sbin/ifconfig eth0:0 172.16.60.111 broadcast 172.16.60.111 netmask 255.255.255.255 up
[root@lvs ~]# /sbin/route add -host 172.16.60.111 dev eth0:0
[root@lvs ~]# /sbin/arping -I eth0 -c 5 -s 172.16.60.111 172.16.60.1 >/dev/null 2>&1
 
添加地址为172.16.60.111:80的lvs集群服务,指定调度算法为轮转。
[root@lvs ~]# ipvsadm -A -t 172.16.60.111:80 -s rr
  
添加真实服务器,指定传输模式为DR
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.180:80 -g
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.181:80 -g
[root@lvs ~]# ipvsadm -a -t 172.16.60.111:80 -r 172.16.60.182:80 -g
  
注意:此处的例子中客户、调度服务器、真实服务器都是位于同一网段的

4) 小案例分析
172.168.60.208 作为LVS负载代理层, 代理后端两个web节点172.16.60.205和172.16.60.206的80端口.
VIP资源为172.16.60.119

1) 在172.16.60.208服务器上安装LVS (安装方式如上)
[root@lvs-208 ~]# yum install -y libnl* popt*
[root@lvs-208 ~]# cd /usr/local/src/
[root@lvs-208 src]# unlink /usr/src/linux
[root@lvs-208 src]# ln -s /usr/src/kernels/2.6.32-431.5.1.el6.x86_64/ /usr/src/linux
[root@lvs-208 src]# wget http://www.linuxvirtualserver.org/software/kernel-2.6/ipvsadm-1.26.tar.gz
[root@lvs-208 src]# tar -zvxf ipvsadm-1.26.tar.gz
[root@lvs-208 src]# cd ipvsadm-1.26
[root@lvs-208 ipvsadm-1.26]# make && make install
[root@lvs-208 ipvsadm-1.26]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
 
2) 在后端两个web节点(realserver)上配置vip (连个realserver节点操作一样)
[root@rs-205 ~]# vim /etc/init.d/realserver
#!/bin/sh
VIP=172.16.60.119
. /etc/rc.d/init.d/functions
        
case "$1" in
# 禁用本地的ARP请求、绑定本地回环地址
start)
    /sbin/ifconfig lo down
    /sbin/ifconfig lo up
    echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
    echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
    /sbin/sysctl -p >/dev/null 2>&1
    /sbin/ifconfig lo:0 $VIP netmask 255.255.255.255 up 
    /sbin/route add -host $VIP dev lo:0
    echo "LVS-DR real server starts successfully.\n"
    ;;
stop)
    /sbin/ifconfig lo:0 down
    /sbin/route del $VIP >/dev/null 2>&1
    echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
    echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
    echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "LVS-DR real server stopped.\n"
    ;;
status)
    isLoOn=`/sbin/ifconfig lo:0 | grep "$VIP"`
    isRoOn=`/bin/netstat -rn | grep "$VIP"`
    if [ "$isLoON" == "" -a "$isRoOn" == "" ]; then
        echo "LVS-DR real server has run yet."
    else
        echo "LVS-DR real server is running."
    fi
    exit 3
    ;;
*)
    echo "Usage: $0 {start|stop|status}"
    exit 1
esac
exit 0
 
 
执行脚本
[root@rs-205 ~]# chmod 755 /etc/init.d/realserver
[root@rs-205 ~]# /etc/init.d/realserver start
LVS-DR real server starts successfully.\n
 
[root@rs-205 ~]# ifconfig
......
lo:0      Link encap:Local Loopback 
          inet addr:172.16.60.119  Mask:255.255.255.255
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
 
 
后端两个web节点的80端口为nginx, nginx安装配置这里省略
[root@rs-205 ~]# ps -ef|grep nginx
root     24154     1  0 Dec25 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx    24155 24154  0 Dec25 ?        00:00:02 nginx: worker process                  
root     24556 23313  0 01:14 pts/1    00:00:00 grep nginx
[root@rs-205 ~]# lsof -i:80
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   24154  root    7u  IPv4  85119      0t0  TCP *:http (LISTEN)
nginx   24155 nginx    7u  IPv4  85119      0t0  TCP *:http (LISTEN)
 
 
3) 在172.16.60.208服务器上管理LVS
添加LVS集群服务, vip为172.16.60.119
接着添加后面两个realserver,指定传输模式为DR
 
[root@lvs-208~]# /sbin/iptables -F
[root@lvs-208~]# /sbin/iptables -Z
[root@lvs-208~]# /sbin/ipvsadm -C
    
[root@lvs-208~]# /sbin/ipvsadm --set 30 5 60
[root@lvs-208~]# /sbin/ifconfig eth0:0 172.16.60.119 broadcast 172.16.60.119 netmask 255.255.255.255 up
[root@lvs-208~]# /sbin/route add -host 172.16.60.119 dev eth0:0
 
[root@lvs-208~]# /sbin/ipvsadm -A -t 172.16.60.119:80 -s wlc -p 600
[root@lvs-208~]# /sbin/ipvsadm -a -t 172.16.60.119:80 -r 172.16.60.205:80 -g
[root@lvs-208~]# /sbin/ipvsadm -a -t 172.16.60.119:80 -r 172.16.60.206:80 -g
  
[root@lvs-208~]# touch /var/lock/subsys/ipvsadm >/dev/null 2>&1     
[root@lvs-208~]# /sbin/arping -I eth0 -c 5 -s 172.16.60.119 172.16.60.1 >/dev/null 2>&1
 
查看vip
[root@lvs-208~]# ifconfig
......
 
eth0:0    Link encap:Ethernet  HWaddr 00:50:56:AC:5B:56 
          inet addr:172.16.60.119  Bcast:172.16.60.119  Mask:255.255.255.255
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
 
查看lvs集群转发情况
[root@lvs-208~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.60.119:80 wlc persistent 600
  -> 172.16.60.205:80             Route   1      0          0        
  -> 172.16.60.206:80             Route   1      0          10  
 
访问http://172.16.60.219/, 就可以负载到两个realserver的80端口了
 
由于配置了持久化, 则600秒内的客户端请求将会转发到同一个realserver节点上.
如果当前请求转发到172.16.60.206节点上, 则关闭该节点的80端口, 则访问http://172.16.60.219/就失败了!
因为手动将该节点从lvs集群中踢出去,如下:
 
[root@lvs-208~]# ipvsadm -d -t 172.16.60.119:80 -r 172.16.60.206
 
[root@lvs-208~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.60.119:80 wlc persistent 600
  -> 172.16.60.205:80             Route   1      0          0   
 
然后访问http://172.16.60.219/ 显示的就是172.16.60.205节点的80端口页面!
 
以上的LVS没有实现后端realserver节点健康检查机制, 如果要想对后端realserver节点进行健康检查,
则需要结合ldirectord软件,  ldirectord配置里有参数可以实现:
realserver节点故障发生时自动踢出lvs集群;
realserver节点故障恢复后重新加入lvs集群;
 
======================================================
ldirectord部分的安装和配置可以参考:  https://www.cnblogs.com/kevingrace/p/10170920.html
 
[root@lvs-208src]# pwd
/usr/local/src
[root@lvs-208src]# ll ldirectord-3.9.5-3.1.x86_64.rpm
-rw-rw-r-- 1 root root 90140 Dec 24 15:54 ldirectord-3.9.5-3.1.x86_64.rpm
 
[root@lvs-208src]# yum install -y ldirectord-3.9.5-3.1.x86_64.rpm
 
[root@lvs-208src]# cat /etc/init.d/ldirectord |grep "config file"
#              Using the config file /etc/ha.d/ldirectord.cf
#       It uses the config file /etc/ha.d/ldirectord.cf.
 
如上查找可知, ldirectord的配置文件为/etc/ha.d/ldirectord.cf
 
[root@lvs-208src]# cd /usr/share/doc/ldirectord-3.9.5
[root@lvs-208ldirectord-3.9.5]# ll ldirectord.cf
-rw-r--r-- 1 root root 8301 Feb  7  2013 ldirectord.cf
[root@lvs-208ldirectord-3.9.5]# cp ldirectord.cf /etc/ha.d/
[root@lvs-208ldirectord-3.9.5]# cd /etc/ha.d/
[root@lvs-208ha.d]# ll
total 20
-rw-r--r-- 1 root root 8301 Dec 26 01:44 ldirectord.cf
drwxr-xr-x 2 root root 4096 Dec 26 01:40 resource.d
-rw-r--r-- 1 root root 2082 Mar 24  2017 shellfuncs
 
配置ldirectord.cf, 实现realserver节点的健康检查机制 (根据文件中的配置范例进行修改)
[root@lvs-208ha.d]# cp ldirectord.cf ldirectord.cf.bak
[root@lvs-208ha.d]# vim ldirectord.cf
checktimeout=3
checkinterval=1
autoreload=yes
logfile="/var/log/ldirectord.log"
quiescent=no                                 #这个参数配置就实现了realserver的监控检查机制
 
virtual=172.16.60.119:80
        real=172.16.60.205:80 gate
        real=172.16.60.206:80 gate
        fallback=127.0.0.1:80 gate     #realserver都故障时, 转发请求到lvs本机的80端口
        service=http
        scheduler=rr
        persistent=600
        #netmask=255.255.255.255
        protocol=tcp
        checktype=negotiate
        checkport=80
        #request="index.html"
        #receive="Test Page"
        #virtualhost=www.x.y.z
 
 
重启ldirectord服务
[root@lvs-208ha.d]# /etc/init.d/ldirectord start
 
[root@lvs-208ha.d]# ps -ef|grep ldirectord
root      4399     1  0 01:48 ?        00:00:00 /usr/bin/perl -w /usr/sbin/ldirectord start
root      4428  3750  0 01:50 pts/0    00:00:00 grep ldirectord
 
这样, 后端的realserver就通过ldirectord配置实现了健康检查!
 
[root@lvs-208ha.d]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.60.119:80 rr persistent 600
  -> 172.16.60.205:80             Route   1      0          0        
  -> 172.16.60.206:80             Route   1      0          0 
 
当172.16.60.205 和 172.16.60.206 中的任意一个节点故障时, 该节点就会自动从lvs集群中踢出来, 此时请求都转发至另一个节点上;
该故障节点恢复后, 该节点就会自动重新加入到lvs集群中; 整个过程对于前面的客户端访问来说是无感知.
 
如172.16.60.205节点的80端口挂了, 则lvs转发情况:
[root@lvs-208ha.d]# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.60.119:80 rr persistent 600
  -> 172.16.60.206:80             Route   1      0          0
 
当172.16.60.205节点的80端口恢复后, 则lvs转发情况
[root@lvs-208ha.d]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.60.119:80 rr persistent 600
  -> 172.16.60.205:80             Route   1      0          0        
  -> 172.16.60.206:80             Route   1      0          0 
 
这就实现了realserver 层面的高可用了!!!
 
但是此时lvs层是单点, 如果还想实现lvs层的高可用, 就要利用keepalived 或 heartbeat了!

转自:https://www.cnblogs.com/kevingrace/p/6137975.html

]]>
<![CDATA[centos8使用grubby修改内核启动参数]]> https://www.msl.la/archives/281/ 2021-05-15T11:01:09+08:00 2021-05-15T11:01:09+08:00 墨少离 https://www.msl.la/ grubby是一个用于更新和显示有关各种体系结构特定的引导程序的配置文件信息的命令行工具。 它主要设计用于安装新内核并需要查找有关当前引导环境的信息的脚本,同时也可以对启动内核的各项信息参数进行修改。

本文主要介绍如何在centos8中使用grubby工具来对系统的内核启动参数和启动顺序进行调整。

使用yum或者dnf可以直接安装grubby工具。

[root@server ~]# yum install grubby
Last metadata expiration check: 1:29:38 ago on Wed 18 Nov 2020 09:44:26 AM +08.
Package grubby-8.40-38.el8.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!

查看当前的默认启动内核:

[root@server ~]# grubby --default-kernel
/boot/vmlinuz-4.18.0-193.28.1.el8_2.x86_64

查看系统安装的全部内核:

[root@server ~]# grubby --info=ALL
index=0
kernel="/boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 rhgb quiet intel_iommu=on $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-5.9.1-1.el8.elrepo.x86_64.img $tuned_initrd"
title="Red Hat Enterprise Linux (5.9.1-1.el8.elrepo.x86_64) 8.2 (Ootpa)"
id="12ab47b22fef4c02bcdc88b340d5f706-5.9.1-1.el8.elrepo.x86_64"
index=1
kernel="/boot/vmlinuz-4.18.0-193.28.1.el8_2.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 rhgb quiet intel_iommu=on $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-4.18.0-193.28.1.el8_2.x86_64.img $tuned_initrd"
title="CentOS Linux (4.18.0-193.28.1.el8_2.x86_64) 8 (Core)"
id="12ab47b22fef4c02bcdc88b340d5f706-4.18.0-193.28.1.el8_2.x86_64"
index=2
kernel="/boot/vmlinuz-4.18.0-193.19.1.el8_2.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 rhgb quiet intel_iommu=on $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-4.18.0-193.19.1.el8_2.x86_64.img $tuned_initrd"
title="CentOS Linux (4.18.0-193.19.1.el8_2.x86_64) 8 (Core)"
id="12ab47b22fef4c02bcdc88b340d5f706-4.18.0-193.19.1.el8_2.x86_64"
index=3
kernel="/boot/vmlinuz-4.18.0-193.el8.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 rhgb quiet intel_iommu=on $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-4.18.0-193.el8.x86_64.img $tuned_initrd"
title="CentOS Linux (4.18.0-193.el8.x86_64) 8 (Core)"
id="12ab47b22fef4c02bcdc88b340d5f706-4.18.0-193.el8.x86_64"
index=4
kernel="/boot/vmlinuz-0-rescue-12ab47b22fef4c02bcdc88b340d5f706"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 rhgb quiet intel_iommu=on"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-0-rescue-12ab47b22fef4c02bcdc88b340d5f706.img"
title="CentOS Linux (0-rescue-12ab47b22fef4c02bcdc88b340d5f706) 8 (Core)"
id="12ab47b22fef4c02bcdc88b340d5f706-0-rescue"

设置新的默认启动内核:

# 使用路径来指定内核,可以使用--set-default=kernel-path
[root@server ~]# grubby --set-default=/boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64
The default is /boot/loader/entries/12ab47b22fef4c02bcdc88b340d5f706-5.9.1-1.el8.elrepo.x86_64.conf with index 0 and kernel /boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64
[root@tiny-server ~]# grubby --default-kernel
/boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64

# 使用index来指定内核,则使用--set-default-index=entry-index
[root@server ~]# grubby --set-default-index=1
The default is /boot/loader/entries/12ab47b22fef4c02bcdc88b340d5f706-4.18.0-193.28.1.el8_2.x86_64.conf with index 1 and kernel /boot/vmlinuz-4.18.0-193.28.1.el8_2.x86_64
[root@tiny-server ~]# grubby --default-kernel
/boot/vmlinuz-4.18.0-193.28.1.el8_2.x86_64

添加/删除内核启动参数:

# 对所有的内核都删除某个参数  
[root@server ~]# grubby --update-kernel=ALL --remove-args=intel_iommu=on

# 对所有的内核都添加某个参数  
[root@server ~]# grubby --update-kernel=ALL --args=intel_iommu=on

# 对某个的内核添加启动参数  
[root@server ~]# grubby --update-kernel=/boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64 --args=intel_iommu=on

查看特定内核的具体信息:

[root@server ~]# grubby --info=/boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64
index=0
kernel="/boot/vmlinuz-5.9.1-1.el8.elrepo.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 rhgb quiet intel_iommu=on $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-5.9.1-1.el8.elrepo.x86_64.img $tuned_initrd"
title="Red Hat Enterprise Linux (5.9.1-1.el8.elrepo.x86_64) 8.2 (Ootpa)"
id="12ab47b22fef4c02bcdc88b340d5f706-5.9.1-1.el8.elrepo.x86_64"
]]>
<![CDATA[Git 设置代理和取消代理]]> https://www.msl.la/archives/268/ 2021-04-16T09:26:07+08:00 2021-04-16T09:26:07+08:00 墨少离 https://www.msl.la/ 如果代理类型是 socks5 进行如下设置即可:

git config --global http.proxy socks5://127.0.0.1:1080
git config --global https.proxy socks5://127.0.0.1:1080

如果是普通的 http/https 进行如下设置即可:

git config --global https.proxy http://127.0.0.1:1080
git config --global https.proxy https://127.0.0.1:1080

取消代理设置:

git config --global --unset http.proxy
git config --global --unset https.proxy
]]>
<![CDATA[Wireguard 全互联模式(full mesh)配置指南]]> https://www.msl.la/archives/267/ 2021-04-12T14:51:00+08:00 2021-04-12T14:51:00+08:00 墨少离 https://www.msl.la/ 上篇文章给大家介绍了如何使用 wg-gen-web 来方便快捷地管理 WireGuard 的配置和秘钥,文末埋了两个坑:一个是 WireGuard 的全互联模式(full mesh),另一个是使用 WireGuard 作为 Kubernetes 的 CNI 插件。今天就来填第一个坑。

首先解释一下什么是全互联模式(full mesh),全互联模式其实就是一种网络连接形式,即所有结点之间都直接连接,不会通过第三方节点中转流量。和前面提到的点对多点架构 其实是一个意思。

1. 全互联模式架构与配置

在 WireGuard 的世界里没有 Server 和 Client 之分,所有的节点都是 Peer。大家使用 WireGuard 的常规做法是找一个节点作为中转节点,也就是 VPN 网关,然后所有的节点都和这个网关进行连接,所有节点之间都通过这个网关来进行通信。这种架构中,为了方便理解,我们可以把网关看成 Server,其他的节点看成 Client,但实际上是不区分 Server 和 Client 的。

举个例子,假设有 4 个节点,分别是 A/B/C/D,且这 4 个节点都不在同一个局域网,常规的做法是选取一个节点作为 VPN 网关,架构如图:

这种架构的缺点我在之前的文章里也介绍过了,缺点相当明显:

  • 当 Peer 越来越多时,VPN 网关就会变成垂直扩展的瓶颈。
  • 通过 VPN 网关转发流量的成本很高,毕竟云服务器的流量很贵。
  • 通过 VPN 网关转发流量会带来很高的延迟。

那么全互联模式是什么样的架构呢?还是假设有 A/B/C/D 四个节点,每个节点都和其他节点建立 WireGuard 隧道,架构如图:

这种架构带来的直接优势就是快!任意一个 Peer 和其他所有 Peer 都是直连,无需中转流量。那么在 WireGuard 的场景下如何实现全互联模式呢?其实这个问题不难,难点在于配置的繁琐程度,本文的主要目标就是精简 WireGuard 全互联模式的配置流程。为了让大家更容易理解,咱们还是先通过架构图来体现各个 Peer 的配置:

配置一目了然,每个 Peer 和其他所有 Peer 都是直连,根本没有 VPN 网关这种角色。当然,现实世界的状况没有图中这么简单,有些 Peer 是没有公网 IP 的,躲在 NAT 后面,这里又分两种况:

  1. NAT 受自己控制。这种情况可以在公网出口设置端口转发,其他 Peer 就可以通过这个公网 IP 和端口连接当前 Peer。如果公网 IP 是动态的,可以通过 DDNS 来解决,但 DDNS 会出现一些小问题,解决方法可以参考 WireGuard 的优化
  2. NAT 不受自己控制。这种情况无法在公网出口设置端口转发,只能通过 UDP 打洞来实现互联.

接着上述方案再更进一步,打通所有 Peer 的私有网段,让任意一个 Peer 可以访问其他所有 Peer 的私有网段的机器。上述配置只是初步完成了全互联,让每个 Peer 可以相互访问彼此而已,要想相互访问私有网段,还得继续增加配置,还是直接看图:

红色字体部分就是新增的配置,表示允许访问相应 Peer 的私有网段,就是这么简单。详细的配置步骤请看下一节。

2. 全互联模式最佳实践

对如何配置有了清晰的思路之后,接下来就可以进入实践环节了。我不打算从 WireGuard 安装开始讲起,而是以前几篇文章为基础添砖加瓦。所以我建议读者先按顺序看下这两篇文章:

咱们直接从配置开始说起。手撸配置的做法是不明智的,因为当节点增多之后工作量会很大,我还是建议通过图形化界面来管理配置,首选 wg-gen-web

现在还是假设有上节所述的 4 个 Peer,我们需要从中挑选一个 Peer 来安装 wg-gen-web,然后通过 wg-gen-web 来生成配置。挑选哪个 Peer 无所谓,这个没有特殊限制,这里假设挑选 AWS 来安装 wg-gen-web

安装的步骤直接略过,不是本文的重点,不清楚的可以阅读我之前的文章 WireGuard 配置教程:使用 wg-gen-web 来管理 WireGuard 的配置。Server 配置如图:

生成 Azure 的配置:

SUBMIT 之后再点击 EDIT,添加私有网段:

查看 wg0.conf 的内容:

$ cat /etc/wireguard/wg0.conf

# Updated: 2021-02-24 07:34:23.805535396 +0000 UTC / Created: 2021-02-24 07:24:02.208816462 +0000 UTC
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = eEnHKGkGksx0jqrEDogjRj5l417BrEA39lr7WW9L9U0=

PreUp = echo WireGuard PreUp
PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PreDown = echo WireGuard PreDown
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Azure /  / Updated: 2021-02-24 07:43:52.717385042 +0000 UTC / Created: 2021-02-24 07:43:52.717385042 +0000 UTC
[Peer]
PublicKey = OzdH42suuOpVY5wxPrxM+rEAyEPFg2eL0ZI29N7eSTY=
PresharedKey = 1SyJuVp16Puh8Spyl81EgD9PJZGoTLJ2mOccs2UWDvs=
AllowedIPs = 10.0.0.2/32, 192.168.20.0/24

下载 Azure 配置文件:

可以看到配置文件内容为:

$ cat Azure.conf

[Interface]
Address = 10.0.0.2/32, 192.168.20.0/24
PrivateKey = IFhAyIWY7sZmabsqDDESj9fqoniE/uZFNIvAfYHjN2o=


[Peer]
PublicKey = JgvmQFmhUtUoS3xFMFwEgP3L1Wnd8hJc3laJ90Gwzko=
PresharedKey = 1SyJuVp16Puh8Spyl81EgD9PJZGoTLJ2mOccs2UWDvs=
AllowedIPs = 10.0.0.1/32, 192.168.10.0/24
Endpoint = aws.com:51820

先不急着修改,一鼓作气生成所有 Peer 的配置文件:

这时发现 wg0.conf 中已经包含了所有 Peer 的配置:

$ cat /etc/wireguard/wg0.conf

# Updated: 2021-02-24 07:57:00.745287945 +0000 UTC / Created: 2021-02-24 07:24:02.208816462 +0000 UTC
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = eEnHKGkGksx0jqrEDogjRj5l417BrEA39lr7WW9L9U0=

PreUp = echo WireGuard PreUp
PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PreDown = echo WireGuard PreDown
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Aliyun /  / Updated: 2021-02-24 07:57:45.941019829 +0000 UTC / Created: 2021-02-24 07:57:45.941019829 +0000 UTC
[Peer]
PublicKey = kVq2ATMTckCKEJFF4TM3QYibxzlh+b9CV4GZ4meQYAo=
PresharedKey = v818B5etpRlyVYHGUrv9abM5AIQK5xeoCizdWj1AqcE=
AllowedIPs = 10.0.0.4/32, 192.168.40.0/24

# GCP /  / Updated: 2021-02-24 07:57:27.3555646 +0000 UTC / Created: 2021-02-24 07:57:27.3555646 +0000 UTC
[Peer]
PublicKey = qn0Xfyzs6bLKgKcfXwcSt91DUxSbtATDIfe4xwsnsGg=
PresharedKey = T5UsVvOEYwfMJQDJudC2ryKeCpnO3RV8GFMoi76ayyI=
AllowedIPs = 10.0.0.3/32, 192.168.30.0/24

# Azure /  / Updated: 2021-02-24 07:57:00.751653134 +0000 UTC / Created: 2021-02-24 07:43:52.717385042 +0000 UTC
[Peer]
PublicKey = OzdH42suuOpVY5wxPrxM+rEAyEPFg2eL0ZI29N7eSTY=
PresharedKey = 1SyJuVp16Puh8Spyl81EgD9PJZGoTLJ2mOccs2UWDvs=
AllowedIPs = 10.0.0.2/32, 192.168.20.0/24

现在问题就好办了,我们只需将 wg0.conf 中的 Aliyun 和 GCP 部分的配置拷贝到 Azure 的配置中,并删除 PresharedKey 的配置,再添加 Endpoint 的配置和 PostUP/PostDown 规则,最后别忘了删除 Address 中的私有网段

$ cat Azure.conf

[Interface]
Address = 10.0.0.2/32
PrivateKey = IFhAyIWY7sZmabsqDDESj9fqoniE/uZFNIvAfYHjN2o=

PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE


[Peer]
PublicKey = JgvmQFmhUtUoS3xFMFwEgP3L1Wnd8hJc3laJ90Gwzko=
PresharedKey = 1SyJuVp16Puh8Spyl81EgD9PJZGoTLJ2mOccs2UWDvs=
AllowedIPs = 10.0.0.1/32, 192.168.10.0/24
Endpoint = aws.com:51820

# Aliyun /  / Updated: 2021-02-24 07:57:45.941019829 +0000 UTC / Created: 2021-02-24 07:57:45.941019829 +0000 UTC
[Peer]
PublicKey = kVq2ATMTckCKEJFF4TM3QYibxzlh+b9CV4GZ4meQYAo=
AllowedIPs = 10.0.0.4/32, 192.168.40.0/24
Endpoint = aliyun.com:51820

# GCP /  / Updated: 2021-02-24 07:57:27.3555646 +0000 UTC / Created: 2021-02-24 07:57:27.3555646 +0000 UTC
[Peer]
PublicKey = qn0Xfyzs6bLKgKcfXwcSt91DUxSbtATDIfe4xwsnsGg=
AllowedIPs = 10.0.0.3/32, 192.168.30.0/24
Endpoint = gcp.com:51820

同理,GCP 的配置如下:

$ cat GCP.conf

[Interface]
Address = 10.0.0.3/32
PrivateKey = oK2gIMBAob67Amj2gT+wR9pzkbqWGNtq794nOoD3i2o=

PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE


[Peer]
PublicKey = JgvmQFmhUtUoS3xFMFwEgP3L1Wnd8hJc3laJ90Gwzko=
PresharedKey = T5UsVvOEYwfMJQDJudC2ryKeCpnO3RV8GFMoi76ayyI=
AllowedIPs = 10.0.0.1/32, 192.168.10.0/24
Endpoint = aws.com:51820

# Aliyun /  / Updated: 2021-02-24 07:57:45.941019829 +0000 UTC / Created: 2021-02-24 07:57:45.941019829 +0000 UTC
[Peer]
PublicKey = kVq2ATMTckCKEJFF4TM3QYibxzlh+b9CV4GZ4meQYAo=
AllowedIPs = 10.0.0.4/32, 192.168.40.0/24
Endpoint = aliyun.com:51820

# Azure /  / Updated: 2021-02-24 07:57:00.751653134 +0000 UTC / Created: 2021-02-24 07:43:52.717385042 +0000 UTC
[Peer]
PublicKey = OzdH42suuOpVY5wxPrxM+rEAyEPFg2eL0ZI29N7eSTY=
AllowedIPs = 10.0.0.2/32, 192.168.20.0/24
Endpoint = azure.com:51820

Aliyun 的配置如下:

$ cat Aliyun.conf

[Interface]
Address = 10.0.0.4/32
PrivateKey = +A1ZESJjmHuskB4yKqTcqC3CB24TwBKHGSffWDHxI28=

PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE


[Peer]
PublicKey = JgvmQFmhUtUoS3xFMFwEgP3L1Wnd8hJc3laJ90Gwzko=
PresharedKey = v818B5etpRlyVYHGUrv9abM5AIQK5xeoCizdWj1AqcE=
AllowedIPs = 10.0.0.1/32, 192.168.10.0/24
Endpoint = aws.com:51820

# GCP /  / Updated: 2021-02-24 07:57:27.3555646 +0000 UTC / Created: 2021-02-24 07:57:27.3555646 +0000 UTC
[Peer]
PublicKey = qn0Xfyzs6bLKgKcfXwcSt91DUxSbtATDIfe4xwsnsGg=
AllowedIPs = 10.0.0.3/32, 192.168.30.0/24
Endpoint = gcp.com:51820

# Azure /  / Updated: 2021-02-24 07:57:00.751653134 +0000 UTC / Created: 2021-02-24 07:43:52.717385042 +0000 UTC
[Peer]
PublicKey = OzdH42suuOpVY5wxPrxM+rEAyEPFg2eL0ZI29N7eSTY=
AllowedIPs = 10.0.0.2/32, 192.168.20.0/24
Endpoint = azure.com:51820

最后在各自的节点上通过各自的配置文件把 WireGuard 跑起来,就搞定了。

整个图形化界面配置过程中不需要手动调整配置,功能还是比较完善的,只有客户端的配置需要手动调整。如果你无法接受手动调整配置,可以尝试另外一个项目:wg-meshconf,这个项目专门用来生成 mesh 的配置,但没有图形化管理界面。各有利弊吧,大家自行选择。

3. 总结

知道,很多人可能还是一头雾水,这玩意儿的应用场景有哪些?我随便举个简单的例子,假设你在云服务器上部署了 Kubernetes 集群,可以用本地的机器和云服务器的某台节点组建 WireGuard 隧道,然后在本地的 AllowedIPs 中加上 Pod 网段和 Service 网段,就可以那啥了,你懂吧?

好吧,又埋了一个坑,关于如何在家中直接访问云服务器 k8s 集群的 Pod IP 和 Service IP,后面会有专门的文章给大家讲解,虽然我也不确定是多久以后。。

]]>