Edited at

マイクロサービスが開発・運用コストの削減にどう貢献するか考えてみた件


はじめに

世の中的に企業のデジタルトランスフォーメーション(以降DXと略)にどう取り組むべきか?というプレッシャーが日々高まっている昨今、皆様いかがお過ごしでしょうか。


DXに取り組むためには、システムのアジリティ(いわゆる俊敏性)を高める必要があると言われており、世の中的にはそれに求められるナレッジが確立されつつあります。その代表格である「マイクロサービスアーキテクチャ」が2014年に提唱され、そろそろ日本でも普及期に入る段階であると理解しています。

マイクロサービスアーキテクチャは最近までデジタルネイティブな企業が使いこなす道具という認識でしたが、2018年3月にコンテナオーケストレーションツールの KubernetesがCNCFを卒業した ことに伴って、エンタープライズ市場にもマイクロサービスアーキテクチャ採用の機運が高まりだしているように思います。

マイクロサービスアーキテクチャはDX推進の文脈で語られることが多い技術ですが、開発・運用コストの削減にどう寄与するのか?について整理してみようと思いました。

経営層のICTシステムへの関心事として大きなファクターを占めるのは、様々なコストになると思いますので(笑)

この記事は私自身がアプリ開発者であるため、その視点で書かれていることにご注意ください。


モノリシックなシステムが今の時代に合わなくなってきた理由

そもそも従来の典型的なモノリシックなアプリが、今の時代に合わなくなってきた理由を考えてみます。

モノリシックなアプリは、データベースなど共通に使われる機能ブロック以外は、一つの塊にパッケージングされ動作するのが特徴です。

内部の機能ブロック間は、関数コールの形でやり取りが行われますが、このような連携方法はバージョンアップを経るごとに機能ブロック間の結合度が増加していく傾向となります。

機能追加や変更の発生頻度が低いビジネス環境においては、このようなアーキテクチャであっても問題になることはありませんでした。

しかし、近年は市場変化のスピードがこれまでより加速、またデータ活用により新たなビジネス領域に挑戦するなど、ビジネス環境が変化し続ける時代では、ICTシステムのアジリティが担保しにくいままでは事業活動に影響を及ぼすリスクが高まると言われています。


アジリティを高めるために必要な技術

モノリシックなアプリではシステムのアジリティが担保しにくいため、DXの推進に影響が出るリスクが高まります。このような課題を解決するため、機能ブロック間を疎結合に保つのが容易なマイクロサービスアーキテクチャが注目されているという事になります。

DXの文脈においては、マイクロサービス以外にもそれを支える技術群がありますので、従来技術と対比して俯瞰できるよう図にしてみました。

image.png


システムのアジリティを高めることに技術がどう貢献するのか

私なりにこれらの技術が、どのようにシステムのアジリティを高めることに貢献するのかを考えてみます。

端的に言えば「システムのオーナーと開発チームが一丸となり、短いサイクルで反復開発をするために必要な技術群」という事になると思います。

この「短いサイクル」というのが非常に重要だと思ってまして、これまでのように半年~数年をかけてシステムを開発していたのでは、世の中の流れに追いつけないでしょ!っていうプレッシャーからの解放につながり、その効果として競合他社に打ち勝つための基礎体力を身に着けられるということになると思います。

ここまでだと、コスト削減の話にはつながりにくいですね・・・。

カテゴリ
技術
貢献できること

開発手法
アジャイル
ウォーターフォールのように上流工程で作る範囲や仕様を決めて開発するのではなく、短いサイクルで反復的に「実際に動くアプリ」を仕様を決めながら開発することで、システムのオーナーが実際に動くものを見ながら、随時方向性を補正していける。

アーキテクチャ
マイクロサービス
モノリシックなアプリを適切に分割し、少しずつ機能を増やしたり、機能の変更をしながら開発していける。アジャイル型開発と組み合わせることで、システムを少しずつ育ていくのに向いた構造。

パッケージ
Dockerコンテナ/Kubernetes
小規模に分割されたアプリを運用するのに最適なパッケージング。小規模に分割されたアプリ毎に、最適な開発言語を組み合わせた開発ができ、アプリの配備を自動化することで運用の手間を減らせる。

