Argoでkubernetesのジョブ管理を導入してみた

先日作った下記のサイトですが小さいながk8sでジョブを動かしてます。

koduki.hatenablog.com

ジョブの構成は

  1. BTCの情報を収集
  2. 学習&予測
  3. スコアリング

の3種類で1 -> 2-> 3の順番に依存があります。で、2の部分は当然アルゴリズムを増やすと増えますし並列実行したくなりますね。 とりあえずはKNNしか入れてなかったのでシンプルにk8sのcronだけで作ってたのですが、流石に管理が面倒なので決定木のアルゴリズムを追加したのに合わせてジョブ管理ツールを導入するすることにして見ました。

今回使ったのは下記のArgo workflowというk8sネイティブのワークフローエンジンです。

argoproj.github.io

JenkinsやRundeckなど比較的手になじんだツールを使おうかとも思ったのですが、折角なのでk8sネイティブで新気鋭のこいつを使うことにしました。 いろんな設定が出来るんですが今回は単純にDAGで依存を付けただけです。

gist9327b361396b07a8e0040adcf6c24ce7

するとargo-uiからは以下のようなワークフローで確認できます。

f:id:pascal256:20180917095511p:plain

簡単ですね。

現時点ではArgo workflow自体はスケジューリングをサポートしてないのでそちらはk8sのcronジョブでjessesuen/argocliをベースに上記のyamlファイルをコンテナ内に入れてargo submitさせることで対応しています。

k8sをベースに作成してしてあるのでargoコマンドじゃ無くてそのままkubectlも使えたり、CPUやメモリのリソース制限やボリュームなどもそのまま利用できるので今後に結構期待のツールです。

まとめ

今回はとりあえず依存実行が欲しかったので入れただけですが、このツール自体は今後色々野心的なところを狙っていきそうなので期待大です。 テキストなのでDockerで管理しやすいのもGoodですね。

一方で、現時点ではargo-uiでは純粋に実行結果を見ることしかできなくて、実行前のネットワークの確認もできなかったり、1ファイルに書くというフォーマットでは、人間がわかる範囲で管理するのは大規模ジョブだと厳しいのかな、とも思ったり増します。 まあ、シェルかなんかで組み立てるなりやりようはありそうなのですけどね。何れにしても個人で使うには十分役立ちそうな感じです。

それではHappy Hacking!

参考

流行りの技術を集めてビットコインの予想サイトを作ってみた

最近、ビットコインが久しぶりに80万円を超えて調子が良いですね!

そう言う訳でと言う訳では無いのですがビットコインの予想サイトを作ってみました。

明日のビットコインを大予想! f:id:pascal256:20180903114801p:plain

最近、特に自分のサービスを運用にしてなかったので新し目の技術をHello World 以上には使えてないなー、てのが最大の動機です。 と言う訳で不必要にいろんな技術を使っています。

  • バッチはk8sベースで作成
  • 予測エンジンはk8sベースのバッチとしてskleanで作成。I/FをObject Storageにしてアプリ全体とは疎結合に。
  • バックエンドはFaaSを試したかったのでCloud Functions
  • フロントエンドはVue.js + Chart.jsで作成してFirebase Hostingにデプロイ。AjaxJQueryとか使わずにFetchAPIで。

と行った感じです。FaaSを使ってると言ってもまだそのまま叩いてるだけで、circuit breakerとかも入れて無いのでこの辺もおいおい。 今までのキャッチアップが足らなすぎて、些細な事にハマって結構時間を費やしてしまったので備忘録としてそのうちまとめよう。一応、バッチに関しては下記にちょっとメモを書いてる

qiita.com

しかし、GCPでまとめてるとはいえ、色んなサービスを使ってるのでデプロイや管理が面倒。 なんと無く、監視も面倒な予感がするのでその辺の知見が貯まるといいな。統合管理的なものはなんかある気がするし。

ちなみにこのサイト自体は予測アルゴリズムを増やして行って胡散臭い予想屋サイトとしてのエクスペリエンスを高めていきたい!

それではHappy Hacking!

僕の考えた最強のDB for 会員むけシステム

ソシャゲとかその他C2Bな会員向けシステムにとって最強のDBはどういうのだろうか? とTwitterでつらつら考えてたのでブログにもまとめ直してみる。

理想的なイメージは1会員で1DBを割り当てて全てストアドプロシージャで処理すること。 自分でユーザIDとかでシャーディングすればできなくは無い気がする。

こんな事が出来ると嬉しい

  1. テーブルのレコード単位で細かくロックを制御する必要は不要でテーブルを超えてユーザID単位でロックが掛かって欲しい
  2. データをDBサーバやアプリサーバに取得する処理が無駄なのでストアド的(というかイメージはSpark)にデータがあるストレージで処理して欲しい
  3. 上記を効率的に実現するためにユーザID単位のデータは全て同じストレージに居て欲しい。あとマスタテーブルは全てのストレージに居て欲しい
  4. ユーザ単位でロックを掛けると一部の処理は少し長めのとある処理より優先したい時はあるのでトランザクションを割り込ませて既存ロジックは再実行したい
  5. そうは言っても集計処理はしたいのでHANAがしてるみたいに分析が得意なDBにリアルタイム連携したい
  6. インサートやデリートでインデックスのロックとか他のレコードに影響を与えないで欲しい。

