IPFSゲートウェイ構築と運用

Yuya Sugano
23 min readApr 18, 2019

--

IPFS(Inter-Planetary File System)は、既存のHTTPプロトコルの補完もしくは置換を目的に開発された 分散型のファイルシステムです。 分散型のファイルシステムであるということの親和性を生かして様々なブロックチェーンベースのDAppsに採用されています。本記事では自身のサーバ(またはAzureやAWS上のインスタンス)をIPFSノードとして、また簡単なIPFSゲートウェイとして構築するための手順をご紹介します。

IPFS Introduction

ゲートウェイ構築のメリット

分散型で既にノードやIPFSゲートウェイが提供されているのに、あえて構築するメリットは何でしょうか。IPFSは使用用途によって、グローバルなCDN(コンテンツデリバリネットワーク)のようなものとも言えます。ユーザやサービスがアクセスしてくる、地理的に近接した箇所にノードやIPFSゲートウェイを構築したいというケースがありそうです。IPFS自体だけでサービス提供もできますが、ブロックチェーンを利用したDAppsでは画像などをブロックチェーン上に保存しないように、オフロード先として活用されている事例が多い印象です。

IPFSゲートウェイとは

HTTPリクエストによってIPFSのコンテンツへアクセスできるよう橋渡しをしてくれるゲートウェイ(サーバ)です。今回はこのIPFSゲートウェイを構築します。また一般に開放されているIPFSゲートウェイはパブリックゲートウェイと呼ばれており、以下のようなURIでコンテンツへアクセスすることが可能です。

https://<DNS or IPアドレス>/ipfs/<コンテンツのハッシュ>

Protocol Labsのパブリックゲートウェイhttps://ipfs.ioはコンテンツの保持期間を極端に短くしているためIPFSの仕様通りの動作をしないことがあるようです。[2]

https://ipfs-gateway.decentralized-web.jp/というパブリックゲートウェイも使用できます。以下はそれぞれ牛の画像のハッシュ値を埋め込んだコンテンツのリンクです。

https://ipfs.io/ipfs/QmXr7KNc5436EZXPPwJP1KUeMhBQHLNSC2oEMTKBGQ8Vs3

https://ipfs-gateway.decentralized-web.jp/ipfs/QmXr7KNc5436EZXPPwJP1KUeMhBQHLNSC2oEMTKBGQ8Vs3

本記事の内容です。

  • IPFS概要
  • IPFSのインストールおよび設定
  • IPFS Daemonの実行
  • IPFSゲートウェイ(HTTP)の設定
  • IPFSピアの接続および指定

IPFS概要

IPFS(Inter-Planetary File System)は、既存のHTTPプロトコルの補完もしくは置換を目的に開発された 分散型のファイルシステムで、Protocol Labsにより開発が進めれられています。HTTPのようなURIによるロケーション指向ではなく、コンテンツ指向でオブジェクトのハッシュ値をもとにノードからデータを取得します。IPFSゲートウェイ経由でアクセスする場合には、HTTP/HTTPSでアクセスすることも可能です。

  • ロケーション指向でなくコンテンツ指向、サーバがどこにあるかは関係ない、コンテンツの内容からコンテンツを持つノードを走査する
  • URIやIPアドレスではなく、コンテンツがアドレスを決定する

「コンテンツをlocationで指定する識別子を生成するのではなく、コンテンツ自体の表現でコンテンツを指定します」

既存のHTTPプロトコルと比べ、以下のようなメリットが考えられます。

  • 耐障害性(コンテンツのハッシュ値から対象ノードを走査)
  • 負荷分散(ユーザはコンテンツを最寄りのノードから取得)
  • 耐検閲性(一部のノードが隔離されたとしても影響がない)
  • 耐改竄性(コンテンツ自身のハッシュから正当性を検証可能)

コンテンツに中央集権的な保持制度がないため、国家による検疫などにも耐性が高いアーキテクチャとなっています。[3]

IPFSのインストールおよび設定

※ 環境はUbuntu 16.04.1 LTSを使用

Goで実装されているgo-ipfsのインストールと設定をします。最新のバイナリからインストールする方法と、ipfs-updateを使用してバージョン指定する方法があります。

公式サイトから環境に合わせたgo-ipfsバイナリパッケージをダウンロードして、実行モジュールをPATHが通っているディレクトリへ配置してください。

https://dist.ipfs.io/#go-ipfs

Linux Binaries
$ wget https://dist.ipfs.io/go-ipfs/v0.4.19/go-ipfs_v0.4.19_linux-amd64.tar.gz
$ tar xvzf go-ipfs_v0.4.19_linux-amd64.tar.gz
go-ipfs/build-log
go-ipfs/install.sh
go-ipfs/ipfs
go-ipfs/LICENSE
go-ipfs/README.md
$ cd go-ipfs/
$ sudo ./install.sh
Moved ./ipfs to /usr/local/bin
$ ipfs version
ipfs version 0.4.20
$ which ipfs
/usr/local/bin/ipfs

