胡思乱想

胡思乱想

专门记录我的灵感,可能是奇怪的小说

IPFS

IPFS#

分散型ファイルシステムで、原理は bt に似ており、ファイルを分割し、各ブロックは CID および各レベルのハッシュを用いて保存と検証を行い、DHT(分散ハッシュテーブル)を通じて検索とルーティングを行います。

IPFS ドキュメント#

https://docs.ipfs.io/ 主に Concepts と How-tos の部分を参照してください。
IPFS は各コンテンツに異なる CID を生成します。固定リンクが必要な場合は IPNS を通じて実現できますが、IPNS は急速に変化するコンテンツには適しておらず、IPNS の更新間隔は分単位です。

IPFS を使用したサービス#

ゲートウェイ#

公式の利用可能なゲートウェイリスト: https://ipfs.github.io/public-gateway-checker/ 以下はテスト済みの利用可能なゲートウェイです。

go-ipfs#

IPFS の Go 言語プロジェクトはその主要なオープンソース実装です: https://github.com/ipfs/go-ipfs/

go-ipfs の設定説明#

詳細な設定説明は https://github.com/ipfs/go-ipfs/blob/master/docs/config.md に記載されており、マニュアルよりも詳細です。

go-ipfs のコンパイル#

参考: https://github.com/ipfs/go-ipfs#download-and-compile-ipfs

Github プロジェクトのソースコードは 30MB 以上あり、クローンに時間がかかります。もし遅すぎてエラーが発生する場合は、プロキシを追加する必要があります。コンパイルには make build コマンドを使用します。コンパイルといっても、実際には依存ライブラリのダウンロードに大半の時間がかかります。
実行前に GOPROXY を設定して、ダウンロードのタイムアウトを避けます。

# goのバージョンを確認、1.13以上が必要
go version
# プロキシを設定
git config --global https.proxy 'socks5://127.0.0.1:10080'
# 確認
git config -l
#
git clone https://github.com/ipfs/go-ipfs.git
cd go-ipfs/
# GOPROXYを設定
export GOPROXY=https://goproxy.cn
# 確認
echo $GOPROXY
# コンパイル
make build
# コンパイル結果を確認
./cmd/ipfs/ipfs version

Amlogic S905L の Armbian 上で IPFS を実行#

GO IPFS Github プロジェクトから Arm64 バージョンをダウンロードします。Armbian のバージョンは 5.99、カーネルは 5.30 です。

# ダウンロード
wget https://github.com/ipfs/go-ipfs/releases/download/v0.5.1/go-ipfs_v0.5.1_linux-arm64.tar.gz
# 解凍
tar xvf go-ipfs_v0.5.1_linux-amd64.tar.gz
# install.shを直接実行してインストールします。このスクリプトはipfsディレクトリを/usr/local/binにコピーします。
cd go-ipfs
./install.sh

初期化後、通常のユーザーで起動します。

# バージョンを確認
ipfs --version
# ノードを初期化
ipfs init
# 説明を確認
ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
 
# このコマンドはバックグラウンドで実行されません。最初にscreenセッションを作成し、その中で実行することをお勧めします。
ipfs daemon

もし IPFS が現在のコンピュータで実行されていない場合、webui にアクセスするためには設定を 2 箇所変更する必要があります。

1 つは Addresses.API と Addresses.Gateway です。

"Addresses": {
  ...
  "API": "/ip4/127.0.0.1/tcp/5001",
  "Gateway": "/ip4/127.0.0.1/tcp/8080"
}

API のアドレスを /ip4/0.0.0.0/tcp/5001 に変更すると、すべてのネットワークインターフェースをリッスンします。もしサーバーにパブリック IP がある場合、これにはセキュリティ上の問題があります。この IP を内部ネットワークインターフェースのアドレスに設定できます。
Gateway のアドレスを内部アドレスまたはパブリックアドレスに変更します。IPFS が内部ネットワークにある場合、外部から NAT を通じてアクセスできます。これを内部アドレスに設定できます。

もう 1 つは API.HTTPHeaders で、現在のコンピュータから IPFS の webui にアクセスする際に cross site エラーを回避します。

"API": {
    "HTTPHeaders": {}
},

HTTPHeaders に 2 項目を追加します。Gateway.HTTPHeaders を参考にできます。

"Access-Control-Allow-Methods": [
  "GET","PUT","POST"
],
"Access-Control-Allow-Origin": [
  "*"
]

