ついにDockerに対応したWSL2を私見で解説してみた

多くの記事も出ていますがMicrosoft Build 2019にて、Windows Subsystem for Linuxの次期版でフル互換のLinuxが動作するとの発表がありました。 これの何が嬉しいってDokcerがWSLで普通に動かせるようになる事です! www.publickey1.jp

セッションを聞いて来たのですが中々面白かったので、自分の理解できた範囲でWSL1から何が変わったのか、どう実現しているのか? を解説していきたいと思います。 私の理解が間違ってるかもしれないので、違っただ誰か指摘ください。

そもそもWindows Subsystem for Linuxって何?

WSLに関して知らない人もいると思いますが、Windows上でLinuxを動かすための仕組みです。 主な用途としては開発ツールチェインを始めとしたLinuxの資産を活かすためなのですが、類似のツールは結構たくさんあります。

などなど。かつてはcoLinuxとかもありました。 Cygwin/MinGW/Git-Bashはツールチェインの移植です。手軽な反面Linuxそのものでは無いので細かく挙動が違いますし、aptなんかも当然使えません。 一方で、Docker for Windows/Docker Toolbox on WindowsWindowsVMを入れて本物のLinuxを起動した上でLinuxを動かすという方法になります。 Linuxに閉じてサーバ開発とかをする分には十分ですが、Windowsから完全に隔離されるのでWindowsの操作を拡張する という用途では使えません。 その辺を両方とも解決してくれるのがWindows Subsystem for Linux すなわちWSLです。

ターミナルとしてLinux環境であるUbuntuSUSEが動くのはもちろんのことWindows環境とファイルは連携されているし、Windows側のPGをWSL上から起動することすらできます。 Windows 10から搭載された機能なので開発者以外には興味ももたれなかったと思いますが、ここ最近で一番アツい機能の一つです。

WSL1はどう動いてる?

WSL1は発表資料を引用すると下記のような構成になっています。 f:id:pascal256:20190509160244p:plain

私の理解だとWSL1はある種のUser Mode Linuxです。 LinuxカーネルへのシステムコールWindows NTカーネル上に作成しバイパスする事で動作しています。

いわゆるx86を全てエミュレートする仮想マシンモデルよりは、こう言ったシミュレータの方が原理的には高速ですし、Windows側との親和性も高く出来ます。 それでいて本物のLinuxディストリビューションが動くのでaptとかyumとか使えて便利ですね。

ただ、以下のような問題もありました。

なので、どうしても微妙に使いづらい感があったのは正直なところです。 特に今はDocker全盛時代でそれが動かないのは個人的には困りました。そこだけDocker Toolboxに接続するとかまあ色んな裏技を試していましたけど、そうすると細かい問題がチラチラと。

この問題はおそらくWindows上でLinuxのシステムコールを実装してることに起因しています。 セッション中でも「メモリモデルとかも違うし単純に1:1に移植できるわけじゃ無いからシステムコールのフル実装は辛い」という感じのことを言っていました。たぶん。 やはり、同じユーザ空間でのシステムコールの実装と言ってもLinux同士で単純にバイパスできるケースがほとんどのUMLやgVisorとは、何もかもが違うNTカーネルLinuxシステムコールを移植するのは相当大変なのでしょう。そもそもマイクロカーネルモノリシックカーネルというレベルで違うし。。。 この辺りが起因してWSL1のいまいち感は出ていました。

WSL2はどう動いている?

というわけで本題のWSL2です。野心的な試みのWSL1に対してWSL2は一見時代に逆行するシンプルなVMアプローチです。 f:id:pascal256:20190510115022p:plain

ただ、VMと言ってもAWSのFireclakcerのように1秒未満の超高速で起動するマイクロVMアプローチになります。 LinuxカーネルもNTカーネルHyper-V上で起動してユーザプロセス同士で通信はするって感じですね。

本物のLinuxカーネルVM上で動いているのでDocker含めて当然全ての機能が動きます。Hyper-Vベースなので技術的にこなれてるのもポイント。

さらにWSL2の環境毎にVMを立ち上げるのではなくLinuxカーネルは一つだけ立ち上げて、それぞれのWSL環境はコンテナで仕切っている様です。 f:id:pascal256:20190510115225p:plain

この辺りはHyper-Vを使ったDockerの実行方法であLinux Containers on Windows (LCOW)の技術を応用してる感じですかね。これによってVMによるオーバヘッドを極小化しているため、demoを見ている限りでは起動など劣化はほぼ見られませんでした。

f:id:pascal256:20190510114820p:plain

