+86 13541016684Mon. - Fri. 10:00-22:00

AWS TCP Load Balancer 使用中需要的注意的细节问题

AWS TCP Load Balancer 使用中需要的注意的细节问题

AWS TCP Load Balancer 使用中需要的注意的细节问题

AWS 的几个”坑”

这篇文章简单说说最近搭建 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

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

image

对于这个问题,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/Oct/2014:08:00:12 -0400] “GET / HTTP/1.1″ 200 0 “-” “curl/7.30.0″ “-” “-” “0.000” “-” “-“

把 DNS 域的 @ 配置成 CNAME 的一个坑

如果你在一个域里面( 比如 awsgood.com. )同时配置下面两条记录:

@ CNAME 默认(或其他线路) www.awsgood.com.
@ MX         默认(或其他线路) 「记录值」.

那么 向 awsgood.com. 域发邮件可能失败,原因是:

CNAME 其实只是一个别名,DNS 服务器在收到常规 DNS 记录 (非 CNAME )请求的时候会检查是否已经有对应的 CNAME 记录,如果有就重新启动对 CNAME 值的查询并返回,如果显示指定查询的就是 CNAME 记录,则直接返回。

所以,即使在配了 CNAME 之后又配置了 A、MX 等记录,也没用,因为 MX 优先级高。

记住,「请不要对 @ 设置 CNAME 记录」。

RFC 的说明如下:

When a DNS resolver encounters a CNAME record while looking for a regular resource record, it will restart the query using the canonical name instead of the original name. (If the resolver is specifically told to look for CNAME records, the canonical name (right-hand side) is returned, rather than restarting the query.)

An alias defined in a CNAME record must have no other resource records of other types (MX, A, etc.). (RFC 1034 section 3.6.2, RFC 1912 section 2.4) The exception is when DNSSEC is being used, in which case there can be DNSSEC related records such as RRSIG, NSEC, etc. (RFC 2181 section 10.1)

参考

http://blog.clanzx.net/2013/09/02/dns-cname.html

 

update at Sat Jul 18 10:13:15 CST 2015

增加一点,AWS 的 route53 可以通过 alias 实现对 @ 增加 CNAME 记录,但只限于 LB 和 S3 等服务,具体请看

http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html

http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html

0FA5B5CB-62E7-4441-AE4C-95D729B51EAC-300x96