物理机上部署的 k8s
没有负载均衡器, 其 nginx-ingress
部署 master
节点上, 并且在 hostnetwork
上监听 80
和 443
端口, 如果 k8s
前面没有 lvs
等负载, 那么请求只会到其中一台 nginx-ingress
上, nginx-ingress
相当于是主备模式, 单个 nginx-ingress
会成为性能瓶颈, 所以需要在 k8s
前面部署 lvs
, 并且使用 DR
模式, 如图
CIP
: 客户的 ip
DIP
: lvs
上用于与后端服务器进行数据交互的 ip
, lvs
修改完 MAC
地址后, 将包从该 ip
发到 lan
上
RIP
: 后端服务器的 ip
, 需要跟 DIP
在同一网段
VIP
: 对外提供服务, 由 keepalived
生成的虚 ip
, 一般是外网
lvs
: lvs
服务器, 部署了 lvs
服务, 用于负载流量到后端真实服务器
RealServer
: 后端真实服务器
DR
模式优缺点lvs
到 RealServer
, 但是相应直接从 RealServer
到客户端, 不再走 lvs
, 所以性能和效率都很高RealServer
和 vip
提供的端口必须一致, 因为 DR
模式, lvs
只修改包的 MAC
地址, 不涉及三层的修改RealServer
与 LVS
不能在同一台机器上, 详见: http://linbo.github.io/2017/08/20/lvs-dripvsadm
来创建 lvs
规则,但没有 vip
保证 ip
高可用,而 keepalived
本身就是 lvs
实现的,所以可以直接通过 keepalived
来实现 lvs
规则,并且有 vip
NAT
模式 lvs
需要开启路由转发(echo "1" > /proc/sys/net/ipv4/ip_forward
),DR
模式不需要DR
模式的每台 lvs
服务器以及 RealServer
服务器的 lo
口上都绑定有 vip
, 而这么多服务器有同一个外网 vip
, 如何保证客户端访问到 lvs
的 vip
, 就需要 RealServer
设置 ARP
抑制lvs
只有一个外网口, DIP
和RIP
需要在同一网段,也就是 DIP
,VIP
,RIP
要在同一网段,也就是都要使用外网 ip
,如果有两台 lvs
,两台 RealServer
,那么总共需要 5
个外网ip
,同时因为 RealServer
有公网 ip
,那么 RealServer
其实可以直接对外提供服务,但将 RealServer
暴露在外网不太安全ip
,并且不安全,所以实际使用中可以只使用一个外网ip
实现 DR
模式,也就是 VIP
与 (DIP
和 RIP
) 使用不同网段,其中 VIP
使用外网段,DIP
和RIP
使用内网段,其中 lvs
要有两个网口,其中一个网口用于绑定 vip
,另一个网口用于绑定内外ip
,与 RealServer
的内网 ip
相通,并且设置 RealServer
的默认路由为自己的内网路由器网关,不能是 DIP
,详见:(如果 RealServer
默认网关想使用 DIP
,需要打补丁,太麻烦)vip
要配置在RealServer
的 lo
口, 并且子网掩码必须是 32
位, 如果不是 32
位, 例如 24
位, lo
会相应整个 24
段的所有 ip
的请求操作系统均为 ubuntu18.04
角色 | 内网ip | 外网ip | vip | 默认网关 | 备注 |
---|---|---|---|---|---|
lvs1 | 192.168.20.21(DIP )(接口: eno2 ) |
119.x.x.238(VIP )(接口: eno1 ) |
119.x.x.225 | ||
lvs2 | 192.168.20.22(DIP )(接口: eno2 ) |
||||
k8s1 | 192.168.20.11(RIP )(接口: enp1s0f1 ) |
119.x.x.234(接口: enp1s0f0 ) |
192.168.20.1 | lo 上绑定 119.x.x.238(VIP ) |
|
k8s2 | 192.168.20.12(RIP )(接口: enp1s0f1 ) |
119.x.x.235(接口: enp1s0f0 ) |
192.168.20.1 | lo 上绑定 119.x.x.238(VIP ) |
|
k8s3 | 192.168.20.13(接口: enp1s0f1 ) |
192.168.20.11 |
注意: 内网网关192.168.20.1
和外网网关119.x.x.225
是交换机或路由器上的同一接口
RealServer
部署待负载的服务nginx-ingress
就是 RealServer
, 待负载端口为 80
和 443
80
和 443
端口要么监听 VIP
, 要么监听 0.0.0.0
, 因为到达 RealServer
时的包目标 ip
是 VIP
总共两台 RealServer
, 主机名分别为 k8s1
和 k8s2
, RIP
分别为 192.168.20.11
和 192.168.20.12
lvs
上部署 keepalived
有两台 lvs
, 主机名分别为 lvs1
和 lvs2
, 其中 lvs1
作为 keepalived
的 MASTER
节点, lvs2
作为 BACKUP
节点
eno2
为内网口, eno1
为外网口, 并且其上除了将绑定的 vip
不存在其他外网 ip
, vip
为 119.x.x.238
mkdir -p /opt/keepalived
vim /opt/keepalived/keepalived.conf
lvs
MASTER
节点配置
global_defs {
router_id LVS_MASTER
script_user root
enable_script_security
}
vrrp_instance VI_1 { # 设置vrrp组,唯一且同一LVS服务器组要相同
state MASTER # 备份LVS服务器设置为BACKUP
interface eno2 # 设置发送 vrrp 包的接口, 设置为内网口, 因为外网口 eno1 上没有外网 ip, 所以无法发送 vrrp 包
virtual_router_id 51 # 设置虚拟路由标识, 同一网段下不通 keepalived 该值不能相同
priority 100 # 设置优先级,数值越大,优先级越高,backup设置小于100,当master宕机后自动将backup高的变为master。
advert_int 1 # 设置同步时间间隔
authentication { # 设置验证类型和密码,master和buckup一定要设置一样
auth_type PASS
auth_pass 1111
}
virtual_ipaddress { # 设置VIP,可以多个,每个占一行
119.x.x.238/27 dev eno1 # 在外网口上绑定 vip, 并且指定子网掩码为 27, 否则不能绑定默认路由
}
notify_master "/sbin/route add default gw 119.x.x.225" # 节点成为 master 时, 配置默认路由, 这样 vip 才能通外网
}
virtual_server 119.x.x.238 80 {
delay_loop 6 # 健康检查时间间隔,单位s
lb_algo wrr # 负载均衡调度算法设置为加权轮叫
lb_kind DR # lvs DR 模式
#persistence_timeout 5 # 会话保持时间,单位s
protocol TCP # 协议
real_server 192.168.20.11 80 { # 真实服务器配置,80表示端口
weight 3 # 权重
TCP_CHECK { # 服务器检测方式设置
connect_timeout 5 # 连接超时时间
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.20.12 80 {
weight 3
TCP_CHECK {
connect_timeout 10
retry 3
delay_before_retry 3
connect_port 80
}
}
}
virtual_server 119.x.x.238 443 {
delay_loop 6
lb_algo wrr
lb_kind DR
#persistence_timeout 5
protocol TCP
real_server 192.168.20.11 443 {
weight 3
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 443
}
}
real_server 192.168.20.12 443 {
weight 3
TCP_CHECK {
connect_timeout 10
retry 3
delay_before_retry 3
connect_port 443
}
}
}
lvs
BACKUP
节点配置
除了 state MASTER
修改为 state BACKUP
, priority 100
改为小一点的值以外, 其他配置一样, 例如:
...
vrrp_instance VI_1 {
state BACKUP
interface eno2
virtual_router_id 51
priority 50
...
在两台 lvs
上安装docker
, 并启动 keepalived
容器
# 设置内核参数
vim /etc/sysctl.conf
net.ipv4.ip_nonlocal_bind=1
sysctl -p
# 启用 ipvs 内核
modprobe ip_vs
modprobe ip_vs_wrr
modprobe ip_vs_rr
modprobe ip_vs_sh
# 开机自动加载内核
cat <> /etc/modules
ip_vs
ip_vs_wrr
ip_vs_rr
ip_vs_sh
EOF
# 验证 ipvs 内核是否加载
lsmod | grep ip_vs
# 一键安装 docker
curl -sSL https://get.daocloud.io/docker | sh
docker run -d --net=host --cap-add=NET_ADMIN --name keepalived --restart always \
-e 'KEEPALIVED_AUTOCONF=false' \
-v /opt/keepalived/keepalived.conf:/etc/keepalived/keepalived.conf \
-v /var/run/docker.sock:/var/run/docker.sock \
img.chinamye.com/arcts/keepalived:1.2.2
RealServer
配置k8s1
netplan
网络配置, 默认网关为内网网关, 所以即使配置了外网 ip
119.x.x.234
也是不通外网的, 这点后面会解释
network:
ethernets:
lo:
addresses: [119.x.x.238/32] # 必须为 32 位
enp1s0f0:
addresses: [119.x.x.234/27]
enp1s0f1:
addresses: [192.168.20.11/24]
gateway4: 192.168.20.1
version: 2
k8s2
netplan
网络配置
network:
ethernets:
lo:
addresses: [119.x.x.238/32]
enp1s0f0:
addresses: [119.x.x.235/27]
enp1s0f1:
addresses: [192.168.20.12/24]
gateway4: 192.168.20.1
version: 2
k8s1
和 k8s2
设置 arp
抑制, 以及 rp_filter
(后文解释)
vim /etc/sysctl.conf
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.rp_filter = 2
sysctl -p
这样之后, lvs
DR
模式就生效的, 在任意 CIP
上测试, 能正常返回内容
CIP
请求 VIP
80
端口, 到达 lvs
MASTER
节点lvs
MASTER
根据 lvs
规则, 将请求转发给 RealServer
, 比如 k8s1
, 这时候 lvs
会修改包二层目的 MAC
为 k8s1
RIP
的 MAC
, 此时数据包的源和目标 ip
地址仍然为 CIP->VIP
, 但源和目标 MAC
地址为 DIP_MAC->RIP_MAC
, 通过 lvs
内网口(eno2
) 发出k8s1
内网口 enp1s0f1
接收到数据包以后, 由于源 IP
119.x.x.238
确实存在(lo
上), 所以不会被丢弃, 但 lo
口无法和外界直接通信, 所以回复包最终会从内网口 enp1s0f1
发送到默认网关, 此时数据包的源和目标 ip
地址为 VIP->CIP
, 源和目标 MAC
地址为 RIP_MAC->g 0/0/1_MAC
g 0/0/1
上的外网网关 119.x.x.225
返回给 Client
接下来解释以下问题:
Q: 为什么 RealServer
上默认网关得设置成内网网关, 而不是外网网关?
A
: 因为 RealServer
回包时, 由其内网口回包, 而内网 ip
无法直接通过外网网关访问外网, 其内网 ip
和 CIP
肯定不在同一个段, 所以需要通过路由来转发, 这也解释了为什么内网网关和外网网关要绑定在交换机或路由器同一端口上, 所以回包时, RealServer
通过内网口发送到默认路由上, 交换机或路由器再通过外网网关发送给客户端
Q: RealServer
如何访问外网?
A
: RealServer
上设置了默认路由为 192.168.20.1
, 所以访问外网时, 数据包的源 ip
是内网 ip
192.168.20.11
, 但是公网上无该 ip
, 所以无法回包. 所以要想访问外网, 如果设备是路由器(可以使用 linux
充当路由器), 只需要在路由上配置 NAT
即可, 而如果设置是交换机, 由于交换机上无法设置 NAT
, 所以我们现在 RealServer
上绑定一个外网 ip
, 然后通过 RealServer
上的外网口来访问外网, 添加如下 iptables
规则
iptables -t nat -A POSTROUTING -s 192.168.20.11/32 -d 192.168.20.0/24 -j RETURN
iptables -t nat -A POSTROUTING -s 192.168.20.11/32 -d 10.233.0.0/16 -j RETURN
iptables -t nat -A POSTROUTING -s 192.168.20.11/32 -j SNAT --to-source 119.x.x.234
该规则含义为: 如果出去的包源地址是 192.168.20.11
, 并且目的地址不是自身内网段, 也不是 k8s
pod
段和 service
段的包, 则将包的源地址改为 RealServer
外网口 enp1s0f0
上的外网 ip
119.x.x.234
然后设置内核参数 net.ipv4.conf.all.rp_filter = 2
, 防止出外网的回包被 linux
丢弃
实际上原理就是出外网时, 将包的源 ip
由内网 192.168.20.11
改为 119.x.x.234
使用该 iptables
规则容易对现有环境造成影响, 可以通过 route -n
查看应该过滤的网段, 过滤掉使用 enp1s0f1
的网段
NAT
即可linux
来充当路由器, 然后 RealServer
的默认网关填该 linux
router
的 ip
, 最后在 linux
router
上配置 iptables
nat
实现 RealServer
访问外网, 并且配置rp_filter = 2
(该方式测试可以玩一玩, 不建议上生产环境)RealServer
上绑定其他外网 ip
, 并通过 iptables
snat
实现访问外网Q: 有没有更简单的做法?
A
: VIP
, DIP
, RIP
均使用同一网段的外网 ip
即可. 这种情况只需要在 RealServer
lo
口配置 vip
并设置 arp
抑制, 默认网关用外网网关, 但总共需要 5
个外网 ip
(VIP
, 两个 DIP
, 两个 RIP
)
而上面的方式 VIP
使用了外网段, DIP
和 RIP
使用了内网段
如果路由设备是路由器, 总共只需要 1
个外网 ip
(VIP
), 也不需要配置 iptables
实现 RealServer
访问外网
如果路由设备是交换机, RealServer
不需要访问外网, 总共只需要 1
个外网 ip
(VIP
), 如果 RealServer
需要访问外网, 总共只需要 3
个外网 ip
(VIP
, 以及 k8s1
和 k8s2
上用于访问外网的外网 ip
), 而其他节点如 k8s3
要想访问外网, 通过 k8s1
做 nat
即可, 操作如下:
k8s3
默认网关为 k8s1
内网 ip
192.168.20.11
k8s1
打开路由转发: net.ipv4.ip_forward = 1
k8s1
上配置 SNAT
: iptables -t nat -A POSTROUTING -s 192.168.20.13 -j SNAT --to-source 119.x.x.234