バイナリが環境に合っていない場合は以下のようなエラーが出るので、正しいバイナリを再度ダウンロードする必要があります。

-bash: /usr/local/bin/ipfs: cannot execute binary file: Exec format error

ipfs-updateを使用する場合は以下のようにipfs-updateのバイナリをダウンロードおよび展開します。ipfs-update installコマンドでバージョンを指定してインストールが可能です。

$ wget https://dist.ipfs.io/ipfs-update/v1.5.2/ipfs-update_v1.5.2_linux-amd64.tar.gz
$ tar xvzf ipfs-update_v1.5.2_linux-amd64.tar.gz
ipfs-update/build-log
ipfs-update/install.sh
ipfs-update/ipfs-update
$ cd ipfs-update/
$ sudo ./install.sh
$ ipfs-update versions
v0.3.2
v0.3.4
v0.3.5
...
v0.4.18
v0.4.19
v0.4.20
$ ipfs-update install latest
$ ipfs version
ipfs version 0.4.20
$ which ipfs
/usr/local/bin/ipfs

リポジトリの初期化を行います。リポジトリはIPFSのコンフィグファイルとデータストアとしてデータの共有に使用されるディレクトリも含みます。また初期化にともなって鍵ペアと、ノードに紐づくハッシュも決定されます。

インスタンスに用意したストレージをマウントしてください。.ipfsというディレクトリをリポジトリとするパスへ作成します。

$ mkdir /mnt/<Repository Path>/.ipfs

初期化にあたってIPFS_PATHを設定する必要があります。

$ export IPFS_PATH=/mnt/<Repository Path>/.ipfs

ipfs initコマンドでローカルのipfsリポジトリを初期化し、ホームディレクトリに作成されるリポジトリを確認します。.ipfsというディレクトリがリポジトリです。.ipfs内に設定ファイル(config)やデータストアが生成されます。IPFS_PATHを設定していない場合、デフォルトでは~/.ipfsに作成されます。--profile server のオプションを渡すことでサーバ用の設定が自動的に適用されます。

$ ipfs init --profile server
initializing IPFS node at /home/.ipfs
generating 2048-bit RSA keypair...done
peer identity: QmW2bRPsnaNaXynmEWSVfXZP9333pHy5zrDAwzop1FMECM
to get started, enter:
ipfs cat /ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme

readmeがローカルのipfsリポジトリに作成されているのでipfsのファイル内容を確認するipfs catコマンドで内容を確認してみましょう。

$ ipfs cat /ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv/readme
Hello and Welcome to IPFS!
If you're seeing this, you have successfully installed
IPFS and are now interfacing with the ipfs merkledag!
-------------------------------------------------------
| Warning: |
| This is alpha software. Use at your own discretion! |
| Much is missing or lacking polish. There are bugs. |
| Not yet secure. Read the security notes for more. |
-------------------------------------------------------
Check out some of the other files in this directory:./about
./help
./quick-start <-- usage examples
./readme <-- this file
./security-notes

ipfs object getコマンドではIPFSオブジェクトおよびLink構造体が確認可能です。

$ ipfs object get QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv
{"Links":[{"Name":"about","Hash":"QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V","Size":1688},{"Name":"contact","Hash":"QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y","Size":200},{"Name":"help","Hash":"QmY5heUM5qgRubMDD1og9fhCPA6QdkMp3QCwd4s7gJsyE7","Size":322},{"Name":"ping","Hash":"QmejvEPop4D7YUadeGqYWmZxHhLc4JBUCzJJHWMzdcMe2y","Size":12},{"Name":"quick-start","Hash":"QmXgqKTbzdh83pQtKFb19SpMCpDDcKR2ujqk3pKph9aCNF","Size":1692},{"Name":"readme","Hash":"QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB","Size":1102},{"Name":"security-notes","Hash":"QmQ5vhrL7uv6tuoN9KeVBwd4PwfQkXdVVmDLUZuTNxqgvm","Size":1173}],"Data":"\u0008\u0001"}

設定については以下のコマンドで確認可能で、IPFSレポジトリは.ipfs配下にあるconfigファイルを変更することで設定します。

$ ipfs config showor$ less /mnt/<Repository Path>/.ipfs/config

IPFS Daemonの実行

IPFSノードを実行するために、インスタンス上のファイアウォールポートTCP 4001およびTCP 8080を事前に開放します。ノードのピアリングおよびIPFSゲートウェイにおけるリッスンポートです。

IPFSノードを起動する前にネットワークに伝播させたいファイルを追加しておきます。※表示されているハッシュ値は牛の画像です

