背景
公司内网kubernetes集群有很多redis, 需要按需暴露相关服务到内网环境, 由于kubernetes的clusterIP只能集群访问, 因此需要暴露到整个内网vpn环境。 实现四层代理的方案有很多, 基于apisix网关来做四层调度, 和apisix的7层代理很相似, 而且apisix支持sni的四层代理, 因此做了这个尝试。
开启apisix的TCP代理
第一步需要apisix支持tcp/udp, 在配置文件添加如下配置, 重启apisix即可生效。这个配置会让apisix来监听9100端口,处理stream_route的请求。
apisix:
stream_proxy: # TCP/UDP proxy
tcp: # TCP proxy port list
- addr: 9100
tls: true
创建Stream_route
第二步,创建stream_route
. 因为用sni可以复用端口,来达到管理方便的目的。 基于一个入口来处理所有的redis流量。 当然,内部程序还是用kubernetes的svc来进行链接。
curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H "X-API-KEY: $API_KEY" -X PUT -d '
{
"sni": "bw-svc-t2.redis.feghong.tech",
"upstream": {
"nodes": {
"172.19.7.82:6379": 1
},
"type": "roundrobin"
}
}'
客户端链接
第三步, 连接apisix的9100端口即可. 因为我这边使用的是 lb 来对接的9100的进行四层代理。
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-AddressType: intranet
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-force-override-listeners: "true"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: lb-xxx
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-network-type: vpc
labels:
app.kubernetes.io/instance: apisix
app.kubernetes.io/managed-by: Tiller
app.kubernetes.io/name: apisix
app.kubernetes.io/version: 2.7.0
helm.sh/chart: apisix-0.3.0
name: apisix-stream
namespace: infras-tx
spec:
externalTrafficPolicy: Local
healthCheckNodePort: 31473
ports:
- name: apisix-stream
port: 6379
targetPort: 9100
selector:
app.kubernetes.io/instance: apisix
app.kubernetes.io/name: apisix
sessionAffinity: ClientIP
type: LoadBalancer
使用redis-cli
来进行链接
$ redis-cli -h bw-svc-t2.redis.feghong.tech -a $password --tls --sni bw-svc-t2.redis.feghong.tech
使用golang客户端链接
import "github.com/go-redis/redis/v8"
rdb := redis.NewClient(&redis.Options{
Addr: "bw-svc-t2.redis.feghong.tech:6379",
Password: "", // no password set
DB: 0, // use default DB
})
To enable SSL, you can specify a
tls.Config
. If you are getting “x509: cannot validate certificate for xxx.xxx.xxx.xxx because it doesn’t contain any IP SANs”, try to setServerName
option:
rdb := redis.NewClient(&redis.Options{
TLSConfig: &tls.Config{
ServerName: "bw-svc-t2.redis.feghong.tech",
},
})
使用RDM链接, 点击一下SSL/TLS即可。 因为证书我们使用了ssl证书,满足tls握手要求。
总结
sni的好处在于我们可以复用9100这个端口。 比如我再加一个redis的暴露, 我只需要添加一个stream_route,即可。然后根据sni来路由到我们的另外一个redis。
比如我再添加一个mysql的路由。
curl http://127.0.0.1:9180/apisix/admin/stream_routes/2 -H "X-API-KEY: $API_KEY" -X PUT -d '
{
"sni": "bw-svc-t3.redis.feghong.tech",
"upstream": {
"nodes": {
"172.19.7.12:6379": 1
},
"type": "roundrobin"
}
}'
访问
$ redis-cli -h bw-svc-t3.redis.feghong.tech -a $password --tls --sni bw-svc-t3.redis.feghong.tech