なお、Linuxカーネル自体もマイクロソフト謹製なので用途最適化されてる様です。たぶんVM前提でデバイスドライバ減らすとかそういうの。 ちなみにWindows UpdateLinuxカーネルがバージョンアップされる様になったとのコメントには会場も爆笑でしたw

パフォーマンス

ファイル周りの遅さが改善されてパフォーマンスは大きく向上している様です。 f:id:pascal256:20190510114336p:plain f:id:pascal256:20190510114450p:plain

理論的にはUML方式のが速い気がするのですが、実装の最適化度合いやファイルシステム周りの違いかな。 NTカーネル経由せずに直接ハイパーバイザを叩くならAPI的にもシンプルな可能性がありますし。 また、VMだと使用メモリ等が気になるところですがデモ中リソースモニタで監視しててもほとんど増えず、カーネルを仮想で動かしてる部分に関してはかなり小さい様です。

CPUヘビーな処理だとどうなるのかはちょっと気になるところですが、最近のVMでCPUが極端に劣化するイメージも無いので通常用途は大丈夫な気がします。

Windows側との連携

これだけだと「LCOWでDokcer起動すれば良くねって?」って話になるのですが、Windowsとの連携こそがWSLの真骨頂。ちゃんとWSL2も以下の特徴は引き継いでる様です。

  • Windows上のファイルがWSLから見れる
  • WSL上のファイルがWindowsから見れる
  • Windowsの実行ファイルをWSLのターミナルから実行できる

WSLからのアクセス f:id:pascal256:20190510120351p:plain

WSLへのアクセス f:id:pascal256:20190510120535p:plain

どうやって実現してるのかというと、まさかの9P(Plan 9 Filesystem Protocol)です。令和の時代にまさからPlan9の単語を聞くとは思わなかったですが分散OSなので、こういうのには向いてるのかもしれないです。 一応「SMBとかNFSとかなんで使わないの?」って質問に対して「こっちのが単純だから」と答えて気がします。ちょっと自信なし。

あと、あまり実装の詳細は分からなかったですが従来通りWindows側の実行ファイルをキックしたりも出来ていたので、この辺りの開発者フレンドリーなUXを作って来るのはさすがMSって感じがしました。

このおかげでWindows環境のツールチェインの一部としてLinuxの機能をフルに利用する事が出来ます。

まとめ

WSL2によりFuseやDockerがWindows10で簡単に動かせる様になりそうです。 これによってCI環境でWindowsコンテナとLinuxコンテナを混在させたかった課題とかが解決しそうで個人的にはドンピシャなソリューション。

それをおいてもWIndowsLinuxの開発はかなりしやすくなりますし、Windowsの補助ツールとしても使える。 もしかしたら将来的にはWIndowsを直接サポートせずにWSL前提なOSSとかも出てくるんじゃないかと思います。リリースが待ち遠しい!

そうなって来ると最近はMacもいまいちだし、Surface欲しくなるなぁ、とそんな気持ちになりますね。Linuxは敵だと言っていたMicrosoftも今は昔。

それではHappy Hacking!

参考

Serverless時代のJavaEEコンテナ - Quarkus

はじめに

Quarkusをご存知ですか? Redhat社が出した爆速のJavaEEコンテナです。

Publickeyの記事でも紹介されていますがGraalVMのnative-imageでコンパイルされるため、JAX-RSCDIJPAや100ms以下の起動速度と省メモリを誇るスーパーソニックなが最大の特徴です。

WildflyやThorntailがあるのになんでまた作り始めたの?」となりますが、サーバレスへの対応は既存のマイクロサービスへの対応だけでは不十分だからです。 ここで言うサーバレスはAWS LambdaのようなFaaSに限ったことではなく、実際のコンピュートではなく使用リソースなどを指定してオンデマンドでプロセスを立ち上げたり課金がされるような仕組みのことをさします。

JavaEEコンテナの世代と移り変わり

JavaEEコンテナは現在大きな変革を求められています。J2EEからJavaEE5にシフトした時以来の大きな変化だと思います。 そもそもJavaEEWebアプリケーションフレームワークというより分散システムとして始まります。 当時はそれ向けエコシステムがあったわけではないでしょうし、基本的に全てJavaEE内でリソース管理をすることを想定しています。 なのでアプリケーション実行エンジンに加えて「負荷分散」「クラスタリング」「QoS/リソース管理」「モニタリング」「デプロイメント/バージョニング」「Isolation」までをカバーするのが他言語のフレームワークと少し違うところです。 ベンダーの独自拡張もありますがそうはいっても上記の基本的な要素はJavaEEの仕様としても入っており標準化されているのが良いですね。これを仮に第1世代と呼びます。