設定を変更した後、IPFS を再起動すると、http://IP:5001/webui/にアクセスでき、現在のノードの状態、保存されているブロックの数とサイズ、接続されているノードの詳細、および現在の設定をグラフィカルに確認できます。このインターフェースは、いくつかのデモ用ファイルブラウジングも提供しています。

Gateway を通じて、IPFS 上のファイルや CID をリモートで照会できるファイルにアクセスできます。アクセスしたファイルブロックはすべてキャッシュされ、再度アクセスする際には、該当するブロックがキャッシュを通じて直接取得され、再度リモートからダウンロードされることはありません。

ファイルを選択してアップロードすると、ページのバックグラウンドで転送が行われます(タブを閉じても問題ないかは未確認です)。ステータスページでは、総ストレージが増加しているのが見えます。異なるサイズのファイルに対して、公開可能な遅延は異なります。10MB 以内のファイルは、リモートゲートウェイを通じてすぐに発見されますが、500MB 近くのファイルは、約 30 分から 1 時間かかります。これは、ノードが自身のコンテンツ CID を他のノードに公開するメカニズムに関連しています。単一のファイルブロックは 256KB(262144 バイト)であり、500MB のファイルの場合、2000 以上の CID が生成されるため、公開に必要な時間が長くなります。

webui でファイルを追加するには、ファイルが完全にアップロードされるまで pin 操作を行うことができません。CID を通じてファイルをアップロードする場合、CID を提出してもファイルの同期はトリガーされません。この CID に対して pin 操作を行うと、ipfs が他のノードからの同期を開始します。ファイルが他のノードからの同期を完了すると、pin リストに表示されます。

サービスへのインストール#

ファイル /etc/systemd/system/ipfs.service を作成し、以下を書き込みます。

[Unit]
Description=IPFS Daemon
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ipfs daemon --enable-namesys-pubsub
User=milton
[Install]
WantedBy=multi-user.target

その後、systemctl を通じて追加します。

設定の説明#

IPFS が外部に提供するポートは主に 3 つです: API、Gateway、Swarm。これらのうち、

  • API: デフォルトではポート 5001 を使用し、webui を提供して IPFS の管理制御に使用されます。リッスンするネットワークインターフェースを設定する際は、パブリックに開放しないよう注意が必要です。
  • Gateway: デフォルトではポート 8080 を使用し、ipfs/CID のコンテンツ検索ダウンロードサービスを提供します。
  • Swarm: デフォルトではポート 4001 を使用し、他の IPFS ノードからのリクエストをリッスンします。
  • Addresses.NoAnnounce: 除外する必要のある内部 IP を追加します。これらの IP は公開されませんが、127.0.0.1 と::1 は除外しないでください。これらは他のノードが現在のノードが ipv4 または ipv6 をサポートしているかを検出するために使用されるようです。これらを除外すると、他のノードとの接続が維持できなくなります(ping や connect は可能ですが、swarm peers に表示されません)。
  • Swarm.AddrFilters: 内部 IP を無視するために追加します。ID 上で内部 IP をアナウンスする peer に対して、これらの内部 IP アドレスはフィルタリングされます。
  • Discovery.MDNS.Enabled: このオプションを false に変更すると、内部ネットワークでノード検索が行われなくなります。
  • Peering.Peers: 接続を維持する必要のあるノードを追加します。

固定ノード#

自分で構築したネットワークでは、自分のノード同士が接続を維持する必要がありますが、IPFS のデフォルトのメカニズムでは、自分で構築したノードをブートストラップとして設定しても、起動後しばらくすると接続されているノードが増え続ける中で、他の自分で構築したノードとの接続が切断されてしまいます。接続を維持するには、設定ファイルの Peering 部分を使用する必要があります。フォーマットは以下の通りで、2 つ目は ipfs.runfission.com の接続アドレスです。

{
  "Peering": {
    "Peers": [
      {
        "ID": "QmPeerID1",
        "Addrs": ["/ip4/18.1.1.1/tcp/4001"]
      },
      {
        "ID": "QmVLEz2SxoNiFnuyLpbXsH6SvjPTrHNMU88vCQZyhgBzgw",
        "Addrs": ["/ip4/3.215.160.238/tcp/4001", "/ip4/3.215.160.238/udp/4001/quic"]
      }
    ]
  }
  ...
}

