クラウド・ネイティブなアプリケーションを動かすには?

新井庸介氏(以下、新井):では、次のセッションを始めます。お集まりいただきまして、ありがとうございます。日本オラクルでクラウドのソリューション・アーキテクトを務めております新井と申します。本日はよろしくお願いします。

今日は「Deploying Cloud Native Applications」と題しましてお話をいたします。

クラウド・ネイティブなアプリケーションをデプロイする先として、一般的にはDockerなど、よく最近は取り上げられております。

もちろん、仮想サーバーやDockerをマネージするクラウドなどは昔からあります。アプリケーション開発のPaaS、最近ではLambdaに代表されるファンクションなどもあります。どういったクラウドサービスが適しているのか、そういった話を少しいたします。

クラウド・ネイティブなアプリケーションのデプロイ先であるクラウドサービスとしては、だいたいこの4つのカテゴリがあるんじゃないかと思います。(スライドを指して)一番左側の赤いところが「Virtual Machine」、いわゆる仮想サーバーですね。AmazonのEC2、Azure VM、OracleのCompute Cloud、Softlayerなどがあるかと思います。

ただ、クラウド・ネイティブなアプリケーションのため、どちらかというと「もっと小さな単位のコンテナがいいよね」とはよく言われています。Dockerをはじめとするコンテナ、それをマネージするクラウドサービスもクラウドベンダーから多く出ていることは、みなさんもご存知かと思います。

しかしクラウド上でアプリケーションを動かすと、それより先にいわゆるPaaS……APaaSとよく言っていますが、Application Platform as a Serviceというものがあり、Herokuはその元祖ですよね。Bluemixや、OracleでもApplication Container Cloudがありますが、こういったクラウド・ネイティブなアプリケーションを動かすことに特化したPaaSがあるわけです。

(スライドを指して)一番右側の紫部分は、一番新しいものです。Functions、いわゆるサーバーレスのサービスとしてAmazonのLambdaやMicrosoftのFunctionsなどさまざまなものがあり「これからこれなんじゃないの?」といった話もあったりします。

「クラウド・ネイティブなアプリケーションを動かすにはどれがいいの?」と言われたときに、「これだ」という答えを私は知っているわけではありません。なにより、そもそもその答えが存在するはずがなく「要件やその時のサービスの成熟度によるよね」が答えになってしまいます。

そのため、このセッションではクラウド・ネイティブなアプリケーションを動かす際に「どのデプロイ先を選ぶか」となりますが、「こういうことを考慮したほうがいいんじゃないか」「こういう観点があるよ」といった考慮ポイントを整理するかたちで進めていきたいと思います。

「クラウド・ネイティブ・アプリケーション」とはなにか?

では、どうやってクラウド・ネイティブなアプリケーションをデプロイ先に選ぶか。

そもそも論として「このセッションで言っている“クラウド”とはなにか」「クラウド・ネイティブ・アプリケーションとはなにか」を整理します。

お集まりのみなさまはクラウド、とくにPaaSやlaaSに関わっている方がけっこう多いんじゃないかと思っています。

みなさまも認識されているとおり、クラウドは現状もっともコストとアジリティに優れたプラットフォームです。とくに新規系のアプリケーション開発では、最初にデプロイ先として考慮されるプラットフォームですし、既存のアプリケーションの更改するタイミングでも「クラウドでいいんじゃないの?」と考える機会も増えています。

コストやアジリティという観点では、もうクラウドを考慮から外すことはないと思います。クラウドをプラットフォームとして考えたときに、その特性を改めて整理してみるとこういった特徴があります。

まず1つ目が、サービスインスタンス。この大きさには制約があります。最近ではけっこう大きなインスタンスが増えてきましたが、無限に大きくできるわけではないのです。これはオンプレと大きく違うところです。

それから、地理的に分散して配備されています。データセンターやリージョン、カントリーなどですね。なにかのアプリケーションを載せようとしたときには、巨大なシングルサービスが動かなかったり、下手をすると載らない可能性もあったりします。

そのため、サービス分割を考える機会として、クラウドをプラットフォームとして考えたタイミングが多いんじゃないかと思います。

考慮できているアプリほど、クラウドを上手く使える

それから、性能拡張や可用性を向上する際には複数のサーバーやインスタンスの利用が前提です。つまり、オンプレのときとは違って、「1台のサーバーが巨大で性能もすごいとまず落ちません」といった世界ではなく、「一つひとつのインスタンスは、まぁ落ちるよね」なのです。

