Executar o JanusGraph no GKE com o Bigtable

Os bancos de dados de grafos ajudam você a descobrir insights com a modelagem das entidades de dados e os relacionamentos entre elas. O JanusGraph é um banco de dados gráfico compatível com grandes volumes de dados. Neste tutorial, mostramos como executar o JanusGraph no Google Cloud com o Google Kubernetes Engine funcionando como a plataforma de orquestração e o Bigtable como back-end de armazenamento. Ele é destinado a arquitetos de sistemas, administradores de bancos de dados e profissionais de DevOps, interessados em executar o banco de dados de grafos do JanusGraph no Google Cloud usando um banco de dados gerenciado como back-end de armazenamento. Pressupomos que você conheça o Google Kubernetes Engine (GKE), os pods do Kubernetes, gráficos do Helm, Bigtable e Elasticsearch. O conhecimento do framework de computação de grafos Apache TinkerPop e da máquina e linguagem de transversal em grafos Gremlin (links em inglês) não é obrigatório, mas é necessário para usar o Janusgraph além dos exemplos fornecidos neste tutorial.

Visão geral

Na terminologia de grafos, as entidades são chamadas de nós ou vértices e as relações são chamadas de arestas. No JanusGraph, os vértices e as arestas podem ter outros dados associados, disponibilizados pelas propriedades.

Exemplo de uma um grafo de propriedade.

A ilustração anterior é um exemplo de um grafo de propriedade.

Os bancos de dados de gráficos ajudam a modelar vários domínios e atividades:

  • redes sociais
  • Transações financeiras (para análise de fraude)
  • Redes de sistema físicas ou virtuais

Ao criar bancos de dados de grafos, às vezes são criados milhões ou até bilhões de vértices e arestas. Quando você usa o JanusGraph com o Bigtable como camada de armazenamento subjacente, é possível executar consultas rápidas (conhecidas como transversais em grafos) e escalonar a camada de armazenamento de maneira independente, de acordo com o tamanho e a capacidade de processamento que você necessita. O JanusGraph também usa um back-end de indexação plugável para fornecer indexação de texto completo para propriedades de vértice e aresta. Neste tutorial, você implanta uma infraestrutura escalonável do JanusGraph no GKE. O Elasticsearch é usado como back-end de indexação para execução em pods de um StatefulSet. O Bigtable é usado como back-end de armazenamento. Quando terminar, transfira os relacionamentos atuais dos dados do grafo. No diagrama a seguir, você vê como esses elementos se encaixam.

Implantação do JanusGraph com o Bigtable no GKE

Esse diagrama exibe a implantação do JanusGraph no GKE com o ElasticSearch e o Bigtable.

Dados do JanusGraph no Bigtable

Os dados do grafo são armazenados pelo JanusGraph como uma lista de adjacências. Cada linha representa um vértice, todos os vértices adjacentes (arestas) e metadados de propriedade sobre os vértices e as arestas. A chave de linha é o identificador exclusivo do vértice. Cada relação entre o vértice e outro vértice e quaisquer propriedades que definam melhor o relacionamento são armazenados como uma aresta ou coluna de propriedades de arestas. O qualificador de coluna e o valor da coluna armazenam dados que definem a borda, de acordo com as práticas recomendadas do Bigtable. Cada propriedade de vértice é armazenada como uma coluna separada, mais uma vez usando o qualificador de coluna e o valor da coluna para definir a propriedade.

No diagrama a seguir, você vê essa estrutura de armazenamento.

Estrutura de armazenamento de listas de adjacências do JanusGraph.

O diagrama mostra a estrutura de armazenamento lógico para um pequeno fragmento de grafo com detalhes lógicos para duas linhas de vértices. No diagrama, as duas linhas de exemplo representam dois vértices. O primeiro vértice é rotulado com uma única propriedade de vértice e está relacionado a dois outros vértices por duas arestas separadas. O segundo vértice contém colunas com duas propriedades e uma aresta.

A ilustração a seguir, do modelo de dados lógico de borda do vértice, mostra alguns detalhes sobre os qualificadores e valores de coluna para uma coluna de borda ou de propriedade de borda.

Colunas de borda e de propriedade de borda do JanusGraph.

