「知見がフィードバックされない」という落とし穴

新井庸介氏(以下、新井):続きまして、The Twelve-Factor Appの5つ目のプリンシプルにいきます。ここにはBuild release runというものがあります。これは、ビルドとリリースと実行のフェーズを明確に分離して、そのフェーズでやることを他のフェーズでやらないようにしましょうね、という話です。

ここで言うビルド、リリース、実行がそれぞれなにを指しているのか。

まずビルドは、コードベースを実行可能な塊に変換する作業をいいます。みなさんがEclipseでバーッと書いて、ビルドして、テストを回すなど、ローカルでやっいてる話はビルドの話ですね。

リリースは、イメージ、ビルドの塊に対して、あるデプロイ先固有の設定を入れ込む作業です。先ほどバインドみたいな話をしましたけど、結合テスト環境で動かす時に参照すべきデータベースの実際の値、参照すべきキューの名前を渡してあげる作業が、このリリースになります。

そしてラン……実行は、この作ったリリースをターゲットである実行環境の上で動かす作業になります。

The Twelve-Factor Appにおいては、各ステージで実施することは、他のステージでやらないようにしようというものです。一番わかりやすいのは、実行ステージで転んでしまい、そこで軽くコードとか直したり、設定ファイルいじったりして、もう走らせちゃうのはやめてねという話です。

これは当たり前ですよね。そこだけで完結しちゃうと、ビルドのほうにその知見がフィードバックされず、また同じ間違いがくり返されるからです。そのため、実行のタイミングでころんだ場合には、もうそれはそれでリリースして、履歴としてとっておく。そしてまた最初からやり直しなさい、ということを言っています。

別の場所で実行される可能性を常に考えておく

次がProcesses。これはプラットフォームの選定に大きく関わってくるファクターです。The Twelve-Factor Appにおいては、アプリケーションの実行単位はプロセスですけど、「プロセスはステートレスかシェアードナッシングであるべき」を強調しています。

これは冒頭で申し上げましたように、クラウドはインスタンスが物理的に分散して配置されていて、ここのインスタンスのサービスレベルは必ずしも高くないですし、こけた場合には別のインスタンスが上がってくるという特性があります。

つまり、あるアプリが物理的な場所……デプロイされる物理的な場所にセンシティブであってはならない。そういった制約があるということです。

メモリやディスクにキャッシュされたものが使えます、という前提にアプリを組んじゃうと別の場所にデプロイされたときに困ります。そういったアプリにはしないでくださいね、という話です。

具体的には、みなさんがWebのアプリケーションを作っているときは、セッションスティッキーをけっこう意識することがあるんじゃないかなと思います。

念のため補足すると、セッションスティッキーは、ロードバランシングの1つのファンクションです。例えば、ロードバランサーを経てノード1にHTTPリクエストが行ったときには、そのリクエストはセッションが生きてるかぎりノード1に行く、ということです。

そのため「ノード1は必ず前回のセッションを知ってるわけだから、より効率的に処理ができますよね」という、Webのアプリケーションをうまく回していく1つの技術なのです。

クラウド・ネイティブ・アプリケーションにおいては、セッションスティッキーを期待しない。つまり、自分のメモリの中にセッションがあるかどうかを前提としない。必ずデータストアへとりにいくように作ってないと、アプリはスケールしないという話です。

将来のリクエストやジョブがぜんぜん別のプロセス、別の場所で実行される可能性を考えて、アプリケーションを作るようにしておく。そうしないと、クラウド・プラットフォームの上ではスケールしないよ、ということを言っています。

なんといいますか、少し面倒くさい作りなんですよね。これまではあまり気にしなくてもよかったところが、ちゃんとリクエストが来たらしかるべきデータストアに聞きにいき、セッションがあったらそれを使う……みたいな構造にしなきゃいけない。そういった面倒くさくなる部分もあるわけです。

スケールアウトで性能を拡張していく

それから、Concurrency。並列度ですけれども。The Twelve-Factor Appもこれはユニークで、プロセスモデルによって「マルチプロセスでスケールしろ」と言っています。

