使用 Bigtable 在 GKE 上运行 JanusGraph

图表数据库可为数据实体及其之间的关系建模,从而帮助您发掘数据洞见。JanusGraph 是一个图表数据库,支持处理大量数据。本教程介绍如何在 Google Cloud 上运行 JanusGraph,并使用 Google Kubernetes Engine 作为编排平台,使用 Bigtable 作为存储后端。本教程适用于有兴趣使用代管式数据库作为存储后端在 Google Cloud 上运行 JanusGraph 图表数据库的系统架构师、数据库管理员和 DevOps 专业人员。本教程假定您熟悉 Google Kubernetes Engine (GKE)、Kubernetes Pod、Helm 图表、BigtableElasticsearch。不需要了解 Apache TinkerPop 图表计算框架和 Gremlin 图表遍历机器与语言,但是若要在本教程中提供的示例之外使用 Janusgraph,则必须具备这些知识。

概览

在图表术语中,实体称为“节点”或“顶点”,它们之间的关系称为“边”。在 JanusGraph 中,顶点和边都可以具有通过属性提供的其他关联数据。

属性图表示例。

上图是属性图表的一个示例。

图表数据库可帮助您为各种网域和活动建模:

  • 社交网络
  • 金融交易(用于欺诈分析)
  • 物理或虚拟系统网络

创建图表数据库时,有时会创建数百万甚至数十亿个顶点和边。当您使用 JanusGraph 并将 Bigtable 作为底层存储层时,您可以执行快速查询(称为“图表遍历”),并根据所需的大小和吞吐量独立扩缩存储层。JanusGraph 还使用可插入的索引后端为顶点和边属性提供全文索引。在本教程中,您将在 GKE 上部署可伸缩的 JanusGraph 基础架构。您将使用 Elasticsearch 作为 StatefulSet 中的 Pod 中运行的索引后端,使用 Bigtable 作为存储后端。完成后,您可以遍历图表数据中存在的关系。下图展示了这些元素是如何组合在一起的。

在 GKE 上部署使用 Bigtable 的 JanusGraph

上图显示了使用 Elasticsearch 和 Bigtable 在 GKE 上部署 JanusGraph。

Bigtable 中的 JanusGraph 数据

图表数据由 JanusGraph 存储为相邻列表。每行表示一个顶点、任何相邻的顶点(边)以及有关顶点和边的属性元数据。行键是顶点的唯一标识符。顶点与另一顶点以及任何进一步定义关系的属性之间的关系存储为边或边属性列。根据 Bigtable 最佳做法,列限定符和列值都会存储定义边缘的数据。通过再次使用列限定符和列值来定义每个顶点属性,将这些属性都存储为单独的列。

下图展示了此存储结构。

JanusGraph 相邻列表存储结构。

该图显示了小型图表片段的逻辑存储结构,其中包含两个顶点行的逻辑详细信息。在该图中,两个示例行表示两个顶点。第一个顶点标有单个顶点属性,并按两个单独的边与其他两个顶点相关。第二个顶点包含带有两个属性和一个边的列。

下图显示了顶点边逻辑数据模型,它提供了有关边或边属性列的列限定符和值的一些详细信息。

JanusGraph 边和边属性列。

对于每个相邻顶点,列存储有关该边的元数据。列限定符包含有关边关系以及边方向的元数据,还包含指向相邻顶点的指针。列值包含边标签以及任何其他边属性。由于可按任意方向采用遍历,因此边会存储两次,一次用于边关系的每个端。双向边存储可显著提高遍历性能,但由于存在额外存储空间和非原子边变更的冗余,因此需要权衡取舍。

下图为顶点属性列的逻辑数据模型。

属性列的 JanusGraph 列值。

上图详细介绍了边列的列限定符和值。

每个顶点属性都存储为单独的列。列限定符是属性键的唯一标识符。列值同时包含属性的标识符和属性值。

JanusGraph 还依赖于 Bigtable 的行和列限定符的字典顺序来增强查询性能。

目标

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

前提条件

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Bigtable, Compute Engine, and GKE APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the Bigtable, Compute Engine, and GKE APIs.

    Enable the APIs

