まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2019/01

ブロックチェーンの特徴の1つに「対改ざん性(一度格納した情報は、たとえ管理者権限を持っている人でも改ざんができない)」があります。その改ざんを困難にするためのマイニングと呼ばれる仕組みをゲーム感覚で学べるサービスを作ってみました:
https://dotnsf.github.io/nonce/
2019012800


少しだけ補足すると、これはブロックチェーンのマイニングの仕組みをシミュレーションするサービスです。実際のマイニングよりもかなりシンプルなマイニング作業を実際に行いながら、nonce やハッシュの仕組みを理解し、ブロックチェーンがなぜ改ざんできない仕組みと言われているのかを遊びながら理解することを目的に作りました。

なお、画面含めて今後変更が加わる可能性があることを申し上げておきます。以下は 2019/01/28 時点でのサービス内容をもとに紹介しています。


ではチュートリアルっぽく、このサービスを紹介していきます。まず上記 URL に(PC の)ウェブブラウザでアクセスすると、以下のような画面が表示されます(スマホのブラウザで見えないことはないと思いますが、情報量の多いページで横スクロールも併用する必要があり、現状スマホからはかなり使いにくいと思っています。どうしてもスマホで利用する場合は画面を横にしてお使いください):
2019012800


この画面は目的別のパーツに分かれています。ここからは各パーツの意味も含めて順に紹介していきます:
2019012801



#パーツ意味
1オプションの内容このゲームのパラメータ(この図では初期値のまま Mining Level = 1 と Goal = 3 となっている)
2ブロック番号何番目のブロックかを示す数値。
上図の場合、1なのでこれが最初のブロック
3body 値これからブロックチェーンに格納したいと思っている内容
4nonce 値整数値(詳しくは後述、最初は1)
5hash 値body と nonce(ブロック番号が2以上の場合は1つ前のブロックに格納されたハッシュ値も)から計算したハッシュ値
6格納されたブロック上記の nonce 値とハッシュ値の値が確定してブロックチェーンに格納されたブロック(この図では空なので、まだ格納していない)


↑の表や意味は後でもう一度確認するとして、まずは一度ゲームを進めてみます。

その前に1つだけ、ゲームスタート時点で hash 値のボタンが上図のように赤ではなく、稀に緑になっている可能性があります。その場合は(本当はゲーム的にはすごくラッキーなんですが・・・)hash 値のボタンが赤くなるまで何度か nonce 値の、初期状態で1と書かれているボタンを押してください(ボタンを押すたびに数値が増えていきますが、とりあえずは無視してください)。以下の説明では hash 値のボタンが赤で始まることを想定しています。このボタンの色の意味は後述します。


【ゲームの目的】
一定の数(オプション Goal の値、デフォルトでは3)のブロックをブロックチェーンに格納することが目的です。なるべく短い時間でブロックチェーンを完成させましょう。

ブロックはマイニングルールと呼ばれるある条件を満たせばブロックチェーンに格納することができます。マイニングルールを満たしていないブロックはブロックチェーンに格納できません。その条件を満たすよう、注意しながら操作し、条件を満たしたタイミングでブロックチェーンに格納します。これを一定の数繰り返すことでゲームをクリアできます。


【ゲームの遊び方】
ややこしい話は後回しにして、とりあえずゲームとしての遊び方を紹介します。すごくシンプルです。

nonce 値のボタンを1回押すごとに nonce 値は1つ増え、同時に hash 値が変化します:
2019012802


そして nonce 値を増やしていくと hash 値がある条件を満たした時にボタンは緑色になります
2019012803


そして hash 値のボタンを緑色の時に押すとブロックがブロックチェーンに格納されます。なお、赤の時に押してもマイニングルールを満たしていないためブロックは格納されません。以下のようなメッセージが表示されます。このままマイニングを続ける場合は「キャンセル」を選択してください:
2019012809