サービスレベルにしたがって落ちることがあり、それをどうにかするために複数台数を使う。複数台数を前提としたフェイルオーバーやスケールアウトが前提になるのです。そのため、この上に載るアプリケーションは、これができないとクラウドを使いにくいのですね。

つまり、性能を上げるときは、1つのインスタンスを上げると性能がリニアにスケールする、もしくは落ちたら物理的にも別の場所やインスタンスでサービスが上がるなど。耐えられるアプリケーションじゃないとクラウドは使いにくい。これは、みなさんもおわかりでしょう。

クラウドの世界では、サービスはAPIに連結しています。サービス分解をしてAPIでやりとりしながら1つの大きなサービスを実現するのは、よく実践されている方法です。

まさにこれがクラウド・ネイティブなアプリケーションの1つと言えると考えています。つまり、クラウドの特性を踏まえた上で、これを一番うまく活かすにはアプリケーションがそうなっていないといけないのですね。

サービス分割ができたり、スケールアウトできたり。物理的に立ち上がり位置が違ってもちゃんと動く。これを考慮できているアプリほど、クラウドを上手く使える。そうじゃないアプリはクラウドに載りにくかったり、クラウドに載ってもいまいちその能力を使えないことになるわけです。

これがクラウドというプラットフォームであり、その上で一番うまく動くアプリケーションがクラウド・ネイティブ・アプリケーションと、ここでは定義付けています。

クラウドにはコントロールできない領域が存在する

では、The Twelve-Factor Appの定義を少し紐解いてみたいと思います。ちなみにThe Twelve-Factor Appはアプリケーションの開発方法論です。この中で「聞いたことがあるよ」という方はどれくらいいらっしゃいますか?

(会場挙手)

ありがとうございます。2割少しくらいな感じですね。The Twelve-Factor Appは……(スライドを指して)ここのURLを見ていただくと公開されている文章ですけれども。まさにクラウドとの親和性が高いアプリケーションを開発するための方法論です。

先ほどお話したように、現状もっともコストとアジリティに優れているクラウドをうまく活かしきるには、アプリ側もプラットフォームに歩み寄らなければいけません。「そうするためにはどうすればいいのですか?」という方法を、12の要素にまとめた文章になっています。

この文章が書かれた背景は2011年、HerokuというPaaS……今はSalesforceのPaaSのラインナップになっています。このHerokuのPaaSエンジニアが、この文章を書いて公開したそうです。

そのエンジニアいわく、Herokuプラットフォームの仕事をする中で、たくさんアプリケーションを動かしたり開発したり運用したりしていく中で「こういったアプリケーションはHerokuみたいなアプリケーションに向いてるよね」「こうやって作ればいいのに」といった気づきをまとめたものなのです。

つまりThe Twelve-Factor Appは、HerokuみたいなPaaSにおいてよりスケールするアプリケーションです。どう作ればいいのか、移植性が高いアプリはどう作ればいいのか。これはほかのクラウドへいきやすいという意味もありますが、もう少し言うと、クラウドには自分でコントロールできない領域が少なからず存在しますよね。

例えば「計画停電があります」「自分でOSの下は触れない」、下手をすると「OSは触れません」といったことがある中で、環境依存をどうコントロールしていくのか。クラウドの中で少し仕様が変わっても問題なく動くようにするにはどうすればいいのか。

また、クラウドの求めるものにはコストやアジリティという大きく2つがありますが、とくにアジリティです。「1日10回以上デプロイできます」を本当に実現するには、アプリをどう作らないといけないのか、属人性の排除など、こういったものを狙って書かれた文章なのです。

The Twelve-Factor Appは12個の要素で成り立っています。これそのものはいわゆるクラウドと親和性の高いアプリケーションの作り方ですが、この中のいくつかはクラウドをデプロイする先を考慮する点でも参考になる要素があります。

そのため、今日はThe Twelve-Factor Appの要素をご紹介しながら、アプリケーション開発のためにかかれた文章ですが、作ったアプリケーションをデプロイする先として紐解きながら考えていきます。

すべての依存関係を明示し、宣言し、管理する

では、前置きが少し長くなりましたがThe Twelve-Factor Appを1つずつ紐解きながら「デプロイメントを選ぶ時にも考慮すべき点」といった感じで進めていきます。

まず1つ目は、Codebaseというプリンシプルです。これは簡単ですが、「デプロイ先ごとにコードを作るな」という話です。コードやGit、Subversionで管理されたコードベースとアプリケーションは常に1対1の関係でありなさいということです。

デプロイ先にはいろいろあります。例えば、開発環境やオフショアの開発環境、ステージング環境、プロダクション環境などたくさんあります。でも、コードベースは1つで、デプロイする先は複数あることがあります。