Para cada vértice adjacente, uma coluna armazena os metadados sobre a aresta. O qualificador de coluna contém metadados sobre o relacionamento da aresta e sobre a direção da aresta, além de um ponteiro para o vértice adjacente. O valor da coluna contém o rótulo da aresta e quaisquer outras propriedades dela. Como as transversais podem ser seguidas em qualquer direção, as bordas são armazenadas duas vezes, uma para cada extremidade da relação da borda. O armazenamento bidirecional da aresta aumenta de forma significativa o desempenho da transversal, mas tem algumas compensações devido à redundância do espaço de armazenamento extra e de mutações de arestas não atômicas.

O diagrama a seguir é o modelo de dados lógico de uma coluna de propriedade de vértice.

Valores da coluna JanusGraph para uma coluna de propriedade.

A ilustração anterior fornece detalhes sobre os qualificadores de coluna e os valores de uma coluna de aresta.

Cada propriedade de vértice é armazenada como uma coluna separada. O qualificador de coluna é um identificador exclusivo da chave de propriedade. O valor da coluna contém um identificador da propriedade e o valor da propriedade.

O JanusGraph também depende da ordenação lexicográfica de linhas e qualificadores de coluna do Bigtable para melhorar o desempenho da consulta.

Objetivos

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Pré-requisitos

  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

Prepare o ambiente

Neste tutorial, você usa o Cloud Shell para inserir comandos. O Cloud Shell fornece acesso à linha de comando no Console do Google Cloud e inclui a Google Cloud CLI e outras ferramentas necessárias para desenvolver no Google Cloud. O Cloud Shell aparece como uma janela na parte inferior do Console do Google Cloud. A inicialização leva vários minutos, mas a janela aparece imediatamente.

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

    Activate Cloud Shell

  2. No Cloud Shell, defina as variáveis de ambiente para a zona do Compute Engine em que você vai criar o cluster do Bigtable e o cluster do GKE, sendo que, para este último, você precisa também dar um nome e especificar o tipo de nó e a versão:

    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
    

    Substitua:

    • PROJECT_ID pelo identificador do projeto;
    • REGION pela zona em que o cluster do Bigtable e o do GKE serão criados;
    • GKE_CLUSTER_NAME pelo nome do cluster do GKE.

    O comando será semelhante ao seguinte exemplo:

    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. Crie o cluster do GKE em que o JanusGraph será implantado:

    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"
    

Criar uma instância do Bigtable

Como back-end de armazenamento do JanusGraph, este tutorial usa o Bigtable, que pode ser escalonado rapidamente para atender às suas necessidades. Neste tutorial, usamos um cluster de desenvolvimento de nó único, que é econômico e suficiente para o este propósito. Inicie projetos com um cluster menor e mova para um maior quando estiver pronto para trabalhar com dados de produção. A documentação do Bigtable inclui uma abordagem detalhada sobre desempenho e escalonamento para ajudar você a escolher um tamanho de cluster para seu próprio trabalho.

  1. No Cloud Shell, defina a variável de ambiente para o identificador da instância do Bigtable:

    export BIGTABLE_INSTANCE_ID=BIGTABLE_INSTANCE_ID
    

    Substitua BIGTABLE_INSTANCE_ID pelo identificador da instância do Bigtable.

  2. Crie a instância do 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}
    

Instalar e configurar o Helm

Você usa o Helm para implantar aplicativos em seu cluster do Kubernetes. Neste tutorial, você usa o Helm para implantar os serviços do JanusGraph e do Elasticsearch no cluster do GKE.

  1. No Cloud Shell, instale o 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. Adicione o repositório de gráfico elastic para que a dependência de gráfico do Elasticsearch seja encontrada durante a implantação do gráfico do JanusGraph:

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

    Esse repositório de gráfico é hospedado pelo Elastic, criador do Elasticsearch.

Usar o Helm para instalar o JanusGraph e o Elasticsearch

Nesta seção, você usa um gráfico do Helm para implantar o JanusGraph e o Elasticsearch no cluster do Kubernetes.