ここでまたちょっと補足で、スレッドとプロセスの話をしますけど。スレッドモデルは……けっこう釈迦に説法なところがありますけど(笑)。スレッドモデルは、プロセスがCPUやメモリをドカンと確保して、それをスレッドで分割して並列処理しましょうというモデルです。JVMなどがそんな感じですよね。

プロセスモデルは、リクエスト量に応じてポコポコ上がって、これで多重処理していくというやり方です。

そこでは、もしかすると「スレッドモデルのほうが効率的なんじゃないの?」というような議論もあったのではないかと思います。「プロセスよりはスレッドのほうがオーバーヘッドも低いし、けっこう多重度を上げられてリソースもあまり割かなくていいんじゃないの?」みたいな話もあったかもしれません。

それはそれとして、The Twelve-Factor Appにおいては「クラウドに親和性の高いアプリケーションの場合、スレッドモデルよりもプロセスモデルを取りなさい」と言っています。

これはクラウドの場合、「インスタンスのスケールアップには限界があるよね」となるからです。そのため、スケールアウトで性能を拡張していくと考えたほうが自然です。そこでマルチプロセスで性能を上げられるようにしておこうね、という話です。

ですので、例えばシングルプロセス、マルチスレッドでしか動かない、あるいは性能が上がらないアプリケーションは、クラウドプラットフォームには少しそぐわないと言っています。

リソース効率の面など、いろいろあるかもしれません。しかし、クラウド・ネイティブ・アプリケーションはすべからくマルチプロセスでスケールアウトできることをThe Twelve-Factor Appでは言っています。

(スライドを指して)これなんかは、デプロイ先を選ぶのにもけっこう参考になるプリンシプルじゃないかなと思っています。つまり、クラウド・ネイティブなアプリケーションを動かす際には、マルチプロセスで動かすことが前提になります。

そうすると、「そのマルチプロセスのオーケストレーション、管理をどうするの?」は、プラットフォームを選定する際に考えるべき大きなポイントですね。

「プロセスを物理的にどこに配置したら効率的かしら?」「マルチプロセスを一元的に管理したり監視したり、死活監視なんかはどうやってやったらいいのかしら?」、スケールアップ、スケールダウンなどですね。あとは「ハイアベイラビリティみたいなものを確保するには、自分で考えなきゃいけないのか、プラットフォームがやってくれるのか?」「負荷分散をどうする?」というあたりを考えなきゃいけないですね。

これは、右へいけばいくほど、プラットフォームがやってくれることが大きいわけです。サーバーレスまでいっちゃいますと、並列度や負荷分散みたいなものは、全部サービスの中でやってくれます。「自分たちでスケールアップの手順はこうで」「ロードバランサーのセッティングはこう」など、あまり考える必要はないかもしれません。

かたやVMとかになると、プロセスの配置からなにからかにから自分で考える必要があります。複数のVMがあったとして、「プロセスの配置が偏らないようにするにはどうしたらいいか?」を、自分で考えなきゃいけないのですね。

正常系と異常系から「いいところ」を見つけ出す

では「それはもう、ファンクションとかAPaaSのほうがいいんじゃないの?」と思うとそうではなくて、プラットフォーム側にマルチプロセスのオーケストレーションを全部任せられるかどうかは、アプリがどれだけステートレスで、シェアードナッシングで動けるかによります。

マルチプロセスのオーケストレーションをプラットフォームに任してしまうと、プロセスがどこに配置されるかわからないですし、落っこちたときにどこで再開されるのかは、全部プラットフォーム任せになるわけですね。そのため、キャッシュやセッション情報みたいなものをあてにできないわけです。

デプロイの場所に対するセンシティビティ、セッションに対する依存性がまったくない。「どこでどう上げてくれても、ステートレスできちんと動くよ」というアプリだったら、「プラットフォーム側でマルチプロセスのオーケストレーションを全部やってください」「大丈夫です」と言えます。しかし、そうじゃないアプリケーションの場合には、自前で少しコントロールのできる余地を残さないといけないんですね。