コードの中にいろいろな環境要素を埋め込んだり、コードのバリエーションを作らないでおこうという話です。これは、みなさんもわかると思います。

次のプリンシプルがDependencies、依存関係です。The Twelve-Factor Appでは、依存関係は明確に宣言しておきましょう、という話です。とくにここはクラウドっぽい要素ですが、プラットフォームとして考えた時には自分でコントロールできないところが少なからず存在するわけですね。

HerokuはOSを選ぶわけじゃないので、「こういうライブラリが入ってます」と暗黙的に前提にすることができないわけです。そのため、あるアプリケーションが必要とするライブラリやコンテナといったものは、厳密に明確に定義をしておいて、ビルドツールなどで管理できるようにしましょう、というのがThe Twelve-Factor Appです。

言語ごとにさまざまな構成管理ツール……ビルド管理ツールが存在します。こういったものをうまく使って、すべての依存関係はちゃんと明示して、厳密に宣言して、管理できるようにしておきましょうね、ということです。「このライブラリが入っているはずだから」「OSがこれに違いないから」といった暗黙的な部分は、なるべく排除しましょうというところです。

これは非常にいいですよね。デプロイするにはけっこう属人的な「この謎の技が必要で……」みたいな感じになってくる。そうなると開発チームとしてもスケールしないので、これも非常にいいプリンシプルじゃないかなと思います。

アプリケーション・コンテナの依存性

Dependenciesについて少しくわしく掘っていくと、まずDependenciesにもいくつかレイヤーで考えるべき部分があります。まず、アプリケーションがありますよね。みなさんが書かれるアプリケーション、JavaだったりRubyだったりPHPだったり、いろいろあるかと思うんです。

アプリケーションというのは、だいたいアプリケーションのライブラリを使います。こういうものはMavenなど、そういったパッケージ管理ツールで管理しておいて、ビルドのときに組み込んでいく、といったやり方をしますよね。

アプリケーションをコンテナの上で動かすものは、けっこう存在します。Java EEなんかそうですよね。TomcatといったWebのサーブレットコンテナで動かすものもあれば、GlassFishのようなJava EEのコンテナを必要とするものもあります。こういうアプリケーション・コンテナの依存性もあるわけです。

その下にはランゲージのランタイムがあります。JavaやRuby、PHP、JavaScriptなど、さまざまなランタイムがアプリケーションには必要です。

そして最後のレイヤーが、OSです。

冒頭で4つのクラウドサービスのカテゴリのお話をしました。この4つのカテゴリのクラウドサービスは、それぞれカバーしてる範囲が異なります。(スライドを指して)右側にいくにしたがって、カバーしている領域が多い、マネージしている範囲が広いなどがあります。

ファンクション、いわゆるLambdaのようなものの場合は、基本はアプリケーション・ライブラリまで、クラウドサービスがまとめて管理して提供します。最近では、自分が使いたいアプリケーション・ライブラリを持ち込めるようになってきつつあります。しかし、基本的にはもうライブラリまでファンクションで提供していて、「その上にアプリケーションを載せて使おうね」というスタイルですね。

「設定はコードから明確に分離しましょう」

APaaSだともう少しゆるやかです。「ライブラリは自分の好きなのを使ってくださいね」「アプリケーション・コンテナはAPaasのほうで用意するよ」「まあ、いくつか選べはするけどね」といった感じになっているものが多いです。

コンテナはさらにもう少しゆるいです。だいたいDockerのレイヤーまで、ランタイムを含んだDockerのレベルまでマネージし、あとのレイヤー……つまり「アプリケーション・コンテナとしてTomcatを使いたい」「どうぞどうぞ」「ライブラリとかアプリケーションは好きにやってくださいね」みたいな感じです。

仮想サーバーはOSしかマネージせず「あとは自分でやってください」というスタイルですね。

(スライドを指して)依存関係を考えたときには、矢印の範囲が管理せねばならない依存関係になります。VMだと、アプリケーション・ライブラリ、それから利用するコンテナからランゲージ・ランタイムまで、この構成を自分で管理しなきゃいけないわけですね。

「こういうライブラリのこのバージョンのが必要で、コンテナはこうで」となる。それを「大変だな」と考えることもあるかもしれません。しかし逆に言えば、自由度があるわけですね。自分の好きなようなものを使えるというものもあるわけです。

そのプロジェクトやアプリケーションの仕様として、どこまでプラットフォームでやってもらえるのか、どこまで自分でやらなきゃいけないのか、どこまで自分で管理しうるかを考えながら、デプロイするプラットフォームを選んでいくことになるんじゃないかと思います。