Peering 設定を使用するノード:

  1. 接続管理の中でこのノードとの接続を保護し、IPFS は決してこの接続を自動的に閉じることはありません。接続数が上限に達しても、この接続は閉じられません。
  2. 起動時に接続が確立されます。
  3. ネットワークの問題や相手ノードのダウンなどの理由で接続が失われた場合、IPFS は再接続を試み続けます。試行の間隔は 5 秒から 10 分の間でランダムです。

公開 NAT 下での IPFS の実行#

実行環境#

パブリック IP のサーバーは Centos7 で、パブリック IP は 118.119.120.121、内部 IP は 192.168.13.10 です。
内部ネットワークサーバーは Ubuntu18.04 で、内部 IP は 192.168.13.25 です。

公開サーバーでのポート転送設定#

全体的な転送スイッチを設定します。

firewall-cmd --permanent --zone=public --add-masquerade

パブリック IP ポート 4002 を内部サーバーの 4001 ポートに転送します。

# TCPポート転送
firewall-cmd --permanent --zone=public --add-forward-port=port=4002:proto=tcp:toaddr=192.168.13.25:toport=4001
# UDPポート転送
firewall-cmd --permanent --zone=public --add-forward-port=port=4002:proto=udp:toaddr=192.168.13.25:toport=4001
# 設定を有効にする
firewall-cmd --reload
# 確認
firewall-cmd --zone=public --list-all

ゲートウェイが OpenWRT ルーターの場合、ファイアウォール -> ポート転送に転送ルールを追加するだけで済みます。転送ルールを追加した後、WAN からこのデバイスのこのポートへのアクセスを許可する通信ルールも追加する必要があります。

接続ノード数の制限#

Swarm/ConnMgr/HighWater というパラメータを通じて設定する必要があります。この設定は 0.6.0 ではうまく機能せず、長時間サーバーを運用するとノード数が設定の制限を超えてしまいますが、0.7.0 では有効です。

"Swarm": {
    ...
    "ConnMgr": {
        "GracePeriod": "30s",
        "HighWater": 500,
        "LowWater": 100,
        "Type": "basic"
    },
    ...
}

内部サーバーで IPFS サービスを設定します。

# インストールプロセスは省略
 
# サーバーモードに初期化
ipfs init --profile=server
 
# 設定を変更、以下の具体的な説明を参照
vi .ipfs/config
 
# 起動
ipfs daemon

サーバーモードは通常モードと比較して、いくつかの変化があります。

  1. Addresses.NoAnnounce: サーバーモードではすべての内部 IP がリストされ、これらの IP は公開されません。
  2. Swarm.AddrFilters: サーバーモードではすべての内部 IP がリストされ、内部 IP を使用して接続する peer はフィルタリングされます。
  3. Discovery.MDNS.Enabled: サーバーモードではこのオプションが false になり、内部ネットワークでノード検索が行われなくなります。

設定する必要があるのは、通常のノードの API、Gateway、API HTTPHeaders に加えて、Addresses.Announce も設定する必要があります。本ノードのパブリック IP とポートを追加する必要があります。

"Announce": [
  "/ip4/118.119.120.121/tcp/4002",
  "/ip4/118.119.120.121/udp/4002/quic"
],

問題に直面した場合#

OpenWRT で設定されたゲートウェイではこの問題は発生しませんが、Centos をゲートウェイとして使用する場合、外部 IP を正しく伝達できないようで、多くの第三者ノードの表示 IP がゲートウェイ IP になってしまいます。本ノードへの接続は成功しますが、ping は失敗します。したがって、Swarm.AddrFilters のリストから同じネットワークセグメントの部分を削除する必要があります。

具体的な理由は、本ノードの swarm peers で確認できます。本ノードから積極的に接続されたノードはパブリック IP が記録されますが、本ノードが受動的に接続されたノード(つまり、ゲートウェイ 118.119.120.121 を通じて接続されたノード)は、すべてゲートウェイの内部 IP が記録されます。AddrFilters のルールに従って、これらのノードは除外されます。これにより、ipfs swarm connect は成功しますが、ipfs ping は失敗する問題が発生します。

 $ ipfs swarm peers
