Timbo Site

write something


AWS Classic Load Balancer配置Proxy Protocol

阿里云SLB不支持在TCP上挂SSL证书,拿HAProxy作为服务前置挂载证书会吃掉2个TCP连接数,单机只能撑近32K(65K的一半)的外部连接数。可以通过一些方法(比如增加Virtual Network Interface)突破这个限制,但服务因需挂SSL证书而额外绑定HAProxy,增加了一层中间件,架构图画出来后总觉得不好看。周一跑去服务器上动手脚,挂好SSL证书,可以拿掉HAProxy,连接数也突破了这个限制。

检查连接的时候发现服务能认TCP Source IP,阿里云SLB默认就开了Proxy Protocol,挺厉害的,不需要Client那边额外设置就能拿到Source IP。于是去AWS控制台找了一圈ELB的选项,没有开启Proxy Protocol的设置,倒是文档上写了可以通过awscli开启,折腾了一下把这个配置打开了,这里记录一下。

Proxy Protocol是什么

Proxy Protocol是HAProxy的作者Willy Tarreau在2010年实现的一个Internet协议,在三次握手之后,由Proxy往TCP上插一个很小的数据包来传递客户端信息,如源IP、目标IP等,在负载均衡和后端服务通信或更复杂的网络环境时非常有用,有点类似HTTP请求上的X-Forwarded-For头。

Proxy Protocol有两个版本,V1和V2。

Human-readable header format (version 1)

V1很简单,1行US-ASCII文本表示,严格遵循下面格式:

PROXY INET_PROTOCOL CLIENT_IP PROXY_IP CLIENT_PORT PROXY_PORT\r\n

假如无法识别PROXY,格式则为:

PROXY UNKNOWN\r\n

IPv4示例:

PROXY TCP4 198.51.100.22 203.0.113.7 35646 80\r\n

IPv6示例:

PROXY TCP6 2001:DB8::21f:5bff:febf:ce22:8a2e 2001:DB8::12f:8baa:eafc:ce29:6b2e 35646 80\r\n

Binary header format (version 2)

V1格式便于人类可读,但解析V1比较麻烦,长度不定,需要找到尾部的CRLF(即\r\n)才停止,遇到UNKNOWN处理起来也比较麻烦,V2使用了新的二进制格式,为了处理效率定制了一套新协议,该协议为:

0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A <13th byte> <14th byte> <15-16th byte> <17th byte onwards>
  • 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A ——协议签名
  • 13th byte——协议版本(v2)与指令(LOCAL or PROXY)
  • 14th byte——地址族(0 - AF_UNSPEC, 1 - AF_INET, 2 - AF_INET6, 3 - AF_UNIX)和协议族(0 - UNSPEC, 1 - STREAM, 2 - DGRAM)
  • 15-16th byte——网络顺序的地址长度
  • 17th byte onwards——网络顺序的地址信息:源IP,目标IP,源Port,目标Port

AWS ELB开启Proxy Protocol

AWS ELB使用的是V1。

前置

AWS ELB开启Proxy Protocol的选项并没有配置在控制台里,只能通过awscli启用,所以需要安装awscli并且通过aws configure配置好access key、access secret和region,切区时需要用aws configure来切。

此外还需要检查当前区域ELB是否支持Proxy Protocol Policy,通过aws elb describe-load-balancers命令,在输出中找到这段则表明可以开启:

{
    "PolicyTypeDescriptions": [
        ...
        {
            "PolicyAttributeTypeDescriptions": [
                {
                    "Cardinality": "ONE",
                    "AttributeName": "ProxyProtocol",
                    "AttributeType": "Boolean"
                }
            ],
            "PolicyTypeName": "ProxyProtocolPolicyType",
            "Description": "Policy that controls whether to include the IP address and port of the originating 
request for TCP messages. This policy operates on TCP/SSL listeners only"
        },
        ...
    ]
}

创建Policy

满足前置条件的话,键入命令创建ELB Policy:

aws elb create-load-balancer-policy --load-balancer-name my-loadbalancer --policy-name my-ProxyProtocol-policy --policy-type-name ProxyProtocolPolicyType --policy-attributes AttributeName=ProxyProtocol,AttributeValue=true

需要注意的是,里面my-loadbalancermy-ProxyProtocol-policy应修改为能更好分辨与记住的名字。

应用Policy

若之前ELB后端服务器已经应用Policy,需要找到之前的Policy,将Proxy Protocol追加到旧有的Policy里;若没有,则需要新增Policy。两个命令是不一样的。

通过命令aws elb describe-load-balancers --load-balancer-name my-loadbalancer 找到BackendServerDescriptions,如显示如下:

{
    "LoadBalancerDescriptions": [
    		...
        {
            "LoadBalancerName": "mqtt-slb",
            ...
            "BackendServerDescriptions": [],
            ...
        }
        ...
}

表明没有任何Policy应用在该ELB的后端服务器上,此时命令为:

aws elb set-load-balancer-policies-for-backend-server --load-balancer-name my-loadbalancer --instance-port 80 --policy-names my-ProxyProtocol-policy

其中80为后端服务器使用的端口。

如显示如下:

{
    "LoadBalancerDescriptions": [
    		...
        {
            "LoadBalancerName": "mqtt-slb",
            ...
            "BackendServerDescriptions": [
                {
                    "InstancePort": 80,
                    "PolicyNames": [
                        "my-existing-policy"
                    ]
                }
            ],
            ...
        }
        ...
}

则表明已经有名为my-existing-policy在发挥作用,此时命令为:

aws elb set-load-balancer-policies-for-backend-server --load-balancer-name my-loadbalancer --instance-port 80 --policy-names my-ProxyProtocol-policy my-existing-policy

如果有多项Policy,那么请在--policy-names后面依次追加。

移除Policy

当然有可能往后不需要这个Policy,那么可以通过如下命令重新设置Policy:

aws elb set-load-balancer-policies-for-backend-server --load-balancer-name my-loadbalancer --instance-port 80 --policy-names my-existing-policy

该命令仅保留my-existing-policy

如需完全移除所有Policy,那么命令为:

aws elb set-load-balancer-policies-for-backend-server --load-balancer-name my-loadbalancer --instance-port 80 --policy-names "[]"

此时如果通过命令aws elb describe-load-balancers --load-balancer-name my-loadbalancer 检查状态,则BackendServerDescriptions应该显示为空数组:

{
    "LoadBalancerDescriptions": [
    		...
        {
            "LoadBalancerName": "mqtt-slb",
            ...
            "BackendServerDescriptions": [],
            ...
        }
        ...
}

结语

之前需要在HAProxy里面开启proxy-pass的配置,现在通过awscli可以帮忙打开,只是同样都属于Load Balancer的设置,只有这个选项不可以通过AWS Management Console改变,不知道是为啥。