はじめに
Neo4jを利用する人が増えてきたとはいえ、まだまだ認知されていない状況。WindowsではNeo4j Desktopがあるので利用には不便しないのだけど、簡単なアプリケーションを作成して、それをチームに配布するとなると、イメージを作るには容量が大きくなるので、Dockerにすると楽。特に今のようなリモートワークをやっていたりするとなおさら。そこで、docker-composeでアプリとセットで配布しようと思ったところ、neo4jのdocker情報があまりなかったので、調べてみたいろいろやって動いたものを備忘として載せる。
フォルダ構成
Folder
│ docker-compose.yml
├─ docker
│ └─neo4j
│ Dockerfile
│ background.sh
│ init.sh
│ init.cypher
└─volumes
└─neo4j
└─plugins
apoc.jar
docker-compose.yml
version: "3"
networks:
app_net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.30.0.0/24
services:
neo4j:
container_name: neo4j
build: ./docker/neo4j
volumes:
- ./volumes/neo4j/data:/data
- ./volumes/neo4j/plugins:/plugins
- ./volumes/neo4j/logs:/logs
- ./volumes/neo4j/import:/import
- ./volumes/neo4j/init:/init
ports:
- "7474:7474"
- "7687:7687"
environment:
# - NEO4JLABS_PLUGINS=["apoc"]
- NEO4J_apoc_export_file_enabled=true
- NEO4J_apoc_import_file_enabled=true
- NEO4J_apoc_uuid_enabled=true
- NEO4J_dbms_security_procedures_unrestricted=apoc.*
- NEO4J_dbms_security_procedures_whitelist=apoc.*
- NEO4J_dbms_memory_heap_initial__size=512m
- NEO4J_dbms_memory_heap_max__size=2G
- NEO4J_dbms_default__listen__address=0.0.0.0
- NEO4J_dbms_connector_bolt_listen__address=:7687
- NEO4J_dbms_connector_http_listen__address=:7474
- NEO4J_dbms_connector_bolt_advertised__address=:7687
- NEO4J_dbms_connector_http_advertised__address=:7474
- NEO4J_dbms_allow__upgrade=true
- NEO4J_dbms_default__database=neo4j
- NEO4J_AUTH=neo4j/p@ssw0rd
- EXTENSION_SCRIPT=/tmp/background.sh
restart: unless-stopped
networks:
app_net:
ipv4_address: 172.30.0.3
解説
# - NEO4JLABS_PLUGINS=["apoc"]
この部分のコメントを外すと、apoc.jarをインターネットから自動でダウンロードしてくるようになる。今回はvolumes/neo4j/pluginsフォルダに最初からapoc.jarを格納しているので、ダウンロードは必要ないため、コメントアウトしている。
apoc.jarのダウンロードはかなり時間がかかるので、あらかじめダウンロードしておいたものを使う方が起動時間を短縮できる。ただし、この機能を有効化するメリットが1つある。今回はNeo4jのDockerイメージにneo4j:4.1.3(2020/10/15時点の最新は4.1.3)を利用しているが、neo4jのバージョンに対応したapoc.jarのバージョンが存在するため、組み合わせによっては動かない場合がある。このコメントを外すと、利用しているNeo4jのイメージに対応したapoc.jarを自動で判別し、インストールしてくれる。
- NEO4J_AUTH=neo4j/p@ssw0rd
neo4jのパスワードを設定
- EXTENSION_SCRIPT=/tmp/background.sh
このEXTENSION_SCRIPTがよく分からなかった。Neo4jのマニュアルでは何をするための用途なのか書いておらず、他者の記事を見るとNeo4j起動後に実行されるスクリプトである
と書いてある。実際の挙動を見てみると、Neo4j起動後というよりは起動前に実行されているっぽい。なおかつこのスクリプトが正常に終了しないとNeo4jが起動されない様子。そのため、今回はbackground.shの中でinit.shを(&を付けて)実行するようにした。そうするとNeo4jも無事に起動され、バックグラウンドでinit.shがNeo4jの起動を待ち受けて初期データをセットアップしてくれる。ちなみに、EXTENSION_SCRIPTでinit.shを&付きで実行してもうまくいかなかった。
Dockerfile
FROM neo4j:4.1.3
RUN apt-get update
RUN apt-get install -y curl
COPY init.cypher /tmp/init.cypher
COPY background.sh /tmp/background.sh
COPY init.sh /tmp/init.sh
RUN chmod -v +x /tmp/background.sh
RUN chmod -v +x /tmp/init.sh
neo4jのイメージを配備すると、apt-getのパッケージリストが空なので、一旦apt-get updateを実行しないと、パッケージがインストールできない。ここでは、Neo4jの起動をチェック(Cypherを実行可能かを確認)するためにcurlをインストールするようにしている。
Proxy環境下の場合はここでENV http_proxy http://hogehoge
などを追加すると動くはず。
background.sh
# !/bin/bash
/tmp/init.sh &
上でも解説しているが、EXTENSION_SCRIPTに指定して、init.shをバックグラウンド実行したいだけのシェル。こちらがinit.shで後述のinit.shがbackground.shの方が正しいんじゃないかという気が...。
init.sh
# !/bin/bash
if [ -f /init/done ]; then
echo "The initialization process is already completed." >> /init/setup.log
exit 1
fi
COUNT=1
while [ $COUNT -lt 20 ]; do
status_code=$(curl --write-out %{http_code} --silent --output /dev/null localhost:7474)
if [[ "$status_code" = 200 ]] ; then
touch /init/done
cypher-shell -u neo4j -p p@ssw0rd -f /tmp/init.cypher
echo "The initialization process is complete." >> /init/setup.log
exit 1
else
echo "The neo4j service has not started yet. [$COUNT]" >> /init/setup.log
fi
COUNT=$(expr $COUNT + 1)
sleep 10s
done
exit 0
このシェルは、起動したNeo4jに初期データを投入したいためのスクリプト。ただし、docker-composeを実行するとどうしてもこのスクリプトが毎回実行してしまうため、初回実行した際に/init/doneというファイルを作成し、そのファイルが存在すると何もせずに終了するようにしている。
解説
if [ -f /init/done ]; then
echo "The initialization process is already completed." >> /init/setup.log
exit 1
fi
この部分で/init/doneの存在チェックをして、あれば終了、なければ後続処理を実施。
while [ $COUNT -lt 20 ]; do
Neo4jが起動してくるまで20回(厳密にこのコードだと19回)リトライする。
status_code=$(curl --write-out %{http_code} --silent --output /dev/null localhost:7474)
curlでhttp://localhost:7474 にアクセスし、HTTPレスポンスコードを取得。
if [[ "$status_code" = 200 ]] ; then
touch /init/done
cypher-shell -u neo4j -p p@ssw0rd -f /tmp/init.cypher
echo "The initialization process is complete." >> /init/setup.log
exit 1
else
echo "The neo4j service has not started yet. [$COUNT]" >> /init/setup.log
fi
curlの結果が200の場合、/init/doneを作成し、/tmp/init.cypherをcypher-shellコマンドで実行する。200でない場合、何もせずリトライする。
COUNT=$(expr $COUNT + 1)
sleep 10s
カウントアップと10秒スリープ。ちなみに、Neo4jイメージではカウントアップに(( COUNT++ ))
は利用できなかった。
init.cypher
MATCH (n) return n;
初期セットアップCypher。ここではサンプルとして全ノード取得を書いているが、実際はCREATE文などを書く。
このinit.shがうまく実行されると、/init(volumes/neo4j/init)配下のsetup.logに下記のようなログが出力され、同様にdoneファイルも生成されるはず。
The neo4j service has not started yet. [1]
The neo4j service has not started yet. [2]
The neo4j service has not started yet. [3]
The initialization process is complete.