しかし、時代は代わりマイクロサービスが台等してくるとエコシステムも進化して、JavaEEがもともと持っていたような管理機能はk8sやIstioによって実現されるようになってきました。 こうなると、他のマイクロサービスと異なりJavaEEだけ独自の管理機能を持ってるのは不便です。そのため他システムとのインテグレーションのために再定義された仕様がMicroProfileであり、MicroProfileだけをターゲットにすることでスリム化を計ったのがPayara MicroやThorntailです。

以下の画像に載ってるように以下のようコンテナ機能およびその管理機能がk8sに移行されミドルウェアの領域が小さくなります。 f:id:pascal256:20190415045549p:plain 引用 - Kubernetesを軸に再定義されつつある、新しい「クラウド対応」の意味とは

これを仮に第2世代と呼ぶとします。JavaEEではありませんがSpringBootも第2世代ですね。

さて、ここからがQuarkusの話ですが第2世代ではまだ不十分です。 それはコンテナ化とマイクロサービス化が進んだ結果としてサーバレスが注目され始めたからです。 実のところサーバレスなアプリケーション実行環境というのは昔からあります。代表的なのはHerokuとGoogle AppEngine(GAE)です。 これらのサービスはリクエストに応じて処理をスケールさせることができ、使ってないときはインスタンスが起動してないのでコストを払う必要がありません。 この構成はコンテナと非常に相性が良い(というか上記サービスは元々独自コンテナでそれを実現していた)ので、リソースやコスト(含む運用コスト)の効率化のために再度注目されてると思います。 そこで重要になってくるのは要求があってから起動してリクエストを返すまでの時間、つまりスピンアップタイムです。なぜならサーバレスではインスタンスが一つも起動してないことは普通にあり得るので。 これはスケールアウトはあっても基本的には常駐させることが前提の従来的なマイクロサービスの管理とは要件が異なります。 これを第3世代---は少し言い過ぎなので2.5世代と呼ぶします。@kisからコメントがあったMicronautJavaEEでは無いですが2.5世代ですね。これもGraalVM使ってネイティブコンパイルしています。

Table: JavaEEコンテナの世代の比較

Item 第1世代 第2世代 第2.5世代
Product Wildfly, GlassFish Payara-Micro, Thorntail Quarkus, Helidon
サポート機能 Full JavaEE Micro Profile Micro Profile
スピンアップ 分オーダー 秒オーダー ミリ秒オーダー
Challenge 分散システムの管理 他のMicro Serviceとの統合 スピンアップタイムの高速化

ミリ秒オーダーの起動を実現するには従来のコンポーネントを徹底的に見直す必要があります。 また、基本的にはリクエスト毎にコンテナが立ち上がれば良いのでスレッド管理周りも強力なものは不要ですし、コンテナでデプロイするのでクラスローダ周りもシンプルにできます。 また、GraalVMのnative-imageによるバイナリへのコンパイルが大きな鍵になります。その際にSpringBoot等も含めて既存のモジュールではビルドが困難なので利用可能なものだけを組み合わせるためフルスクラッチで作り直してるのだと思います。 OracleHelidonもnative-imageはまだ実現していませんがコンセプトは同様です。

Quarkus

さて前置きが長くなりましたがQuarkusについてです。 MiroProfileによりJAX-RS, CDI, JPA, Config, OpenAPI, Tracingなどが使えます。それに加えてVert.xやKafkaとの連携も組み込まれています。 それでいて爆速起動/省メモリなのが特徴です。

f:id:pascal256:20190415053510p:plain 引用 - Quarkus公式

また、ビルド形式をJARとnative-imageの2種類用意してあります。

まずはmvnコマンドでプロジェクトを作成します。

mvn io.quarkus:quarkus-maven-plugin:create        
....
[INFO] --- quarkus-maven-plugin:0.13.1:create (default-cli) @ standalone-pom ---
Set the project groupId [org.acme.quarkus.sample]: 
Set the project artifactId [my-quarkus-project]: 
Set the project version [1.0-SNAPSHOT]: 
Do you want to create a REST resource? (y/n) [no]: y
Set the resource classname [org.acme.quarkus.sample.HelloResource]: 
Set the resource path  [/hello]: 
Creating a new project in /private/tmp/my-quarkus-project
Configuration file created in src/main/resources/META-INF/application.properties

...

[INFO] BUILD SUCCESS