准备环境

在本教程中,您将使用 Cloud Shell 输入命令。通过 Cloud Shell,您可以访问 Google Cloud 控制台中的命令行。此外,它还包含 Google Cloud CLI 以及在 Google Cloud 中进行开发所需的其他工具。Cloud Shell 显示为 Google Cloud 控制台底部的一个窗口。初始化可能需要几分钟,但窗口会立即显示。

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  2. 在 Cloud Shell 中,为要在其中创建 Bigtable 集群和 GKE 集群的 Compute Engine 区域设置环境变量,并为 GKE 集群设置名称、节点类型和版本:

    export PROJECT_ID=PROJECT_ID
    export GCP_ZONE=REGION
    export GKE_CLUSTER_NAME=GKE_CLUSTER_NAME
    export GKE_NODE_TYPE=n1-standard-4
    export GKE_VERSION=1.20
    

    替换以下内容:

    • PROJECT_ID 替换为您的项目标识符。
    • REGION 替换为将在其中创建 Bigtable 集群和 GKE 集群的区域。
    • GKE_CLUSTER_NAME 替换为您的 GKE 集群的名称。

    该命令应类似于以下示例:

    export PROJECT_ID=bt-janusgraph-project-id
    export GCP_ZONE=us-central1-f
    export GKE_CLUSTER_NAME=janusgraph-gke
    export GKE_NODE_TYPE=n1-standard-4
    export GKE_VERSION=1.20
    
  3. 创建一个将要部署 JanusGraph 的 GKE 集群:

    gcloud container clusters create ${GKE_CLUSTER_NAME} \
        --zone=${GCP_ZONE} \
        --cluster-version=${GKE_VERSION} \
        --machine-type ${GKE_NODE_TYPE} \
        --scopes "https://www.googleapis.com/auth/cloud-platform"
    

创建 Bigtable 实例

对于 JanusGraph 存储后端,本教程使用 Bigtable,它可以快速扩缩以满足您的需求。本教程使用单节点集群,它足以完成本教程的目标且经济实惠。您可以从较小的集群开始项目,然后在准备好处理生产数据时迁移到更大的集群。Bigtable 文档中包含有关性能和扩缩的详细讨论,可帮助您根据自己的工作选择集群大小。

  1. 在 Cloud Shell 中,为您的 Bigtable 实例标识符设置环境变量:

    export BIGTABLE_INSTANCE_ID=BIGTABLE_INSTANCE_ID
    

    BIGTABLE_INSTANCE_ID 替换为您的 Bigtable 实例的标识符。

  2. 创建 Bigtable 实例:

    gcloud bigtable instances create ${BIGTABLE_INSTANCE_ID} \
        --cluster-config=id=${BIGTABLE_INSTANCE_ID}-${GCP_ZONE},zone=${GCP_ZONE},nodes=1 \
        --display-name=${BIGTABLE_INSTANCE_ID}-${GCP_ZONE}
    

安装和配置 Helm

使用 Helm 将应用部署到 Kubernetes 集群。在本教程中,您将使用 Helm 在 GKE 集群上部署 JanusGraph 和 Elasticsearch 服务。

  1. 在 Cloud Shell 中,安装 Helm:

    curl -fsSL -o get_helm.sh \
        https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
    chmod 700 get_helm.sh
    DESIRED_VERSION=v3.5.0 ./get_helm.sh
    
  2. 添加 elastic 图表代码库,以便可在 JanusGraph 图表部署期间找到 Elasticsearch 图表依赖项:

    helm repo add elastic https://helm.elastic.co
    

    此图表代码库由 Elasticsearch 的创建者 Elastic 托管。

使用 Helm 安装 JanusGraph 和 Elasticsearch

在本部分中,您将使用 Helm 图表JanusGraphElasticsearch 部署到 Kubernetes 集群。