表の一番右の列にブロックが格納されます(通常、この時点でブロックは暗号化されて格納されますが、このサービスでは暗号化せずにそのままマイニングしてそのまま格納しています)。1つのブロックが格納されると、次のブロックが下段に表示されます。このブロックも同様に hash 値のボタンが緑になるまで nonce 値のボタンを押し、緑になったら押してブロックチェーンに格納します:
2019012804


これを繰り返して、ブロックをブロックチェーンに格納していきます:
2019012806


一定数(デフォルトでは3)のブロックがブロックチェーンに格納できたらゲームクリアとなります。ゲームクリア時にゲーム開始からクリアまでかかった時間と合わせてゲームクリアの情報が表示されます:
2019012807


なお途中で赤のボタンを押して、「自動計算しますか?」の問いに「OK」を選択するとギブアップしたことになります。ギブアップすると hash 値のボタンを緑にすることはできますが、クリア時に「途中で自動計算を使いました」というメッセージが表示されます):
2019012808


また hash 値のボタンがいちど緑になった後、そのボタンを押す前に nonce 値のボタンを押してしまった場合、nonce 値を戻すことはできません。そのまま更に nonce 値を1つずつ増やして、再度マイニングルールを満たす nonce 値を探す必要があります。


と、こんな感じのゲーム感覚でマイニングを体験するサイトです。サービスそのものは Github Pages を使って、(CDN 以外は)1枚の HTML で作りました:
https://github.com/dotnsf/nonce/tree/gh-pages


本当は別の機会にちゃんと説明したいのですが、長くなりそうなのでシンプルに「なぜブロックチェーンに記録されたデータの改ざんが難しいのか」を説明しておきます。要するに記録したデータに対して、マイニングというそれなりに CPU パワーを必要とする処理を施した上でのチェックビットのような仕組みを加え、かつそのマイニング結果を前後のブロックに持たせてつなげる(「ハッシュポインタ」)ことが対改ざん性としての鍵になっているのです(更に実際にはデータに暗号化を加えることもあります)。 この仕組みによって、記録したデータを単純に(例えば振込額を増やすとか、振込先を変えるとか)書き換えるとその hash 値が変わり、記録内容とは異なってしまうので、書き換えたことがすぐにバレてしまいます。書き換えをごまかすためには hash 値を再計算する必要があるのですが、マイニングルールを満たす条件があるため、ここでそれなりの処理能力が必要になります。更に1つのブロックのマイニングが完了して書き換えに成功したとしても、次のブロックは1つ前の(つまり今書き換えたばかりの)ブロックの hash 値を持っているので、やはりここでも書き換えがバレてしまいます。これをごまかすためにはこのブロックの hash も書き換える必要があり、ここを書き換えると再度マイニングが必要になり・・・ とブロックチェーンの最後のブロックまで全てマイニングし直して書き換える必要がでてきてしまいます。マイニングルールが1桁とか2桁程度であればまだ処理できるかもしれませんが、実際の仮想通貨取引では 10 桁以上のマイニングルールが課せられており、全てのブロックを書き換えることは事実上不可能とされているのでした。



このサービスではマイニングが完了したブロックを仮想的なブロックチェーンに格納していますが、近い将来にこのページの HTML を改良して認証を加え、自動計算なしでクリアしたら本当にその記録をブロックチェーンに格納する、というものをリリースする予定です。お楽しみに。


先日紹介したこの記事の続き、というか、本命版です:
ラズベリーパイと鳩サブレ缶で docker swarm クラスタを構築する

豊島屋の鳩サブレ缶がちょうどラズベリーパイ(以下「ラズパイ」)を4つ格納できるような仕切りになっていることに気付いて、この鳩サブレ缶とラズパイでクラスタ環境を作れないか、と思い立ったことがきっかけでした。先日は docker swarm のクラスタを構築しましたが、今回は kubernetes のクラスタを構築します:
20190117


