使用客户端路由时 Neo4j 集群的外部暴露
本章介绍如何在使用客户端路由时,将来自外部世界或互联网的流量路由到在 Kubernetes 中运行的 Neo4j 集群。
通常,仅当 Neo4j 版本早于 4.3.0 时才需要这些说明。如果您使用的是 Neo4j 4.3.0 或更高版本,请查看外部暴露说明
概述/问题
如用户指南中所述,默认情况下,在安装 Neo4j 时,集群中的每个节点都会获得一个私有的内部 DNS 地址,并将其广告给其客户端。
这在“开箱即用”的情况下无需了解您的本地寻址或 DNS 情况即可正常工作。缺点是外部客户端无法使用 bolt+routing 或 neo4j 协议连接到集群,因为它们无法将流量路由到严格的集群内部 DNS 名称。使用默认的 helm 安装,即使正确暴露了 Pod,来自外部的连接也会失败,因为
-
客户端连接到 Neo4j
-
获取路由表,其中包含类似
graph-neo4j-core-0.graph-neo4j.default.svc.cluster.local
的条目 -
外部客户端尝试连接到路由表条目但失败
-
总体连接失败或超时。
本文深入讨论了这些背景问题。这些说明旨在作为一种快速暴露 Neo4j 集群的方法,但您可能需要根据您的配置进行其他工作。
解决方案方法
要修复外部客户端,我们需要两件事
-
每个 Neo4j 节点内部的
dbms.connector.*_address
设置为外部可路由地址 -
客户端可以连接到的外部有效 DNS 名称或 IP 地址,该地址将流量路由到 Kubernetes Pod
有关正在发生的事情的一些可视化图表可以在此处找到体系结构文档。
我们将通过对 Neo4j Pod 本身进行一些特殊配置来解决第 1 点。我将首先解释 Neo4j 配置位,然后我们将将其与外部连接起来。其中最复杂的部分是确保每个 Pod 都有正确的配置。
我们将通过 Kubernetes 负载均衡器来解决第 2 点。我们将在 Neo4j StatefulSet 中为每个 Pod 创建一个负载均衡器。我们将静态 IP 地址与这些负载均衡器相关联。这使得数据包能够从 Kubernetes 外部流向正确的 Pod/Neo4j 集群成员。
正确的 Neo4j Pod 配置
在该存储库中的 helm 图表中,Neo4j 核心成员是 StatefulSet 的一部分,并获得索引。在特定命名空间中进行部署后,您将获得以下主机名
-
<deployment>-neo4j-core-0.<deployment>-neo4j.<namespace>.svc.cluster.local
-
<deployment>-neo4j-core-1.<deployment>-neo4j.<namespace>.svc.cluster.local
-
<deployment>-neo4j-core-2.<deployment>-neo4j.<namespace>.svc.cluster.local
此存储库中的 helm 图表可以获取可配置的 ConfigMap 以在这些 Pod 上设置 env 变量。因此,我们可以定义自己的配置并将其传递给启动时的 StatefulSet。此目录中的 custom-core-configmap.yml
文件就是此示例。
为入站集群流量创建静态 IP 地址
我正在使用 GCP,因此它是这样完成的。这里的重要说明,在 GCP 上,区域必须与您的 GKE 区域匹配,并且网络层必须是高级的。在其他云上,这里的概念步骤相同,但细节会有所不同:您需要分配 3 个静态 IP 地址,我们将在后面的步骤中使用。
# Customize these next 2 for the region of your GKE cluster,
# and your GCP project ID
REGION=us-central1
PROJECT=my-gcp-project-id
for idx in 0 1 2 ; do
gcloud compute addresses create \
neo4j-static-ip-$idx --project=$PROJECT \
--network-tier=PREMIUM --region=$REGION
echo "IP$idx:"
gcloud compute addresses describe neo4j-static-ip-$idx \
--region=$REGION --project=$PROJECT --format=json | jq -r '.address'
done
如果您使用 Azure 进行此操作,请注意,静态 IP 地址必须与您的 Kubernetes 集群位于同一资源组中,并且可以使用 az network public-ip create 创建,如下所示(仅一个示例):az network public-ip create -g resource_group_name -n core01 --sku standard --dns-name neo4jcore01 --allocation-method Static
。使用的 Azure SKU 必须是标准的,您需要的资源组可以在 Kubernetes 负载均衡器中找到,该负载均衡器[按照 Azure 教程](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough)为您设置。
在本教程的其余部分,让我们假设我在这里分配的核心 IP 地址如下;我将它们称为这些环境变量
export IP0=35.202.123.82
export IP1=34.71.151.230
export IP2=35.232.116.39
我们还需要 3 个要向客户端宣传的暴露地址。我将这些设置为与 IP 地址相同,但如果您已映射 DNS,则可以在这里使用 DNS 名称。
对于后面的步骤,我们拥有两个 IP 和地址非常重要,因为它们的使用方式不同。
export ADDR0=$IP0
export ADDR1=$IP1
export ADDR2=$IP2
每个主机配置
回想一下,Helm 图表允许我们使用自定义 ConfigMap 配置核心节点。这很好。但是,使用一个 ConfigMap 适用于所有 3 个核心的问题是,每个主机都需要不同的配置才能正确暴露。因此,在 helm 图表中,我们将 neo4j 设置分为基本设置和可覆盖设置。在自定义 configmap 示例中,您将看到如下所示的行
$DEPLOYMENT_neo4j_core_0_NEO4J_dbms_default__advertised__address: $ADDR0
$DEPLOYMENT_neo4j_core_1_NEO4J_dbms_default__advertised__address: $ADDR0
过一会儿,在将 $DEPLOYMENT 扩展为“graph”后,这些变量将具有“主机前缀” - graph_neo4j_core_0_*
设置仅适用于主机 graph-neo4j-core-0
。(连字符更改为 _,因为连字符在 env 变量命名中不受支持)。非常重要的是要注意,这些覆盖设置已将 Pod 名称/主机名“烘焙”到其中,因此在设置之前了解您计划如何部署 Neo4j 非常重要。
这些“地址设置”需要更改为与我们在上一步中分配的 3 个静态 IP 匹配。有四个关键的 env 变量,所有这些变量都需要为每个主机配置:* NEO4J_dbms_defaultadvertisedaddress
* NEO4J_dbms_connector_bolt_advertisedaddress
* NEO4J_dbms_connector_http_advertised
address
* NEO4J_dbms_connector_https_advertised__address
使用覆盖,即 12 个特殊覆盖(3 个容器的每个 4 个变量)
因此,使用这种“覆盖方法”,我们可以拥有一个 ConfigMap 来指定集群 3 个成员的所有配置,同时仍然允许每个主机的配置设置有所不同。所讨论的覆盖方法在 core-statefulset.yaml
文件中的一小段 bash 中实现。它只是读取环境并应用默认值,如果覆盖与正在应用更改的主机匹配,则允许覆盖。
在下一个命令中,我们将应用自定义 configmap。在这里,您将上一步中的 IP 地址用作 ADDR0、ADDR1 和 ADDR2。或者,如果这些 IP 地址与 DNS 条目关联,则可以使用这些 DNS 名称。我们称它们为地址,因为它们可以是您想要宣传的任何地址,并且不必是 IP。但这些地址必须解析为我们之前步骤中创建的静态 IP。
export DEPLOYMENT=graph
export NAMESPACE=default
export ADDR0=35.202.123.82
export ADDR1=34.71.151.230
export ADDR2=35.232.116.39
cat tools/external-exposure-legacy/custom-core-configmap.yaml | envsubst | kubectl apply -f -
自定义后,我们现在拥有一个 ConfigMap,我们可以将我们的 Neo4j 部署指向它,以便进行正确的宣传。
安装 Helm 图表
从此存储库的根目录中,导航到 stable/neo4j 并发出此命令以使用“graph”的部署名称安装 helm 图表。部署名称必须与您在前面的步骤中执行的操作匹配,因为请记住,我们在上一步中提供了特定于 Pod 的覆盖。
export DEPLOYMENT=graph
helm install $DEPLOYMENT . \
--set core.numberOfServers=3 \
--set readReplica.numberOfServers=0 \
--set core.configMap=$DEPLOYMENT-neo4j-externally-addressable-config \
--set acceptLicenseAgreement=yes \
--set neo4jPassword=mySecretPassword
请注意传递的自定义 configmap。
外部暴露
几分钟后,您将拥有一个完全形成的集群,其 Pod 显示为就绪状态,并且您可以连接到它,**但是**它将发布 Kubernetes 尚未路由的值。因此,接下来我们需要做的是为**每个 Neo4j 核心 Pod**创建一个负载均衡器,并将loadBalancerIP
设置为我们在前面步骤中预留的静态 IP 地址,并使用自定义 ConfigMap 进行发布。
已提供load-balancer.yaml
文件作为模板,以下是如何为给定的静态 IP 地址创建 3 个负载均衡器。
export DEPLOYMENT=graph
# Reuse IP0, etc. from the earlier step here.
# These *must be IP addresses* and not hostnames, because we're
# assigning load balancer IP addresses to bind to.
export CORE_ADDRESSES=($IP0 $IP1 $IP2)
for x in 0 1 2 ; do
export IDX=$x
export IP=${CORE_ADDRESSES[$x]}
echo $DEPLOYMENT with IDX $IDX and IP $IP ;
cat tools/external-exposure-legacy/load-balancer.yaml | envsubst | kubectl apply -f -
done
您会注意到我们正在为 3 个 Pod 使用 3 个负载均衡器。从某种意义上说,为单个 Pod 进行“负载均衡”有点愚蠢。但是,如果没有很多额外的软件和配置,这是最佳选择,因为 LB 将支持 TCP 连接(入口不会),并且 LB 可以获得自己的独立 IP 地址,这些地址稍后可以与 DNS 关联。如果我们使用 NodePorts,我们将受制于更动态的 IP 分配,并且还必须担心 Kubernetes 集群成员本身出现故障。ClusterIP 完全不适用,因为它们不会为您提供外部地址。
在这些服务内部,我们使用externalTrafficPolicy: Local
。因为我们正在路由到单个 Pod 并且不需要任何负载分发,所以本地就足够了。请参阅 Kubernetes 文档以获取有关此主题的更多信息。
还有其他更高级的选项,例如nginx-ingress 控制器,但在此配置中,我们追求尽可能简单的方案,您可以使用现有的 Kubernetes 原语来实现,而无需安装可能尚未安装的新软件包。
**潜在的绊脚石**:在 GKE 上,将静态 IP 与负载均衡器关联所需的唯一操作是在 YAML 中使用此loadBalancerIP 字段。在其他云中,可能需要其他步骤才能将静态 IP 分配给 Kubernetes 集群。请查阅您所在云的本地文档。 |
整合
我们可以像这样验证我们的服务是否运行良好
$ kubectl get service | grep neo4j-external
zeke-neo4j-external-0 LoadBalancer 10.0.5.183 35.202.123.82 7687:30529/TCP,74.3.140843/TCP,7473:30325/TCP 115s
zeke-neo4j-external-1 LoadBalancer 10.0.9.182 34.71.151.230 7687:31059/TCP,74.3.141288/TCP,7473:31009/TCP 115s
zeke-neo4j-external-2 LoadBalancer 10.0.12.38 35.232.116.39 7687:30523/TCP,74.3.140844/TCP,7473:31732/TCP 114s
完成所有这些步骤后,您应该最终获得一个正确公开的集群。我们可以像这样恢复密码,并连接到 3 个静态 IP 中的任何一个。
export NEO4J_PASSWORD=$(kubectl get secrets graph-neo4j-secrets -o yaml | grep password | sed 's/.*: //' | base64 -d)
cypher-shell -a neo4j://34.66.183.174:7687 -u neo4j -p "$NEO4J_PASSWORD"
此外,由于我们公开了 7474 端口,因此您可以转到 7474 端口上的任何静态 IP,并最终获得 Neo4j 浏览器并能够连接。
下一步
-
如果您有静态 IP,则当然可以将 DNS 与它们关联,并获取已签名的证书。
-
这反过来将允许您使用标准 Neo4j 技术公开已签名的证书 HTTPS,并且如果需要,还可以允许发布 DNS 而不是裸 IP。
参考
-
有关一般 Kubernetes 网络公开问题的背景信息,我建议您阅读本文:Kubernetes $TYPE 与 LoadBalancer 与 Ingress?我应该在什么时候使用什么?