高可用 Kubernetes 集群能够确保应用程序在运行时不会出现服务中断,这也是生产的需求之一,并且在私有网络环境中,使用固定IP对外提供服务,并且基于Kubernetes service 4层负载均衡可以在保证高用的情况下,又可以获取到客户端的真实IP,这里我们使用。keepalived+haproxy 来实现。通过使用Proxy Protocol 代理协议透传客户端IP,来实现。

集群架构:

示例集群有三个主节点,以及一个虚拟 IP 地址。本示例中的虚拟 IP 地址也可称为“浮动 IP 地址”。这意味着在节点故障的情况下,该 IP 地址可在节点之间漂移,从而实现高可用。

在这里插入图片描述

keepalived + haproxy 安装

Keepalived 提供 VRRP 实现,并允许您配置 Linux 机器使负载均衡,预防单点故障。HAProxy 提供可靠、高性能的负载均衡,能与 Keepalived 完美配合。

由于在所有集群上安装了 Keepalived 和 HAproxy,如果其中一个节点故障,虚拟 IP 地址(即浮动 IP 地址)将自动与另一个节点关联,使集群仍然可以正常运行,从而实现高可用。若扩展集群,可以继续 添加更多安装 Keepalived 和 HAproxy 的节点。

yum install -y haproxy keepalived

keepalived

master 配置

vim 编辑 /etc/keepalived/keepalived.conf


! Configuration File for keepalived

