nebula-algorithm配置流程学习【3】—— 开启nebula之旅

1,241 阅读12分钟

前言:因为是纯小白入门,我暂时不可能做分布式部署,没那么优秀性能的机器,身为学生买服务器也只能赶在双11,目前就只有手上一台服务器。所以所有操作都在这小小一个上面完成的

学习目标

  • 单机配置启动一个nebula图数据库
  • 安装并配置好nebula-importer,nebula-console,nebula-graph-studio,nebula-spark-connector
  • 处理生成一个适于导入的数据集,并导入图空间
  • 使用nebula-spark-connector连接到nebula数据库并读取出导入的节点数据

项目背景

1.已经在本地配置好了java、spark、scala、maven,以及docker(本次全部采用docker部署)
2.准备好了一个图数据集:cora数据集

cora数据集(以及一些其他的数据集)下载链接可以在这里找到:

Datasets | LINQS (ucsc.edu)

安装Nebula Graph

接下来我会介绍一下nebula官方文档里写的整个安装流程及需要注意的点(坑)

准备资源 - Nebula Graph Database 手册 (nebula-graph.com.cn)

安装部署-准备资源

基础硬件设施

由于只是学习,不是生产环境,所以我需要满足nebula graph的编译级别配置:

1.png

软件环境要求

不建议centOS 8傻傻去执行yum update,会把原来没毛病的镜像源覆盖成国内用不了的源。。。如果手贱点了,建议参考一下阿里的这个解决方案,用他们的yum源,就可以重新用yum了 centos镜像-centos下载地址-centos安装教程-阿里巴巴开源镜像站 (aliyun.com)

Docker Compose部署Nebula Graph

使用 Docker Compose 部署 - Nebula Graph Database 手册 (nebula-graph.com.cn)

在这里,前三步按官方顺序执行即可,启动后执行docker ps -a也可以看到分别有三个graphd,storaged,metad服务已经启动了 image.png

需要提及的是第4点:连接Nebula Graph这里。

如果不知道这个nebula console是什么,去Releases · vesoft-inc/nebula-console (github.com)下载个。在先前的文章中,因为我的目标就是3.0.0版本的nebula-algorithm,所以这里也是下载3.0.0版本的nebula console。下载完了执行:

rename nebula-console-linux-amd64-v3.0.0 nebula-console nebula-console-linux-amd64-v3.0.0
chmod 111 nebula-console
# 根据你的服务器ip和端口来定,默认端口9669,用户名root,密码nebula
./nebula-console -addr <ip> -port <port> -u <username> -p <password>

image.png

这个Nebula Console相当于命令行版控制台,所有原生NGQL语句在这里就可以直接提交给图数据库执行。

Docker部署Nebula Graph Studio (可选)

这里官方提了一嘴该镜像仓库。因为docker默认都是从Docker Hub那拿的,有的镜像文件可能拉取的巨慢。

改镜像仓库的方法就是:

cd /etc/docker
vi daemon.json
##### 以下均为输入 #####
{ 
    "registry-mirrors": [ 
        "https://registry.docker-cn.com", 
        "http://hub-mirror.c.163.com", 
        "https://docker.mirrors.ustc.edu.cn" 
    ] 
}

启动后进在浏览器访问你的ip:7001,主机号是ip:9669,用户名root,密码nebula,就可以进nebula自己的前端了

本地部署Nebula importer并导入数据集

Cora数据集介绍及预处理

  • Cora数据集共2708个样本点,每个样本点都是一篇科学论文,所有样本点被分为8个类别,类别分别是
    1. 基于案例
    2. 遗传算法
    3. 神经网络
    4. 概率方法
    5. 强化学习
    6. 规则学习
    7. 理论
  • 每篇论文都由一个1433维的词向量表示,即每个样本点具有1433个特征。词向量的每个元素都对应一个词,且该元素只有0或1两个取值。取0表示该元素对应的词不在论文中,取1表示在论文中。所有的词来源于一个具有1433个词的字典。
  • 每篇论文都至少引用了一篇其他论文,或者被其他论文引用,也就是样本点之间存在联系,没有任何一个样本点与其他样本点完全没联系。如果将样本点看做图中的点,则这是一个连通的图,不存在孤立点。
  • Cora数据集有两个文件:cora.citescora.content,都是文本文件,其中,cites中包含了各论文的引用关系,content中包含了各论文的编号id以及其词向量特征

处理过程其实就是把cites直接以文本形式转csv,把content中间的词向量全去掉只保留id和类型,最终两个csv大概长这样:

image.png

nebula importer YAML配置编写