なお、構築手順の途中までは先日と全く同じです。具体的にはこのページでも紹介している3台のラズパイそれぞれに docker を導入する所までは同じ手続きを行います。なお、ここまでの作業が完了している前提で以下を紹介します。


【スワップメモリの無効化】
ここからが Kubernetes 環境のための手順となります。まず Kubernetes 1.8 以降ではスワップメモリが有効な環境下では kubelet が起動しないため、3台のラズパイそれぞれでスワップメモリを無効にします:
$ sudo dphys-swapfile swapoff

$ sudo dphys-swapfile uninstall

$ sudo update-rc.d dphys-swapfile remove


【kubeadm, kubectl, kubelet のインストール】
いよいよ Kubernetes をインストールします。具体的には kubeadm, kubectl, kubelet を導入するのですが、まずはリポジトリを登録&更新します:
$ curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg|sudo apt-key add -

$ echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kube.list

$ sudo apt-get update

そして kubeadm, kubectl, kubelet をインストールします。ただこちらで検証した限りでは kubernetes のバージョン 1.8 以下しか動作しませんでした。というわけでバージョン 1.8 を指定して kubernetes コマンド群をインストールします:
$ sudo apt-get install kubelet=1.8.14-00 kubeadm=1.8.14-00 kubectl=1.8.14-00 kubernetes-cni=0.5.1-00

これでラズパイノードに Kubernetes のコマンドが導入できました。ここまでの作業は3台それぞれで実施する必要があります。


【マスターノードの準備】
Kubernetes のマスターノードを作成します。今回は3台のラズパイの中の raspi001 をマスターノードとするので、以下のコマンドを raspi001 でのみ実施します。またその結果、この前に導入した docker のバージョンとの不整合が起きてしまうので、インストールコマンド時に「バージョンチェックをしない」ための --skip-preflight-checks オプションを指定します:
$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --skip-preflight-checks

成功したら、成功時の画面に表示される以下のコマンドを順次実行して pod network をデプロイします:
$ mkdir -p $HOME/.kube

$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

後は flannnel をデプロイします:
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml

これでマスターノードの準備は完了です。


【ワーカーノードの準備】

2台のワーカーノードをマスターノードに接続します。マスターノードの sudo kubeadm init コマンドを実行して成功した時に表示される kubeadm join コマンドを各ワーカーノードで実行します:
$ kubeadm join --token XXXXXX....XXXXXX 192.168.10.101:6443 --discovery-token-ca-cert-hash sha256:ZZZZZZZZZZZZ....ZZZZZZZZZZZZZZZ

【接続状態を確認】

マスターノードで "kubectl version" コマンドを実行してサーバーとクライアントのバージョンを、"kubectl get nodes" コマンドを実行して各ノードの状態が表示されることを確認します:
20190128


なんとかラズパイで kubernetes クラスタ環境が作れました。


 
(参考)
https://qiita.com/hatotaka/items/48a88ecb190e1f5e03c3

https://qiita.com/MahoTakara/items/2b39e06f077927bafa2c



ラズベリーパイに日本語の形態素解析エンジンである MeCab をインストールする方法を紹介します。

少しだけ補足をしておくと、ただ単にラズベリーパイに MeCab をインストールするだけであれば、普通に apt-get を使って、
$ sudo apt-get install mecab libmecab-dev mecab-ipadic

とかあれば導入できることは確認しました。ただこの方法で導入した MeCab を使うと、実行結果がことごとく文字化けしてしまうようでした(EUC-JP が指定されている?)。

という背景もあって、ラズパイでも文字化けせずに実行できる方法で、具体的にはソースコードからビルドする方法で MeCab を導入してみます。

まずは MeCab のダウンロードサイトから MeCab 本体と、MeCab とセットで一般的に使われている IPA 辞書のソースコードをダウンロードし、ラズベリーパイ内のファイルシステムに保存します:
2019012301


