発明のための再発明

Webプログラマーが、プログラムの内部動作を通してプログラムを作る時の参考になるような情報を書くブログ(サーバーサイドやDevOpsメイン)

gh-ostの動き

gh-ostとは

gh-ostは、binlogを利用したmysql用のデータベースマイグレーションツールです。
大抵のマイグレーションツールがtriggerを使いつつ移行するのに対して、gh-ostはbinlogを使うことで、負荷制御やテスト可能性を実現しています。

githubはここ。
github.com

マイグレーション方法

goroutineを使って並行に動かしているので、図が正しいわけではないですが、おおよそ下図のような動きをします。

f:id:mrasu:20180722194425p:plain

要約すると、
1. ゴーストテーブルを作成してテーブルを変更する
2. binlogを監視ししつつ、既存テーブルを順にコピーし始める。
3. binlogの中に、元テーブルの変更をするものがあれば、ゴーストテーブルにも反映する
4. 既存テーブルのコピーが終わったら、テーブル名を変更

という動きです。

テーブルコピー vs binlog反映

テーブルコピーとbinlogの反映は並行処理をしているので面倒そうですが、gh-ostでは下のように対処しています。

コピーしていない行がbinlogで反映された場合

binlogを基にクエリ実行
insertなら追加され、それ以外は無視される。

コピーした行がbinlogで反映された場合

ただ、実行する。binlogの変更が有るべき姿なので、問題なし

コピー中の行に対して、binlogが反映される場合

もし、取得と挿入の間にbinlogの変更が反映されると、binlogの反映がなかったことになる可能性が有るので、注意が必要です。
gh-ostは、既存行のコピー中にはbinlogの変更を停止することで、この問題を回避しています。 (INSERT INTO SELECTを使っています) ちなみに、既存行のコピーはデフォルトで1000行づつ行われ、1回のコピーが終わった後に、溜まっているbinlogの変更を反映するようになっています。
こうすることで、未反映のbinlogが溜まり過ぎないようになっています。

参考

gh-ostには資料が多く有ります。たとえば、

WebエンジニアがVRに手を出した軌跡

Web業界で働いていて、普段はRubyを書いているエンジニアがVRに手を出して、なんとか自分がしたかったことが形になりました。
その経験から、「VR開発ってこんな感じ」というのを伝えられればと思います。

結論を言えば、「Webエンジニアにとってはブラウザ、アプリに加えて第三のフロントができた」という感じなので、今までの経験を活かしてVR開発ができます。
自分が今作っているものはまだ作りたいものの全てはできていませんが、それは作業時間の問題であって実装自体に大きな壁があるわけではないという印象です。

作ったものは、こんな感じです。
VR上で、保有している株の推移を一覧できます。

マルチディスプレイで株価が見たい!

趣味で株をやっているので、映画やドラマの中でトレーダーが何個もディスプレイを使っている姿を見て「いっぱい見れて、いいなぁ」と思っていたのですが、
VRでならできるじゃないか!」と思い、作りました。
今は、自分が持っている株の推移が見れるようになったところです。
ここからさらに、持っていない株のチャートも観れるようになれば、優良株を見つけやすくなるのではと期待しています。

VR開発の軌跡

OculusとGPUを用意する

とにもかくにも、VR環境が無くてはどうにもならないので、用意します。それと、Unity
最初はAndroidVRで動かそうと思っていましたが、スペックのせいか画面がカクついて気持ち悪くなったので、Oculusを買いました。

VRを勉強する

入門として、「UnityによるVRアプリケーション開発 ―作りながら学ぶバーチャルリアリティ入門」を読みました。

UnityによるVRアプリケーション開発 ―作りながら学ぶバーチャルリアリティ入門

UnityによるVRアプリケーション開発 ―作りながら学ぶバーチャルリアリティ入門

これを読んで、「モデリング要素は畑違いだが、プログラミング要素は今の知識で十分」と感じました。
モデリング部分は全く初めてで、Webの仕事でもUI/UX部分は避けているので、「あ、これ、きつい」という感じがしました。
逆に、プログラミングはC#メインで、Webとは違った使い方をするわけでも無かったので、簡単です。 Webにはない概念だなと思ったのは、「角度や距離」といった、普段はブラウザが計算してくれているものを自分で計算する必要が出てくる時です。そのあたりは、数学を思い出して、慣れる必要があります。

ただ、この本を読んでいて、途中からVR入門ではなく、プログラミング入門のような話に移っていったので、途中で読むのをやめて実際に作り始めました。
Webエンジニアにとっては、Unityやmayaの操作方法とVR独自のUIがわかれば、入門書は十分だと思います。

作る

本を読んでいて、「後は実際に作って、問題に当たったほうが早い」と感じたので、本を読み終えずに作り始めました。
本で出会わなかった問題は、

  1. Oculus touchのボタン操作で移動したい
  2. 外部通信したい
  3. グラフ書くのが面倒

でした。

Oculus touchのボタン操作で移動したい