下载方面的话,我是通过直接把git项目扒拉下来然后本地编译出二进制可执行文件nebula-importer

创建一个yaml文件,编写如下代码

# 3.0版本的
version: v3.0
removeTempFiles: false
clientSettings:
  retry: 3
  concurrency: 10
  channelBufferSize: 128
  # 使用的图空间:cora
  space: cora
  connection:
    user: root
    password: nebula
    address: 你的ip:9669
logPath: ./err/1.log
files:
- path: ./contents.csv
  failDataPath: ./err/contents.csv
  batchSize: 128
  inOrder: false
  type: csv
  csv:
    # 无表头csv:上文中的contents
    withHeader: false
    withLabel: false
    delimiter: ","
  schema:
    # vid为int类型的节点article
    type: vertex
    vertex:
      vid:
        index: 0
      tags:
      - name: article
        props:
        - name: type
          type: string
          index: 1
        
- path: ./cites.csv
  failDataPath: ./err/cites.csv
  batchSize: 128
  inOrder: false
  type: csv
  csv:
    # 无表头csv:上文中的cites
    withHeader: false
    withLabel: false
    delimiter: ","
  schema:
    # 边名为cite
    type: edge
    edge:
      name: cite
      srcVID:
        type: int
        index: 0
      dstVID:
        type: int
        index: 1

这个yaml是以默认存在一个cora图空间为基础的。我最初是直接在nebula graph studio前端操作了。

image.png 如果不方便手动在nebula graph studio创建的话,就需要在yaml文件中添加NGQL语句。添加位置在上面的clientSettings中,如果还弄不清可以看官方文档那个示例确认代码位置

    postStart: # 配置连接 Nebula Graph 服务器之后,在插入数据之前执行的一些操作。
        commands: |
            DROP SPACE IF EXISTS cora; 
            CREATE SPACE IF NOT EXISTS cora(partition_num=5, replica_factor=1, vid_type=INT64); 
            USE cora; 
            CREATE TAG article(type string); 
            CREATE EDGE follow(); 
        # 执行上述命令后到执行插入数据命令之间的间隔。 
        afterPeriod: 15s

nebula importer文件导入

现在我们把上面的yaml,csv文件放到一个文件夹里。目前我的想法是,为了更加规范的调用,我直接在nebula-importer这个文件夹下建立了一个nebula-csvs文件夹,内部按不同的图空间进行编号并创建文件夹,随后放入yaml和csv文件。

因此,我执行如下代码:

./nebula-importer --config <yaml_config_file_path>

执行完成后,进入nebula graph studio看看效果。随便进行几次查询,芜湖!

image.png

安装nebula-spark-connector

仍然是先按官方的来,从git拉取,并使用mvn编译生成jar包

官方之后的介绍似乎就开始各种模棱两可了,这也导致接下来我弄出来了一堆错误,花了整整一天才想明白咋回事。。。

使用方法???在哪使用啊

因为在之前的学习过程中我是在Windows操作系统上搞IDEA开发然后打包再去给服务器执行的。所以呗,我应该还是先在本地IDEA配个能编码spark-connector的环境才对啊!

官方只放了这么一句话。。。

image.png

那好吧,我本地确实是Maven项目。所以我先改pom呗

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spark-connector</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.vesoft</groupId>
            <artifactId>nebula-spark-connector</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>2.4.4</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>

 问题1:居然告诉我maven找不到这个jar\color{red}\ {问题1:居然告诉我maven找不到这个jar包}

阿哲,可是我确实按照官方给的dependency写的啊

好吧,先把导包问题解决掉。右键项目,选择Maven,create/open settings.xml,进行如下修改,改一下Maven下载的镜像仓库。随后reload Maven即可

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <mirrors>
        <mirror>
            <id>nexus</id>
            <name>internal nexus repository</name>
            <url>https://repo.maven.apache.org/maven2</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>
</settings>

image.png

 问题2:官方样例代码无法解决spark.read.nebula()???\color{red}\ {问题2:官方样例代码无法解决spark.read.nebula()???}

image.png

image.png

这里这个spark并不是Maven本来有的那个org.apache.spark,而应当是构建一个sparkSession。实际上还是应该去看官方在github上的示例才对。。。

nebula-spark-connector (github.com)

 问题3:复制出来一个读取节点的代码但是java.lang.NoClassDefFoundError\color{red}\ {问题3:复制出来一个读取节点的代码}\\{但是java.lang.NoClassDefFoundError}