この依存関係というポイントは、デプロイメント・ターゲットを選ぶときにも、1つ参考になります。

次のプリンシプルへいきます。次はConfig。設定をコードから分離するという原則が、The Twelve-Factor Appでは提唱されています。

ここで言うConfigは、例えばデータベースやキャッシュ、キューの位置、それからAmazonのS3やTwitterのような外部サービスを使う場合には、その認証情報などいわゆる設定周りです。

けっこうカッコ良く言ってますけど、つまるところ「設定をハードコードするなよ」という話です。経験の長いみなさまにとっては「こんなん当たり前じゃん」という話かもしれませんが、ここで明確に定義をしています。「設定はコードから明確に分離しましょう」「設定はデプロイごとに大きく変わるでしょうね」と。

開発環境で参照するデータベースとステージング環境で参照するデータベース、本番環境で参照するデータベースは、場所は当然異なります。もしかするとSIDも違うかもしれません。そのため「そういうものをいちいちコードに書いておくのやめようね」という、ここはみなさんも「そりゃそうでしょ」となる話かと思います。

バイナリを変えずに、環境を移していくことができる

The Twelve-Factor Appではさらに一歩踏み込んで、「設定は環境変数に格納しようね」という話をしています。つまり、アプリケーションの中ではデータベースの位置ですとかTwitterの認証情報というのは、いわゆる環境変数で言うところの定数だけを読んでいる。実体の値は持たない、と。

そして実体の値、データベースの物理的な位置、TwitterのIDとパスワードなど、実際の値はデプロイのタイミングやランのタイミングで環境変数として渡してあげましょうね、と。そうすると、アプリケーションとしてのバイナリは変えずに、参照するデータベースやTwitterのIDとかを切り替えることができるようになる。そういったところまで踏み込んで、The Twelve-Factor Appでは話をしています。

つまり、「コードの中には宣言だけしておいて、実数はなにかのタイミングで与えるようなやり方が望ましいよね」と言っています。これも、どこにデプロイされるかわからないクラウド・アプリケーションの場合には有効かなと思います。

あとはDevOpsを考えられてる方にはいいかもしれないですね。バイナリを変えずに、環境を移っていくことができるので。これも非常に参考になるファクター、原則です。

次のBacking servicesでも、基本的には同じことを言っています。

アプリケーションはすべからくバックエンドのサービス、データベースだったり、キューだったり、メールサーバーだったり、キャッシュサーバーだったりを参照するわけです。しかし、これが自社でオンプレで持っているデータベースであれ、他社が管理しているクラウドサービスであれ、コード的には区別せずに接続できるようにしておくべきです。

コード的にはMySQLにつなぐだけです。ただ、その物理的な位置がこう違うとしておくのがベストですね、という話ですね。

考慮すべきは、動的な設定をどう実現するか

その4つのカテゴリのプラットフォームを考えたときに、この設定をコードから分離する、サービスはリソースとして扱う観点で、考慮すべきは動的な設定をどう実現するかです。

Configのところでもお話しましたが、アプリの中では環境変数として宣言をしておく。実際のデータベースの位置や認証情報は、ビルドのタイミングだったり、デプロイのタイミングだったり、ランのタイミングだったりで、実数を渡す。これが望ましいとされています。しかし、これをどう実現するかは、1つのプラットフォームの選定にも影響を与えるポイントです。

例えば、ファンクションでは実行時に環境変数を渡せるような仕掛けが、少し前に導入されたりしましたね。Lambdaなんか、そういう機能があると聞いています。例えば、プラットフォーム側で動的な設定に対応してるものがあったりします。

ファンクションもそうですし、APaaSも、例えばOracleのApplication Container Cloud Serviceは、デプロイのタイミングでの宣言に対して実際の設定値をあらかじめバインドさせることができる仕掛けになってます。

こういうようなプラットフォームが提供する動的設定の仕組みを使ってうまくやっていく、あるいはSpring Cloud Configなどをうまく使って、ビルド時に設定をバインドしていく仕掛けを自分で作る選択肢ももちろんあるかと思います。たぶんVMを使う場合にはそうなるでしょうし、コンテナの場合には……どうでしょうか?

コンテナをマネージしてるサービスによっては、動的な設定の機能を持っているクラウドサービスもあるかもしれません。

しかしまず1つ、The Twelve-Factor AppのConfigを参考にして、動的な設定、ワンバイナリで接続先を変えていくことをどう実現するかは、1つクラウドを選択する際には考慮したほうがいいでしょう。