続いて実行してみます。以下のコマンドでホットデプロイが効く開発モードで起動が可能です。

$ mvn compile quarkus:dev
...
2019-04-14 13:31:50,697 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
2019-04-14 13:31:51,988 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 1291ms
2019-04-14 13:31:52,564 INFO  [io.quarkus] (main) Quarkus 0.13.1 started in 1.966s. Listening on: http://[::]:8080
2019-04-14 13:31:52,566 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

curlで実行してみます。

$ curl -i http://localhost:8080/hello
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain;charset=UTF-8
Content-Length: 5
Date: Sun, 14 Apr 2019 20:33:55 GMT

hello% 

次にネイティブイメージにコンパイルして実行します。 ネイティブイメージへのビルドは依存関係を全部コンパイルし直してるのでちょっと時間がかかります。

$ export GRAALVM_HOME=/Applications/Graalvm.app/Contents/Home/
$ mvn package -Pnative

実行してみましょう。

まずはJVMから。

% java -jar my-quarkus-project-1.0-SNAPSHOT-runner.jar   
...
INFO: Quarkus 0.13.1 started in 1.356s. Listening on: http://[::]:8080
Apr 14, 2019 2:07:52 PM io.quarkus.runtime.Timing printStartupTime
INFO: Installed features: [cdi, resteasy]

1.3秒で起動しました。まあ悪くない数字です。第2世代と比べても速いですし、第1世代では質レベルで違う速度。 続いて使用メモリをチェックしてみましょう。

% ps aux 18124
USER     PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
koduki 18124   0.0  0.2  8014528  18684 s002  S+    2:07PM   0:03.41 /usr/bin/java -jar my-quarkus-project-1.0-SNAPSHOT

8MB程度使っていますね。いつも1GBはおろか32GBとか平気でJavaに指定してる身からすれば劇的に小さいです。

では続いてネイティブバージョン。

$ ./my-quarkus-project-1.0-SNAPSHOT-runner     
2019-04-14 13:59:01,168 INFO  [io.quarkus] (main) Quarkus 0.13.1 started in 0.011s. Listening on: http://[::]:8080
2019-04-14 13:59:01,303 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

0.011秒という劇的な速度で起動してることが分かります。10msとか並みのレスポンス速度よりも速いですね。。。 続いて使用メモリ。

$ ps aux 18084          
USER     PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
koduki 18084   0.0  0.1  4353676   4448 s002  S+    1:59PM   0:00.04 ./my-quarkus-project-1.0-SNAPSHOT-runne
/Users/koduki% 

こちらも4MB程度でJVM版よりもさらに小さい使用量となります。

比べると以下の通り。

Item JVM Native
起動速度(sec) 1.356 0.011
使用メモリ(MB) 7.6 4.2

まとめ

Quarkusが爆速であることと、なぜその速度がServerless時代には求められるかを書きました。 このレベルの速度で起動するならプロセス常駐させる必要はなく、結果としてJavaの鬼門の一つであるGCも実質解決ができます。 つまり、QuarkusはJavaの難点と言われていたスピンアップタイムとGCを改善したクラウドネイティブなJavaEE環境と言えるかと思います。 OracleのHelidonも同じ路線のはずなので、いい感じに刺激しあえると良いですね。

なお、JPAを使ったもうちょっと本格的なアプリに関しては下記で記事を書いてますのでこちらも参考ください。

追加: ブコメで「これCGIじゃね?」と言うのがありましたが私も同感です。特にFaaSやKNativeは今風の良い感じなCGI2.0的なものかと。 実のところCGIはスレッドではなくプロセスなので取り回しは悪くなかったと思うのですがパフォーマンスの問題がありました。 で、現在のインフラだとリソースを安全にシェアできるので横に必要に応じてたくさん並べれば多少効率落ちても良くね? と言うことで回帰してると思っています。

それではHappy Hacking!

参考

Unlimited Process Works - 無限にスケールするためのアーキテクチャープロトタイプ

「行くぞDocker! プロセスの貯蔵は十分か!?」

はじめに

さて、以前顧客向けシステムに最適なDBは何だろう? と思ってこんな記事を書きました。

koduki.hatenablog.com

で、その中でも顧客向けシステムはユーザ毎にDB作れば良いんじゃね? Dockerでいけるんじゃね? ということ書いたんですがシステムアーキテクチャを考える上でのプロトタイプとして作ってみました。

github.com

システム名は[Heaven’s Feel]II.lost butterfly見たいけど近所に無いから見れないので、出始めのコメントを思いついて勢いで付けた次第です。後悔はしていない。