/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
/ip4/111.231.85.77/tcp/4001/p2p/QmWv1eLMNHPpwYKzREQBEpDfYjW6YXCrVpVyBZVjAuSd2i
...
/ip4/192.168.13.10/tcp/10041/p2p/QmXUZth5Pr2u1cW65F7gUeFwjkZFfduE1dwqiysNnrTwXd
/ip4/192.168.13.10/tcp/10053/p2p/QmPM3bepMUKpYTza67coD1Ar3efL7FPBFbGRMc42QLf4q9
/ip4/192.168.13.10/tcp/10202/p2p/QmVBzjW2MyNrSuR48GjcqB6SAJTnw8Y9zaFbgbegh4bRx4
/ip4/192.168.13.10/tcp/1024/p2p/QmbBEfFfw59Vya9CJuzgt4qj9f57sPZGiyK7fup8iKqkTr
/ip4/192.168.13.10/tcp/1025/p2p/QmcmhwCeLkBJvcq6KJzN58BRZg1B1N8m3uA3JyQKpVn64E
...
/ip4/192.168.13.10/udp/6681/quic/p2p/QmcSCBpek4YF5aAsY7bUMxiL7tacoYMeXUJUpU4wctqX4w
/ip4/192.168.13.10/udp/8050/quic/p2p/QmWeuXCNKAHfbineKMqo3U3dvVSz2em1w67pj5Up6tkUXo
/ip4/206.189.69.143/tcp/31071/p2p/12D3KooWHDr5W3Tse17mr4HSzuQm44dVQYp8Bb638mQknsyeHXSP
/ip4/206.189.69.250/tcp/30511/p2p/12D3KooWRd1BNPd8PMfxpCT7TNCFY4XSZsy8v8Cmm36H136yxzub
...

さらに、本ノードから受動的に接続されたノードにアクセスできるかを確認します。Peer ID の 1 つを使って ping テストを行います。

ipfs ping QmXUZth5Pr2u1cW65F7gUeFwjkZFfduE1dwqiysNnrTwXd
PING QmXUZth5Pr2u1cW65F7gUeFwjkZFfduE1dwqiysNnrTwXd.
Pong received: time=26.43 ms
Pong received: time=25.70 ms
Pong received: time=26.31 ms
...

これにより、ゲートウェイ内部 IP のノードは利用可能であることが示され、保持する必要があります。

これらのゲートウェイを通じて接続されたノードは、2 つのカテゴリに分けられます:パブリック IP のないノードとパブリック IP のあるノードです。

  • パブリック IP のあるノードについては、初回接続後に相手の Announce アドレスを使用して ping を行うかどうかは不明です。もしそうであれば、結果に基づいてノードのアドレスを更新する場合、これらのノードはこの表示された内部 IP のリストに短期間留まり、その後パブリック IP アドレスに更新されます。これにより、他のノードが接続する際に、他のノードがそのパブリック IP アドレスを通じて接続できるようになります。
  • パブリック IP のないノードについては、サーバーノードが相手の Announce アドレスを使用して ping を行うことはできず、本方のゲートウェイの内部 IP を通じて接続することしかできません。したがって、これらのノードはこの表示された内部 IP のリストに留まり続けます。これらのノードは他のノードと共有することができません。

ダウンロード速度の最適化#

CID を通じてファイルを読み取ることを希望する場合、まずは速度の速いゲートウェイを選択する必要があります。ipfs.io ゲートウェイを通じてファイルを取得するのが最も信頼性が高いですが、接続の問題により速度が非常に遅くなる可能性があります。

CID を通じてファイルを送信することを希望する場合、相手が使用しているゲートウェイが現在ファイルを保存しているインスタンスの接続リストに含まれていることを確認する必要があります。したがって、速度の速いゲートウェイのリストを維持することが非常に重要であり、これらのゲートウェイを自分の Peering.Peers に追加することで、ファイルの公開速度を大幅に向上させることができます。

IPFS のアップグレード#

大規模なバージョンアップグレードには、fs-repo-migrations ツールを使用する必要があります。Doc の説明を参照してください。手順は 2 つで、.ipfs ディレクトリをバックアップし、fs-repo-migrations コマンドを実行します。この前に ipfs サービスを停止する必要があります。

IPFS Desktop#

Windows10 で IPFS Desktop をインストールすると、デフォルトでユーザーディレクトリ C:\Users\Milton\AppData\Local\Programs\IPFS Desktop にインストールされます。インストール後、プログラムディレクトリは 265MB、
データディレクトリは C:\Users\Milton.ipfs で、形式と内容は go-ipfs と同じです。バックグラウンドで 2 つの IPFS Desktop プロセスと 2 つの ipfs プロセスが実行され、合計で約 500MB のメモリを占有します。

ステータスウィンドウは実際には webview で、webui の内容を表示します。