デプロイ
CI/CDツールで自動配備
アプリのソースコード修正から、サーバーへの配備までを自動化し、開発者がコードを修正することに専念できるようになるとともに、運用チームの手間も減らすことができる。

インフラ
クラウド
オンプレミスなインフラは、処理スピードや利用者の増加に対応するため、ハードウェアを買い足す必要があります。またハードウェアの故障によりメンテナンスも必要です。一方、クラウドのインフラは、設定次第でインフラ増減に対する自由度が高い。DXで新たな領域にチャレンジするときも、小規模なインフラから始めることができる。

ではそれぞれの技術について、もう少し掘り下げていきますが、特にアプリ開発者に特に影響が大きいと考える技術に絞って(図の赤い点線の枠内)整理してみます。


Dockerコンテナアプリとは

私が過去に携わったWebアプリの開発プロジェクトでは、Apache Tomcatと呼ばれるサーブレットコンテナを活用し、J2EEパッケージのアプリを動作させていました。

「J2EEパッケージのアプリもコンテナアプリじゃないの?」

と思われる方もいらっしゃると思います(もちろんそれも間違いではありません)。

しかし、マイクロサービスの文脈コンテナアプリと言えば、「Docker(ドッカー)コンテナ(※)でパッケージングしたアプリ」を指すことになります。

※ コンテナ技術としてはDocker以外にも存在しますが、現在主流となっているのはDockerだと理解しています

ではDockerコンテナアプリは、従来のサーブレットコンテナ上で動作するJ2EEパッケージのアプリと比較してとんなメリットがあるのでしょうか?


Dockerコンテナアプリで開発するメリット

一つ目のメリットは、開発言語に対する自由度が高いこと。

サーブレットコンテナ上で動作するアプリを開発してきた人は、それがJavaで開発したアプリしか動作しないことはご存知の事だと思います。

モノリシックなアプリの開発では、システムの規模が大きくなると、それに合わせて開発チームの数も多くなります。このような大規模開発では、開発言語や利用する技術が統一されていることで、開発チーム全体を統率することが楽であるという前提があると理解しています。

しかし、昨今では開発言語も多様化し、言語を統一することよりも「効率的に開発できること」の方が重要視されることが増えているように思います。

またDXの文脈においては、アジリティを高めることが重要視されますので、開発メンバーのスキルセットや実現したいことと「最も相性のよい開発言語」を選択するという考え方が導入されています。

例えばJavaScriptでサーバーサイドのアプリを開発するために使われるNode.jsというJavaScript実行環境は、多くのWebサービスで利用されている主要な技術のひとつです。

フロントエンド側の開発でJavaScriptに慣れたエンジニアがいれば、サーバー側のアプリもJavaScriptで開発できるようになると、より効率的に開発ができるようになりますよね。

このようにアプリの開発言語の自由度を高めるため、Dockerのような仮想化技術が使われるようになってきた背景があるように思います。

誤解のないように強調させていただきますが、従来から使われてきたサーブレットコンテナアプリも不要な技術になるわけではなく、Dockerコンテナの中にパッケージングして運用しましょう。ということを意味します。

以下3種類の言語で開発されたアプリをDockerコンテナにパッケージングしたイメージを示します。

image.png

アプリをDockerコンテナでパッケージングすることにより、中のアプリがどんな技術で開発されていても、運用チームはコンテナの中身をあまり意識せずに運用できるようになります。

この特徴は、二つ目のメリットであるポータビリティの高さにつながります。

アプリ開発者がサーバーアプリを作るとき、アプリが動作するために必要な環境設定を同時に定義することが多いと思います。

例えば環境変数は〇〇を設定しておいてほしい、固定パスにディレクトリを作成して必要な権限を設定しておいてほしい、アプリが参照する設定ファイルは固定パスに配備しておいてほしい、などアプリの運用には多かれ少なかれ環境設定が必要になります。

一方、Dockerコンテナアプリは、環境設定が予め実施済みの状態でパッケージングが可能です。つまりDockerコンテナの動作基盤があれば、環境構築や設定が不要で、どの環境でも(※)アプリを動かすことができるようになります。

※ アプリが、CPUアーキテクチャなどの実行環境に依存しない設計で実装されている前提です