$ ipfs add <YOUR FILE>
added QmXr7KNc5436EZXPPwJP1KUeMhBQHLNSC2oEMTKBGQ8Vs3
1.89 MiB / 1.89 MiB [==================================================================================] 100.00%

コンテンツのサイズが256Kを超える場合は、256K以下のサイズのチャンクに分割して保存されます。ipfs lsコマンドでハッシュ値を指定することで以下のように各チャンクの情報を確認できます。

$ ipfs ls QmXr7KNc5436EZXPPwJP1KUeMhBQHLNSC2oEMTKBGQ8Vs3
QmcTKH3Pj5yqTBFwk7zV4dxEvEXyvygndWPPwUD2PZncuV 262144
QmQu5T5VTbEkF4FGNJ4j587gH6KydAa8fiGUBPr572N3LL 262144
QmQcpb3Tyuw6M6WrLzsM1DRRNe6iXPojf9QNNvEvHoo4BD 262144
QmVULKwDAgY9DZkfKDBcEpxt57jSMUGcLQnaRk5VSr7fXz 262144
QmSzWYdnjieQ3h8BXpWqJu1NtBVnmjrpkGh8h1d2KzVXkZ 262144
QmUjoVQpRkCVjjfqdN7gcYdJAXjE7py4xYsgVJhSXn1g4F 262144
QmbiyWeui1MWtgeXaubsNSf2VSPf99SwXUH539w5d1Lzy7 262144
QmdiPCErwAoejiKhzCb9GNcE6pJxFs2MSDhcfFhqM6C2Ca 150803

.ipfs/configファイルを変更して”API”および”Gateway”の設定にインスタンスのIPアドレスを入力してください。daemonを起動することで追加したファイルがネットワーク経由でP2Pネットワークに伝播され、パブリックゲートウェイおよびプライベートゲートウェイからデータを確認できるようになります。

"Addresses": {
"Swarm": [
"/ip4/0.0.0.0/tcp/4001",
"/ip6/::/tcp/4001"
],
"Announce": [],
"NoAnnounce": [],
"API": "/ip4/X.X.X.X/tcp/5001",
"Gateway": "/ip4/X.X.X.X/tcp/8080"
}

ipfs daemonでノードを起動します。

$ ipfs daemon
Initializing daemon...
go-ipfs version: 0.4.19-
Repo version: 7
System version: amd64/linux
Golang version: go1.11.5
Swarm listening on /ip4/127.0.0.1/tcp/4001
Swarm listening on /ip4/X.X.X.X/tcp/4001
Swarm listening on /p2p-circuit
Swarm announcing /ip4/127.0.0.1/tcp/4001
Swarm announcing /ip4/X.X.X.X/tcp/4001
API server listening on /ip4/X.X.X.X/tcp/5001
WebUI: http://X.X.X.X:5001/webui
Gateway (readonly) server listening on /ip4/X.X.X.X/tcp/8080
Daemon is ready

デフォルトではストレージ容量が10Gとなっているので、必要に応じて起動前に修正してください。テスト時は1Gぐらいでもいいと思います。

ipfs daemonは CTRL+C で終了できます。バックグラウンドで動作させることでノードを起動させたままにできますが、systemctlsupervisord でサービス管理したほうがベターです。

以下、簡単なサンプルですがsystemctl での設定例を記載しておきます。

$ sudo vim /opt/ipfs.sh
#!/bin/bash
ipfs daemon

$ sudo chmod 0755 /opt/ipfs.sh

Unit定義ファイルを作りましょう。

$ sudo vim /etc/systemd/system/ipfs.service
[Unit]
Description=ipfs
After=network.service
[Service]
Type=simple
Restart=on-success
ExecStart=/opt/ipfs.sh
[Install]
WantedBy=multi-user.target

systemct start で起動できるようになります。

$ sudo systemctl start ipfs
$ sudo systemctl status ipfs
? ipfs.service - ipfs
Loaded: loaded (/etc/systemd/system/ipfs.service; disabled; vendor preset: enabled)
Active: active (running) since Thu 2019-04-18 20:12:55 JST; 1s ago
Main PID: 19324 (ipfs.sh)
CGroup: /system.slice/ipfs.service
tq19324 /bin/bash /opt/ipfs.sh
mq19326 ipfs daemon
Apr 18 20:12:55 <your hostname.com> systemd[1]: Started ipfs.
Apr 18 20:12:55 <your hostname.com> ipfs.sh[19324]: Initializing daemon...
Apr 18 20:12:55 <your hostname.com> ipfs.sh[19324]: go-ipfs version: 0.4.19-
Apr 18 20:12:55 <your hostname.com> ipfs.sh[19324]: Repo version: 7
Apr 18 20:12:55 <your hostname.com> ipfs.sh[19324]: System version: amd64/linux
Apr 18 20:12:55 <your hostname.com> ipfs.sh[19324]: Golang version: go1.11.5