复制并修改了一下的代码
import com.facebook.thrift.protocol.TCompactProtocol
import com.vesoft.nebula.connector.connector.NebulaDataFrameReader
import com.vesoft.nebula.connector.{NebulaConnectionConfig, ReadNebulaConfig}
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession

object test {
  def main(args: Array[String]): Unit = {
    // 开启一个新的spark会话进程
    val sparkConf = new SparkConf
    sparkConf
      .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
      .registerKryoClasses(Array[Class[_]](classOf[TCompactProtocol]))
    // 指定本次spark submit任务的master等配置信息
    val spark = SparkSession
      .builder()
      .master("local")
      .config(sparkConf)
      .getOrCreate()
    println("read vertex")
    readVertex(spark)
    spark.close()
    sys.exit()
  }

  def readVertex(spark: SparkSession): Unit = {
    // 配置nebula连接。由于spark-connector是要从metad里面去获取storaged的地址之类的信息的,另外,根据官方文档,读取数据不需要配置graphd地址
    val config =
      NebulaConnectionConfig
        .builder()
        .withMetaAddress("127.0.0.1:9559")
        .withConenctionRetry(2)
        .build()
    // 配置要读取的图空间及其标签
    val nebulaReadVertexConfig: ReadNebulaConfig = ReadNebulaConfig
      .builder()
      .withSpace("cora")
      .withLabel("article")
      .withNoColumn(false)
      .withReturnCols(List())
      .build()
    // 将图数据加载为RDD模式
    val vertex = spark.read.nebula(config, nebulaReadVertexConfig).loadVerticesToDF()
    vertex.printSchema()
    vertex.show(20)
    println("vertex count: " + vertex.count())
  }
}
报错信息

随后打包这个程序,传到服务器去,执行spark-submit,结果报错了。截取一下开始报错的那几行日志,发现是java.lang.NoClassDefFoundError: com/facebook/thrift/protocol/TCompactProtocol???

阿哲,所以我在本地编译好的jar包是没包含这些运行需要的库的,我人傻了(对maven打包理解不到位,我的)

把算法包导入spark中

在上文介绍nebula-spark-connector时,我一笔带过了,说按官方操作生成一个jar包就行,结果我可倒好,真把这个包给忘了。。。现在我放一下图,看下这个jar包的位置。图中可以清晰看到生成好了的包,那么事实上这个包里就已经包含了我们在Windows IDEA开发环境里用到的库啦(因为版本都是对应好的!)只需把这个jar包复制到spark文件夹下的/jars目录里即可

image.png

image.png

 问题4:运行代码但是java.net.ConnectException\color{red}\ {问题4:运行代码}\\{但是java.net.ConnectException}

报错信息
......
22/04/22 14:16:09 INFO NebulaDataSource: create reader
22/04/22 14:16:09 INFO NebulaDataSource: options {spacename=cora, nocolumn=false, enablestoragessl=false, metaaddress=127.0.0.1:9559, label=article, type=vertex, connectionretry=2, timeout=6000, executionretry=1, enablemetassl=false, paths=[], limit=1000, returncols=, partitionnumber=100}
Exception in thread "main" com.facebook.thrift.transport.TTransportException: java.net.ConnectException: Connection refused (Connection refused)
	at com.facebook.thrift.transport.TSocket.open(TSocket.java:206)
	at com.vesoft.nebula.client.meta.MetaClient.getClient(MetaClient.java:148)
	at com.vesoft.nebula.client.meta.MetaClient.doConnect(MetaClient.java:127)
        ......
是否是端口有问题?

我查看了一下,执行docker ps -a,emm,乍一看没什么,但是以普遍理性而言,这里显示的metad端口情况分别是

编号端口
metad00.0.0.0:49161->9559/tcp, :::49155->9559/tcp
metad10.0.0.0:49155->9559/tcp, :::49161->9559/tcp
metad20.0.0.0:49160->9559/tcp, :::49160->9559/tcp

image.png

所以???我是应该去从外部访问,比如,49155端口才对???这明显是由于docker-compose的部署模式导致的,虽然在容器内部确实是在用9559端口部署metad服务,但在容器外部的主机上,没有任何程序占用9559,相反,此时是存在一个端口映射,把外部主机的49155等端口映射到了容器内部的9559端口。所以这就导致我使用的localhost:9559根本啥也获取不到,没进程用这个端口,自然会被拒绝访问

 问题5:迎合一下端口映射吧但是java.net.UnknownHostException\color{red}\ {问题5:迎合一下端口映射吧}\\{但是java.net.UnknownHostException}