Oculusが提供しているライブラリを使えば、ボタン入力やスティックの角度が取得できます。
移動にスティックを使ったので、スティックの角度とカメラの角度を合わせることで、移動方向が決められます。
sin,cos使った回転が出てきたりして、「あー、ゲームで三角関数を使うっていう話は、こういうときのことを言ってたんだなぁ」としみじみしました。

ちなみに、移動速度を間違えると本当に気分が悪くなるので、速度にはかなり注意が必要です。

外部通信したい

UnityにはJSONパーサーがあるので、パーサーの癖さえつかめば、WebのAPI通信の知識で外部通信は問題なくできます。

グラフ書くのが面倒

Graph And Chart」というAssetを買いました。
時間を金で買った感じです。
他にも、今は使っていないものの無料のAssetで室内を作ったりもしました。

VRの感想

初心者レベルのものであれば、Webエンジニアでも十分作れると感じました。
デザインは全然違うので、VRのデザインを学ぶ必要はありますが、プログラミングの考え方は同じです。
さらに、サーバーへの通信より先は、Webの領域と全く同じです。
つまり、「フロントの技術にブラウザ・アプリに加えて『VR』という項目が増えた」という考え方でよいと思います。
なので、そこまで難しくないです。
VR開発には、億単位の金をかけられるという話もあるみたいなので、凝れば凝るだけすごいものを作れるのでしょうが、趣味で適当なものを作る程度のものは、十分できるので、Webエンジニアでもやればできるなぁという感想です。

gVisorのechoを読む

gVisorが面白そうだったので、どんな風に動いているか書きます。

使用したコミットは、

GitHub - google/gvisor at c400a0356b856e71fd30e3fe10372d7bb94356cb

です。

gVisor概要

gVisorは、先日googleが公開した、アプリケーションとホストの分離を目指したコンテナランタイムです。
システムコールをコンテナから直接実行するのではなく、システムコールをgVisorが受け取って実行を制御することで、コンテナをより安全に動かせるようにしています。

GitHubは、ここ

github.com

gVisorのコードは短くはないのですが、色々な機能を実装するために多くなっているだけで、各機能に必要なコードはそれほど多くなく、つまみ食いにはちょうど良い大きさという印象です。

動かし方

bazelでビルドして、Dockerでランタイムを指定すれば動きます。
READMEに詳しく書かれているので、簡単に動かせると思います(https://github.com/google/gVisor#building)

gVisorのechoの動き

本題の、コンテナ内でechoを実行したときに何が起きているか見ていきます。
今回は、ubuntuを起動し、その中で echo "Hello"を実行しました。
簡単に図にすると、下のように動いています。

f:id:mrasu:20180504185959p:plain

順番に見ていきます。

1. ubuntuシステムコール

echo "Hello" のコマンドを実行したところから始まります。

2. gVisorが気づく

echoが動かすWriteシステムコールにgVisorが反応するところから、gVisor仕事が始まります。
Task#run(pkg/sentry/kernel/task_run.go)という関数がループの中でシステムコールを待っています。

3. システムコールの内容をptraceで取得する

まず、gVisorがどんなシステムコールが実行されたかを知る必要が有ります。
そのために、Task#setRegsからptraceを呼び出します。
PTRACE_GETREGSを使用することで、対象プロセスのレジスタをコピーします。
これによって、gVisorはシステムコールの内容を把握できます。
例えば、Orig_raxにあるシステムコールの番号や、rdiなどにある引数などが取得できる情報です。

4. 実行するべきでないものを除外

システムコールの番号がわかったので、それを実行するべきかどうかを判定します。
Task#checkSeccompSyscallという関数が実行処理の判定をし、許可していないシステムコールを弾いています。

5. 前処理

システムコール(ここでは、Write)を実行するとなったら、まずは前処理として、.bootにログを出したりします。

6. システムコールに対応した処理を実行

gVisorはホストにシステムコールを直接投げるのではなく、gVisor自身が別のシステムコールを実行します。
どのようにシステムコールを取り扱うかは、pkg/sentry/syscalls/linux/linux64.go に色々と処理が書かれています。
ただ、まだ全部の処理が実装されているわけではなく、TODOがあったり、318までしかシステムコール候補がなかったりします。

7. Writeを実行

echoの出すWriteは当然実装されていて、pkg/sentry/syscalls/linux/sys_write.goに処理内容が書かれています。

8. ホストへWriteする処理を実行

sys_writeのwriteには共通処理が書かれているのですがechoはホストに書く処理なので、続いて個別処理としてpkg/sentry/fs/host/file.goのWriteが実行されます。

9. コマンドプロンプトに表示される。

ホスト用のWriteが動いて、ようやくコマンドプロンプトに "Hello"が表示されます。

まとめ

以上が、 echo "Hello" の流れでした。

このように、gVisorはコンテナがシステムコールを直接実行するのではなく、gVisorがホストの間に入っています。
この機能がsentryで、怪しいコンテナが変なシステムコールを実行するのをブロックしてくれます。
そのおかげで、コンテナがより安全に動かせるようにできるようになっているというわけです。