設定はデフォルトの設定に水位線を 50、300 に変更しただけで、他は同じです。

リソースが直接接続されたノードに存在する(キャッシュされている)場合はアクセス可能ですが、そうでない場合はアクセスできません。

IPFS プライベートネットワークとクラスター#

参考: https://labs.eleks.com/2019/03/ipfs-network-data-replication.html

IPFS をサービスとして実行し、起動時に自動的に開始するように設定します。

# サービスファイルを作成
sudo vi /etc/systemd/system/ipfs.service
 
# ファイル内容
[Unit]
Description=IPFS Daemon
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ipfs daemon --enable-namesys-pubsub
User=root
[Install]
WantedBy=multi-user.target
# ファイル内容終了
 
# サービスを追加
sudo systemctl daemon-reload
sudo systemctl enable ipfs
sudo systemctl start ipfs
sudo systemctl status ipfs

IPFS クラスターをサービスとして追加します。

# サービスを作成
sudo nano /etc/systemd/system/ipfs-cluster.service
 
# ファイル内容開始、注意: Afterにはipfsサービスが含まれており、これにより起動順序が保証されます。
[Unit]
Description=IPFS-Cluster Daemon
Requires=ipfs
After=syslog.target network.target remote-fs.target nss-lookup.target ipfs
[Service]
Type=simple
ExecStart=/home/ubuntu/gopath/bin/ipfs-cluster-service daemon
User=root
[Install]
WantedBy=multi-user.target
# ファイル内容終了
 
# システムサービスに追加
sudo systemctl daemon-reload
sudo systemctl enable ipfs-cluster
sudo systemctl start ipfs-cluster
sudo systemctl status ipfs-cluster

IPFS のアプリケーションシナリオ#

実際のテストを経て、公共ネットワーク内の IPFS ノードは、一度接続を確立すると CID の公開と読み取り速度が非常に速いことがわかりました。ネットワーク自体の遅延を除けば、リクエストから転送開始まで基本的に 2 秒以内であり、転送速度は 2 点間の帯域幅に依存します。

ファイル共有#

IPFS のコンテンツインデックス形式は、チーム間のファイル共有に非常に適しており、各変更がインデックスの変化を引き起こし、バージョン管理が可能です。分散型のファイルアクセスポイントとダウンロードポイントは、クラスターのスケーラビリティを容易にし、キャッシュ機能によりホットデータが帯域幅リソースに与える影響を軽減します。

音声・動画配信#

既存の PT ダウンロードネットワークの代替として使用されます。PT で配信される単一ファイルは非常に大きく、PT グループ内のノード全体に対して統一的な pin 管理を行う必要があります。一方で、各エンドノードがホットコンテンツへのアクセス速度を確保し、長尾コンテンツが十分にバックアップされ、失われないようにします。

ストリーミングとダウンロード加速#

IPFS の特性は、CDN サービスの代替として自然に使用できます。静的ファイル(画像、CSS、JS、圧縮ファイル)、さらには時効性の要求がそれほど高くないライブ配信サービスなどです。現在、通信事業者のブロードバンドは IPv6 を普及させており、これらのパブリック IPv6 アドレスを持つ家庭の帯域幅リソースを十分に活用して地域コンテンツの加速を行うことができます。

libp2p#

libp2p は IPFS から独立したパッケージ化された p2p モジュールで、モジュール内には PeerId、MultiAddress、Protocol Handler などのメカニズムが内蔵されており、自分のアプリケーションを簡単に拡張できます。

Go 言語の実装: https://github.com/libp2p/go-libp2p、サンプルコード: https://github.com/libp2p/go-libp2p-examples

コード内での使用は、一般的に以下のステップです。

  1. Host を作成
  2. 指定されたプロトコルに対して、Host 上で SetStreamHandler を設定
  3. ローカルにサービスを提供するポートがある場合、対応するサービスを作成し、ポートをリッスン
  4. 目標ノードとプロトコルを指定し、Stream を作成
  5. Stream にデータを書き込み
  6. Stream からデータを読み取り、ビジネスニーズに応じて Stream を閉じるかどうかを決定

Host が起動した後、実行を維持する必要がある場合は、以下の方法を使用できます。

# 方法1、selectを使用
select {} // 永遠にハング
 
# 方法2、内蔵サービスのListenAndServeを使用
http.ListenAndServe(serveArgs, p)
 
# 方法3、チャネルを使用
<-make(chan struct{}) // 永遠にハング
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。