従来のシステム開発の現場では、アプリ開発者が環境設定の手順をドキュメント化、それを運用チームに渡し、インフラ構築時に必要な環境設定をするといったフローで作業していたのではないでしょうか。

このような従来の手法では、ドキュメントに書かれた内容で、アプリの動作に必要な情報を人から人に受け渡しをすることになりますし、当然のことながらドキュメントに書かれていないものは運用チームが理解することはできません。

仮にドキュメントに不備があった場合に何が起きるかというと、開発チームの環境で動作していたアプリが、本番環境では動かないといった問題が生じます。このような問題は、この業界にいらっしゃる方ならご経験があるのではないでしょうか。

この記事の対象とはしていませんがDevOpsという開発手法も、このあたりの困りごとに起因して、Dockerコンテナの中に環境設定ができる機能が取り込まれています。

(いわゆる「コード化されたインフラストラクチャ」という側面ですね)

ではDockerコンテナアプリってどうやって開発するのでしょうか?

例として以下の記事をご覧ください。

Docker入門 Hello world

Go言語で単純なWebアプリケーションを構築した例となりますが、アプリ開発者が記述すべきファイルはたったの二つ(main.goとDockerfile)だけであることがご理解いただけると思います。

また、デプロイに必要な手順もコマンドを二つ叩くだけです。

アプリをDockerコンテナとして仕立てることで、その後の運用を楽にする効果を得ることができます。

逆を言えばアプリ開発者がインフラ領域の事もある程度把握して作るようになることが、従来から大きく変わる点になります。

整理すると以下のようになりますね。


  • 開発者が最も効率の良い技術を使うことで、開発期間が短縮され開発コストが削減

  • アプリ開発者が運用チームへのインプットをコード化し、伝達を効率化することで運用コストが削減

もちろん削減できる前提は、開発チームがその技術を身に着けていることが前提となります。


Dockerコンテナアプリの実行環境について

Dockerコンテナアプリがシステムの運用を楽にする効果があることをご理解いただけたと思います。

ではDockerコンテナはどのような実行環境で動作するのでしょうか?

代表的な実行環境は以下のパターンになると思います。


  • オンプレミスの物理マシン上にLinux + Dockerエンジンをデプロイした環境

  • オンプレミスの物理マシン上にLinux + コンテナオーケストレーション環境をデプロイした環境

  • クラウド上の仮想マシンにLinux + Dockerエンジンをデプロイした環境

  • クラウド上の仮想マシンにLinux + コンテナオーケストレーション環境をデプロイした環境

  • クラウド上のマネージドコンテナオーケストレーション環境 (例. Amazon EKSなど)

  • クラウド上のサーバーレスコンテナ実行環境 (例. Amazon Fargateなど)

下に行くにしたがって、システムの柔軟性を高めることができるとともに、システム構築や運用の手間を省くことが可能(※)になるイメージとなります。

※ 低いレイヤーのインフラ運用をクラウドベンダーが肩代わりしているために得られる効果です

ようするに、いざシステムの規模を大規模化させる時も、クラウドならスケールアウトするための手間が減りますし、サーバーレスなインフラになっていけばIaaSを管理する工数も減っていく事になるということです。


コンテナオーケストレーションツール(=Kubernetes)とは

Dockerコンテナアプリについてはご理解いただけたと思いますが、それらのコンテナが1個や2個程度であれば管理はそう難しくはありません。

しかし、機能追加によりシステムの規模が増えるにしたがって、Dockerコンテナアプリは増えていくことになります。また、扱うデータ量やシステムの利用者が増えた際は、コンテナを沢山配備することでシステムをスケールアウトする必要が出てきます。

そのような面倒な運用を簡単にするため、コンテナオーケストレーションという考え方が出てきます。

コンテナオーケストレーションとは、コンテナアプリの運用・管理を表す言葉で、例えば以下のようなことを指します。


  • コンテナアプリの配備自動化

  • コンテナアプリの死活監視

  • コンテナアプリが異常な時の再起動

  • etc...

このような運用・管理の面倒なことを自動化してくれるツールがKubernetes(クーバネティス)です。

コンテナオーケストレーションツールもKubernetesだけではなく、Docker Swarm など他の技術もありますが、いわゆるデファクトスタンダードとしての地位を確立したのはKubernetesとなります。