「このアプリは、このインスタンスで動かさないとまずいんだよね」など、そういうものがもしあるのでしたら、よりマネージ度の低いクラウドサービスを選ばざるを得ないかもしれません。

マネージ度が高いクラウドサービスだと、マルチプロセスのオーケストレーションなどを全部やってくれます。しかし、クラウドは中が変わるケースもあったりします。一時的にマルチプロセスで動かしていても、このインスタンスとこのインスタンスの中身が微妙に違う……なんてことも起こり得たりします。ある意味、環境依存性があればあるほど、クラウド側に任せきれない部分もあります。

マルチプロセスのオーケストレーションを自分がどこまでやるか、プラットフォームにどこまで任せるか。それはアプリのステートレス度合いや、シェアードナッシング度合いによりますし、「どこまで任せられるのかな?」を踏まえながら、「どのクラウドサービスが一番いいところかな?」と考える必要があるかなと思います。

正常系の場合だけ考えれば「ある程度このへんかな」が見つかるかもしれません。しかし、異常系の部分などを考え始めると、途端にまた難しくなります。

例えば、「可用性の確保とかは、全部プラットフォームにお任せです」「落ちたとしてもリトライなどしてくれるから、僕はなんにも考えません」としても、クラウドはまだ発展途上のサービスですから、なにかしらの制約がやはりあるわけですね。

サービスの性能制約だったり、可用性制約だったり。例えば、多重度に制約があったり、リトライの回数に制約があったり、実行時間に制約があったりします。

そうすると、自分でなんとかしなきゃいけない。「要件的に許容できるからいいや」とあきらめたり、「自分でそこは拾ってリトライの仕組みを作り込む」「じゃあ、初めからコンテナとかのほうがいいんじゃないの?」という観点もあったりします。

このマルチプロセスのオーケストレーションをどこまでやらせるかは、正常系と異常系と両方を考えて、いいところを見つけ出さなければなりません。

すぐ起動できることが、スケールアウトの実現につながる

……ええと、僕のセッションは2時半に始まりましたから、あと15分ですね。はい。では、少しテンポを上げていきます。

続きまして、Disposability。「捨てられること」が直訳です。日本語で言うと、高速な起動とグレースフルシャットダウンができることで、「高い堅牢性を実現しましょう」という話です。

The Twelve-Factor Appにおいては、アプリケーションを動かすプロセスは、すぐに、時間をたてずに、起動や終了できなければいけないと定めています。

Prosessesのところでもお話しましたが、The Twelve-Factor Appの場合には、マルチプロセスでスケールアウトする、性能を拡張していくことが前提です。そのため、「プロセスの起動に手順も含めて3日かかります」ですと(笑)。その間、性能を上げられないことになりますので。すぐ起動できることが、スケールアウトの実現につながります。

あと、アプリケーションをどんどんリリースしていく際には、プロセスを止めたりすると思います。しかし、「プロセス止めるのに3日かかります」ですと、10 deploys per dayは無理なので、起動も終了もすぐできなきゃいけません。

そのための1つのニーズとして、グレースフル・シャットダウンを実装すべきだと提唱しています。グレースフル・シャットダウンは、聞いたことある方も聞いたことがない方もいるかとは思いますが、直訳すると「礼儀正しく止まる」ということです。

つまり、シャットダウンするときに、周りに迷惑かけずにきちんと死んでいくようにしなきゃいけないよねと。そういうふうに作っとこうねという話です。

例えば、WebのHTTPリクエストを受け付けるプロセスでしたら、シャットダウンをやりますと、まずHTTPリクエストのListenを止めて、処理中のリクエストを全部処理して、HTTPレスポンスを全部返してから止まるようにしておくなど。

Workerプロセスの場合には、処理中のジョブをコミットさせずにキューに戻すなり、全部処理しきるなりしてから落ちるようにしておかないと、きちんと止まらない。

あるいは、何とかさんしか止められない謎の技みたいなものができてしまい、属人性が高まっていくことがある。このあたりのグレースフル・シャットダウンをきちんと作り込んでおこうね、というお話がThe Twelve-Factor Appでは提唱されています。

