问题:
1. 有雀在虚拟化(Libvirt)部署时,worker 节点可以动态调整(横向扩展),导致 ingress(80、443端口) 的地址可能随机掉落到某两个worker节点,导致apps方式提供的路由无法访问。
2. 虚拟化部署的节点均为NAT网络,虚拟化提供的dnsmasq 解析apps 域名只能解析到一个节点,但ingress是两个节点,无法做到dns级负载均衡。 如果改用dnsmasq的addn-hosts 就无法进行泛解析。
3. 离线部署,宿主机上的 registry镜像仓库与负载均衡器 80、443端口冲突,只能用http代理,无法使用tcp代理。
分析:
ingress 是通过deployment 方式部署两个ingress pod,然后这两个pod 做端口映射到pod所在节点,开放80、443两个端口提供路由服务。
现状:
通过 podman network 创建一个桥接网路, 分配172.10.0.0/24 网段, registry-1 分配 172.10.0.5:5000 、registry-2 分配 172.10.0.6:5000、registry-3 分配 172.10.0.7:5000 。通过nginx 区分镜像仓库项目名进行流量转发(只能是http代理) 占用443端口。
server {
listen 443 ssl;
server_name registry.uniontech.com;
......
location /v2/uccps-components/ {
proxy_pass http://registry-1;
}
location /v2/uccps-samples/ {
proxy_pass http://registry-2;
}
location /v2/uos-container/ {
proxy_pass http://registry-3;
}
但外部访问NAT网络下的集群也需要一层代理,而且也在宿主机,因此也会占用 宿主机的443, 被迫使用nginx 区分 SNI实现分流。
但nginx的check模块并不好使,入栈流量在200多个ip内不停轮询,导致服务访问异常缓慢。
解决方法 1:
既然 dnsmasq 的泛解析 不能使用addn-hosts,那就用coredns 写一个插件,corefile给定一个网段,指定监测的端口,及dns 返回规则,可以实现每次dns请求都会准确的到达ingress所在的节点,配合nginx的resolver 实现dns级别的负载均衡。
# Corefile
# 注: 未实现插件
.:53 {
....
ipset {
# 指定要查询的ip段
ipset 192.168.120.0/24
# 检测端口
check_port 443
# dns 返回策略 随机
dns_policy random
# 缓存超时 1分钟
cache_timeout 1m
# 缓存大小,仅缓存两个ip
cache_size 2
# 无法解析的dns 传递到下一个插件
fallthrough
}
....
}
# nginx 配置
resolver 192.168.120.1;
server {
listen 443;
server_name *.apps.libvirt-22.example.com;
....
location / {
proxy_pass https://$hosts;
....
}
}
解决方法2:
给registry挪个位置, 443 ---> 5000,利用有雀提供的 ImageContentSourcePolicy api,实现 crio 的mirror功能,也就是 拉取的镜像地址为 registry.uniontech.com/uccps/nginx:latest 在拉取过程中这将被替换为 registry.uniontech.com:5000/uccps/nginx:latest , 然后443 端口可以被haproxy独占,可以使用tcp 负载均衡,搭配 haproxy的check模块检测 每个ip的443端口,实现流量能转发到特定的两个节点。
apiVersion: operator.openshift.io/v1alpha1
kind: ImageContentSourcePolicy
metadata:
name: images-mirror-test
spec:
repositoryDigestMirrors:
# 这将 所有 registry.uniontech.com 替换成 registry.uniontech.com:5000
# 不影响镜像的拉取、存放、运行
- mirrors:
- registry.uniontech.com:5000
source: registry.uniontech.com
mirrorByDigestOnly: false
# haproxy.cfg
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats
defaults
log global
option tcplog
option dontlognull
timeout connect 2s
timeout client 1m
timeout server 1m
timeout check 200
frontend ingress-https
bind *:443
default_backend ingress-https
mode tcp
backend ingress-https
balance source
mode tcp
# 下面就可以写整个Machine Network CIDR地址
#
server worker51 192.168.217.51:443 check
server worker52 192.168.217.52:443 check
.........
server worker52 192.168.217.254:443 check
但 ImageContentSourcePolicy 只能根据 digest 拉取的镜像,无法使用tag拉取。 因此 通过machine config配置一个 containers 的registry.conf 文件。
写一个 registry.conf
[[registry]]
prefix = ""
location = "registry.uniontech.com"
insecure = true
mirror-by-digest-only = false
[[registry.mirror]]
location = "registry.uniontech.com:5000"
insecure = true
[[registry]]
prefix = ""
# 如果没有证书,就需要配置免密
location = "registry.uniontech.com:5000"
insecure = true
base64 加密上述文件
base64 -w0 registry.conf
W1tyZWdpc3RyeV1dCiAgcHJlZml4ID0gCiAgbG9jYXRpb24gPSByZWdpc3RyeS51bmlvbnRlY2guY29tCiAgaW5zZWN1cmUgPSB0cnVlCiAgbWlycm9yLWJ5LWRpZ2VzdC1vbmx5ID0gZmFsc2UKCiAgW1tyZWdpc3RyeS5taXJyb3JdXQogICAgbG9jYXRpb24gPSByZWdpc3RyeS51bmlvbnRlY2guY29tOjUwMDAKICAgIGluc2VjdXJlID0gdHJ1ZQoKW1tyZWdpc3RyeV1dCiAgcHJlZml4ID0gCiAgIyDlpoLmnpzmsqHmnInor4HkuabvvIzlsLHpnIDopoHphY3nva7lhY3lr4YKICBsb2NhdGlvbiA9IHJlZ2lzdHJ5LnVuaW9udGVjaC5jb206NTAwMAogIGluc2VjdXJlID0gdHJ1ZQo=
编写 一个machine config配置 (worker同样需要)
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
labels:
machineconfiguration.openshift.io/role: master
name: 99-master-mirror-by-digest-registries
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:text/plain;charset=utf-8;base64,W1tyZWdpc3RyeV1dCiAgcHJlZml4ID0gCiAgbG9jYXRpb24gPSByZWdpc3RyeS51bmlvbnRlY2guY29tCiAgaW5zZWN1cmUgPSB0cnVlCiAgbWlycm9yLWJ5LWRpZ2VzdC1vbmx5ID0gZmFsc2UKCiAgW1tyZWdpc3RyeS5taXJyb3JdXQogICAgbG9jYXRpb24gPSByZWdpc3RyeS51bmlvbnRlY2guY29tOjUwMDAKICAgIGluc2VjdXJlID0gdHJ1ZQoKW1tyZWdpc3RyeV1dCiAgcHJlZml4ID0gCiAgIyDlpoLmnpzmsqHmnInor4HkuabvvIzlsLHpnIDopoHphY3nva7lhY3lr4YKICBsb2NhdGlvbiA9IHJlZ2lzdHJ5LnVuaW9udGVjaC5jb206NTAwMAogIGluc2VjdXJlID0gdHJ1ZQo=
filesystem: root
mode: 420
path: /etc/containers/registries.conf.d/99-master-mirror-by-digest-registries.conf
应用 machine config
oc apply -f 99-master-mirror-by-digest-registries.yaml
现在master和worker节点可以通过5000端口拉取镜像,不用修改其余配置。但部署集群时引导节点会有问题,引导节点并不应用machine config, 因为没有 机器配置池。因此需要对 引导节点的ignition进行修改
# 把上述 base64 加到 引导节点的ign里,帮助引导节点拉取镜像
jq '.storage.files[0].contents.source += "W1tyZWdpc3RyeV1dCiAgcHJlZml4ID0gCiAgbG9jYXRpb24gPSByZWdpc3RyeS51bmlvbnRlY2guY29tCiAgaW5zZWN1cmUgPSB0cnVlCiAgbWlycm9yLWJ5LWRpZ2VzdC1vbmx5ID0gZmFsc2UKCiAgW1tyZWdpc3RyeS5taXJyb3JdXQogICAgbG9jYXRpb24gPSByZWdpc3RyeS51bmlvbnRlY2guY29tOjUwMDAKICAgIGluc2VjdXJlID0gdHJ1ZQoKW1tyZWdpc3RyeV1dCiAgcHJlZml4ID0gCiAgIyDlpoLmnpzmsqHmnInor4HkuabvvIzlsLHpnIDopoHphY3nva7lhY3lr4YKICBsb2NhdGlvbiA9IHJlZ2lzdHJ5LnVuaW9udGVjaC5jb206NTAwMAogIGluc2VjdXJlID0gdHJ1ZQo="' bootstrap.ign.json > bootstrap.ign
mv bootstrap.ign.json bootstrap.ign
总结:
除了这两种方法,还有一些比较奇葩的解决方法,就不再提了。
两种方法各有优劣,第一种得写一个coredns 插件。 第二种虽然能镜像到5000端口,但过程繁复。
tcp 负载效率比http高出不少,随缘取舍吧。
容器并不是所有的场景都需要加 privileged 权限,也不是所有场景都要用 host网络,云场景下安全性不得不考虑。