主要なクラウドベンダーもマネージドサービスとしてKubernetesを提供しており、現時点でKubernetesを採用することは、ベンダーが固定化されてしまう問題(ベンダーロックイン)を回避することにもつながると思います。

従来の手法では、クラウドサービス上に構築したIaaSのひとつひとつにSSH接続し、アプリケーションの配備作業やログの取得等をしていたことと思います。

それをKubernetesの制御用コマンドラインツール(kubectlと言います)ひとつで、リモート制御することができるようになります。

リモート制御には仮想化されたインフラを制御するための設定ファイルを用いることで、インフラの構成管理をすることができます。

Kubernetesは、コンテナアプリが異常終了した際の再起動などを自動化するなど、運用時の負荷を下がることに貢献できそうです。

また、Kubernetesを使いこなすことで、システム無停止更新など高度な運用も可能となります。

しかし、当然のことながらKubernetesを制御するための新しい操作方法を覚えることが必要です。

ここは新しいツールの宿命となりますが、今後このようなコンテナオーケストレーションツールが主流になると考えれば、エンジニアとしては素直に学習して身に着けておく必要があると思います。

操作が難しい問題については、アジリティを高めるのに必要な技術で述べた「CI/CDパイプライン」を予め構築することで、開発者にとっての面倒な操作を隠蔽することも可能です。

つまり、コンテナを管理する仕組みをKubernetesで抽象化することで、クラウドベンダー依存の操作も最小限にすることができますし、Kubernetesが運用チームの負荷を下げることにも貢献できます。

だいぶコスト削減の香りがプンプンしてきました。


マイクロサービスとは

端的に表現すれば、マイクロサービスとは小規模なDockerコンテナアプリのひとつひとつを指します。

「マイクロサービスアーキテクチャ」とは、このような小規模なDockerコンテナの集合体で大きなシステムを構成するアーキテクチャと言い換えられると思います。

例えば、マイクロソフトが提供するマイクロサービス指向のアプリケーションサンプルをご覧ください。

このサンプルコードでは、バックエンドサービスを複数のサービス(Identity microservice、Catalog microservice、Ordering microserviceなどなど・・・)に分割していることがおわかりいただけると思います。

Dockerコンテナアプリの中に多くの機能を盛り込んでしまうと、マイクロサービス単体の見通しが悪くなりますし、バグ修正などによるバージョンアップの影響範囲も大きくなります。

このようにモノリシックなアプリを小規模に分割し、それぞれのサービスの独立性を高めることによってシステム更新の手間が少なくなる=開発期間も短くなる!=それってコストの削減につながりますね!

なるほど、それがマイクロサービスを採用する大きなメリットになるということであることが理解できました。

その他メリット/デメリットについては、以下の記事を参照いただくとよいかと思います。

マイクロサービス・アーキテクチャのメリット/デメリットまとめ - Qiita


マイクロサービスの分割設計

マイクロサービスアーキテクチャを取り入れたシステムをより良い形で仕上げるには、サービスの分割を最適に行う必要があるとされています。

残念ながらサービスの分割設計はこの記事で語りつくすのが困難な物量であるため、とりあえずこちらの解説でお茶を濁したいと思います・・・。

ここでは「ドメイン駆動設計」という設計スキルが必要があるということが語られていまして、マイクロサービスアーキテクチャを採用したシステムを設計する上で避けては通れない重要な技術なので、追々理解を深めようと思います。


マイクロサービス間の連携と独立性の担保

分割された複数の小規模なDockerコンテナアプリケーションは、ネットワーク経由で通信することで協調動作をさせる必要があります。

このようなマイクロサービスアーキテクチャ採用のアプリは、マイクロサービス間の独立性を高めた設計とすることで、個々のアプリの振る舞いが他のアプリに与える影響を最小限になるように考慮して設計をします。

いわゆる疎結合にするというお話です。

疎結合とした効果として、システムを停止することなく個々のアプリの機能更新や追加が容易に実施できるようになります。しかし、アプリが分散配備されることになるため、アプリ間はネットワークを経由して連携することが必要となります。

つまり、マイクロサービスアーキテクチャ型システムの特徴を最大限発揮させるためには、アプリ間連携の設計を適切に実施することが肝要となります。