O gráfico Helm é extraído do GitHub. A implantação incluída no repositório do gráfico Helm implanta um conjunto de três pods JanusGraph por trás de um serviço que iniciará um balanceador de carga de aplicativo interno. Quando os pods estão em execução, as sondagens de inicialização e atividade fazem solicitações HTTP para realizar verificações de integridade no servidor JanusGraph em cada pod. Além disso, o gráfico inclui uma tabela de dependências fornecida pelo Elastic que implanta três pods do Elasticsearch em um StatefulSet.

  1. No Cloud Shell, defina as variáveis de ambiente para os nomes do Helm e do 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. Extraia o gráfico do Helm do GitHub:

    git clone https://github.com/GoogleCloudPlatform/${HELM_REPO} \
       --branch ${HELM_CHART_RELEASE_TAG}
    
  3. Navegue até o diretório do gráfico Helm:

    cd ${HELM_REPO}
    
  4. Por motivos de segurança, use o hash de confirmação para verificar:

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

    Se a resposta não for semelhante ao seguinte, não continue, porque a integridade da tag clonada não foi verificada.

    Commit hash verified
    
  5. Atualize as dependências do gráfico:

    helm dep update
    
  6. Navegue até o diretório pai:

    cd ..
    
  7. Defina variáveis de ambiente para os nomes das entidades Helm e JanusGraph:

    export HELM_RELEASE_NAME=janusgraph-bigtable-elastic
    export ELASTICSEARCH_CLUSTER_NAME=${HELM_RELEASE_NAME}-elasticsearch
    export BIGTABLE_JANUSGRAPH_TABLE=janusgraph-table
    
  8. Crie um arquivo values.yaml, que forneça ao Helm as propriedades de configuração a serem usadas ao implantar o gráfico do 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. Implante o gráfico JanusGraph do Helm usando o arquivo values.yaml que você criou:

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

    O processo de instalação aguarda até que todos os recursos estejam prontos antes de ser concluído. Esse processo pode levar vários minutos.

Verificar a implantação do JanusGraph

Quando o processo de instalação do Helm é concluído, é exibida uma seção NOTES que descreve uma experiência de primeiros passos. Siga as etapas descritas na seção NOTES para verificar se o ambiente do JanusGraph está funcionando.

  1. No Cloud Shell, verifique se os componentes do gráfico Helm foram implantados no GKE:

    1. Verifique a implantação do JanusGraph:

      kubectl get deployments
      

      Quando a implantação é bem-sucedida, a resposta é a seguinte:

      NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
      janusgraph-bigtable-elastic   3/3     3            3           3m28s
      
    2. Verifique o StatefulSet do Elasticsearch:

      kubectl get statefulsets
      

      Quando tudo está funcionando, a resposta é a seguinte:

      NAME                                               READY   AGE
      janusgraph-bigtable-elastic-elasticsearch-master   3/3     4m13s
      
  2. Defina uma variável de ambiente para o nome de um pod do Kubernetes que está executando o servidor JanusGraph Gremlin. O rótulo app do pod que está executando o servidor Gremlin é derivado do nome do gráfico Helm definido no arquivo Chart.yaml.

    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. Conecte-se ao pod e execute o console do Gremlin, um shell de loop de impressão de avaliação de leitura (REPL, na sigla em inglês). O nome do contêiner também é derivado do nome do gráfico Helm em Chart.yaml.

    export GREMLIN_CONTAINER=${HELM_CHART_NAME}
    kubectl exec \
            -c ${GREMLIN_CONTAINER} \
            -it $POD_NAME \
            -- /opt/janusgraph/bin/gremlin.sh
    
  4. No console do Gremlin, conecte-se ao servidor Apache TinkerPop:

    1. Inicie a sessão:

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

      A resposta será semelhante a:

      ==>Configured localhost/127.0.0.1:8182-[b08972f2-a2aa-4312-8018-bcd11bc9812c]
      
    2. Conecte-se ao servidor:

      :remote console
      

      A resposta será semelhante a:

      ==>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. No console do Gremlin, verifique se o servidor dele está sendo executado corretamente, para isso, inspecione a variável graph, que representa a instância do grafo:

    graph
    

    A resposta mostra que o servidor JanusGraph está sendo executado com um banco de dados compatível com o HBase, neste caso, o Bigtable, como o back-end de armazenamento.

    ==>standardjanusgraph[hbase:[127.0.0.1]]
    
  6. No Gremlin, crie dois vértices

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

    Uma resposta do console semelhante à seguinte mostra que os dois vértices foram adicionados:

    ==>v[4344]
    ==>v[4152]
    
  7. Crie uma aresta que faça a conexão de ambos os vértices:

    v1.addEdge('followedBy', v2)
    

    Uma resposta do console semelhante à seguinte mostra que a borda entre os dois vértices foi adicionada:

    ==>e[17j-3co-4fmd-oe054][4344-followedBy->4152]
    
  8. Confirme a transação:

    graph.tx().commit()
    

    Uma resposta do console com a palavra null mostra que as operações foram confirmadas:

    ==>null
    

    O diagrama a seguir ilustra o grafo criado pelos comandos.

    Vértices e arestas de exemplo do JanusGraph.

    O vértice identificado hello está conectado por uma aresta direcionada, identificada como followedBy para o vértice world identificado.

  9. Emita uma consulta do Gremlin para ver qual rótulo é para o vértice que segue uma aresta rotulada com followedBy do vértice que foi rotulado como hello:

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

    A sintaxe da consulta é explicada na próxima seção. Por enquanto, você verá a palavra world como resposta à consulta:

    ==>world
    