時間のギャップ、人材のギャップ、ツールのギャップ

それから、Dev/prod parityですけど、これは開発とステージングと本番環境をできるだけ一致させましょう、というプリンシプルです。プロジェクトに関わってるみなさんはご認識のように、開発環境と本番環境のギャップは、小さければ小さいほどいいですよね。

The Twelve-Factor Appでは、3つのギャップが挙げられています。時間と人材とツールです。

まず、時間のギャップとして挙げられているのは、開発者がアプリケーションをリリースしました。でも、本番に反映されるまでに時間的なギャップ、数日とか数週間、数ヶ月かかることがある。

さすがに数ヶ月も寝かせられますと……例えば、その間に謎の脆弱性が判明して、なにかのパッチを当てねばならず、テスト環境と微妙にバージョンが異なってしまう。「JDKが違うんです」などですね。なにか証明書がエクスパイアしてしまう、みたいなことがあったりします。時間のギャップは短ければ短いほど、不測の事態は避けられる。それはおわかりかなと思います。

あとは、人材のギャップですね。開発者が書いて、インフラエンジニアがデプロイする。こういったオーバーヘッドを避けるために、DevOpsと言われたりもしています。こういうものもできればやめて、ワンチームで開発からデプロイからメンテナンスまでできるといいよね、というのがあったりします。

プラットフォームの選定に関しては、一番参考になるのはツールのギャップです。本番環境と開発環境で違うものを使っているということですね。

まさにこれはあるあるです。私もかつてWebLogic Serverという製品を担当していました。本番ではWebLogicを使ってるんだけど、開発環境ではTomcatで対応しているといった話をよく聞いたりしますけど(笑)。これは「揃えられれば揃えるべきだよね」と、当たり前ですけれど言われています。

環境は持ち運べるか?

この開発、ステージング、本番環境をできるだけ一致させる観点は、デプロイメント・ターゲットを選択する際にも、参考になる考慮ポイントです。

まず1つ目が、「環境は持ち運べるか?」という話です。

Dockerが支持されているのはまさにこのポイントです。いったん開発環境でDockerのイメージを作っちゃえば、それを持っていける。

あとはうまいこと、設定だけ外側で動的に注入できれば、バイナリを変えずにいけるよねというのは、すごく評価されてるポイントですよね。なかなか仮想サーバーで、VMイメージを持ち回るのは、いろんな意味で難しいのではないでしょうか。

しかし、「APaaSとかファンクション、サーバーレスはどうなの?」となったときに、こういったプラットフォームで悩まれるのは、「ローカル環境で開発してテスト、できましたっけ?」という話があったりしますね。

もちろん「Eclipseのプラグインがあって、ローカルでもLambdaを動かせます」「Herokuもローカルの開発環境があります」もありますが、「それは厳密に本番環境と一緒か?」と言われると、違うわけです。

どこまでそれを許容するのか、というのがありますね。どれだけ環境を持ち運びできるのか。開発者にとって一番気になるのは、「ローカルでちゃんと開発できて、それが信頼できるんだよね?」という話ですね。

今少し話してしまいましたけど、環境を可搬にする際には、動的な設定切り替えに対応しなくてはいけません。環境を移すたびに設定ファイルの中を書き変えないといけないというのは、あまり美しくありません。

できれば、動的に外部からリリースイメージを作るタイミングや、デプロイのタイミング、ランのタイミングで設定を、その環境にふさわしいデータベースの位置や、認証情報の実際の値を与えられるような仕掛けになっているといいでしょう。

こういった環境の可搬性、ローカル環境での開発、動的な設定切り替えが、どこまで自分でやらなきゃいけないのか。プラットフォームとして提供されているのか。それは要件的に合っているのか。これがデプロイメント・ターゲットの選定のポイントとしては、1つあるんじゃないかなと思います。

自由度をとるか、生産性をとるか

ちょっと時間がないので、この2つ飛ばしますね。そしてThe Twelve-Factor Appいくつかちょっと飛ばしたりしましたけど、ご紹介をしました。