基本コンセプト

READMEにも書いてますが、基本コンセプトはCGIのようにリクエスト毎にDockerを立ち上げてそのコンテナでsqlliteをユーザ毎に作成してバインドするとう作りになっています。 Dispatcherが同じユーザなら同じDBにアサインする形ですね。

その上で、参照専用のDBとしてGlobal DBをpostgresqlで持っています。 ここにそれぞれのユーザのDBの内容を非同期で連携していきます。その上でマテリアライズドビュー等を使用して集計したり扱い易い構造にします。

https://cacoo.com/diagrams/OFL4JoIEKUjLIooU-30F44.png

sqliteだと分散トランザクションがー、とか大容量データでの性能大丈夫? とか色々感じるかも知れないですが、 一般的に顧客データは顧客が増えるとレコードが増えますがユーザ毎では数MBとか数百MBで収まるケースも多いです。 また、顧客を超えて読み書きすることは(マスタデータ以外)ありません。

不正検知やレポートを作る作業をはじめとした横断的な集計をする作業はGlobal DBを利用することで高速に実行できます。 そのため、sqlite側でDB間の一貫性を保つ必要はないので分散トランザクションなどの処理は不要です。 これらの作業は大抵はタイムスロットがあるので一定の時間より後のデータで整合性が保証されてるならリアルタイムのそれは問題にならないため、遅延連携が可能となります。

ユースケース

このシステムは超簡単な銀行システムなので以下のことができます。

  • 自分のアカウントに入金
  • 自分のアカウントの中身を表示
  • 全員の残高ランキングを取得

まあ、セキュリティ的にヒドい銀行ですがそれはサンプルということでw

テーブル構造

基本的にはまんまシンプルなのですが、ポイントの一つはIDにシーケンスではなくUUIDを使っていることです。 UUIDを使うことでシーケンスの発行で競合することがありません。加えて、sqlite側と連携先のpostgresql側で同じIDが使えるので整合性チェックも容易になります。 連携が容易にしやすいようにsqlite側ではアップデートをせずにInsertのみで利用する使い方を想定しています。

sqlite

CREATE TABLE account (
        id UUID,
        amount BIGINT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
);

PostgreSQL