保存した2つのファイル(2019/01/23 時点では mecab-0.996.tar.gz と mecab-ipadic-2.7.0-20070801.tar.gz)を展開しておきます:
$ tar -xvf mecab-0.996.tar.gz

$ tar -xvf mecab-ipadic-2.7.0-20070801.tar.gz

まずは MeCab 本体をビルドしてインストールします。展開したフォルダで --with-charset=utf8 オプションを付けて configure し、その後でビルド&インストールします:
$ cd mecab-0.996

$ ./configure --with-charset=utf8

$ make

$ make check

$ sudo make install

$ cd ..

次に IPA 辞書をビルド&インストールします。こちらではビルド前に各種ファイルの文字コードを無理やり UTF-8 に変換し、かつ dicrc 内の config-charset 指定を UTF-8 にします。その後にソースコードをビルド&インストールします:
$ sudo apt-get install nkf

$ cd mecab-ipadic-2.7.0-20070801

$ nkf -w --overwrite *.csv

$ nkf -w --overwrite *.def

$ vi dicrc

config-charset = EUC-JPUTF-8   config-charset の値を UTF-8 に変更して保存

$ ./configure

$ make

$ sudo make install

$ cd ..

この方法で作成した MeCab と IPA 辞書はラズパイでも文字化けすることなく動作させることができます:
$ mecab -d /usr/local/lib/mecab/dic/ipadic
おはようございます
おはよう        感動詞,*,*,*,*,*,おはよう,オハヨウ,オハヨー
ござい  助動詞,*,*,*,五段・ラ行特殊,連用形,ござる,ゴザイ,ゴザイ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
EOS




きっかけはこのツイートでも紹介した、豊島屋の鳩サブレ缶がちょうどラズベリーパイを4つ格納できるような仕切りになっていることに気付いたことでした:
20190117


面白そうなので、本当にラズパイのケースにしてみよう、それもせっかくなのでこの鳩サブレ缶の中だけでクラスタリング環境を作ることに挑戦してみよう、と思いたちました。ちなみに私自身はクラスタ化とか専門ではなく、あまり得意な方ではないです。思いついた時点では構築方法として Docker Swarm か、Kubernetes か、まあその辺りが動けばいいな、くらいに考えていました。。

当初は「さてとりあえずラズパイを4台買って・・」と思っていましたが、冷静に考えるとスイッチングハブとか、USBハブとか、電源周りとか、ラズパイ本体以外でもそこそこのサイズの機器を収納する必要がありそうだと気づきました。というわけで、それらをどこか1つにまとめるとして、ラズパイ本体は3台体制で行けるかな・・・ という想定でお買い物しました:
IMG_3782


購入したモノリスト:
買ったモノ個数目的
Raspberry Pi 3 Model B3クラスタノードになるホスト
マイクロ USB ケーブル上記ラズパイの電源ケーブル
マイクロSDカード(64GB)上記ラズパイのストレージ
ラズパイケースプラスチックの仕切りの上にラズパイ本体を直接載せるのは熱的にまずいかと思ったので・・・
LANケーブル3台のラズパイを同一ネットワークに接続(結局、今回は使わず※)
スイッチングハブ1LANケーブルのハブ(結局、今回は使わず※)
USB ハブ電源ケーブルのハブ(結局、今回は使わず※)


※今回は結局無線 LAN で接続することにしたので有線の LAN ケーブルとハブを使わずに構築しました。また USB ハブは購入したのですが、この USB ハブ1つに3台のラズパイを接続すると電流量が足りませんでした。というわけで別途 USB からの電源変換プラグ3つと、テーブルタップ1つを買い足しています。

上記に含まれていないものとして、マイクロ SD カードにイメージを書き込むための Windows PC と、マイクロ SD カードを読み書きするための USB アダプタ、USB -> 電源プラグの変換アダプタ、各ホストでネットワークが有効になるまでの操作用に LCD モニタ、HDMI ケーブル、USB キーボードと USB マウスを使います(これらは所有していたものをそのまま使います)。加えてケースとなる 18 枚入りの鳩サブレー缶を用意しました。