修改readVertex函数中metad的地址配置
val config =
  NebulaConnectionConfig
    .builder()
    // 先试试能不能访问metad0吧
    .withMetaAddress("127.0.0.1:49155")
    .withConenctionRetry(2)
    .build()
报错信息

结果还是报错了???截取报错那几行信息

......
22/04/22 22:38:00 INFO NebulaDataSource: create reader
22/04/22 22:38:00 INFO NebulaDataSource: options {spacename=cora, nocolumn=false, enablestoragessl=false, metaaddress=127.0.0.1:49155, label=article, type=vertex, connectionretry=2, timeout=6000, executionretry=1, enablemetassl=false, paths=[], limit=1000, returncols=, partitionnumber=100}
22/04/22 22:38:00 ERROR MetaClient: Get Space Error: java.net.UnknownHostException: metad2
Exception in thread "main" com.facebook.thrift.transport.TTransportException: java.net.UnknownHostException: metad2
	at com.facebook.thrift.transport.TSocket.open(TSocket.java:206)
	at com.vesoft.nebula.client.meta.MetaClient.getClient(MetaClient.java:148)
	at com.vesoft.nebula.client.meta.MetaClient.freshClient(MetaClient.java:168)
	at com.vesoft.nebula.client.meta.MetaClient.getSpace(MetaClient.java:230)
        ......

哦,所以你甚至已经可以访问这个地址却无法解析主机名了是么???

容器外部真的能被允许去容器里访问东西吗

使用exchange-2.6.1从clickhouse导入数据至nebula报错 UnknownHostException以及中文显示乱码

spark 写入数据报错 能插入部分数据

逛评论区,官方大佬给了一个很好的参考思路。首先,提问者使用nabula-exchange写数据时出现了同样的java.net.UnknownHostException错误;另一个问题是使用spark-connector的,出现了java.net.ConnectException: Can't assign requested address (connect failed)的报错。下面是大佬的几个回答

image.png

image.png

总结一下,可以概括为:\bold{总结一下,可以概括为:}

  1. spark-connector去获取nebula的数据时也是要从存储区拿,即访问storaged。但又不是直接访问storaged,而是需要通过metad去拿到storaged的地址
  2. spark事实上是要去扫storage去获取信息,如1所述,这需要能够访问metaclient,然而在默认的nebula graph部署中,由于是docker-compose模式,压根就拒绝了外部访问,meta,storage,graph开出来的9个服务里只有一个graphd是对外开放的
  3. 但是,如果spark和以上这些服务都运行在同一容器网络中,就可以正常访问数据了
  4. nebula graph studio就是个前端组件,外部可以用7001端口访问,访问后它是docker内部做容器间通信的

Ohhhhhhhhhhhhhhhhhhhhhhhhhhh\large\bold{Ohhhhhhhhhhhhhhhhhhhhhhhhhhh}

最终解决方案

\large\bold\color{red}\ {核心:运行Spark在与meta所属同一容器网络中}

创建一个新容器,直接挂载Spark,加入容器网络

查看容器网络情况

image.png

image.png

容器名容器内网ip
nebula-docker-compose_storaged0_1172.21.0.5/16
nebula-docker-compose_storaged1_1172.21.0.6/16
nebula-docker-compose_storaged2_1172.21.0.7/16
nebula-docker-compose_metad0_1172.21.0.2/16
nebula-docker-compose_metad1_1172.21.0.3/16
nebula-docker-compose_metad2_1172.21.0.4/16
nebula-docker-compose_graphd_1172.21.0.10/16
nebula-docker-compose_graphd1_1172.21.0.9/16
nebula-docker-compose_graphd2_1172.21.0.8/16
nebula-docker-compose_console_1172.21.0.11/16

创建容器

docker run -itd --name spark-master -v /usr/local/java:/usr/local/java -v /usr/local/spark:/usr/local/spark --network nebula-docker-compose_nebula-net kdvolder/jdk8:latest

随后即可查看到容器内网中这一新成员:

容器名容器内网ip
spark-master172.21.0.12/16

修改spark配置信息以迎合容器主机名

由于是映射的spark,而且临时弄了个jdk8的环境,为了java和spark都可以运行,所以同时挂载了主机的java和spark安装目录。

进入spark-master这个容器

docker exec -it spark-master /bin/bash

所以现在需要修改的只有两样东西:

  1. java和spark的环境变量(之前文章里讲过~/.bash_profile,一样的操作流程)
  2. spark-env.sh

image.png

image.png

修改scala程序中的meta地址

val config =
  NebulaConnectionConfig
    .builder()
    .withMetaAddress("172.21.0.3:9559")
    .withConenctionRetry(2)
    .build()

image.png

image.png