+86 135 410 16684Mon. - Fri. 10:00-22:00

AWS TCP Load Balancer 的一个坑

AWS TCP Load Balancer 的一个坑

AWS TCP Load Balancer 的一个坑

今天在迁移服务的时候发现了aws TCP的Load Balancer 的一个大坑, TCP 负载均衡其实是把客户端的请求截断,然后自己发送一个请求给后端,拿到后端返回的数据之后再返回给 客户端,这样后端看到的是 负载均衡器的IP,看不到客户端的真实IP了 (如果用基于HTTP的Load Balancer,会自动在HTTP头记录 X-Forwarded-For , 后端自然很容易获取到源IP )。其实这和LVS FULLNAT 模式有点像,LVS FULLNAT的解决办法是把真实IP写在TCP option里面,然后后端用toa模块拿到。

8612DFCC-601A-4F8D-92C0-CC863DDCF40E

 

对于这个问题,AWS给了一个解决办法,叫做  Proxy Protocol ,可以对TCP 负载均衡器开启Proxy Protocol,它会在请求的第一行写入 源IP、源端口等信息,以 \r\n 结尾,格式如下:

PROXY_STRING + single space + INET_PROTOCOL + single space + CLIENT_IP + single space + PROXY_IP + single space + CLIENT_PORT + single space + PROXY_PORT + “\r\n”

 

先说说如何开启 Proxy Protocol 。

第一步,为集群 ProxyProtocolTest  创建一个Proxy Protocol,叫 EnableProxyProtocol
# aws elb create-load-balancer-policy –load-balancer-name ProxyProtocolTest –policy-name EnableProxyProtocol  –policy-type-name ProxyProtocolPolicyType –policy-attributes AttributeName=ProxyProtocol,AttributeValue=True

第二步,为 ProxyProtocolTest  激活 EnableProxyProtocol
# aws elb set-load-balancer-policies-for-backend-server –load-balancer-name ProxyProtocolTest –instance-port 80 –policy-names EnableProxyProtocol

然后确认开启
# aws elb describe-load-balancers –load-balancer-name ProxyProtocolTest

可以看到类似下面的字段:

“BackendServerDescriptions”: [
{
“InstancePort”: 80,
“PolicyNames”: [
“EnableProxyProtocol”
] }
]

如果想关闭Proxy Protocol 的话,用下面的命令
# aws elb set-load-balancer-policies-for-backend-server –load-balancer-name ProxyProtocolTest –instance-port 80 –policy-names “[]”

 

开启Proxy Protocol之后,看看后端如何获取到源IP,这里只看nginx。

nginx 从1.5.12 版本开始支持Proxy Protocol ,只需要配置一下就OK了,说说怎么配。

第一部分,在http 里面配置如下:
set_real_ip_from   172.0.0.0/8;
real_ip_header     proxy_protocol;

表示把来在172.0.0.0/8 段(TCP负载均衡器的IP段)的所有请求的来源地址,都改成 $proxy_protocol_addr,并且记录在 $remote_addr 变量里。

log_format  main  ‘$proxy_protocol_addr $remote_addr – $remote_user [$time_local] “$request” ‘
‘$status $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for” ‘
‘”$upstream_addr” “$request_time” “$upstream_response_time” “$upstream_cache_status”‘;

这是日志格式。

第二部分,在server里面linsten的时候开启 proxy_protocol

listen      80  proxy_protocol;

此时我请求的时候,日志刷出来了,这里可以看到 $proxy_protocol_addr  和 $remote_addr  都是客户端源IP。

202.134.72.73 202.134.72.73 – – [09/Mar/2016:08:00:12 -0400] “GET / HTTP/1.1″ 200 0 “-” “curl/7.30.0″ “-” “-” “0.000” “-” “-“

 

这篇文章简单说说最近搭建 aws 基础环境遇到的几个”坑”,有兴趣的可以参考下呃。

1. 默认公网IP 在机器重启之后会变化,如果想保持固定,可以申请一个 弹性IP 与实例绑定。

2. Classic 网络里 内网IP 在重启之后是有可能变化的,VPC 则不会。

3. /etc/resolv.conf 文件里面的 nameserver 重启之后会被重置(重置成aws内部的DNS IP),如果想固定,可以修改 /etc/rc.d/rc.local

4. 用aws的AMI创建出来的实例,assign 一个新的内网IP 给实例,实例里会自动生成这个IP 的网卡配置,这个是 ec2-net-utils 包 实现的;而 如果你用 redhat 或者 centos 创建的实例,默认不装 ec2-net-utils,所以你得手动配置新的地址了,当然你也可以给 redhat  或者 centos  装上  ec2-net-utils (这个包我有,可以找我要:| )

5. 如果想在aws ec2 里面实现 可以在实例之间移动的虚拟IP,比如 两台实例A 和 B,可以先给A assign 一个新的内网IP,然后A 和 B 互相检测,假如B 发现A 挂了,可以把 之前 assign 给A 的内网IP reassign 给 B 。

    命令类似:

     /opt/aws/bin/ec2-assign-private-ip-addresses -n $ENI_ID –secondary-private-ip-address $VIP –allow-reassignment –region $REGION –aws-access-key ${aws_access_key} –aws-secret-key ${aws_secret_key}

    如果需要公网的虚拟IP,那么再创建一个弹性IP 和 这个内网IP 绑定。

6. 实例不要创建在Classic 网络里,要自己建立VPC,实例都放在VPC里,VPC 比 Classic 有几个好处(不止):

   1). 支持实例有多个私有IP

   2). 实例停止后,弹性IP会依然保持与实例的关联

   3). 实例创建的时候可以不创建公网IP

7. 如果 VPC 的子网是私有子网(创建机器不自动分配公网IP),在这个子网创建的实例即使手动分配了弹性IP 也是无效的(从外面也连不上),所以想创建能从外部连上的实例,需要在公网子网(自动分配外网IP)。

8. 建立 LB 选取子网的时候要选择「公有子网」,LB 的实例会建在 「公网子网」内,所以如果选择了私有子网会连不上。

36751CB1-44B8-42BF-803D-5C241132B71A

9. 对于建立在 VPC 的 RDS,如果想从外网访问,除了选择「公开访问」 ,对于子网组,要选择「公网子网」,因为分配的 RDS 实例在选择的子网中,如果是私有子网,从外网连不上,和 EC2 LB 的子网选择类似。

F0D0795D-59A1-4B84-86C0-14BF892E2D75-600x367