また構築する環境は最近流行り(遅い?)の Kubernetes 、にしたかったのですが、訳あって Docker Swarm にしました。コンテナ・オーケストレーションとしてはほぼデファクトスタンダートになった Kubernetes を選びたかったのですが、とりあえず 2019/01/21 時点ではマスターノードを初期化する kubeadm init コマンドを実行した際に再現率 100% で Timeout エラーになってしまうようでした。


どうもこの Issue に近い現象だと思っています:
2019012101


ただ Issue そのものは Closed になっていて、でもスレッドを見ていると本当に治っているのかなんとも微妙・・ 一方で成功例も報告されているのでラズパイで発生する環境依存問題なのか、タイミングが悪かったのか・・・ なんとも言えないのですが、仮に Kubernetes の障害であったとしてもその修正を待つのも変なので、Docker Swarm によるクラスタリング環境構築を優先することに決めました。

というわけで、改めて3台のラズパイを使った Docker Swarm 環境の構築を目標に作業を進めてみます:
2019012200



【構成図】
このような最終型を目指すことにします:
2018012201

raspi001, raspi002, raspi003 の3台のラズベリーパイを Docker Swarm でクラスタリングします。raspi001 を管理ノード、raspi002 と raspi003 をワーカーノードとします。



【ラズパイノードの準備】
Docker Swarm 環境の核となる3台のノードをラズパイで作ります。前提として最新版の Raspbian OS をマイクロ SD カードに書き込んで起動し、初回起動時のセットアップは3台ぶん済んでいるものとします。また SSH も有効にしておいてください。そして以下の手順を3台それぞれに対して行います。

【ホスト名の変更】
今回は3台のラズパイに raspi001, raspi002, raspi003 という3つの名前をそれぞれ付与して使いますこのため、まずは各ラズパイのホスト名をそれぞれ変更します。
$ sudo vi /etc/hostname

raspberrypiraspi001  raspberrypi をホスト名に変更して保存


また raspi001, raspi002, raspi003 の名前でアクセスできるよう、/etc/hosts も変更しておきます(DNS などが有効であればそちらで対応いただくのがいいと思います):
$ sudo vi /etc/hosts

127.0.1.1       raspberrypiraspi001  raspberrypi をホスト名に変更

192.168.10.101  raspi001
192.168.10.102  raspi002
192.168.10.103  raspi003   raspi00X の名前でアクセスできるよう IP アドレスを指定して追加し保存

【SSH 鍵の共有】
この3台のラズパイ間ではパスワードなしで SSH 接続ができるように SSH 鍵を共有しておきます:
$ ssh-keygen -t rsa
$ ssh-copy-id raspi001
$ ssh-copy-id raspi002
$ ssh-copy-id raspi003

【Docker のインストール】
こちらで紹介した手順でラズパイに Docker をインストールします:
$ curl -sSL https://get.docker.com | sh

$ sudo usermod -aG docker pi

(このコマンドの後、一度ログアウトして再ログインする)

以上、ここまでの手順は3台のラズパイ全てで共通に行っておく必要があります。ちと面倒ですが、全てのラズパイに Docker をインストールするところまで実行しておきます。


【管理ノードの作成】
ここからは3台のラズパイの役割ごとに作業が変わる部分になります。まずは管理ノードを作成します。raspi001 において、自分のアドレスを指定して以下のコマンドを実行します:
$ docker swarm init --advertise-addr 192.168.10.101

Swarm initialized: current node (xenxp2w34x5dhvtdmq0ndwolk) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-0dg4im1kvb41odjj9zmhwug0bcvg6n8tt0d8548jxho5cp8cvo-df89pmegbdf4a3vy37apftv8r 192.168.10.101:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.


↑コマンドが成功すると青字のような出力になります。この中の太字部分(docker swarm join で始まる行)は後でワーカーノードで実行することになるコマンドなのでコピペできるようにしておきましょう。