P2Pネットワーク上のピアおよびコンテンツが共有されているかを確認します。ipfs swarm peersでピアの確認ができます。

$ ipfs swarm peers
/ip4/1.173.34.68/tcp/4001/ipfs/QmRpz2j1dr6Jo5VZ5nZ6zqEYuqFpTXWupF69ccNtfJgQwT
/ip4/1.195.28.42/tcp/1062/ipfs/QmXSuy2t8eTETCaaSpnqAtfwe8ShHdhAuMkX67vcCR61d4
...
/ip4/99.74.58.141/tcp/4001/ipfs/QmTcqpgP6hmzsUbViS7wdkSVXgs2LLbqp359qn4xc79Mry

対象のハッシュ値を持つコンテンツがどのピアと共有されているかが確認できます。ピアが表示されていればネットワーク上でオブジェクトの走査が可能です。ここでは2つのピアが対象のコンテンツを保持していることが分かりました。

$ ipfs dht findprovs QmXr7KNc5436EZXPPwJP1KUeMhBQHLNSC2oEMTKBGQ8Vs3
QmW2bRPsnaNaXynmEWSVfXZP9333pHy5zrDAwzop1FMECM
QmdKFbzojwRAqFJ3DZDMKxaW8EZsaCCp8Kv1LBEv1PUdKh

ここまででIPFSノードの基本設定および起動は終了です。

IPFSゲートウェイ(HTTP)の設定

.ipfs/configファイルを変更してインスタンスがIPFSゲートウェイとして動作できるように設定します。太字が変更・追加した設定です。

"Gateway": {
"HTTPHeaders": {
"Access-Control-Allow-Headers": [
"X-Requested-With",
"Access-Control-Expose-Headers",
"Range"
],
"Access-Control-Expose-Headers": [
"Location",
"Ipfs-Hash"
]
,
"Access-Control-Allow-Methods": [
"POST",
"GET"
],
"Access-Control-Allow-Origin": [
"*"
],
"X-Special-Header": [
"Access-Control-Expose-Headers: Ipfs-Hash"
]

},
"RootRedirect": "",
"Writable": true,
"PathPrefixes": [],
"APICommands": [],
"NoFetch": false
}

ipfs daemonもしくはsystemct start でIPFSノードを起動します。

$ sudo systemctl start ipfs

IPFSノードのIPを<your_ip_address>の箇所と置き換え、以下のURIにブラウザでアクセスしてください。愛らしい猫の画像がでてくるはずです。

http://<your_ip_address>:8080/ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg
Isn’t she adorable ??

httpsを利用してセキュアにIPFSゲートウェイを構築したい方は、nginxをリバースプロキシとして設定することができるので、以下参考にしてみてください。[4]

IPFSピアの接続および指定

ipfs swarm peersでIPFSノードのピアを確認することができました。

$ ipfs swarm peers
/ip4/1.173.34.68/tcp/4001/ipfs/QmRpz2j1dr6Jo5VZ5nZ6zqEYuqFpTXWupF69ccNtfJgQwT
/ip4/1.195.28.42/tcp/1062/ipfs/QmXSuy2t8eTETCaaSpnqAtfwe8ShHdhAuMkX67vcCR61d4
...
/ip4/99.74.58.141/tcp/4001/ipfs/QmTcqpgP6hmzsUbViS7wdkSVXgs2LLbqp359qn4xc79Mry

対象ノードを指定したピアリングも可能です。フィルター機能を使用することで特定のピアとだけ通信することができるので、インターネット上にプライベートなIPFSネットワークを構築する、という使い方も検討できます(あるアプリケーションやエンドポイントに非パブリックの分散型のファイルシステムを提供したい場合など)。

  • ipfs swarm connect <IP address>で指定したノードへ接続
  • ipfs swarm disconnect <IP address>で指定したノードから切断

ホワイトペーパーを読んでみました。比較的読みやすかったのでお勧めです。[6]

https://ipfs.io/ipfs/QmV9tSDx9UiPeWExXEeH6aoDvmihvx6jD5eLb4jbTaKGps

IPFS White paper

以上、簡単なIPFSゲートウェイ(HTTP)の構築でした。

まとめ

  • IPFS(Inter-Planetary File System)を利用することで分散型のファイルシステムをストレージとして利用することができる
  • IPFS(Inter-Planetary File System)は分散型アーキテクチャのためDappsと親和性が高い
  • Javascriptを利用したクライアントも開発されている[5]

--

--

Yuya Sugano

Cloud Architect and Blockchain Enthusiast, techflare.blog, Vinyl DJ, Backpacker. ブロックチェーン・クラウド(AWS/Azure)関連の記事をパブリッシュ。バックパッカーとしてユーラシア大陸を陸路横断するなど旅が趣味。