Helm 图表是从 GitHub 中提取的。Helm 图表代码库中包含的部署会在 Service 后面部署一组三个 JanusGraph pod,以启动内部应用负载均衡器。pod 运行时,启动和活跃性探测会发出 HTTP 请求,以在每个 pod 上执行 JanusGraph 服务器健康检查。此外,该图表还包含 Elastic 提供的依赖项图表,用于在 StatefulSet 中部署 3 个 Elasticsearch pod。

  1. 在 Cloud Shell 中,为 Helm 和 JanusGraph 名称设置环境变量:

    export HELM_REPO=bigtable-janusgraph-helm
    export JANUSGRAPH_VERSION=0.5.3
    export HELM_CHART_RELEASE_VERSION=1
    export HELM_CHART_RELEASE_TAG=${JANUSGRAPH_VERSION}-${HELM_CHART_RELEASE_VERSION}
    export HELM_CHART_RELEASE_TAG_HASH=f8b271a4854d4a553dd5e9ba014d077fb098d9ab
    export HELM_CHART_NAME=janusgraph-bigtable
    
  2. 从 GitHub 拉取 Helm 图表:

    git clone https://github.com/GoogleCloudPlatform/${HELM_REPO} \
       --branch ${HELM_CHART_RELEASE_TAG}
    
  3. 导航到 Helm 图表目录:

    cd ${HELM_REPO}
    
  4. 出于安全考虑,请使用提交哈希值进行验证:

    HEAD_COMMIT_HASH=$(git rev-parse --verify HEAD)
    if [ _${HEAD_COMMIT_HASH} == _${HELM_CHART_RELEASE_TAG_HASH} ]
    then
        echo "Commit hash verified"
    fi
    

    如果输出如下所示,则请勿继续,因为尚未验证克隆标记的完整性。

    Commit hash verified
    
  5. 更新图表依赖项:

    helm dep update
    
  6. 导航到父级目录:

    cd ..
    
  7. 为 Helm 和 JanusGraph 实体的名称设置环境变量:

    export HELM_RELEASE_NAME=janusgraph-bigtable-elastic
    export ELASTICSEARCH_CLUSTER_NAME=${HELM_RELEASE_NAME}-elasticsearch
    export BIGTABLE_JANUSGRAPH_TABLE=janusgraph-table
    
  8. 创建一个 values.yaml 文件,该文件为 Helm 提供部署 JanusGraph 图表时要使用的配置属性:

    cat > values.yaml << EOF
    
    image:
      repository: docker.io/janusgraph/janusgraph
      tag: 0.5.3
      pullPolicy: IfNotPresent
    
    replicaCount: 3
    
    service:
      type: LoadBalancer
      port: 8182
      serviceAnnotations:
        networking.gke.io/load-balancer-type: "Internal"
    
    elasticsearch:
      deploy: true
      clusterName: ${ELASTICSEARCH_CLUSTER_NAME}
    
    properties:
      storage.backend: hbase
      storage.directory: null
      storage.hbase.ext.google.bigtable.instance.id: ${BIGTABLE_INSTANCE_ID}
      storage.hbase.ext.google.bigtable.project.id: ${PROJECT_ID}
      storage.hbase.ext.hbase.client.connection.impl: com.google.cloud.bigtable.hbase2_x.BigtableConnection
      storage.hbase.short-cf-names: true
      storage.hbase.table: ${BIGTABLE_JANUSGRAPH_TABLE}
      index.search.backend: elasticsearch
      index.search.hostname: ${ELASTICSEARCH_CLUSTER_NAME}-master
      index.search.directory: null
      index.search.elasticsearch.health-request-timeout: 90s
      cache.db-cache: true
      cache.db-cache-clean-wait: 20
      cache.db-cache-time: 180000
      cache.db-cache-size: 0.5
      cluster.max-partitions: 1024
      graph.replace-instance-if-exists: true
    
    persistence:
      enabled: false
    
    debugLevel: INFO
    EOF
    
  9. 使用您创建的 values.yaml 文件部署 JanusGraph Helm 图表:

    helm upgrade --install \
                 --wait \
                  --timeout 600s \
                  ${HELM_RELEASE_NAME} \
                  ./${HELM_REPO} \
                  -f values.yaml
    

    安装过程会等到所有资源都准备就绪后再完成。此过程可能需要几分钟时间。

验证 JanusGraph 部署