【ワーカーノードの接続】
次にワーカーノード側(raspi002, raspi003)から管理ノードに接続します。上記の管理ノード作成時に実行したコマンドの実行結果をそのまま入力して実行します:
$ docker swarm join --token SWMTKN-1-0dg4im1kvb41odjj9zmhwug0bcvg6n8tt0d8548jxho5cp8cvo-df89pmegbdf4a3vy37apftv8r 192.168.1.153:2377

コマンドの実行が成功すると "This node joined a swarm as a worker." といったメッセージが表示されます。これを raspi002 と raspi003 の両方で(4台以上で構成する場合は全てのワーカーノードで)実行します。


【ノードの状態を確認】
ここまでの作業でこの環境が作れています:

2018012201


ノードの状態を確認してみます。raspi001 から以下のコマンドを実行します:
2019012202

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
xenxp2w34x5dhvtdmq0ndwolk *   raspi001            Ready               Active              Leader              18.09.0
hztpov7qya7y99m4oifx73ugt     raspi002            Ready               Active                                  18.09.0
wksx0mlyu9wsilk9c4jigvcib     raspi003            Ready               Active                                  18.09.0

実行結果↑の青字部分をみると raspi001, raspi002, raspi003 がリストされ、全て Active になっています。また raspi001 の MANAGER STATUS が Leader とマークされており、ここが管理ノードになっていることが確認できました。


あとはこの3つのラズパイと電源ケーブル等をつなげて鳩サブレー缶に押し込みます:
IMG_3802


鳩サブレ版の(?) docker swarm 環境が完成しました!
IMG_3800



(参考)
https://blog.ruanbekker.com/blog/2018/10/23/setting-up-a-docker-swarm-cluster-on-3-raspberrypi-nodes/


ラズベリーパイ(Raspberry Pi)の無線 LAN で固定 IP アドレスを使う場合の設定方法を調べたのでブログにまとめておきます。

ラズベリーパイ3やラズベリーパイゼロでは標準で無線 LAN を使うことができます。最近はサーバー用途で使う人もいると思うのですが、サーバーで使う場合は固定 IP アドレスにしておくと利用する側が便利です。ただ無線 LAN の固定 IP アドレス化は少し面倒なので、備忘録としてまとめておくことにしました。


【無線 LAN を有効にする】
まず、ラズベリーパイで無線 LAN が使えるようにしておく必要があります。既に(DHCP などで)使えている場合は、ここを無視して下に(固定 IP アドレスを使うための設定に)進んでください。無線 LAN が使えず、GUI の設定がなくてインタラクティブに無線 LAN が設定できない場合に有効にする方法を以下で紹介します。

ラズベリーパイで無線 LAN を有効にするにはコマンドラインから以下を実行します:
$ sudo sh < 'wpa_passphrase SSID PASSPHRASE >> /etc/wpa_supplicant.conf'

SSID は接続する無線 LAN の SSID、PASSPHRASE はその SSID に接続するためのパスフレーズです。実際に指定する場合の値に置き換えて実行してください。

実行すると /etc/wpa_supplicant.conf に、指定した SSID に接続するための設定が加わり、指定内容に間違いがなければ無線 LAN に接続できます。


【無線 LAN を固定 IP アドレスにする】
無線 LAN での接続を固定 IP アドレスを指定して接続するには、/etc/dhcpcd.conf ファイルに以下の内容を追加します:
interface wlan0
static ip_address=192.168.10.100/24
static routers=192.168.10.1
static domain_name_servers=192.168.10.1

上記例では wlan0 インターフェースに対して、固定アドレス 192.168.10.100/24、デフォルトゲートウェイ 192.168.10.1、DNS サーバー 192.168.10.1 を指定しています。

設定後に再起動して、設定した内容で無線 LAN 接続が有効になっていることを確認します。




このページのトップヘ