使用客户端路由时 Neo4j 集群的外部暴露

本章介绍了当使用客户端路由时,如何将来自外部世界或互联网的流量路由到在 Kubernetes 中运行的 Neo4j 集群。

通常,这些说明仅适用于 Neo4j 4.3.0 版本之前的版本。如果您使用的是 Neo4j 4.3.0 或更高版本,请参阅外部暴露说明

概述 / 问题

正如用户指南所述,默认情况下,当您安装 Neo4j 时,集群中的每个节点都会获得一个私有内部 DNS 地址,并将其通告给客户端。

这“开箱即用”,无需了解您的本地寻址或 DNS 情况。缺点是外部客户端无法使用 bolt+routing 或 neo4j 协议连接到集群,因为它们无法将流量路由到严格意义上的集群内部 DNS 名称。使用默认的 helm 安装,即使 Pod 已经适当暴露,来自外部的连接也会失败,原因如下:

  1. 客户端连接到 Neo4j

  2. 获取路由表,其中包含诸如 graph-neo4j-core-0.graph-neo4j.default.svc.cluster.local 之类的条目

  3. 外部客户端尝试连接到路由表条目并失败

  4. 整体连接失败或超时。

本文深入探讨了这些背景问题。这些说明旨在作为一种快速暴露 Neo4j 集群的方法,但您可能需要根据您的配置进行额外工作。

解决方案方法

要解决外部客户端的问题,我们需要两件事

  1. 每个 Neo4j 节点内部的 dbms.connector.*_address 设置为外部可路由地址

  2. 客户端可以连接的、将流量路由到 kubernetes Pod 的外部有效的 DNS 名称或 IP 地址

关于正在发生的事情的一些视觉图表可以在这里的架构文档中找到

我们将通过对 Neo4j Pod 本身进行一些特殊配置来解决第一点。我将首先解释 Neo4j 的配置部分,然后我们将它与外部连接起来。这其中最复杂的部分是确保每个 Pod 都有正确的配置。

我们将使用 Kubernetes Load Balancer 来解决第二点。我们将在 Neo4j StatefulSet 中的每个 Pod 创建一个 Load Balancer。我们将静态 IP 地址与这些 Load Balancer 相关联。这使得数据包能够从 Kubernetes 外部流向正确的 Pod / Neo4j 集群成员。

正确的 Neo4j Pod 配置

在此仓库的 Helm Chart 中,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 Chart 可以接受可配置的 ConfigMap,用于设置这些 Pod 上的环境变量。因此,我们可以定义自己的配置并在启动时将其传递给 StatefulSet。此目录中的 custom-core-configmap.yml 文件就是示例。

为入站集群流量创建静态 IP 地址

我使用的是 GCP,所以操作如下。这里的重要注意事项是,在 GCP 上,区域必须与您的 GKE 区域匹配,网络层级必须是高级版(premium)。在其他云上,这里的概念步骤是相同的,但细节会有所不同:您需要分配 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 必须是标准版(standard),您需要的资源组可以在 Kubernetes Load Balancer 中找到,该 Load Balancer 是 按照 Azure 教程为您设置的。

在本教程的剩余部分,我们假设我在这里分配的核心 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 Chart 允许我们使用自定义 ConfigMap 配置核心节点。这很好。但对于所有 3 个核心节点使用 1 个 ConfigMap 的问题在于,每个主机需要不同的配置才能正确暴露。因此,在 Helm Chart 中,我们将 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。(破折号已更改为下划线,因为环境变量命名不支持破折号)。非常重要的一点是,这些覆盖设置已经将 Pod 名称/主机名“内置”其中,因此在设置此项之前了解您计划如何部署 Neo4j 非常重要。

这些“地址设置”需要更改以匹配我们在上一步中分配的 3 个静态 IP。对于每个主机,都需要配置四个关键的环境变量: * NEO4J_dbms_default_advertised_address * NEO4J_dbms_connector_bolt_advertised_address * NEO4J_dbms_connector_http_advertised_address * NEO4J_dbms_connector_https_advertised__address

通过覆盖,总共有 12 个特殊覆盖(3 个容器,每个容器 4 个变量)

因此,使用这种“覆盖方法”,我们可以使用 1 个 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 Chart

从此仓库的根目录,导航到 stable/neo4j 并执行此命令安装 Helm Chart,部署名称为“graph”。部署名称必须与您在之前步骤中设置的匹配,因为请记住我们在上一步中进行了针对 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 是很愚蠢的。但如果没有大量的额外软件和配置,这是最好的选择,因为 LBs 支持 TCP 连接(Ingress 不支持),并且 LBs 可以获得独立的 IP 地址,稍后可以与 DNS 关联。如果我们使用 NodePorts,我们将受制于更动态的 IP 分配,并且还需要担心 Kubernetes 集群成员本身出现故障。ClusterIP 完全不适用,因为它们不提供外部地址。

在这些服务内部,我们使用 externalTrafficPolicy: Local。因为我们路由到单个 Pod 并且不需要任何负载分散,所以 local 是没问题的。有关此主题的更多信息,请参阅 Kubernetes 文档

还有其他更高级的选项,例如 nginx-ingress controller,但在这种配置中,我们力求尽可能简单,以便您可以使用现有的 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,您可以在任何一个静态 IP 的 7474 端口上访问 Neo4j Browser 并进行连接。

下一步

  • 如果您有静态 IP,您当然可以将 DNS 与它们关联,并获取签名证书。

  • 这反过来将允许您使用标准的 Neo4j 技术暴露签名证书的 HTTPS,并且如果需要,也允许通告 DNS 而不是裸 IP。

参考资料

© . All rights reserved.