でもって以下のことは許容できる

  • 極端な低レイテンシーは要らない。ビジネスロジック混みで数百ms(もちろんロジック次第では数秒)程度で良い。
  • 強い一貫性の保証は同一ユーザIDのものだけで良い
  • 分析用DBへの連携は非同期で数秒程度の差分があっても大丈夫。どうせ集計だし
  • where句は無くても良い。キーアクセスだし。
  • ユーザ自体が瞬時に大量に増えた時にユーザ登録処理速度が低下するのは仕方ない。普通無いだろうし。

今の所イメージに一番近いのはAzureのCosmos DBの気がする。

GoogleのCloud Spannerはストアドは無いみたいだし。まあ、ストアド無くてもFunctionsとかで処理すれば関連するデータが同じストレージにあってJOINペナルティとかが無いならあまり問題にならないかも。ドキュメントDBだとユーザーの下が無限に増えるからストアド的なのが無いとやはり厳しいだろうけど。

ユーザ単位で処理を考えるとやっぱりドキュメントDBやオブジェクトDBのがしっくり来るなぁ。 基本的にはユーザ単位でアトミックでロックやトランザクションが効けば良くてデータが同じ箇所に集中してる必要があるとすると、データモデルはリレーショナルよりはオブジェクトモデルのが物理実体とも近いから作りやすそうだし。

○○なユーザを抽出って処理とかでユーザを跨ぐことはあるけどそれって結局抽出した後に集計するかユーザ単位に処理するかなので、 集計ならDWHで集計で良いし、最終的にユーザ単位の処理なら会員数分の並列で良い気がする。

こういう特製のデータベースってなんか無いだろうか?

自分で作るならsqliteあたりを包んだdockerコンテナを作ってユーザ毎にコンテナ作製して、ルーティングアプリを手前に置いてgRpcで通信すれば良さそう。 それならデータ量が多いユーザは別なPodへ移動させるとかも出来るから1ユーザーが1台のマシンのストレージを超える(インメモリでも数百GB、SSDとかならTBクラス))容量にならない限りは、この方式でスケールするから普通のシステムなら破綻はしなさそう。 その場合、DWHへの連携はトリガーを書きまくれば良いのかな?

ただ、詰まるところシャーディングだからテーブルスキーマの変更はちょいめんどそうかも。

でも、スキーマ変更をローリングでして困るケースも実はあんまり無い気がするので万能選手を期待しなければ結構良い感じになる気がしてきた。

まとめ

システムがデカくなるとデータが増えて処理性能が落ちるんだけど、本来ならばほとんどの処理は横にスケールする筈だよなぁ、と思って自分の理想の仕組みを考えてみました。

全く同じものは世になかったとしても、一回考えると今後の技術要件を探る時には役立ちそうですし。特にクラウド系分散DBは似たような課題をクリアしようとしてのものな気がするし。

それではHappy Hacking

nodejsの"nobody" does not have permission to access the dev dir

gypなライブラリをDockerでnpmからインストールする時に-g付けてグローバルに出そうとすると下記のエラーが出て正常にインストールが出来なかった。

gyp WARN EACCES user "nobody" does not have permission to access the dev dir "/usr/lib/node_modules/firebase/node_modules/grpc/.node-gyp/10.5.0"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/lib/node_modules/firebase/node_modules/grpc/.node-gyp"

同じ問題には下記でも出てたみたいで、--unsafe-permにしてやれば良いみたい。

github.com

というわけで以下のような形でDockerfileを作ればOK.

RUN npm --unsafe-perm -g install ...

しかし、面倒ね。

JavaでのUT作成基準を整理してみた

チームが小さいとよしなにですむのだけど、大人数になってくると明文化しとかないと結局テストが書かれないのでUTの作成基準とかを整理してみた。

自分のチームで使う想定のイメージで書いてみたけど、体制やプロダクトの性質によっても変わってくるだろうし色んな人のコメントが聞いてみたいところ。

UT作成基準

前提

  • 本ドキュメントではユニットテストJUnitのような単体テストを実行するテストコードと定義します。
  • また、単体テストとはV字モデルにおけるモジュールの詳細設計に基づきメソッドレベルの振る舞いを保証するテストとします。

ユニットテストとは?

ユニットテストは「動く仕様書」です。

ユニットテストはプロダクトコードの振る舞いを保証してくれます。

何故、ユニットテストを書くのか?

品質と安心のために。ユニットテストを書く事で以下のような効果があります。

不具合発生時の問題の切り分け

結合テスト等のフェーズで問題が発生した際に、UTで書かれてる範囲の内容は問題ないと切り分けできます。