くり返しになってしまいますが、「クラウドに親和性の高いアプリケーションを作るには、こう作ったらいいよ」という、アプリケーション開発のベストプラクティスです。しかし、クラウド・ネイティブなアプリケーションをデプロイする先を選択する際にも、けっこう参考になるような内容が多く含まれています。これに則ってご紹介をしました。

少しまとめます。いくつかのThe Twelve-Factor Appをご紹介しながら、「デプロイメント・ターゲットを選択するには、こういうような考慮するポイントがありますね」という話をしましたけど、結局は「自由度をとるか、生産性をとるか」という部分じゃないかなと思うんですね。

これ、上がcontrolable、自由度が高い、右が生産性が高い、つまりマネージされてる度合いが広い、深い。

VMは一番自由度が高いんですよね。OSから下、仮想化のレイヤーは触れないけど、それ以外はかなり自分の自由にできます。その代わり、依存性の管理から、環境の可搬性などなにからなにまで全部自分で考えなきゃいけないことも多い。

かたやファンクションは、ほとんどアプリケーション・ライブラリまで全部お任せで、マネージしてくれる。そのため、自分で考えることはすごく少なくて、工数は減らせる。しかし、「要件的にそれでいけるのか?」は、常に考えなければいけない。トレードオフな関係にあると、ずっと言ってまいりました。

環境の可搬性とかローカル環境での開発を踏まえて選ぶ

クラウド・ネイティブなアプリケーションを選定する際に考慮するポイントは、いくつかこのプレゼンテーションの中で述べてまいりました。

例えば、自由度をとるのか、生産性をとるのか。依存関係の管理をどこまでプラットフォームにやってもらうか、やらせられるのか。

あと、The Twelve-Factor Appに則るのであれば、クラウド上では、マルチプロセスで動かすのがスケーラビリティを確保するためには良くて、そうすると、マルチプロセスのオーケストレーションは誰がやるのか。自分でやらざるをえないのか、プラットフォームに任せられるのか。それが、障害の時も含めてプラットフォームに任しうるのか。

プラットフォームに任せるんだとしたら、アプリはきちんとスレートレスでシェアードナッシングで動かないといけないけど、そういうふうにできるのか。要件的にそれがありえるのか、とか。

あと、生産性や開発環境と本番環境を一致させるために、環境の可搬性とかローカル環境での開発を踏まえたときに、どれが今一番いいのか、このへんを考えて、デプロイする先を選ぶのがいいでしょう。

というわけなので、デジタルに「これからはファンクション一択です」「どこかを使う以外の選択肢はありえない」というのがないのは、みなさん当然ご存知のとおりです。

クラウド・ネイティブなアプリケーションはどれだけ普及していくか

最後に、このOracle CloudをリードしているチームのBrunoというメンバーが、1ヶ月半ぐらい前にTwitter経由でアンケートをしたんですね。

このアンケートでは「マイクロサービスみたいな、いわゆるクラウド・ネイティブなアプリケーションをデプロイする先として、みんなどれがいいと思う?」と質問しています。

ここで選択肢として挙げられてるのは、まず一番上が「VM、仮想サーバーの上に自分でやって、Dockerとかマネージしたりしてやるのがいいんじゃない」という回答。その下が、「いわゆるContainer-as-a-Service。Dockerをホストとする、マネージド・コンテナ・サービスとして使うのが今いいんじゃないかな」という話。

3つ目が、「APaaSを使うといいんじゃないかな」という話。4つ目が、「いや、これからはファンクションでしょ」という話です。

1ヶ月半前のアンケートにおいては、「Container-as-a-Serviceが一番いいところなんじゃないの」という回答結果になりました。このあたりは、クラウドサービスの成熟と、あとそれから、クラウド・ネイティブなアプリケーションがどれだけ普及していくかによって、どんどん変わっていくでしょう。

本当にデジタルな解答ではなくて恐縮ですが(笑)、このような意見がありましたよ、ということを紹介して、私のセッションを終えたいと思います。

ご清聴ありがとうございました。

(会場拍手)