基于sni使用apisix代理四层TCP/UDP

基于sni使用apisix代理四层tcp

post thumb
Kubernetes
作者 Louis 发表于 2021年7月6日

背景

公司内网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 set ServerName 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
上一篇
记一次阿里云arms监控和log-pilot日志系统冲突及解决