Virtuso on GCP: Faster with Container-Optimised OS
はじめに
自前のオープンデータでエンドポイントを建てよう!と考えたときに,意外と難しいし手間がかかるという問題があります.これを解決しようと取り組んだのが 林 正洋さん(公共オープンデータ利活用研究室 mirko) の "Google Cloud Platform の無期限無料枠を利用した Virtuoso の構築方法 - Qiita" でした[1].
しかし,このアプローチにはいくつかの欠点があり,そのせいで実際にエンドポイントを建てるまでには高いハードルが存在しているように私は感じました 😢
本記事では,これらを克服し,より簡単に SPARQL エンドポイントを建てられるような方法を紹介します.
先行事例の欠点
さて,まず先述したアプローチには二つの欠点と一つの大きな誤りがあります:
欠点:
- Openlink Virtuoso のインストールに滅茶苦茶な時間がかかる
- 必要なデータのロードするのに破茶滅茶な時間がかかる
誤り:
- オープンデータを志す人は,当然サーバ関連知識も熟知しているので,コマンドがたくさん出てきても大丈夫
詳細な説明
欠点 ①:インストールで時間を浪費しがち
- ただでさえ時間がかかる
make
コマンドをわざわざ貧弱な計算機にやらせているため,無駄に時間を消費してしまっている
ソースのビルドではなく,既存のイメージをそのまま流用したほうが良いはず → Docker を活用しよう!
欠点 ②:データのロードで時間を浪費しがち
- 数 KB 程度のファイル単体ならいざしらず,数 MB のファイルやら複数ファイルをまとめて Bulk load したいと思ったときに,データのロードにめちゃくちゃ時間がかかる or 失敗して起動しなくなる
これもまた,わざわざ貧弱な計算機で(ry
GCP の Always Free を使いたい場合には,貧弱な計算資源で賄うのも致し方ない……と思ったのもつかの間,データのロードはローカルでやっておけばよいことに気づき,GCS も活用することにした.
誤り:コマンドがたくさん出てきても大丈夫
そもそも,多くの人にとっては
_人人人人人人人人人人人人人人_
> サーバ環境に触れたくない <
 ̄ Y^Y^Y^Y^Y^Y^Y^Y^Y^Y  ̄
というのが本音であろうと思われる.
よくわからないコマンドが並んでいるだけでも,かなりウッとなるらしい.
せめて最小限のコマンドで,しかもコピペですべて終えられたら嬉しいと考えた.
Time flies like an arrow
これらの問題は Docker および GCS[2] を活用することで解決できます!
以下では,その方法について細かく説明します.また,その際に以下の GitHub リポジトリを使用します(ぜひこちらにもスターください笑)
動作要件
まず,以下に示すものが必要です:
- GCP のアカウント
- 以下のコマンドが動作する環境:
-
docker
: cf. https://docs.docker.com/get-docker -
gcloud
: cf. https://cloud.google.com/sdk/docs/install -
gsutil
: cf. https://cloud.google.com/storage/docs/gsutil_install
-
GCP への登録等については先行事例にも書いてあるのでそちらに譲ります.後者については,利用している OS によってインストール方法が異なるため,この記事内で具体的な方法には触れません.それぞれの URL から説明を読んで各自インストールしてください(インストールだけであれば,こちらもコマンドをコピペするだけで済むと思われます).
概要
はじめに,必要なものを揃えたリポジトリを git clone
してください:
git clone https://github.com/Ningensei848/virtuoso-on-gcp-with-cos.git
このリポジトリを元にすれば,以下の4ステップでやりたいことが実現します:
- 設定項目を書き換える ← 最重要!!
- ローカル環境に Virtuoso コンテナを建て,RDF データを読み込ませる
- virtuoso.db を GCS にアップロードする
- GCE インスタンスを作成する
設定がうまく機能していれば,作成された GCE インスタンスが自動起動し,あなたが設定したドメインに対して https でアクセスできるようになるでしょう.
Step 1. 設定項目を書き換える
まず,.env.example
を開いてください.このファイルには,以降の手順でも使う設定がまとめられています.しかし,このままでは使えません.あなたの環境に置き換えて書き直す必要があります.
また,書き直した後はファイル名を .env
にリネームしてください
設定を記述するにあたって GCP 側でやらねばならないことがいくつかあります:
- プロジェクトの作成
- 静的外部 IP アドレスの予約
- ドメインの確保
- DNS の設定
詳細をみる
〈プロジェクトの作成〉については,GCP にアカウントを登録した際に,コンソール画面へ移行するときに作成したものを使ってもいいし,新たに作っても構いません.何であれ,GCP 上で動かす汎ゆるサービスは,この「プロジェクト」という括りの中で動作する仕組みになっています(ので,なにをするにも必要な情報です)
〈静的外部 IP アドレスの予約〉については,公式のドキュメントを参照してください.いままで GCP に触れたことがなければ "静的外部 IP アドレスを予約して、そのアドレスを新しい VM インスタンスに割り当て" というアプローチを取ることになるでしょう
〈ドメインの確保〉について,freenom を使えばいいという話もありますが,ここは敢えて Cloud Domains を推しておきます.これは,Google Domains として独立していたサービスを,GCP 上でも統合して使えるようにしたものです(ドメイン管理と支払いがラクになった).
あなただけのドメインを入手したら,次はそのドメイン名をブラウザに入力すると画面遷移(いわゆる名前解決)してくれるように〈DNS を設定〉します.これもまたおすすめなのは Cloud DNS です.が,何であれ DNS サーバにドメイン名を入力し,A レコードに予約済み静的 IP アドレスを登録してください.DNS サービスの仕様によってまちまちですが,実際にドメイン解決ができるようになるまでに一日程度待つ必要がある場合もあるようです(なお Cloud DNS なら遅くとも 300 秒程度です)
ここまでの情報をもとに,.env.example
を編集し,.env
にリネームしてから保存してください.
各種の変数についてもっと知る
必須項目:
-
USERNAME
: ユーザ名 -
USER_EMAIL
: メールアドレス (for letsencrypt) -
SERVER_NAME
: 取得したドメイン名 (e.g. your-doma.in) -
GCP_PROJECT_NAME
: GCP のプロジェクト名 -
GCE_*
: インスタンスに関するお好み設定 -
GCS_BUCKET_NAME
: GCS に作成したいバケットの名前
任意項目:
-
Parameters_NumberOfBuffers
&Parameters_MaxDirtyBuffers
: virtuoso performance tuning -
TOKEN_LINE
: enable notification cf. https://notify-bot.line.me
Step 2. ローカル環境に Virtuoso コンテナを建て,RDF データを読み込ませる
※git clone
してきたリポジトリに data/
フォルダが含まれていることを確認してください.
- RDF データを
data/
以下に好きなように配置する - スクリプトを実行し,
isql
プロシージャを得る - Virtuoso コンテナを建てる
- virtuoso にデータを読み込ませて
virtuoso.db
を得る
data/
以下に好きなように配置する
Step 2.1. RDF データを まずは, Virtuoso で読み込みたいデータを data/
に集めて置いてください.初めは公開済みデータセットを探してきて試すのが良いでしょう.また,もし自前でデータを作りたい場合には,XML よりも Turtle 形式で記述することをおすすめします.
isql
プロシージャを得る
Step 2.2. スクリプトを実行し,次に,スクリプトを実行して isql
プロシージャを取得します.これは,virtuoso に対してどこにどのデータが有るのか・どのようにデータを読み込めば良いのかを教えてあげるものです.ブラウザ経由でファイルをアップロードする方法もあるにはありますが,こちらは大規模にデータをロードするのには向いていないので紹介するのは控えます.
以下のコマンドをコピペして実行してください:
source .env
docker run --rm -v $(pwd)/data:/data -v $(pwd)/script:/script -u "$(id -u $USERNAME):$(id -g $USERNAME)" python:3.10-alpine python /script/configureSQL.py ttl --origin https://$SERVER_NAME
source .env
で書き換えた設定項目を読み込み,コマンドラインから呼び出せるようにしています.また docker run
では,ローカルのディレクトリをボリュームマウントした上で,Python によるスクリプトを実行しています.実行が完了すると,script/
以下に initialLoader.sql というファイルが見つかるでしょう.
Step 2.3. Virtuoso コンテナを建てる
docker-compose
を利用してコンテナを建てます.もし docker
をインストールした際に附属していなかった場合には,適宜インストールしてください.
以下のコマンドをコピペして実行してください:
source .env
docker-compose up -d virtuoso
次のステップに進む前に,Virtuoso が本当に起動したのか確認しましょう:
# Check if the virtuoso server is online at port XXXX
docker-compose logs
logs
コマンドを使うことで,立ち上げたコンテナが現在どのような状態になっているのか知ることができます.
以下のように,"Server online at XXXX" (XXXX はポート番号)という表示があれば準備完了です.
virtuoso_container | HH:MM:SS Server online at $PORT_VIRTUOSO_ISQL (pid 1)
virtuoso.db
を得る
Step 2.4. virtuoso にデータを読み込ませて ※公式ドキュメント:http://docs.openlinksw.com/virtuoso/rdfperfloading/
ローカル環境で行なう最後の作業として,RDF データを読み込ませて virtuoso.db
を得ます.実は,この作業のために先程取得した initialLoader.sql が必要なのでした.
以下のコマンドをコピペして実行してください:
source .env
nohup docker exec -i virtuoso_container isql $PORT_VIRTUOSO_ISQL -U dba -P $PASSWORD_VIRTUOSO < ./script/initialLoader.sql &
このコマンドはなにか
ここで,nohup $@ &
とは,$@
に相当するコマンドをバックグラウンドかつ現在の接続状態とは独立して実行させるコマンドです.なぜこのようにするかというと,$@
に相当する docker exec ~
コマンドの実行に結構な時間がかかる(からバックグラウンドでやりたい)からという理由が一つと,もう一つは nohup.out
というファイルに現在の進捗が記録されるからという理由があります.
コマンドが実行された後に,カレントディレクトリに nohup.out
というファイルが生成されているのが確認できるでしょうか? virtuoso によるデータの読み込みが終わった際には,この nohup.out
の一番最後に "initialLoader.sql completed" という表示が出力されます.
この文字列が確認できたら,次のステップに進んでください.
Step 3. virtuoso.db を GCS にアップロードする
GCS とはなにか
GCS (Google Cloud Storage) は,同じく GCP 上のサービスです.GCP における「プロジェクト」,GCE におけるインスタンスのように,GCS には「バケット」という単位があります.このバケットをルートディレクトリとして,ファイルやディレクトリ等のデータを保存することができます.
まだバケットを持っていない場合には,新たに作成します.gsutil
コマンド経由で作成する場合には以下のようなコマンドを実行します:
source .env
gsutil mb -p $GCE_PROJECT_NAME gs://$GCS_BUCKET_NAME
GCS のバケットに,virtuoso.db をアップロードします.すなわち,ローカルにあるファイルをクラウドストレージ上にコピーします.
source .env
gsutil cp ./.virtuoso/virtuoso.db gs://$GCS_BUCKET_NAME
Step 4. GCE インスタンスを作成する
最後に,これまで記述・作成したきたものを GCE インスタンス上にデプロイします.以下で実行しているのは create
コマンドではありますが,引数の中に startup-script
というメタデータを含んでおり,これによってインスタンスが作成された後に自動でスクリプトが走り,サーバ上に必要なあれこれを展開してくれる仕組みになっています.
以下のコマンドをコピペして実行してください:
source .env
gcloud compute instances create $GCE_CREATE_ARGS
$GCE_CREATE_ARGS の中身を知りたい
.env
を編集する際にチラッと見えたかもしれませんが,GCE_CREATE_ARGS の中身は次のようになっています:
GCE_CREATE_ARGS="$GCE_INSTANCE_NAME \
--project $GCE_PROJECT_NAME \
--zone $GCE_ZONE \
--machine-type $GCE_MACHINE_TYPE \
--tags $GCE_TAGS \
--create-disk $GCE_CREATE_DISK \
--metadata-from-file user-data=$PWD/gcp/cloud-config.yml,NGINX_CONFIG=$PWD/nginx/default.conf.template,DOTENV=$PWD/.env,COMPOSE_FILE=$PWD/docker-compose.yml,startup-script=$PWD/gcp/startup.sh \
--metadata google-logging-enabled=true,cos-metrics-enabled=true,USERNAME=$USERNAME \
--address $GCE_STATIC_IP_ADDRESS \
--shielded-secure-boot \
--shielded-vtpm \
--shielded-integrity-monitoring"
引数オプションの一つに metadata-from-file
があるのがわかるでしょうか?ここに与えた変数によってローカルのファイルを GCE 上に「メタデータ」として渡し,startup-script
に指定されている gcp/startup.sh に記述された処理によってインスタンス内部にコピーしてくる仕組みです.
Tips: インスタンス起動時のログを見たい
インスタンスを(停止状態から)起動するたびに,startup.sh
というスクリプトが走っています.この実行ログは,以下のコマンドを実行することで確認することが出来ます.
source .env
# (ブラウザウィンドウで接続する場合はスキップ)
# 1. インスタンスに ssh 接続する
gcloud compute ssh $GCE_INSTANCE_NAME --zone $GCE_ZONE
# 2. 以下のコマンドをインスタンス上で実行する
sudo journalctl -u google-startup-scripts.service
※閲覧状態から抜け出すには,Q キーを押下してください (quit の意)
インスタンスが本当に起動できているのか?(あるいは起動できなかった理由は何なのか)を探る際にご活用ください.
Congratulations !!
これにてすべての工程は完了です!!
GCP のプロジェクトページを開き,インスタンスが立ち上がっていることを確認してください 🔍
まとめ
先行事例のウィーク・ポイントであった「時間的コストが大きすぎる」「サーバよくわからん人には難しい」という問題を,docker
と GCP の各種コマンドで解決しました.本来必要だった処理は,script/configureSQL.py
やら gcp/startup.sh
, gcp/cloud-config.yml
の中にすべて記述されています.もし自分なりにアレンジしたいという要望があれば,ぜひそちらのファイルも覗いてみてください.
-
Google Cloud Storage のこと(詳細はリンク先を参照) ↩︎
Discussion
これは素晴らしい💡
GCSを使ってロードも速くするアイデアがすごい
ドメインをGCP内で確保するところも◎
私の記事からもリンクさせていただきます
ありがとうございます
RDBとは違い,RDFデータさえ手元にあれば実はDB実体はどうでもよい(クエリのロギングとかしたくなってくると別途方法は考える必要がある)と気づけたことで,GCSでやりくりするアイデアにたどり着きました🌟
※着想元記事とは異なり,完全に無料というわけではない(例:外部静的IPの予約)ことだけがネックですが,十分安く回せる料金ではあると思います