Helm 安装过程完成后,它会显示一个 NOTES 部分,用于描述入门体验。您可以按照 NOTES 部分概述的步骤验证 JanusGraph 环境是否正常运行。

  1. 在 Cloud Shell 中,验证部署到 GKE 的 Helm 图表组件:

    1. 检查 JanusGraph 部署:

      kubectl get deployments
      

      如果部署成功,则会输出如下内容:

      NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
      janusgraph-bigtable-elastic   3/3     3            3           3m28s
      
    2. 检查 Elasticsearch StatefulSet:

      kubectl get statefulsets
      

      如果一切正常,则输出内容如下所示:

      NAME                                               READY   AGE
      janusgraph-bigtable-elastic-elasticsearch-master   3/3     4m13s
      
  2. 将环境变量设置为运行 JanusGraph Gremlin 服务器的 Kubernetes pod 的名称。运行 Gremlin 服务器的 pod 的 app 标签派生自 Chart.yaml 文件中定义的 Helm 图表名称。

    export APP_LABEL_FROM_CHART_NAME=${HELM_CHART_NAME}
    export POD_NAME=$(kubectl get pods \
                         --namespace default \
                         -l "app=${APP_LABEL_FROM_CHART_NAME}, \
                             release=${HELM_RELEASE_NAME}" \
                         -o jsonpath="{.items[0].metadata.name}")
    
  3. 连接到 pod 并运行 Gremlin 控制台,这是一个读取评估输出循环 (REPL) shell。容器的名称也派生自 Chart.yaml 中的 Helm 图表名称。

    export GREMLIN_CONTAINER=${HELM_CHART_NAME}
    kubectl exec \
            -c ${GREMLIN_CONTAINER} \
            -it $POD_NAME \
            -- /opt/janusgraph/bin/gremlin.sh
    
  4. 在 Gremlin 控制台中,连接到 Apache TinkerPop 服务器:

    1. 启动会话:

      :remote connect tinkerpop.server conf/remote.yaml session
      

      输出类似于以下内容:

      ==>Configured localhost/127.0.0.1:8182-[b08972f2-a2aa-4312-8018-bcd11bc9812c]
      
    2. 连接到服务器:

      :remote console
      

      输出类似于以下内容:

      ==>All scripts will now be sent to Gremlin Server - [localhost/127.0.0.1:8182]-[b08972f2-a2aa-4312-8018-bcd11bc9812c] - type ':remote console' to return to local mode>
      
  5. 在 Gremlin 控制台中,通过检查表示图表实例的 graph 变量来验证 Gremlin 服务器是否正常运行:

    graph
    

    输出表明 JanusGraph 服务器正在使用与 HBase 兼容的数据库(在本例中为 Bigtable)作为存储后端运行。

    ==>standardjanusgraph[hbase:[127.0.0.1]]
    
  6. 在 Gremlin 中,创建两个顶点

    v1 = graph.addVertex(label, 'hello')
    v2 = graph.addVertex(label, 'world')
    

    如果控制台输出如下所示,则表示添加了两个顶点:

    ==>v[4344]
    ==>v[4152]
    
  7. 创建一条连接两个顶点的边:

    v1.addEdge('followedBy', v2)
    

    如果控制台输出如下所示,则表示已添加两个顶点之间的边:

    ==>e[17j-3co-4fmd-oe054][4344-followedBy->4152]
    
  8. 提交事务:

    graph.tx().commit()
    

    如果控制台输出为 null,则表示操作已提交:

    ==>null
    

    下图显示了命令创建的图表。

    JanusGraph 示例顶点和边。

    标记为 hello 的顶点通过标签为 followedBy 的定向边连接到标签为 world 的顶点。

  9. 发出一个 Gremlin 查询,查看从标签为 hello 的顶点沿着标签为 followedBy 的边的顶点采用哪个标签。

    g.V().has(label, 'hello').out('followedBy').label()
    

    下一部分将介绍查询语法。现在,您可以看到查询的输出为 world

    ==>world
    

加载和查询示例数据集

