最新要闻
- 世界播报:《阿凡达2》差的远!2022国内电影票房前10:第一超40亿
- 前沿热点:折叠旗舰卖到白菜价!moto razr 2022宣布调价至4999元
- 美国人钱包年末又迎重击!极端寒潮导致上周电价飙升超6000%
- 天天时讯:或售70万对刚比亚迪!东风猛士M-Terrain量产实车曝光:凶悍
- 只有Redmi做到了!米粉没想到2022年2500元的手机都有无线充电
- 河南郑新黄河大桥因大雾多车相撞:涉及200多辆车
- 速递!安卓手机不卡顿!一加11内存基因重组技术揭秘:数据抓取量提升16倍
- 特斯拉股价年内暴跌70% 韩国散户疯狂抄底!背后原因不简单
- 女生病假期上9天班反欠公司三百多:被扣10天工资
- 环球速看:曝特斯拉上海工厂将在1月实施减产计划 原因未知
- 直降120:百度网盘超级会员12个月SVIP 178元大促
- playgo是什么意思?playgo是什么牌子?
- 十年之痒是什么意思?十年之痒的婚姻感悟小说有哪些?
- 【天天聚看点】女子投资100万元 本金4年仅剩1.71万元!基金经理被集体起诉
- 神价手慢无:OATLY噢麦力燕麦奶1L*2瓶/19.9元抄底
- 焦点速读:轿车加气站去加气 一开后备厢车被炸报废
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
当云原生网关遇上图数据库,NebulaGraph 的 APISIX 最佳实践
本文介绍了利用开源 API 网关 APISIX 加速 NebulaGraph 多个场景的落地最佳实践:负载均衡、暴露接口结构与 TLS Termination。
(资料图片仅供参考)
API 网关介绍
什么是 API 网关
API 网关是位于客户端和服务器之间的“中间人”,用于管理、监控和保护 API。它可以在 API 之前执行一些操作,例如:身份验证、授权、缓存、日志记录、审计、流量控制、安全、防火墙、压缩、解压缩、加密、解密等。
API 网关可以工作在 TCP/IP 4 层和 OSI 7 层。跑在 7 层的 API 网关可以使用多种协议,例如:HTTP、HTTPS、WebSocket、gRPC、MQTT 等。在这些应用层协议中做一些操作,比如,请求的重写、转发、合并、重试、缓存、限流、熔断、降级、鉴权、监控、日志、审计等等。
这里举例一下借助 API 网关可以做的具体的事:
- 在网关层增加认证层,比如:JWT 认证、OAuth2 认证、OpenID 认证等等,这样不需要在每个服务中都做具体的认证集成工作,进而节省许多开发成本。
- 借助网关给跳板机 SSH 流量增加无需客户端修改的复杂认证,比如:跳转任何客户端的 SSH 登录,给出一个网址或者输入框,引导登陆者通过网页的 SSO 认证(包含多因素认证),再通过网关转发到 SSH 服务。
- 甚至在网关层做 Serverless 数据库!TiDB 社区的同学们就在做这个事儿,他们从普通的 MySQL 客户端的登录请求中解析能推断出转到需要的 TiDB 示例的信息,并且在需要 cold start 唤醒实例的时候把连接保持住,可以参考这篇文章:TiDB Gateway。
- 如果你特别惨在维护屎山项目,不得不针对旧版本的应用程序对新版本的服务端进行兼容,这时候 API 网关也可以通过一些请求重写,把旧版本的请求转换成新版本的请求。
只要脑洞大,理论上 API 网关可以做很多事。但显然不是所有的事情都是适合在这一层去做的,通常那些比较通用的事情才适合在这一层去做,上面我只是给出一些典型和极端的具体例子。
Apache APISIX
API 网关是从 LB、Reverse Proxy 项目演进过来的。随着云原生的兴起,API 网关也逐渐成为了云原生的一部分,流行的开源网关有:
- Nginx
- Apache APISIX
- Kong
- Lura
- OpenResty
- Tyk
- Traefik
- Istio
- Envoy
而且其中很多都是基于 Nginx/OpenResty 的下游项目。这里就以 Apache APISIX 为例,介绍一下 NebulaGraph 借助 API 网关的几个实践。
NebulaGraph 介绍
NebulaGraph 是一个开源的分布式图数据库,它的特点是:
- 高性能:可达到每秒百万级的读写,具有极高的扩展性,在千亿点、万亿边的数据规模下支持毫秒级的查询。
- 易扩展:分布式的架构可在多台机器上扩展。每台机器上可以运行多个服务进程,它的查询层是无状态的计算存储分离架构,可以容易地引入不同配置、不同类型的计算层,实现同一集群上 TP、AP、图计算等不同负载的混合查询。
- 易使用:类 SQL 的原生查询语言,易于学习和使用,同时支持 openCypher。
- 丰富生态:NebulaGraph 的生态系统正在不断壮大,目前已经有了多个客户端,包括 Java、Python、Go、C++、JavaScript、Spark、Flink 等,同时也有了多个可视化工具,包括 NebulaGraph Studio、NebulaGraph Dashboard、NebulaGraph Explorer 等。
本文讨论的问题
本文给出了基于 NebulaGraph 集群应用中涉及到 API 网关的几个场景。
- 查询接口的负载均衡
- 底层存储接口的暴露
- 传输层的加密
查询接口负载均衡
首先是图数据库查询接口 graphd 的负载均衡与高可用的问题。
NebulaGraph 内核由三种服务组成:graphd、metad 和 storaged:
所以,在默认情况下,集群只会暴露 graphd 的接口,提供给客户端连接,执行 nGQL 的查询。其中,graphd 是无状态的,这意味着可以在多个 graphd 之间做负载均衡。这里,我们有两种方法:基于客户端的(Client-Side LB)与基于代理的。
客户端的负载均衡
客户端的负载均衡,就是在客户端,也就是应用程序中,实现负载均衡的逻辑。NebulaGraph 的各个语言的客户端里边已经内置了轮询(Round-Robin)负载均衡,我们只需要在客户端配置多个 graphd 的地址就可以了。比如,我们在创建连接池的时候,指定了两个不同的 graphd 的地址(对应不同进程实例),下面以 Python 代码为例:
from nebula3.gclient.net import ConnectionPoolfrom nebula3.Config import Configconfig = Config()config.max_connection_pool_size = 10connection_pool = ConnectionPool()connection_pool.init([("127.0.0.1", 9669), ("127.0.0.1", 49433)], config)
在取得连接的时候,就会从连接池中随机取得一个连接:
In [10]: connection0 = connection_pool.get_connection()In [11]: connection1 = connection_pool.get_connection()# 这两个连接的 graphd 地址是不同的In [12]: connection0._port, connection1._portOut[12]: (9669, 49433)
这种客户端负载均衡的问题在于配置、实现细节与应用代码耦合在一起,如果需要修改负载均衡的策略,就要修改应用代码,这样就会增加应用的复杂度。
代理的负载均衡
基于代理的负载均衡,就是在应用程序之前,增加一个代理层,来实现负载均衡的逻辑。这样,应用程序就不需要关心负载均衡的问题了。在 K8s 里的话,我们可以使用 K8s 的 Service 来实现这个代理层。
这是一个在 Minikube 中为 NebulaGraph 集群中 graphd 创建的 Service:
cat <
创建后,我们就可以通过它暴露的单独端口来访问 NebulaGraph 集群中的 graphd 了:
In [13]: connection_pool = ConnectionPool() ...: connection_pool.init([("192.168.49.2", 9669)], config)Out[13]: TrueIn [14]: connection0 = connection_pool.get_connection()In [15]: connection1 = connection_pool.get_connection()In [16]: connection0._ip, connection1._ipOut[16]: ("192.168.49.2", "192.168.49.2")
可以看到,在连接层面上来看,客户端只知道代理的地址,而不知道 NebulaGraph 集群中的 graphd 的地址,这样就实现了客户端与 NebulaGraph 集群中的 graphd 的解耦。
然而,当我们在 Connection 之上创建 Session 的时候,就能看到实际上客户端的不同请求是落在了不同的 graphd 上的:
In [17]: session = connection_pool.get_session("root", "nebula")In [18]: session._session_idOut[18]: 1668670607568178In [19]: session1 = connection_pool.get_session("root", "nebula")In [20]: session1._session_idOut[20]: 1668670625563307# 得到每一个 session 的 IDIn [21]: session.execute("SHOW SESSIONS")# 它们分别对应了两个不同的 graphd 实例Out[21]: ResultSet(keys: ["SessionId", "UserName", "SpaceName", "CreateTime", "UpdateTime", "GraphAddr", "Timezone", "ClientIp"], values: [1668670607568178, "root", "", utc datetime: 2022-11-17T07:36:47.568178, timezone_offset: 0, utc datetime: 2022-11-17T07:36:47.575303, timezone_offset: 0, "nebula-graphd-0.nebula-graphd-svc.default.svc.cluster.local:9669", 0, "172.17.0.1"],[1668670625563307, "root", "", utc datetime: 2022-11-17T07:37:05.563307, timezone_offset: 0, utc datetime: 2022-11-17T07:37:03.638910, timezone_offset: 0, "nebula-graphd-1.nebula-graphd-svc.default.svc.cluster.local:9669", 0, "172.17.0.1"])
底层存储接口的暴露
在 NebulaGraph 中,可以通过 StorageClient 来访问底层的存储接口,这个接口可以用来做一些分析型、数据全扫描计算的工作。
然而,存储层的分布式服务实例不像 graphd 那样,它们是有状态的。这其实与 K8s 或者 Docker Compose 的部署模型是相违背的。如果访问的应用 storaged 客户端在集群外部,我们需要在 NebulaGraph 集群中的每一个存储实例上都部署一个代理 Service。这非常不方便,有时候还是一种浪费。
此外,由于 NebulaGraph 内部服务发现机制和 storaged 客户端的实现机制决定,每一个 storaged 服务实体都是由其内部的 host:port
唯一确定和寻址的,这给我们中间的代理工作也带来了一些麻烦。
总结来看,我们的需求是:
- 能够从集群外部访问 NebulaGraph 的存储层每一个实例
- 每一个实例的访问地址(host:port)和内部的地址是完全一致的
为了实现这个需求,我之前的做法是为每一个实例单独部署一个 graphd 代理(消耗一个地址,保证端口不变),再在外部手动搭一个 Nginx 作为代理,配合 DNS 把内部的地址解析 Nginx 上,然后通过域名找到上游(每一个单独的 graphd 代理)。本文的延伸阅读 1、2 中给出了相关的实验步骤。
最近,我找到了一个相对优雅的可维护的方式:
- 在 NebulaGraph 集群同一个命名空间下引入一个 APISIX 网关;
- 利用 APISIX 中的 Nginx TCP 代理的封装 stream-proxy 来暴露 storaged 的接口;
- 为了最终只利用一个集群的出口(Service,我们利用其支持的 TLSv1.3 中的 extend host name 字段:SNI 来路由上游),做到用不同域名的 TCP over TLS 指向后端的不同 storaged;
- 只需要 Storage 客户端能支持 TLSv1.3(发送 SNI),并且能解析所有 storaged 的地址到 APISIX 的 Service 上即可;
示例图:
┌────────────────────────────────────────────────────────────────────────────────────┐ │ K8s Cluster │ │ ┌──────────────────────────┐ │ │ ┌────────────────────────────────────┐ │ NebulaGraph Cluster │ │ │ │ APISIX API-GATEWAY │ │ ┌──────────────┐ │ │ │ │ │ │ │ storaged-0 │ │ │ │ │ │ ┌────┼──────▶│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌────────────────────────────┐ │ │ │ └──────────────┘ │ │ │ │ │ stream-proxy │ │ │ │ │ │ ┌─────┐ │ .─────. │ │ ┌────┐ │ │ │ │ ┌──────────────┐ │ │ │ │ │╱ ╲ │ │ - addr: 9559 │ │──────┼───┼─┘ │ │ storaged-1 │ │ │━━┫ DNS ┣━━( Service )╋━━━╋▶ tls: true │ │ │ │ ┌────┼──────▶│ │ │ │ │ │ │`. ," │ │ │ │──────┼───┼─┘ │ │ │ │ │ └─────┘ │ `───" │ │ │ │ │ │ │ └──────────────┘ │ │ │ │ │ │SNI │ │ │ │ │ │ │ │ │ │ │──────┼───┼─┐ │ ┌──────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ storaged-2 │ │ │ │ │ │ │ │ │ │ └────┼──────▶│ │ │ │ │ │ │ │ │──────┼───┼─┐ │ │ │ │ │ │ │ │ └────┘ │ │ │ │ └──────────────┘ │ │ │ │ └────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ storaged-3 │ │ │ │ │ │ └────┼──────▶│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────┘ │ │ │ └────────────────────────────────────┘ └──────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────────┘
这样做的好处是:
- 在 APISIX 中比较优雅地维护代理的配置,并且可以用到 APISIX 现代化的流量管理能力;
- 不需要为每一个 storaged 单独创建 Service,只需要一个 Service、集群地址就可以了;
- 为流量增加了 TLSv1.3 的加密,提高了安全性。同时,没有给 NebulaGraph 集群内部的南北流量带来的性能损耗;
在本文的结尾,给出了实验过程,包含了本文提到的所有要点和细节。
传输层的加密
我们在前一个问题中提及到了,在 APISIX 网关中 terminate TLSv1.3 的连接,借助 SNI 信息路由 storaged 的方法。其实,单独将 graphd 接口的 TLS 交给网关来做,好处也是非常明显的:
- 证书管理在统一的网关控制面做,更加方便;
- 证书运维无 NebulaGraph 集群配置侵入(NebulaGraph 原生支持 TLS 加密,但是加密之后带来了集群内部通信的开销,而且配置和集群其他层面配置在一起,证书更新涉及进程重启,不够灵活);
具体的方法在后边实操中也是有体现的。
实操:利用 APISIX 的 stream-proxy 暴露 storaged 的接口
实验环境:Minikube
本实验在本地的 Minikube 上做。首先,启动一个 Minikube。因为 APISIX 内部的 etcd 需要用到 storageclass,我们带上穷人版的 storageclass 插件。同时,为了在 K8s 外部访问 storaged 的时候用和内部相同的域名和端口,将把 node-port
允许的端口扩充到小于 9779 的范围。
--addons="default-storageclass" \ --extra-config=apiserver.service-node-port-range=1-65535
实验环境:NebulaGraph on K8s
这里,我们使用 Nebula Operator 来部署 NebulaGraph 集群,具体的部署方法可以参考 Nebula Operator 文档:https://docs.nebula-graph.com.cn/3.3.0/nebula-operator/1.introduction-to-nebula-operator/。
咱们做实验,就偷个懒,用我写的 Nebula-Operator-KinD 来一键部署:
curl -sL nebula-kind.siwei.io/install-on-K8s.sh | bash
实验环境:APISIX on K8s
首先,是安装。在 Helm 参数中指定打开 stream-proxy 的开关:
helm repo add apisix https://charts.apiseven.comhelm repo add bitnami https://charts.bitnami.com/bitnamihelm repo updatehelm install apisix apisix/apisix \ --set gateway.type=NodePort \ --set gateway.stream.enabled=true \ --set ingress-controller.enabled=true# dashboard 也装上,方便我们绕过 admin API call 做一些方便的操作。helm install apisix-dashboard apisix/apisix-dashboard
因为截止到现在,APISIX 的 Helm Chart 之中并没有提供 stream-proxy TCP 的监听端口的 TLS 支持的配置格式,见:https://github.com/apache/apisix-helm-chart/issues/348。我们需要手动更改 APISIX 的 ConfigMap,把 stream-proxy 的 TLS 配置加上:
kubectl edit ConfigMap apisix
我们编辑把 stream_proxy.tcp
改写成这样:
stream_proxy: # TCP/UDP proxy only: false tcp: # TCP proxy port list - addr: 9779 tls: true - addr: 9559 tls: true
这里我们需要重建 APISIX Pod,因为 APISIX 的 stream-proxy 的 TLS 配置是在启动的时候加载的,所以我们需要重建 APISIX Pod:
kubectl delete $(kubectl get po -l "app.kubernetes.io/name=apisix" -o name)
开始实验
这个实验的目标是把 NebulaGraph 的 storaged 的接口暴露出来,让外部的客户端可以访问到,而暴露的方式如图:
┌────────────────────────────────────────────────────────────────────────────────────┐ │ K8s Cluster │ │ ┌──────────────────────────┐ │ │ ┌────────────────────────────────────┐ │ NebulaGraph Cluster │ │ │ │ APISIX API-GATEWAY │ │ ┌──────────────┐ │ │ │ │ │ │ │ storaged-0 │ │ │ │ │ │ ┌────┼──────▶│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌────────────────────────────┐ │ │ │ └──────────────┘ │ │ │ │ │ stream-proxy │ │ │ │ │ │ ┌─────┐ │ .─────. │ │ ┌────┐ │ │ │ │ ┌──────────────┐ │ │ │ │ │╱ ╲ │ │ - addr: 9559 │ │──────┼───┼─┘ │ │ storaged-1 │ │ │━━┫ DNS ┣━━( Service )╋━━━╋▶ tls: true │ │ │ │ ┌────┼──────▶│ │ │ │ │ │ │`. ," │ │ │ │──────┼───┼─┘ │ │ │ │ │ └─────┘ │ `───" │ │ │ │ │ │ │ └──────────────┘ │ │ │ │ │ │SNI │ │ │ │ │ │ │ │ │ │ │──────┼───┼─┐ │ ┌──────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ storaged-2 │ │ │ │ │ │ │ │ │ │ └────┼──────▶│ │ │ │ │ │ │ │ │──────┼───┼─┐ │ │ │ │ │ │ │ │ └────┘ │ │ │ │ └──────────────┘ │ │ │ │ └────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ storaged-3 │ │ │ │ │ │ └────┼──────▶│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────┘ │ │ │ └────────────────────────────────────┘ └──────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────────┘
我们已经有了所有的框架,我们要往里填箭头和圆圈就行。
$ kubectl get poNAME READY STATUS RESTARTS AGEapisix-6d89854bc5-5m788 1/1 Running 1 (31h ago) 2d4hapisix-dashboard-b544bd766-nh79j 1/1 Running 8 (31h ago) 2d10hapisix-etcd-0 1/1 Running 2 (31h ago) 2d10hapisix-etcd-1 1/1 Running 2 (31h ago) 2d10hapisix-etcd-2 1/1 Running 2 (31h ago) 2d10hnebula-graphd-0 1/1 Running 2 (31h ago) 3d4hnebula-metad-0 1/1 Running 2 (31h ago) 3d4hnebula-storaged-0 1/1 Running 2 (31h ago) 3d4hnebula-storaged-1 1/1 Running 2 (31h ago) 3d4hnebula-storaged-2 1/1 Running 2 (31h ago) 3d4h
配置 APISIX 的 stream-proxy
参考 APISIX 文档:https://apisix.apache.org/docs/apisix/stream-proxy/#accept-tls-over-tcp-connection。
我们用 APISIX 的 API 来配置 stream-proxy:
apisix_api_key="edd1c9f034335f136f87ad84b625c8f1"apisix_pod=$(kubectl get po -l \ "app.kubernetes.io/name=apisix" -o name)kubectl exec -it $apisix_pod -- \ curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 \ -H "X-API-KEY: $apisix_api_key" -X PUT -d \"{ "sni": "nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local", "upstream": { "nodes": { "172.17.0.13:9779": 1 }, "type": "roundrobin" }}"kubectl exec -it $apisix_pod -- \ curl http://127.0.0.1:9180/apisix/admin/stream_routes/2 \ -H "X-API-KEY: $apisix_api_key" -X PUT -d \"{ "sni": "nebula-storaged-1.nebula-storaged-headless.default.svc.cluster.local", "upstream": { "nodes": { "172.17.0.18:9779": 1 }, "type": "roundrobin" }}"kubectl exec -it $apisix_pod -- \ curl http://127.0.0.1:9180/apisix/admin/stream_routes/3 \ -H "X-API-KEY: $apisix_api_key" -X PUT -d \"{ "sni": "nebula-storaged-2.nebula-storaged-headless.default.svc.cluster.local", "upstream": { "nodes": { "172.17.0.5:9779": 1 }, "type": "roundrobin" }}"
这里需要注意,目前,APISIX 的 stream-proxy 上游节点不支持域名解析是受限于上游的 lua 库,详见 issue:https://github.com/apache/apisix/issues/8334。理想情况下,这里应该给出每一个 storaged 的 SNI 相同的地址作为 upstream.nodes
。像这样:
kubectl exec -it $apisix_pod -- \ curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 \ -H "X-API-KEY: $apisix_api_key" -X PUT -d \"{ "sni": "nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local", "upstream": { "nodes": { "nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local": 1 }, "type": "roundrobin" }}"
配置 APISIX 中 storaged 地址的 TLS 证书
在生产环境下,我们应该以云原生的方式去管理自签或者公共信任的证书。这里,我们就手动利用 MKCert 工具来做这件事儿。
安装 MKCert:
# 首次运行,需要安装 mkcert,并且生成根证书# macOS 的话brew install mkcert# ubuntu 的话apt-get install wget libnss3-tools# 然后再去 https://github.com/FiloSottile/mkcert/releases/ 下载 mkcert
签发证书:
mkcert "*.nebula-storaged-headless.default.svc.cluster.local"
利用 APISIX Dashboard 将证书导入到 APISIX 之中。单独开一个终端,运行:
export POD_NAME=$(\ kubectl get pods \ -l "app.kubernetes.io/name=apisix-dashboard,app.kubernetes.io/instance=apisix-dashboard" \ -o jsonpath="{.items[0].metadata.name}")export CONTAINER_PORT=$(\ kubectl get pod $POD_NAME \ -o jsonpath="{.spec.containers[0].ports[0].containerPort}")kubectl \ port-forward $POD_NAME 8080:$CONTAINER_PORT --address="0.0.0.0"
浏览器访问:http://10.1.1.168:8080/ssl/list,账号密码都是 admin
。点击 Create
按钮,将刚刚生成的证书导入到 APISIX 之中。
增加 APISIX 的 NodePort Service
创建一个 NodePort Service,用于暴露 APISIX 的 9779 端口。这样,我们就可以通过外部的 IP 地址访问到 APISIX 了。
cat <
因为前边 Minikube 中我们配置了端口的范围覆盖到了 9779,所以我们可以看到,这个 NodePort Service 的端口在宿主机上也可以从 Minikube ip 的同一个端口访问到:
$ minikube service apisix-svc$ minikube service list|------------------------|---------------------------------|-------------------|---------------------------|| NAMESPACE | NAME | TARGET PORT | URL ||------------------------|---------------------------------|-------------------|---------------------------|...| default | apisix-svc | thrift/9779 | http://192.168.49.2:9779 |<---...|------------------------|---------------------------------|-------------------|---------------------------|
当然,Minikube 假设我们的服务都是 HTTP 的,给出的 URL 是 HTTP://
的。不用理会它,我们心里知道它是 TCP over TLS 就好了。
配置 K8s 外部 DNS
这里需要配置一个 DNS 服务,让我们可以通过 nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local
等三个域名通过 Minikube 的 NodePort Service 访问到 NebulaGraph 的 storaged 服务。
获得 Minikube 的 IP 地址:
$ minikube ip192.168.49.2
配置 /etc/hosts
192.168.49.2 nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local192.168.49.2 nebula-storaged-1.nebula-storaged-headless.default.svc.cluster.local192.168.49.2 nebula-storaged-2.nebula-storaged-headless.default.svc.cluster.local192.168.49.2 nebula-metad-0.nebula-metad-headless.default.svc.cluster.local
验证 NebulaGraph Storage Client 可以从所有的节点中获取到数据
这里,为了方便,我们用到 Python 的客户端。
由于在写本文的时候,NebulaGraph Python 客户端的 StorageClient 尚未支持 TLS,对它支持的 PR 刚好是我为了本实验写的:https://github.com/vesoft-inc/nebula-python/pull/239。
所以,这里从个人分支安装这个客户端:
git clone https://github.com/wey-gu/nebula-python.gitcd nebula-pythonpython3 -m pip install .python3 -m pip install ipython# 进入 ipythonipython
我们在 iPython 中交互式验证:
from nebula3.mclient import MetaCache, HostAddrfrom nebula3.sclient.GraphStorageClient import GraphStorageClientfrom nebula3.Config import SSL_configimport sslimport osmeta_cache = MetaCache([("nebula-metad-0.nebula-metad-headless.default.svc.cluster.local", 9559)], 50000)storage_addrs = [HostAddr(host="nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local", port=9779), HostAddr(host="nebula-storaged-1.nebula-storaged-headless.default.svc.cluster.local", port=9779), HostAddr(host="nebula-storaged-2.nebula-storaged-headless.default.svc.cluster.local", port=9779)]# 自签证书配置current_dir = os.path.abspath(".")ssl_config = SSL_config()ssl_config.cert_reqs = ssl.CERT_OPTIONALssl_config.cert_reqs = ssl.CERT_OPTIONALssl_config.ca_certs = os.path.join( os.path.expanduser("~/.local/share/mkcert"), "rootCA.pem")ssl_config.keyfile = os.path.join( current_dir, "nebula-storaged-headless.default.svc.cluster.local+1-key.pem")ssl_config.certfile = os.path.join( current_dir, "nebula-storaged-headless.default.svc.cluster.local+1.pem")# 实例化 StorageClientgraph_storage_client = GraphStorageClient(meta_cache, storage_addrs, 5000, ssl_config)# 验证可以从所有的节点中获取到数据resp = graph_storage_client.scan_vertex( space_name="basketballplayer", tag_name="player")while resp.has_next(): result = resp.next() for vertex_data in result: print(vertex_data)
结果✅:
("player112" :player{name: "Jonathon Simmons", age: 29})("player117" :player{name: "Stephen Curry", age: 31})("player119" :player{name: "Kevin Durant", age: 30})("player134" :player{name: "Blake Griffin", age: 30})("player141" :player{name: "Ray Allen", age: 43})("player144" :player{name: "Shaquille O"Neal", age: 47})("player149" :player{name: "Ben Simmons", age: 22})("player100" :player{name: "Tim Duncan", age: 42})("player101" :player{name: "Tony Parker", age: 36})("player110" :player{name: "Cory Joseph", age: 27})("player126" :player{name: "Kyrie Irving", age: 26})("player131" :player{name: "Paul George", age: 28})("player133" :player{name: "Yao Ming", age: 38})("player140" :player{name: "Grant Hill", age: 46})("player105" :player{name: "Danny Green", age: 31})("player109" :player{name: "Tiago Splitter", age: 34})("player111" :player{name: "David West", age: 38})...
总结
- NebulaGraph 查询接口的负载均衡可以借助 K8s Service来做;
- NebulaGraph 底层存储接口的暴露在 K8s 中可以利用 APISIX Stream Proxy 和 SNI 来优雅实现;
- 利用 API 网关对出口传输层的加密是一个很好的选择,相较于用 NebulaGraph 原生的 TLS 的方式。
一些坑
fbthrift Python 并不支持发送 extend host name(SNI):https://github.com/vesoft-inc/nebula-python/pull/238,写了 PR 去做支持。这时候 APISIX 中的报错是 failed to find SNI
:
2022/11/15 10:18:26 [error] 78#78: *1744270 stream [lua] init.lua:842: stream_ssl_phase(): failed to fetch ssl config: failed to find SNI: please check if the client requests via IP or uses an outdated protocol. If you need to report an issue, provide a packet capture file of the TLS handshake., context: ssl_certificate_by_lua*, client: 172.17.0.1, server: 0.0.0.0:9779
参考延伸阅读的 3-6。
此外,我还发现 APISIX stream 里边不解析上游 node 域名,我查了所有 DNS 都没有问题,去提了 issue 才知道是已知问题:https://github.com/apache/apisix/issues/8334,只好先手配 IP:Port
作罢。
2022/11/15 12:26:59 [error] 44#44: *9538531 stream [lua] resolver.lua:47: parse_domain(): failed to parse domain: nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local, error: failed to query the DNS server: dns client error: 101 empty record received while prereading client data, client: 172.17.0.1, server: 0.0.0.0:97792022/11/15 12:26:59 [error] 44#44: *9538531 stream [lua] upstream.lua:79: parse_domain_for_nodes(): dns resolver domain: nebula-storaged-0.nebula-storaged-headless.default.svc.cluster.local error: failed to query the DNS server: dns client error: 101 empty record received while prereading client data, client: 172.17.0.1, server: 0.0.0.0:97792022/11/15 12:26:59 [error] 44#44: *9538531 stream [lua] init.lua:965: stream_preread_phase(): failed to set upstream: no valid upstream node while prereading client data, client: 172.17.0.1, server: 0.0.0.0:9779
延伸阅读
- https://gist.github.com/wey-gu/950e4f4c673badae375e59007d80d372
- https://gist.github.com/wey-gu/699b9a2ef5dff5f0fb5f288d692ddfd5
- https://docs.python.org/3/library/ssl.html#ssl.SSLContext.sslsocket_class
- https://github.com/apache/thrift/commit/937228e030569bf25ceb379c9491426709792701
- https://github.com/apache/thrift/pull/894
- https://github.com/apache/thrift/blob/e8353cb46e9f5e71f9b76f55d6bf59530b7f98ef/lib/py/src/transport/TSSLSocket.py#L184
谢谢你读完本文(///▽///)
要来近距离体验一把图数据库吗?现在可以用用 NebulaGraph Cloud 来搭建自己的图数据系统哟,快来节省大量的部署安装时间来搞定业务吧~ NebulaGraph 阿里云计算巢现 30 天免费使用中,点击链接来用用图数据库吧~
想看源码的小伙伴可以前往 GitHub 阅读、使用、(з)-☆ star 它 -> GitHub;和其他的 NebulaGraph 用户一起交流图数据库技术和应用技能,留下「你的名片」一起玩耍呢~
-
当云原生网关遇上图数据库,NebulaGraph 的 APISIX 最佳实践
本文介绍了利用开源API网关APISIX加速NebulaGraph多个场景的落地最佳实践:负载均衡、暴露接口结构与TLSTermination。
来源: -
天天看点:LOJ 6041 「雅礼集训 2017 Day7」事情的相似度 题解 (SAM+启发式合并)
题目链接首先很容易想到的是对反串求SA和LCP,然后询问就是求起点在某个区间内的所有后缀两两LCP的最大...
来源: -
一分钟搞定Netty 三大组件,如果搞不定,再看3遍
1 三大组件简介Channel与BufferJavaNIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开...
来源: 当云原生网关遇上图数据库,NebulaGraph 的 APISIX 最佳实践
天天看点:LOJ 6041 「雅礼集训 2017 Day7」事情的相似度 题解 (SAM+启发式合并)
当前热讯:WinNTSetup V5.3.0 Bata5 单文件版
一分钟搞定Netty 三大组件,如果搞不定,再看3遍
世界播报:《阿凡达2》差的远!2022国内电影票房前10:第一超40亿
前沿热点:折叠旗舰卖到白菜价!moto razr 2022宣布调价至4999元
美国人钱包年末又迎重击!极端寒潮导致上周电价飙升超6000%
天天时讯:或售70万对刚比亚迪!东风猛士M-Terrain量产实车曝光:凶悍
只有Redmi做到了!米粉没想到2022年2500元的手机都有无线充电
环球热文:隐私计算之多方安全计算(MPC,Secure Multi-Party Computation)
河南郑新黄河大桥因大雾多车相撞:涉及200多辆车
速递!安卓手机不卡顿!一加11内存基因重组技术揭秘:数据抓取量提升16倍
特斯拉股价年内暴跌70% 韩国散户疯狂抄底!背后原因不简单
女生病假期上9天班反欠公司三百多:被扣10天工资
环球速看:曝特斯拉上海工厂将在1月实施减产计划 原因未知
直降120:百度网盘超级会员12个月SVIP 178元大促
今日关注:交叉编译esp8089
【环球新要闻】Python中itertools详解
playgo是什么意思?playgo是什么牌子?
十年之痒是什么意思?十年之痒的婚姻感悟小说有哪些?
【天天聚看点】女子投资100万元 本金4年仅剩1.71万元!基金经理被集体起诉
神价手慢无:OATLY噢麦力燕麦奶1L*2瓶/19.9元抄底
焦点速读:轿车加气站去加气 一开后备厢车被炸报废
诚意碾压苹果官网!京东开启年终优惠:iPhone 14直降900元
用户已破6亿!钉钉7.0版本发布:解决产业链协同问题
室内地坪是什么意思?室内地坪漆用什么颜色?
苏武留胡节不辱是什么意思?苏武的精神品质是什么?
调配奶粉是什么意思?调制奶粉的营养价值有哪些?
亚热带水果有哪些?亚热带水果的生长环境有哪些?
拜托小姐大结局是什么?拜托小姐演员表
东非大裂谷是哪两个板块张裂形成的?东非大裂谷形成的原因是什么?
龙应台的作品有哪些?龙应台最经典的句子
乌龟和老鹰的寓意是什么?乌龟和老鹰告诉我们什么道理?
【世界热闻】Python实验报告(第8章)
全球观速讯丨Java HashMap原理
天天亮点!认证管理(锐捷交换篇)
天天快看:[PHP]用socket写一个简单的WEB服务器
游戏盒子哪个好?2022年游戏盒子排行榜
无线网络受限或者是无连接是什么原因?无线网受限制或无连接怎么办?
网上订火车票如何取票?网上订火车票用什么软件最好?
小米2s和小米2的区别有哪些?小米2s开不了机怎么办?
网通玩电信游戏卡是什么原因?网通玩电信游戏卡怎么办?
849元 墨案电纸书Air发布:24级冷暖光、30天长续航
焦点简讯:亿万富翁芒格:别再抱怨了、现在的生活比过去好了5倍多
天天报道:比亚迪推出“疯狂星期三”活动:奖品够香 附答案!
落差很大 五菱“电动吉姆尼”谍照曝光:像老头乐
全球要闻:Redmi K60系列最香版本!米粉评K60:2499元无敌 把门焊死了
天天速讯:「实操」结合图数据库、图算法、机器学习、GNN 实现一个推荐系统
天天短讯!HTML 常用标签 tag
Python爬虫实战,requests+openpyxl模块,爬取小说数据并保存txt文档(附源码)
关注:面试官问:为啥不建议使用 Select *?请你大声地回答他!!
即时:DAG任务调度系统 Taier 演进之道,探究DataSourceX 模块
【天天速看料】苹果推出跨年优惠 其实一点也没便宜
时讯:株洲卡丁车比赛事故致车手死亡后续 车手俱乐部声讨场地方
全球今日讯!彻底重构安卓内存底层!一加11将全球首发“内存基因重组”技术
天天微资讯!马斯克被曝内部圈子缺乏持不同观点的人:全是马屁精
全球滚动:K60发布后 倪飞发声:努比亚Z50才是旗舰焊门员
世界观点:RTX 4090玩游戏性能过剩 外星人懂了:将推500Hz高刷显示器
苹果股价三连跌创一年多来最低:2022累计蒸发27%
性能仅比3080略强?RTX 4070 Ti售价曝光 7199元你还可能买不到
全球快资讯:6年过去了:一代神卡GTX 1060依然值得入手
天天微头条丨平价神器!新iPad mini曝光:苹果加量不加价、还在密谋折叠屏惊喜
环球快看点丨马斯克赔哭 公司股价腰斩!消息称特斯拉上海工厂又要减产 卖不动国人不敢兴趣?
速看:中国GPU欲弯道超车:国产显卡向AMD、NV发起冲击
【天天新要闻】实现小程序onShow第一次页面加载不执行
Java String类为什么用final修饰
天天通讯!Spring IOC官方文档学习笔记(五)之bean的作用域
【播资讯】华硕发布灵耀AX小魔方Pro分布式路由:双频3000M 449元
威联通发布TS-1655 NAS:可塞入16块硬盘 超级别墅
全球热文:男子嫌妻子开车水平差 醉驾被查:这下5年内别嫌弃了
一家比苹果还赚钱的日本企业:日本人自己都不知道
快讯:一文带你入门Transformer
全球观焦点:飞机票+高铁票可以一起买了:仅一笔支付 支持30个城市
今日要闻!听吐了!一对夫妻决定众筹买下《圣诞曲》版权将其下架
观点:国际空间站宇宙飞船发生泄漏 俄罗斯公布原因:外部机械性损坏
AcWing. 1165.单词环
全球热推荐:绚丽又冷静!酷冷至尊莫比乌斯风扇评测:马力全开也只有60分贝
当前资讯!剧情澎湃!张艺谋新片《满江红》定档春节 双男主沈腾+易烊千玺
【天天报资讯】字节掐准了商家命门
全球即时看![数据结构]单向链表的翻转(C语言)
【世界速看料】诺基亚、爱立信宣布退出 俄罗斯移动网路恐将倒退30年
速读:日系车笑了:国人把丰田买成了全球第一
每日速递:差价800元 Redmi K60与K60 Pro如何选?这三点不同
《王者荣耀》吕布FMVP皮肤来了:2023年第一款皮肤
观点:与微念和解 停更531天的李子柒何时复出?回应来了
全球播报:FreeSWITCH编译加载新模块
python学习: fire库的使用教程
可能是Redmi最帅手机!Redmi K60 Pro亮相
焦点速讯:卢伟冰:Redmi K60 Pro干掉了电竞手机 将被市场淘汰
支付宝迎来“史诗级”更新:深色模式终于开启测试
天降横财!土耳其在黑海发现580亿立方米天然气
焦点讯息:日本N多影院设备陈旧 无法播放《阿凡达2》48帧版:影迷大规模退票
window10/window11不能登录微软账户等
焦点速递!cmd命令curl的简单使用以及通过ip查所对应地址的方法
环球今亮点!Gateway
相当差劲!中汽研公布新一批碰撞测试:合创Z03仅获三星
博主称餐厅虚假营销:抓起帝王蟹扯腿吃掉
世界通讯!十年N饭称AMD显卡驱动有一个优点 就是不太稳定
全球消息!轻松瓦解重油污 奥妙桂花青梅香型洗洁精4 x 1.1KG 29.9元
鹤岗铁路开通运营:“绿巨人”开到中国最北端!时速160公里