疎結合な設計とは

昔からよく言われる「疎結合」という考え方ですが、マイクロサービスの文脈では以下の様に理解しておけばよいと思います。


  • データベースはマイクロサービス間で共有しないこと

    マイクロサービス間でデータベースを共有したとたん、特定サービスのテーブル構造変更に対し、他のマイクロサービスを巻き込んでしまうため、独立性が担保できなくなります。

  • マイクロサービスはインターフェースを明確に定義していること

    必ず明確に定義されたインターフェースを介し、ネットワーク経由での呼び出しをすることで、バージョンアップ時の動的な差し変えに対応することができる。

  • インターフェースの変更がない限り、他のマイクロサービスに影響を出さないこと

    個々のマイクロサービスの変更に対し、依存関係を持たせないようにするため、インターフェースの変更なしに中身の変更をする。逆に、インターフェースの変更をする場合は、旧インターフェースの後方互換性を保って新規インターフェースを追加するようにする


疎結合なマイクロサービスを設計するためのポイント

マイクロサービスアーキテクチャ型情報システムでは、モノリシックなアプリの開発時にはなかった新たな問題領域があります。

これはアーキテクチャの変更によって必然的に生じる問題であり、それを解決しなければマイクロサービスアーキテクチャ型ICTシステムは実現できません。問題の解決には既存の設計とは異なる考え方が必要となるので、正しく理解をした上で進める必要があります。

マイクロサービスの文脈では、先行されているChris Richardson氏の書籍で問題領域がうまくまとめられていると思います。

これまで記載した内容とChris Richardson氏が定義した問題領域から、マイクロサービスアーキテクチャを採用したアプリの設計に対して、最低限のエッセンスを抽出すると以下のようになります。

適切なインターフェース設計など、モノリシックなアプリの時代でも必要な設計もありますが、それであってもマイクロサービスアーキテクチャの設計として観点が追加になっていることに注意が必要です。

設計
効果

適切なインターフェース設計
マイクロサービスを疎結合に保つ

適切な非同期メッセージング設計
マイクロサービスを疎結合に保つ

システム全体の俯瞰的監視の手法(ログの集約と可視化)
マイクロサービスが分散デプロイされることで生じる新たな課題を解決する

サービス名ベースでのマイクロサービス間通信
スケーラブルなインフラ構成を実現するために生じるアプリ側への影響を避けるために設計する

ネットワークエラー発生時のリカバリ戦略
マイクロサービスがネットワーク経由で連携するために生じる新たな問題を解決するために設計する

マイクロサービス毎に分割されたデータベースの整合性維持
マイクロサービスの独立性を高めるため、データベースを分割したことによって生じる新たな課題を解決するために設計する

データベースの効率的なアクセス
マイクロサービスの独立性を高めるため、データベースを分割したことによって生じる新たな課題を解決するために設計する

すべてのアジリティを高める技術を使いこなした場合、以下の図のように関わる人たちが全て幸せになる可能性があるということです。

image.png


さいごに

マイクロサービスアーキテクチャが開発・運用コストの削減にどう貢献するかを、周辺技術とともに整理してみました。

ポイントは以下になると思います。


  • マイクロサービスアーキテクチャを中心として、それらを支える技術に移行することでシステムのアジリティを高めることができる

  • マイクロサービス移行に伴う運用の面倒なことは、便利なツールやインフラに任せることができる

  • マイクロサービスのアプリ開発に対し、開発チームが従来とは異なる設計思想や実装スキルを身に着ける必要がある

短期的にはアジリティを高めるためのトレードオフとして移行に伴うコストがかかりますが、中長期で見ると開発・運用コスト削減につなげられると考えます。

次回は開発チームが身に着けるべき実装スキルについて、もう少し詳細に踏み込んでみようと思います。

つづく(かもしれません)。。。

つづきの記事がリリースされたので、こちらも合わせてごらんください。


デジタルトランスフォーメーションにおけるシステムの俊敏性とは?を考える


謝辞

今回の記事執筆にあたり、図中で利用させていただいたアイコンは以下のサイトのライセンス条件に従って利用させていただきました。

アイコン素材ダウンロードサイト「icooon-mono」

この場を借りて感謝いたします。