そのためデータとかI/Fの不備とか環境不備とか設計/実装レベルの考慮漏れとかに注力できるので修正コストを下げることが出来ます。

改修時のテスト工数削減

リファクタリングや性能改善をした際にユニットテストに書かれている範囲内で差分が無いことを保証できます。

機能改修の場合も修正箇所のテストを見直すだけになるので、影響調査コストを下げることが出来ます。

リグレッションテスト

リグレッションテストを実施することで「想定外の場所に」影響が出てないことを確認することが出来ます。

人手でやった場合には膨大なコストがかかりますが、UTであればリグレッションテストのコストを大きく下げることが可能です。

いつ、ユニットテストを書くのか?

  • コードレビューのタイミングまでに作成してください
  • TDD/BDD的な観点で書くのが推奨ですが、コードレビュー時までに完成していれば必ずしもテストファーストでなくても問題ありません
  • ユニットテストもプロダクトコードと同様に適切なテストケース(=詳細設計がテストとして記述されているか)になっているかのレビュー対象となります

何をユニットテストに書くのか?

詳細設計にもとづく内容をブラックボックス観点で作成してください。原則、以下の観点が必要です

  • 同値分割
  • 境界値分析
  • 無効値/異常系

また、同値分割が適切であることを確認するために、カバレッジを確認してください。

無効値に関しては本来nullが入りえないモジュールで、入った場合は単純にNullPointerExceptionになるケースなどは作成不要です。特にハンドリングしないシステム異常もUTの作成は不要です。

カバレッジはどうするか?

  • 前述のブラックボックス観点が満たせていれば問題ありませんが、参考値として作成/改修したビジネスロジックの90%から100%をブランチ網羅を目標値としてください。
  • 上記の数字にならない場合はレビュー時にレビューアに理由を説明してください。
  • ブランチ網羅の確認にはJaCoCoを利用してください。

ユニットテストを何に対して書くのか?

  • ユニットテストはプロダクトコードのうちビジネスロジックのpublicメソッドに対して作成します。
  • プレゼンテーションレイヤーや、ビジネスロジックであってもprivateメソッド等には実施しません。
  • ログ出力や標準出力のテストも実施しません。ファイル出力も原則テスト不要です。

Tips:

  • 画面への表示順などはモデルとビューを適切に切り離して、なるべくビジネスロジックのUTで検証できるように設計してください。
  • privateメソッドがテストすべき重要なメソッドである場合はpublicメソッドとして実装し、必要があればクラスを分ける等の対応を行ってください。
  • ログ出力や標準出力、ファイル出力は出力前の内容をテストできるように設計してください。

また、ユニットテストは以下の2つに大別します

  • Quick Test
  • Slow Test

Quick Test

Mavenのビルド時に毎回実行されるテストです。

入出力ファイル、DB、外部API等に依存しないJavaの引数と戻り値のみで期待値検証ができるテストケースを作成してください。

なるべくSlow TestではなくQuick Testで動作が保証できるように設計してください。

Slow Test

通常のMavenビルド時には実行されず「testプロファイル」を指定した際に実行されるテストです。

入出力ファイル、DB、外部API等を利用するテストはこちらに記載します。

SQLが重要なビジネスロジックであるようなケースなど結合テストでの実施ではテストコストが高すぎる場合に作成します。

ファイル出力や外部API出力などQuick Testで十分に内容が保証できる場合は、Slow Testを作成せずに結合テストフェーズでの担保で問題ありません。

いつ、ユニットテストを実行するのか?

  • Quick Testはビルド時に常に回します。原則、skipTests=trueを利用してはいけません。
  • Slow TestはPullRequest前などの節目のタイミングで回します。また、CI環境ではDailyまたは日に数回実行します。

まとめ

とりあえず思いつく事をわーっと、書いてみた。他の人はどうしてるのかも知りたいのでいろんな意見求む!

それではHappy Hacking!

関連リンク

Intel Xeonから見るCPUクロックとコア数の10年間の遷移 - 2017版

タイトルの通りなのですが、良くある「フリーランチは終わった」的な話の資料を作りたくて、CPUクロックとコア数の遷移を追った資料が欲しかったんですがここ最近のものが意外と無かったので作ってみました。

インテル® 製品の仕様情報 - 高度検索 でCPU情報を拾ってGoogle Sheetsで加工した感じ。 Google SheetsにSQLライクな処理が書けるQuery関数はマジ便利ですね。Excelにも導入してほしい...

折角作ったので公開しときます。とりあえずまとめたものは以下。

f:id:pascal256:20171211230859p:plain CPU History - Google スプレッドシート

良い感じにCPUコア数が増えて、クロックが鈍化してるのが分かりますね。 実のところ1998年から2017年までの最高クロックベースで比べると400MHzから4GHzと10倍にはなってるのですが、3GHz超えたあたりからほぼ伸びてないのでグラフ上は変化がない感じになってます。

まあ、分かりきってた話なので可視化しても特に新しい事実は出てこないですが、改めてみると本当にコア数しか増えてないな感はあります。

それではHappy Hacking!