现在您已成功部署了 JanusGraph,并且可以使用 Gremlin 与其进行连接,接下来您可以开始加载和查询自己的数据。要了解该过程的实际行为,请加载 JanusGraph 附带的示例数据集,即描述万神殿神话中的神及其位置属性的众神图

  1. 在 Gremlin 中,加载您之前创建的图表:

    GraphOfTheGodsFactory.load(graph)
    

    输出如下所示:

    ==>null
    
  2. 发出一个图表遍历查询,以查找朱庇特神的所有兄弟:

    g.V().has('name', 'jupiter').out('brother').values('name')
    

    下表说明了查询遍历的步骤。

    遍历步骤 说明
    g.V() 从顶点集合开始。
    has('name', 'jupiter') 找到属性 name 的值为 jupiter 的顶点。
    out('brother') 从该顶点沿着标有 brother 的边出发。
    values('name') 获取这些边到达的顶点的 name 属性。
    输出如下所示:

    ==>neptune
    ==>pluto
    

    如需通过众神图数据集熟练掌握遍历查询,请使用 JanusGraph 文档中的其他示例查询进行尝试。

验证数据是否存储在 Bigtable 中

现在您已在 JanusGraph 集群中创建了一些示例数据,接下来可以验证 Bigtable 是否用作存储后端。

  1. 关闭 Gremlin 控制台:

    :q
    
  2. 在 Cloud Shell 中,验证数据是否已保存到 Bigtable 中的 janusgraph 表中:

    cbt -project=${PROJECT_ID} \
        -instance=${BIGTABLE_INSTANCE_ID} \
         count ${BIGTABLE_JANUSGRAPH_TABLE}
    

    输出类似于以下内容:

    2021/03/02 02:32:19 -creds flag unset, will use gcloud credential
    101
    

    输出中的 101 值表示 janusgraph table 中的行数,可能有所不同。

验证 Elasticsearch 中的搜索索引创建

  1. 在 Cloud Shell 中,为 Elasticsearch pod 索引和名称设置变量:

    export ELASTICSEARCH_POD_ORDINAL=0
    export ELASTICSEARCH_POD_NAME_ROOT=${ELASTICSEARCH_CLUSTER_NAME}-master
    export ELASTICSEARCH_POD=${ELASTICSEARCH_POD_NAME_ROOT}-0
    

    Elasticsearch pod 的名称由 Elasticsearch Helm 依赖项定义。pod 名称由您创建的 values.yaml 文件中提供的集群名称、master 一词以及零索引的序列号(由连字符分隔)组成。在此步骤中,请选择第一个 pod(表示为零 (0))。

  2. 使用 Elasticsearch Aliases REST API 检查索引:

    kubectl exec \
            -c elasticsearch \
            -it ${ELASTICSEARCH_POD} \
            --  \
            curl -XGET "127.0.0.1:9200/_aliases?pretty=true";
    

    输出显示了 JanusGraph 创建的两个索引 janusgraph_verticesjanusgraph_edges,以便使用顶点和边属性提供高效查找:

    {
      "janusgraph_vertices" : {
        "aliases" : {
          "janusgraph" : { }
        }
      },
      "janusgraph_edges" : {
        "aliases" : {
          "janusgraph" : { }
        }
      }
    }
    
  3. 使用 Elasticsearch Search REST API 查询其中一个索引中的值:

    kubectl exec \
           -c elasticsearch \
           -it ${ELASTICSEARCH_POD} \
           --  \
           curl -XGET "127.0.0.1:9200/janusgraph_edges/_search?pretty=true&q=*";
    

    搜索结果显示 JanusGraph 创建的索引中有条目。您看到的输出类似于以下截断结果,即 janusgraph_edges 索引中有条目。

    {
     "took" : 94,
     "timed_out" : false,
     "_shards" : {
       "total" : 1,
       "successful" : 1,
       "skipped" : 0,
       "failed" : 0
     },
     "hits" : {
       "total" : {
         "value" : 6,
         "relation" : "eq"
       },
       "max_score" : 1.0,
       "hits" : [
         {
           "_index" : "janusgraph_edges",
           "_type" : "_doc",
           "_id" : "6bvp-5ovc-b2t-2yko",
           "_score" : 1.0,
           "_source" : {
             "reason" : "loves waves"
           }
         },
         {
    …
    

删除项目

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

后续步骤