最近、東京リージョンにきたNeptuneを開発効率をあげるため、Docker内から接続してみようと思います。
環境
- docker
- docker-compose
- python3.6
大まかな流れ
- docker imageの用意
- ポートフォワーディングの設定
- publicなEC2の用意(省略)
- docker-compose.ymlの設定
- extra_hostsの追加
- ホストの
.ssh/config
にポートフォワーディングの設定 - 起動時にホストの
.ssh/
をイメージ内の適当なところにマウント
ssh/configにポートフォワーディングを書く
Host neptune
HostName 踏み台EC2のPublicIP
User ec2-user
IdentityFile ~/.ssh/EC2の鍵ファイル.pem
GatewayPorts yes
LocalForward 8182 接続したいNeptuneクラスターエンドポイント:8182
Dockerfileの用意
とりあえずpython3.6の公式イメージを使います。
FROM python:3.6
RUN apt-get update -y && apt-get upgrade -y
RUN apt-get install autossh -y
RUN mkdir /root/.ssh
docker-compose.ymlの用意
足りないところは補完してください。
私がやっていることは
- ホストの
.ssh/config
をコンテナの/root/.sshdummy
にマウントする - コンテナ内で、EC2の鍵ファイルを、/root/.sshにコピーする
- 鍵ファイルのパーミッションを設定する
- ポートフォワーディングする
- /bin/bashで起動
また、extra_hostsでローカルホストにNeptuneクラスターのエンドポイントを紐づけている理由は、今回pythonで使うgremlin-python
がhttps://localhost
という接続だとエラーが起こるためです。
version: '3'
services:
gremlin:
command: >
/bin/bash -c "cp /root/.sshdummy/config /root/.ssh/ &&
cp /root/.sshdummy/EC2の鍵ファイル.pem /root/.ssh/ &&
chmod 600 /root/.ssh/EC2の鍵ファイル.pem &&
autossh neptune -f -N &&
/bin/bash"
volumes:
- ~/.ssh:/root/.sshdummy
-
extra_hosts:
- "Neptuneクラスターエンドポイント:127.0.0.1"
とりあえず、接続チェック
docker-compose run gremlin
で、中にはいって以下を叩いてみます。
curl https://Neptuneクラスターのエンドポイント:8182
> {"requestId":"28b45dc4-842c-c129-2d4d-******","detailedMessage":"no gremlin script supplied","code":"MissingParameterException"}
帰ってきました。接続オッケーです。
gremlin-pythonを使ってみる
パッケージのインストールをします。
pip install gremlinpython
私は、現在駅すぱあとという経路検索サービスを提供する会社に現在勤めているので、駅データと路線データをNeptuneにロードして簡単な経路検索をしてみようと思います。
せっかくグラフDBを使うので、N頂点〜N頂点の経路検索を一度にやってみます。
from gremlin_python import statics
from gremlin_python.structure.graph import Graph
from gremlin_python.process.graph_traversal import __
from gremlin_python.process.strategies import *
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
from gremlin_python.structure.graph import VertexProperty
import time
graph = Graph()
g = graph.traversal().withRemote(DriverRemoteConnection('wss://Neptuneクラスターのエンドポイント:8182/gremlin','g'))
# 出発地点
origin = g.V().has('station', '県コードキー', '県コード').has('駅種別キー', 駅種別).sample(10).toList()
# 到着地点
destination = g.V().has('station', '県コードキー', '県コード').has('駅種別キー', 駅種別).sample(10).toList()
# 頂点IDの配列化
origin_ids = [x.id for x in origin]
destination_ids = [x.id for x in destination]
start_time = time.time()
result = g.V().hasId(*origin_ids).repeat(__.outE().hasLabel('特定の辺のラベルのみ').inV().simplePath()).until(__.hasId(*destination_ids)).path().limit(100).map(__.unfold().hasLabel('station').values('name').fold())
for path in result.toList():
print('->'.join(path))
print(time.time() - start_time)
結果
大崎広小路->五反田->戸越->中延
大崎広小路->戸越銀座->荏原中延->旗の台->荏原町->中延
青物横丁->新馬場->北品川->品川->大崎->五反田->大崎広小路
大崎広小路->五反田->大崎->大井町->下神明->戸越公園->中延
〜〜〜〜 省略 〜〜〜〜〜
代田橋->笹塚->新宿->四ツ谷->御茶ノ水->秋葉原->浅草橋->両国->錦糸町->押上
大崎広小路->五反田->大崎->品川->新橋->東銀座->銀座->東京->上野->日暮里->新三河島
青物横丁->新馬場->北品川->品川->新橋->東銀座->銀座->東京->上野->日暮里->新三河島
代田橋->笹塚->新宿->池袋->板橋->十条(東京都)->赤羽->尾久->上野->日暮里->新三河島0.43123531341552734(←処理時間
できました。
気をつけたいこと
-
Neptuneで使えるGremlinは完全互換があるとは言えません。
- 公式ドキュメントの実装の相違点をよく読みましょう
-
Gremlinムズカシー(愚痴)