Carregar e consultar um conjunto de dados de amostra

Agora que você implantou o JanusGraph e pode se conectar a ele usando o Gremlin, comece a carregar e consultar seus próprios dados. Para ver como é esse processo, carregue o conjunto de dados de amostra que está incluído no JanusGraph, o Gráfico dos deuses, que mostra divindades mitológicas do panteão romano e os respectivos imóveis no local.

  1. No Gremlin, carregue o grafo criado anteriormente:

    GraphOfTheGodsFactory.load(graph)
    

    A resposta é a seguinte:

    ==>null
    
  2. Emita uma consulta de transversal em grafo que encontre todos os irmãos de Júpiter:

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

    A tabela a seguir contém explicações sobre as etapas que a consulta percorre.

    Etapa de análise Explicação
    g.V() Comece com a coleção de vértices.
    has('name', 'jupiter') Encontrar um que tenha a propriedade name com o valor jupiter.
    out('brother') Deste ponto, seguir as arestas com o rótulo brother.
    values('name') Para os vértices de destino dessas arestas, usar a propriedade name.
    A resposta é a seguinte:

    ==>neptune
    ==>pluto
    

    Para se familiarizar mais com as consultas de percurso que são possíveis no conjunto de dados "Gráfico dos Deuses", teste outras consultas de amostra dos documentos do JanusGraph.

Verificar se os dados estão armazenados no Bigtable

Agora que você criou alguns dados de amostra no cluster do JanusGraph, verifique se o Bigtable foi usado como back-end de armazenamento.

  1. Feche o console do Gremlin:

    :q
    
  2. No Cloud Shell, verifique se os dados foram mantidos na tabela janusgraph no Bigtable:

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

    A resposta será semelhante a esta:

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

    O valor 101 na resposta representa o número de linhas em janusgraph table e pode ser diferente para você.

Verificar a criação do índice de pesquisa no Elasticsearch

  1. No Cloud Shell, defina as variáveis para o índice e o nome do pod do Elasticsearch:

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

    Os nomes dos pods do Elasticsearch são definidos pelas dependências do Elasticsearch Helm. Os nomes do pod são compostos pelo nome do cluster, conforme fornecido no arquivo values.yaml que você criou, com a palavra master e um número ordinal indexado por zero, todos separados por hifens. Para esta etapa, escolha o primeiro pod, representado como zero (0).

  2. Use a API REST Aliases Elasticsearch para inspecionar os índices:

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

    A resposta mostra dois índices, janusgraph_vertices e janusgraph_edges, que foram criados pelo JanusGraph para fornecer pesquisas eficientes usando propriedade de vértice e borda:

    {
      "janusgraph_vertices" : {
        "aliases" : {
          "janusgraph" : { }
        }
      },
      "janusgraph_edges" : {
        "aliases" : {
          "janusgraph" : { }
        }
      }
    }
    
  3. Consulte os valores de um dos índices usando a API REST Search Elasticsearch:

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

    Os resultados da pesquisa mostram que há entradas nos índices criados pelo JanusGraph. A resposta que você vê é semelhante aos seguintes resultados truncados que indicam a presença dessas entradas no índice 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"
           }
         },
         {
    …
    

Excluir o projeto

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

  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.

A seguir