CREATE TABLE account (
    id UUID,
    name TEXT,
    amount BIGINT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

CREATE MATERIALIZED VIEW v_account_summary AS 
    SELECT name, SUM(amount) FROM account GROUP BY name;

性能特性

レイテンシー

まずDispatcherですがリクエスト毎にDocker起動させてるのでオーバーヘッドが凄いです。あらゆるリクエストが2秒くらいかかります。 いくら極限のレイテンシーは要求されないとは言え流石に遅すぎですね。これをちゃんとすると多分FaaSになるのかな? でちゃんとする方法は例えばFast Containerを導入するとかになるかも知れないです。

hb.matsumoto-r.jp

ただ、致命的な点としてsqliteのために永続化層が必要です。ステートフルなのでプロセスの再利用が難しいことを考えるとデータロードだけでもレイテンシーを稼ぐ必要があります。 そのためのアイデアとしては2つ

  • KVSなど高速なストレージに乗せる
  • パーティションなどで階層化してすぐ使うデータはメモリなど高速なストレージに置いて、残りは遅延で読み込む

sqliteのdbファイルをKVSに置くのが一番良い気がしますね。それで容量の問題が出るならパーティーション化とか階層化するしかないのかな、と。 なんとなくですがGoogle SpanerとかもKVSの上に構築されたRDBっぽいので、似たような発想なのではないかと。もちろん仕組みはもっと高度でしょうが。

スケーリング

Workerは現在は純粋なプロトタイプv1なのでコンテナをローカルに作ってるため1マシンを超えてスケールしないですが、実際はk8sやswarmを使って複数マシンを使うことでスケールさせる事ができると思います。 Dispatcherは特にステートを持たないのでこれはスケールアウトさせることができます。 ASync ETLは本来的にはKafkaなどを手前に置くことになると思いますがこの状態ならスケールするのではないかと。 Global DBはトラディショナルなRDBなのでスケールの問題がありますが、ユーザトランザクションからの書き込みはないのでリードレプリカで対応できます。

負荷試験を実施してないので断定は出来ないのですが、理論上はこの構造ならかなりのところまで金の弾丸でスケールするんじゃないかな、と。

メンテナンス

メンテナンスですがユーザDBのスキーマを変えるにはGlobal DBとAsync ETLにも変更が必要なのでまあ大変ですね。 とは言え、基本的にはユーザDBを徐々に変更していって、最終的にGlobal DBを変えるという手順が使えるので全体的なメンテナンスコストはオーバーヘッド分ありますが、ここのメンテナンスはそこまで大変でもないかと想定します。

アプリとDBがセットのコンテナをリリースして入れ替えていくだけなので、MySQLとかでsハーディングしてる時より小さくアップデートできて楽かも。

まとめ

とりえず無限にスケールするアーキテクチャというのをコンセプトにシステムを組んでみました。 素朴に作ってみた感じでは無限にスケールはなんとかなりそうだけど、せめてレイテンシーが1秒を切らないと使い物にならないだろうという感じなので、ちゃんとしたミドルウェアを使うのが当面は良さそうです。仮に問題が解決してもこの構造で運用するのは多くのケースでオーバーテクノロジーな気もしますし。

ただ、こうして自分でコンセプトモデルを作ることでスケールのためにアプリケーションやミドルウェアが備えるべき要件もクリアになりましたし、ミドルウェア側の振る舞いもある程度想像ができるようになったので目的は達したかな、と。

勉強にはなるので暇を見てもうちょっと改造はしてみたいなー。 それではHappy Hacking!

信頼性の高いサービス運用のための5つの要素

さて少し遅れてしまったのですがSRE 2 Advent Calendar 2018の23日目です。

SREでどんな事をしているというより、どういうポリシーで信頼性を実現するか、という観点でまとめてみました。 SRE観点で運用改善をしていく! と言っても闇雲にやると「何やってるの?」になりがちなので。 まだ自分の中でもはっきりとしたものがある訳ではないのですが、やはり言語化してまとめ無いとフワフワしたままなので書いてみた感。 内容的に技術ポエムの方にしようかとも思ったのですが結局こちらに。

概要

サービスの運用にとって最も重要なことはサービスの信頼性を維持/向上させることです。 そのためには以下の5つの要素を意識した改善をし続けていくことが重要です。

  • 回復性の設計
  • Isolation
  • トイルの撲滅
  • 可視化
  • モニタリング

サービスの信頼性を保つための5つの基本要素

高い回復性の実現

安定運用を実現するために最も重要なのは回復性(Realizability)の高いシステムの実現です。 回復性の高いシステムとは、障害が起こることを前提に素早く復旧しユーザに影響を見せないシステムをさします。 例えばリアルタイムシステムであれば100ms, バッチであれば数分でTake over/Re-runが実現できるのであればシステム障害の影響をユーザが感じることはありません。 最も顕著な例はHP NonStopnのプロセスペアです。アクティブ系が落ちても瞬時にスタンバイにテイクオーバーすることでシステム全体としての高い信頼性を実現しています。

システムが落ちないこはももちろん重要な指標ですが、MTTRを極小化して回復性の設計をアプリケーション、インフラ、及び運用手順により作り込むかがサービスの信頼性につながります。 理由の一つに技術の進歩のみならクラウド時代に突入しシステムが壊れる事が前提となっているためです。これはインフラの耐久性が落ちた訳ではなく大量のリソースを管理するため確率の問題です。 よってオンプレであっても同様の状態にあります。

Isolation

回復性を実現するためにはシステムごとのIsolationを高くする必要があります。障害の影響範囲が大きければ必然的に復旧時間が伸びる可能性が高いためです。 また、高いIsolationを持つシステムはポータビリティに優れシステムの部分的な入れ替えをしやすいためフレキシブルな運用も実現しやすくなります。 そのため、ミドルウェアやライブラリのバージョンアップをより低いリスクで実現する事ができるようになり、セキュアで効率的なシステムを保ち続ける事ができます。

これは仮想化のような集約基盤を使わないことを意味しません。 むしろ、仮想化特にDockerのようなコンテナ技術を積極的に利用する事でマシンからアプリケーションの独立ができ任意のマシンで動作可能なポータビリティを実現できます。

トイルの撲滅

高いIsolationを実現するとシステムが分散され複数のシステムを運用することとなり運用コストが増えてしまいます。 また、そうで無くてもサービスの成長と共にシステムは増え運用コストは増大していきます。 このような状態で高い回復性を持つシステムを構築するには、トイルを撲滅しシステムの数と運用コストの比例関係を無くすことが重要です。

トイルとは具体的には以下の状況を満たすものを言います。

  • 手作業であること
  • 繰り返されること
  • 自動化できること
  • 戦術出来であること
  • 長期的な価値を持たないこと
  • サービスの成長に対して比例的に増えること

これらはサービスが小さいうちは問題になりませんが、サービスが成長しシステムが増えていくにつれて多くの問題を引き起こします。リリース作業とか分析のための定期的なデータ抽出とかはといるの典型でしょう。 特に繰り返される手作業はスピード感を損ない回復性に致命的な影響を与えるのみならず障害の原因となります。大規模障害の多くはシステムの故障ではなくオペレーションミスによって引き起こされることは珍しくありません。 トイルを撲滅するために自動化や運用プロセスの見直しを進めることでシステムの堅牢性を高めると共に運用負荷の比例的な上昇も削減することができます。

可視化の徹底

あらゆる改善は計測から始まります。可視化されてない状態では改善することはもちろん現状を把握することもできてないからです。 特にシステムの信頼性に直結するレイテンシやエラーは運用のKPIそのものであり、これらのメトリクスは必ず可視化されている必要があります。

また、上記のKPIを改善するためにトラフィックやシステムリソースの利用率などのメトリクスも可視化されている必要があります。 重要性を判断する観点でアプリケーションログから業務的なメトリクスを可視化することも必要です。そのためログは機械が読みやすいフォーマットで出すことが非常に重要です。 さらにほとんどのシステムはDB/ファイル連携またはRPCにより連携しているので、分散トレーシングやData Lineageも意識した可視化が重要になってきます。

適切なモニタリング

可視化は人間向けにメトリクスを表現するのが中心になりますが、モニタリングはシステムによるアクションを含んだ要素です。 何らかのメトリクスが閾値を越えるとされるアクションのことをさします。モニタリングは下記に分類できます。

  • ページ(アラート): 即時対応が必要
  • チケット: 業務時間または数日以内に対応が必要
  • ロギング: 対応は不要。記録だけ残し性能分析や監査のために利用

これはITILのイベント管理におけるERROR/WARN/INFOと同様のものです。また、下位のイベントでも1分間に10回発生など閾値を超えた場合には上位のイベントとしてエスカレーションします。 特にページャー(オンコールの回数=アラート回数)は回復性に大きな影響を与えるのでKPIとして計測し、いかに削減していくかが重要になってきます。

適切なモニタリングとはまずイベントが上記の分類にしっかりとされているか否か、そしてそれが継続的にメンテナンスされているか(未知の問題から既知の問題になることでアラートからチケットに変わるのは本来は頻繁にある)が重要になります。

実現のための原則というかキーワード

本当はこれを原則集的なものにしたいのだけどそこまで体系化できてないので、とりあえず思ってることを単なに箇条書き。 多分アプリ観点としてはTwelve-Factor Appを守ってください、って感じかなー。

  • アプリケーションにIPアドレスなど環境依存の値を入れさせない(DNSとかhostsとかで名前をつけるか環境変数)
  • ログフォーマットやメトリクスを集めて後から分析する。人間のみやすさより機械の見易さ
  • Infrastructure as a Codeを積極的に活用する
  • ConfigurationはDB化し誰でもアクセス出来るようにする(担当者に聞かないとネットワークやサーバ設定が分からないを無くす)
  • GUIよりCLI、手順書よりスクリプトを徹底する
  • デプロイ単位が異なるものを同時にビルドしない
  • SLAが異なるものを同じ場所(OSやJVM, コンテナなどリソース管理の単位)にデプロイしない
  • ステートレスと冪等性を意識する
  • 長時間動くバッチは悪である。並列化等で対処できないかを考える
  • モジュールは小さく保ち起動速度を速くする

まとめ

実現のための手段は無限にありますし組織によって異なります。Dokcer化したり、モニタリングツール変えたり追加したり、マイクロサービス化してサーキットブレイカーを入れたりなどなど。 その作業を進める中でどういう効果があってそれをするかってのを今回記載したような観点で整理していけば説明しやすくなるんじゃ無いかなー、と。

それでは来年もHappy Hacking!

第四世代言語って知ってるか? - 古の次世代言語 4GL

ロストテクノロジー Advent Calendar 2018 14日目です。

皆さんは古の次世代言語 4GLをご存知でしょうか? 

我々が普段使ってるプログラミング言語は所詮第三世代言語、それに続く言語の次なる進化が第四世代言語です。

www.weblio.jp

第一世代言語機械語
第二世代言語アセンブラ
第三世代言語高級言語(手続き言語)
第四世代言語New

といった分類になります。 4GL自体の明確な定義は知らないのですが基本的には「機械が読むことを優先した言語ではなく、人間中心の言語へ」と言うコンセプトの元にエンドユーザ向けの言語であることが特徴です。

有名どころではRPGやEasyとかですかね?。ちゃんと現代にも生き残ってる代表格はSQLとRですね。 当時を知らないのでなんともなのですがエンドユーザ向けといってもコールセンターのオペレータが使うような想定ではなく、ベンダーではなくユーザ企業の電算部?の人たちを指してエンドユーザと呼んでるのではないかと推測されます。 なので、単純に生産性を高めるような方向性の言語も混じっていますし、SQLなんかが典型ですが手続き型のアンチテーゼとしての宣言型言語も比較的多い気がします。

闇に消えた4GLですが理由は寡聞にして知りません。まあキーワードである「エンドユーザ向け言語」すなわちEUC(End User Computing)自体が色々の課題と共に消えたので言葉として消えていったのかな、と。 その上で、生産性の向上は第3世代言語自体の進化で実施されてきたのではないかと。Rubyをはじめとしたスクリプト言語はかなり簡潔にかけるようになりましたし。

あと、そもそもDSLの一種だよね? と言う話もあり現代では同じような用途の言語はDSLと呼ばれるので表に出ることがないと言うのもあります。 仕様記述言語とかスキーマ定義言語とかクエリ言語系とかレイアウト系言語は当時なら4GLと呼ばれたのでHTMLやJSONなんかも4GLって呼べる気はします。

平成どころか昭和の技術じゃね? と言われそうですが平成の終わりに触ったので闇に消えることを祈って記事にしてみました。

それでは来年もHappy Hacking!

そうだアドベントカレンダーを書こう - 年末の〆切ドリブン

取り敢えず今年分は下記。

qiita.com qiita.com qiita.com qiita.com qiita.com Docker Advent Calendar 2018 - Qiita qiita.com

盛りだくさんだけど、落とさないようにがんばるぞい、と。

Oracle Code One 2018に参加してきました

JavaOneには結局ついぞ参加する機会がなかった私ですが、Oracle Code Oneになったのを契機にというかたまたまサンフランシスコ近辺に住んでるので参加してきました! 紆余曲折あって会社のブース展示とかの手伝いもしてたので、セッションを十全に回れた訳では無いですが、とても楽しかったです。

蟹Oneを始めコミュニティイベントにも参加できましたし。 今回は事前の準備というかチェックが足らなかったので次の機会にはもっといろんなアフターイベントに計画的に参加しないとです。 f:id:pascal256:20181021194604j:plain

個別に色々面白いセッションはあったのですが、それは良い記事がたくさん出てるでしょうから他に譲るとして、キーノート含めて全体のコメントとしては以下になります。

  • DKは依然としてFree, OpenJDKでのリリースも計画通り進んでる系の内容がJVM系キーノート含めては多かった。Javaは依然としてフリー
  • 結果として初めて知る新機能見たいなのはオープンであるが故になかったけど、進み方とかが聞けてよかった。Record型も早く来て欲しい
  • Jakarata EEの話はあんまり無しだが、地味にEclipse GlassFish 5 RC1のリリースSPECjEnterprise® 2018 Web Profileが出たりと足回りは強化中
  • 反面、マイクロプロファイルネタは多かった気がする。マイクロサービスも。Helidonは面白そうなのでちょっと試してみる
  • クラウドはかなり推してた。Oracle Open Worldではもちろんだけど、CodeOneのKeynoteまでOracle Cloudが侵食してた
  • Graalの現状が聞けてよかった。native-image + JRubyも行けるようなので今度試してみる
  • JFR/JMCのオープンソース化/API化が熱い(これは多分完全に主観)

ざっとこんな感じでした。 個人的には個別のセッションが聞けたのはやっぱよかったですね。いつもはキーノートだけ中継で見てるので。 もうちょっとJakarata EEネタを聴きたかったけど反面マイクロサービス系の話を聞きながらやはり自分のイベントドリブンシステム/非同期ドリブンシステムの知見が足らなさ過ぎると感じたので要強化ポイント。

マイクロサービス自体も少なくとも仕事上で触ってるアプリでは今の所不向きだから使う想定は無いのだけど、ツールチェインや開発スタイルを取り入れれるものはあるだろうから、そろそろちゃんと手を出さねば...

あと、お金をちゃんと払うとLST版のEOSLが9年くらいと余りに長いので、どうやってJDKのバージョンを上げていくかが今後の課題になってくるかなー。 EOSLがもう少し短ければ「しかたがないから」を理由に新陳代謝が維持できたんだけど、ちゃんとサポートされてしまうなら別な方法を考えないと負債が貯まりそう。

それではHappy Hacking!