global_defs {
   notification_email {
        zhanglu90@126.com
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_1
   vrrp_skip_check_adv_addr
#  vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}
vrrp_instance VI-kube-master {
    state MASTER #需要将状态设置为(MASTER)
    interface eno1  #这个地方需要特别注意,一个服务器有很多虚拟网卡,而这个网卡是真实网卡,可以通过ifconfig 命令查看你服务器中的名字,我的服务器中ip地址对应的网卡名称就是eno1  ,这个不能写错
    virtual_router_id 51 #这个id可以是其他数字,但是该数字必须在master和node中要保持一致,也可以照我这个写,不需要修改的
    priority 100  #权重设置,数值大的选为master 节点
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        10.41.39.206  # vip  #这个就是虚拟出来的ip了,简称VIP,需要确保集群环境中不能有ip与这个vip冲突,其次需要确定该vip能和集群中所有节点ping的通
    }
}

node 配置

vim 编辑 /etc/keepalived/keepalived.conf

! Configuration File for keepalived

global_defs {
   notification_email {
        zhanglu90@126.com
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_1
   vrrp_skip_check_adv_addr
#   vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}
vrrp_instance VI-kube-master {
    state BACKUP #此处需要设置为备份状态(BACKUP)
    interface eno1 #这个地方需要特别注意,一个服务器有很多虚拟网卡,而这个网卡是真实网卡,可以通过ifconfig 命令查看你服务器中的名字,我的服务器中ip地址对应的网卡名称就是eno1  ,这个不能写错
    virtual_router_id 51  #这个id可以是其他数字,但是该数字必须在master和node中要保持一致,也可以照我这个写,不需要修改的
    priority 80 #权重设置,数值大的选为master 节点 
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        10.41.39.206 # vip  #这个就是虚拟出来的ip了,简称VIP,需要确保集群环境中不能有ip与这个vip冲突,其次需要确定该vip能和集群中所有节点ping的通
    }
}

启动keepalived并设置开机自启


#启动
systemctl start keepalived
 
#查看状态
systemctl status keepalived
 
#设置开机自启
systemctl enable keepalived

haproxy

「HAProxy」- 在 TCP Mode 下,从 Haproxy 向 Nginx 传递客户端真实网络地址

在这里插入图片描述

Haproxy配置介绍:

HaProxy配置(haproxy.cfg)中分为五部分内容(五大组件),可以根据需要选择进行配置。

五大组件:global、defaults、frontend、backend、listen

1、global:参数是进程级的,通常和操作系统(OS)相关。这些参数一般只设置一次,如果配置无误,就不需要再次配置进行修改。
2、defaults:配置默认参数的,这些参数可以被利用配置到frontend,backend,listen组件。
3、frontend:接收请求的前端虚拟节点,Frontend可以根据规则直接指定具体使用后端的 backend(可动态选择)。
4、backend:后端服务集群的配置,是真实的服务器,一个Backend对应一个或者多个实体服务器。
5、listen:Frontend和Backend的组合体。

HAProxy的调度算法有如下8种:

  1. balance roundrobin # 轮询,软负载均衡基本都具备这种算法
  2. balance static-rr # 根据权重
  3. balance leastconn # 最少连接者先处理
  4. balance source # 根据请求源IP,建议使用
  5. balance uri # 根据请求的URI
  6. balance url_param # 根据请求的URl参数’balance url_param’ requires an URL parameter name
  7. balance hdr(name) # 根据HTTP请求头来锁定每一次HTTP请求
  8. balance rdp-cookie(name) # 根据据cookie(name)来锁定并哈希每一次TCP请求

定义负载均衡算法,可用于defaults、listen和backend

常用的算法:

  1. roundrobin:基于权重进行轮叫,在服务器的处理时间保持均匀分布时,这是最平衡、最公平的算法。此算法是动态的,这表示其权重可以在运行时进行调整,不过,在设计上,每个后端服务器仅能最多接受4128个连接;
  2. static-rr:基于权重进行轮叫,与roundrobin类似,但是为静态方法,在运行时调整其服务器权重不会生效;不过,其在后端服务器连接数上没有限制;
  3. leastconn:新的连接请求被派发至具有最少连接数目的后端服务器;在有着较长时间会话的场景中推荐使用此算法,如LDAP、SQL等,其并不太适用于较短会话的应用层协议,如HTTP;此算法是动态的,可以在运行时调整其权重;
  4. source:将请求的源地址进行hash运算,并由后端服务器的权重总数相除后派发至某匹配的服务器;这可以使得同一个客户端IP的请求始终被派发至某特定的服务器;不过,当服务器权重总数发生变化时,如某服务器宕机或添加了新的服务器,许多客户端的请求可能会被派发至与此前请求不同的服务器;常用于负载均衡无cookie功能的基于TCP的协议;其默认为静态,不过也可以使用hash-type修改此特性; hash-type:map-based:静态;哈希算法,consistent:动态;一致性哈希算法

HAProxy 开启 proxy protocol 协议

配置其实不用添加什么,只是在最后的listen <- sase-service-nginx 和 listen <-sase-customer-nginx 的server里的最后面添加上了 send-proxy-v2-ssl-cn 参数,这样HA就把proxy protocol协议发送到后端Nginx上了。

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    stats socket /var/run/haproxy-admin.sock mode 660 level admin
    stats timeout 30s
    user root  #启动用户
    group root #启动用户组
    daemon
    nbproc 1

defaults
    log     global
    timeout connect 5000
    timeout client  10m
    timeout server  10m

listen  admin_stats
    bind 0.0.0.0:16688  #这里需要设置一个不和其他程序冲突的端口,查看端口是否占用:netstat -anp| grep 10080
    mode http
    log 127.0.0.1 local0 err
    stats refresh 30s
    stats uri /status
    stats realm welcome login\ Haproxy
    stats auth admin:123456
    stats hide-version
    stats admin if TRUE
# 业务下游 nginx 转发配置 ,nginx 部署在 k8s 中 

listen sase-service-nginx
    mode tcp
    bind *:9443 #这里需要设置一个不和其他程序冲突的端口,查看端口是否占用:netstat -anp| grep 9443
    balance roundrobin # 调度算法,使用轮询
    # 下游nginx 地址并开启 proxy protocol 代理协议 send-proxy-v2-ssl-cn  
    server nginx-01 10.41.39.195:31498 send-proxy-v2-ssl-cn weight 1 maxconn 10000 check inter 10s

    server nginx-01 10.41.39.196:31498 send-proxy-v2-ssl-cn weight 1 maxconn 10000 check inter 10s

    server nginx-01 10.41.39.197:31498 send-proxy-v2-ssl-cn weight 1 maxconn 10000 check inter 10s

listen sase-customer-nginx
    mode tcp
    bind *:443  #这里需要设置一个不和其他程序冲突的端口,查看端口是否占用:netstat -anp| grep 7443
    balance roundrobin

	# 下游nginx 地址并开启 proxy protocol 代理协议 send-proxy-v2-ssl-cn 
    server nginx-01 10.41.39.195:31134 send-proxy-v2-ssl-cn weight 1 maxconn 10000 check inter 10s

    server nginx-01 10.41.39.196:31134 send-proxy-v2-ssl-cn weight 1 maxconn 10000 check inter 10s

    server nginx-01 10.41.39.197:31134 send-proxy-v2-ssl-cn weight 1 maxconn 10000 check inter 10s

注意,「PROXY协议」由两个版本,对应的HAProxy中的配置为send-proxy与send-proxy-v2指令,这是用于HTTP协议的。如果是HTTPS协议要使用send-proxy-v2-ssl或者send-proxy-v2-ssl-cn指令。我们用的https 协议,所以配置为send-proxy-v2-ssl-cn。

通过「PROXY协议」传递给Nginx的信息是客户端IP地址、代理服务器IP地址、两个端口号。接下来就是配置Nginx服务接受PROXY协议。

📢:haproxy 要监听1024 以下的端口需要用root 用户与root用户组启动,否则无法监听到端口

启动HAProxy并设置开机自启

#启动
systemctl start haproxy
 
#查看状态
systemctl status haproxy
 
#设置开机自启
systemctl enable haproxy

获取客户端IP

通常web应用获取用户客户端的真实ip一个很常见的需求,例如将用户真实ip取到之后对用户做白名单访问限制、将用户ip记录到数据库日志中对用户的操作做审计等等。

k8s中运行的应用通过Service抽象来互相查找、通信和与外部世界沟通,在k8s中是kube-proxy组件实现了Service的通信与负载均衡,流量在传递的过程中经过了源地址转换SNAT,因此在默认的情况下,常常是拿不到用户真实的ip的。

通过 NortPort 或者 LoadBalancer 访问获取真实 IP

获取不到客户端真实 IP 的原因是 SNAT 使得访问 SVC 的源 IP 发生了变化。将服务的 externalTrafficPolicy 改为 Local 模式可以解决这个问题。
将服务的 externalTrafficPolicy 设置为 Local 模式:
在这里插入图片描述
Cluster 隐藏了客户端源 IP,可能导致第二跳到另一个节点,但具有良好的整体负载分布。 Local 保留客户端源 IP 并避免 LoadBalancer 和 NodePort 类型服务的第二跳,但存在潜在的不均衡流量传播风险。
在这里插入图片描述
当请求落到没有服务 Pod 的节点时,将无法访问。用 curl 访问时,会一直停顿在 TCP_NODELAY , 然后提示超时:
在这里插入图片描述

使用PROXY protocol获取客户IP

在生产环境,通常会有多个节点同时接收客户端的流量,如果仅使用 Local 模式将会导致服务可访问性变低。引入 HAproxy 的目的是为了利用其探活的特点,仅将流量转发到存在服务 Pod 的节点上,提高集群的可用性,并且通过Proxy Protocol 代理协议来透传客户端IP。

Proxy Protocol是HAProxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取用户真实IP时非常有用:

  • 多层NAT网络
  • TCP代理(四层)或多层tcp代理
  • https反向代理http(某些情况下由于Keep-alive导致不是每次请求都传递x-forword-for)

其本质是在三次握手结束后由代理在连接中插入了一个携带了原始连接四元组信息的数据包。 目前Proxy Protocol有两个版本,v1仅支持human-readable报头格式(ASCIII码),v2需同时支持human-readable和二进制格式,即需要兼容v1格式。

代理协议分为v1和v2两个版本,v1人类易读,v2是二进制格式,方便程序处理。Proxy protocol是比较新的协议,但目前已经有很多软件支持,如haproxy、nginx、apache、squid、mysql等等,要使用proxy protocol需要两个角色sender和receiver,sender在与receiver之间建立连接后,会先发送一个带有客户信息的tcp header,因为更改了tcp协议,需receiver也支持proxy protocol,否则不能识别tcp包头,导致无法成功建立连接。

限制:Proxy Protocol的接收端必须在接收到完整有效的Proxy
Protocol头部后才能开始处理连接数据。因此对于服务器的同一个监听端口,不存在兼容带Proxy Protocol包的连接和不带Proxy
Protocol包的连接。如果服务器接收到的第一个数据包不符合Proxy Protocol的格式,那么服务器会直接终止连接。

Nginx proxy protocol 配置

目前,HAProxy已经将客户端的IP地址通过PROXY协议发送给Nginx服务了。接下来就是在Nginx中获取客户端的IP地址,有两种办法:

1.在Nginx中,使用’‘ p r o x y p r o t o c o l a d d r ′ ′ 和 ′ ′ proxy_protocol_addr''和'' proxyprotocoladdrproxy_protocol_port’‘变量捕获原始客户端IP地址和端口。’‘ r e m o t e a d d r ′ ′ 和 ′ ′ remote_addr''和'' remoteaddrremote_port’‘变量捕获HAProxy的IP地址和端口。
2. 在Nginx中,启用RealIP模块。启用RealIP模块后:Nginx会重写’‘ r e m o t e a d d r ′ ′ 和 ′ ′ remote_addr''和'' remoteaddrremote_port’‘变量的值,用客户端IP地址和端口替换负载均衡的IP地址和端口;而’‘ r e a l i p r e m o t e a d d r ′ ′ 和 ′ ′ realip_remote_addr''和'' realipremoteaddrrealip_remote_port’‘变量保留负载均衡的IP地址和端口;’‘ p r o x y p r o t o c o l a d d r ′ ′ 和 ′ ′ proxy_protocol_addr''和'' proxyprotocoladdrproxy_protocol_port’'变量保留原始客户端IP地址和端口。

这对Nginx也有所要求:

  • 接受PROXY协议v2,需要NGINX 1.13.11及更高版本,或NGINX Plus R16及更高版本;
    * 接受HTTP的PROXY协议,需要NGINX 1.5.12及更高版本,或NGINX Plus R3及更高版本;(在nginx-1.5.12的http中,指令listen首次支持proxy_protocol)
    * 接受TCP的PROXY协议,需要NGINX 1.11.4及更高版本,或NGINX Plus R11及更高版本;(在nginx-1.11.4的stream中,指令listen首次支持proxy_protocol)
    * 对于TCP客户端PROXY协议支持,需要NGINX 1.9.3及更高版本,或NGINX Plus R7及更高版本;(在nginx-1.9.2中,加入了proxy_protocol指令,用于请求PROXY服务)
    * 如果使用RealIP模块,则需要进行安装,默认没有安装该模块。

在Nginx中进行如下配置:

server {
    charset utf-8;
    listen [::]:443 default_server ssl ipv6only=on proxy_protocol; # proxy_protocol 
    listen 0.0.0.0:443 default_server ssl proxy_protocol;
    location ^~ /v1/ {
        proxy_set_header X-Real-IP $proxy_protocol_addr;
        proxy_set_header X-Forwarded-For $proxy_protocol_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://backend/v1/;
    }
}

有一点需要说明,指令set_real_ip_from的参数为负载均衡的IP地址或网段。该参数标记出负载均衡的网段,然后Nginx会进行递归排除,递归排除之后剩下的IP地址就是客户端的IP地址。这个地方不要配置错了。

参考:
使用haproxy+keepalived实现k8s集群访问总代理
「HAProxy」- 在 TCP Mode 下,从 Haproxy 向 Nginx 传递客户端真实网络地址

proxy-protocol

总结

本文介绍了如何在k8s 集群中 haproxy+keepalived 为集群总代理,以及使用俩种获取真实 IP 的部署方式:

  1. 直接通过 NortPort 访问获取真实 IP
    受制于 Local 模式,可能会导致服务不可访问。需要保证对外提供入口的节点上,必须具有服务的负载。
  2. 通过 haproxy -> Service Proxy Protocol 代理访问获取真实 IP
    利用 haproxy 的探活能力,能够提高服务的可访问性。
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