12 要素アプリとして知られるガイドラインは、10 年以上前に初めて公開されました。 それ以来、ほぼすべての義務付けられたプラクティスが、Web アプリの作成と展開の事実上の標準方法になりました。 また、アプリの構成や展開方法が変化しても、これらのプラクティスは適用可能ですが、アプリの開発と展開のためのマイクロサービスパターンにこれらのプラクティスがどのように適用されるかを理解するには、追加のニュアンスが必要になる場合もあります。
このブログでは、要因 3 「環境に構成を保存する」に焦点を当てています。
マイクロサービスに移行しても、これらのガイドラインを遵守することはできますが、必ずしも 12 要素アプリの文字通りの解釈に正確に一致するとは限りません。構成データを環境変数として提供するなどの一部のガイドラインは、問題なく引き継がれます。 その他の一般的なマイクロサービス プラクティスは、12 要素アプリの基本原則を尊重しながらも、その拡張版のようなものです。 この記事では、Factor 3 の観点から、マイクロサービスの構成管理の 3 つの中核概念について説明します。
マイクロサービスに Factor 3 を適応させる議論に入る前に、いくつかの重要な用語と概念を理解しておくと役立ちます。
モノリシック アプリケーションでは、組織内のすべてのチームが同じアプリケーションと周囲のインフラストラクチャで作業します。 モノリシック アプリは一般的に、理論上はマイクロサービスよりもシンプルに見えますが、組織がマイクロサービスへの移行を決定する一般的な理由がいくつかあります。
もちろん、マイクロサービスには、複雑さの増大、可観測性の低下、新しいセキュリティ モデルの必要性など、独自の課題が伴いますが、多くの組織、特に大規模または急成長中の組織は、顧客に提供するエクスペリエンスの信頼性が高く安定した基盤を構築するために、チームにさらなる自律性と柔軟性を与えるためには、これらの課題を乗り越える価値があると判断しています。
モノリシック アプリをマイクロサービスにリファクタリングする場合、サービスは次の条件を満たす必要があります。
モノリシック アプリの場合、プロセスにおけるわずかな矛盾や共有された前提への依存は重要ではありません。 しかし、多数の個別のマイクロサービスがある場合、それらの不整合や仮定によって多くの問題や混乱が生じる可能性があります。 マイクロサービスで行う必要のある変更の多くは技術的な必要性によるものですが、驚くほど多くの変更は、チームの内部での作業や他のチームとのやり取りに関係しています。
マイクロサービス アーキテクチャによる注目すべき組織的変化は次のとおりです。
マイクロサービス アーキテクチャで要素 3 を拡張する必要がある領域の 1 つは、サービスの構成を含むサービスに関する特定の重要な情報を明確に定義し、他のサービスとの共有コンテキストを最小限に抑える必要があることです。 要因 3 はこれを直接的には扱いませんが、アプリケーションの機能に貢献する多数の個別のマイクロサービスでは特に重要です。
マイクロサービス アーキテクチャのサービス所有者として、チームはシステム全体で特定の役割を果たすサービスを所有します。 あなたのサービスとやり取りするサービスを持つ他のチームは、コードやドキュメントを読んだり貢献したりするために、あなたのサービスのリポジトリにアクセスする必要があります。
さらに、ソフトウェア開発の分野では、開発者の入社や退職だけでなく、社内の再編によってもチームのメンバー構成が頻繁に変わるのが残念な現実です。 また、特定のサービスに対する責任もチーム間で移管されることが多々あります。
これらの現実を考慮すると、コードベースとドキュメントは極めて明確で一貫性がある必要があり、これは次の方法で実現されます。
多くのアプリケーション フレームワークは、必要な構成を定義する手段を提供します。 たとえば、Node.js アプリケーション用のconvict
NPM パッケージは、単一のファイルに保存された完全な構成「スキーマ」を使用します。 これは、Node.js アプリの実行に必要なすべての構成の信頼できるソースとして機能します。
堅牢で簡単に検出できるスキーマにより、チームのメンバーと他のユーザーが自信を持ってサービスとやり取りできるようになります。
アプリケーションに必要な構成値を明確に定義したら、デプロイされたマイクロサービス アプリケーションが構成を取得する 2 つの主要なソース間の重要な違いを尊重する必要もあります。
デプロイメント スクリプトは、マイクロサービス アーキテクチャにおける一般的なコード編成パターンです。 これらは、12 要素アプリの最初の公開以来新しく追加されたものであるため、必然的に 12 要素アプリの拡張版となります。
近年では、アプリケーション コードと同じリポジトリに、 infrastructure (またはその名前のバリエーション) というフォルダーを置くことが一般的になっています。 通常、次のものが含まれます。
一見すると、これは、構成はコードから厳密に分離するという Factor 3 の規定に違反しているように見えるかもしれません。
実際、アプリケーションの横に配置すると、インフラストラクチャ フォルダーはルールを実際に尊重すると同時に、マイクロサービス環境で作業するチームにとって重要な貴重なプロセス改善が可能になります。
このパターンの利点は次のとおりです。
このパターンによってもたらされる利点は、個々のチームの自律性を強化すると同時に、展開および構成プロセスにさらなる厳密さが確実に適用されることに注目してください。
実際には、インフラストラクチャ フォルダーに保存されているデプロイメント スクリプトを使用して、スクリプト自体に明示的に定義された構成と、デプロイ時に外部ソースから取得される構成の両方を管理します。そのためには、サービスのデプロイメント スクリプトを次のようにします。
サービスの特定のデプロイメントに固有で、チームによって完全に制御される構成値は、インフラストラクチャ フォルダー内のファイルで直接指定できます。 例としては、アプリによって開始されたデータベース クエリの実行が許可される時間の長さの制限などが挙げられます。 この値は、デプロイメント ファイルを変更し、アプリケーションを再デプロイすることによって変更できます。
このスキームの利点の 1 つは、このような構成の変更は必ずコード レビューと自動テストを経由するため、誤った構成値によって停止が発生する可能性が低くなることです。 コードレビューを通過する値の変更と、特定の時点での構成キーの値は、ソース管理ツールの履歴で検出できます。
アプリケーションの実行に必要だがチームの制御下にない値は、アプリケーションがデプロイされる環境によって提供される必要があります。 例としては、サービスが依存する別のマイクロサービスに接続するホスト名とポートがあります。
そのサービスはチームによって所有されていないため、ポート番号などの値について想定することはできません。 このような値はいつでも変更される可能性があり、変更が手動で行われるか、何らかの自動プロセスによって行われるかに関係なく、変更されたときに何らかの中央構成ストレージに登録する必要があります。 その後、それらに依存するアプリケーションによってクエリを実行できるようになります。
これらのガイドラインは、マイクロサービス構成の 2 つのベスト プラクティスにまとめることができます。
サービスが対話するサービスの場所など、デプロイメント スクリプトに特定の値をハードコードするのが最も簡単に思えるかもしれません。 実際には、そのようなタイプの構成をハードコーディングすることは、特にサービスの場所が頻繁に変更されるような現代の環境では危険です。 2 番目のサービスを所有していない場合は特に危険です。
スクリプト内でサービス場所を最新の状態に保つには自分自身の努力に頼ることができる、あるいはさらに悪いことに、場所が変わったときに所有チームが通知してくれると信頼できると考えるかもしれません。 ストレスの多い時期には注意力が散漫になることが多く、人間の厳しさに頼ると、システムが予告なしに機能しなくなるリスクが生じます。
位置情報がハードコードされているかどうかに関係なく、アプリケーションは重要なインフラストラクチャが特定の場所にあることに依存してはなりません。 代わりに、新しくデプロイされたサービスは、システム内の共通ソースに「データベースはどこにありますか?」などの質問をし、その外部リソースの現在の場所に関する正確な回答を受け取る必要があります。 すべてのサービスがデプロイ時にシステムに登録されると、物事がはるかに簡単になります。
システムが「データベースはどこにあるのか?」や「依存している「サービス X」はどこにあるのか?」という質問に答える必要があるのと同様に、サービスは、他のサービスが、そのサービスがどのようにデプロイされているかを知らなくても簡単に見つけて通信できるように、システムに公開される必要があります。
マイクロサービス アーキテクチャにおける重要な構成方法は、サービス検出です。サービス検出とは、新しいサービス情報を登録し、他のサービスによってアクセスされたときにその情報を動的に更新することです。 マイクロサービスにサービス ディスカバリが必要な理由を説明した後、NGINX Open Source と Consul を使用してそれを実現する方法の例を見てみましょう。
サービスの複数のインスタンス (デプロイメント) を同時に実行することは一般的な方法です。 これにより、追加のトラフィックを処理できるだけでなく、新しいデプロイメントを起動してダウンタイムなしでサービスを更新することもできます。 NGINX などのツールはリバース プロキシおよびロード バランサとして機能し、受信トラフィックを処理して最も適切なインスタンスにルーティングします。 これは良いパターンです。サービスに依存するサービスは NGINX にのみリクエストを送信し、デプロイメントについて何も知る必要がないためです。
たとえば、リバース プロキシとして機能する NGINX の背後で実行されているmessengerというサービスのインスタンスが 1 つあるとします。
あなたのアプリが人気になったらどうしますか? これは良いニュースと考えられますが、その後、トラフィックの増加により、メッセンジャーインスタンスが CPU を大量に消費し、リクエストの処理に時間がかかっている一方で、データベースは正常に動作しているように見えることに気付きます。 これは、メッセンジャーサービスの別のインスタンスを展開することで問題を解決できる可能性があることを示しています。
メッセンジャーサービスの 2 番目のインスタンスをデプロイすると、NGINX はどのようにしてそれがライブであることを認識し、トラフィックの送信を開始するのでしょうか。 NGINX 構成に新しいインスタンスを手動で追加することも 1 つの方法ですが、サービスのスケールアップとスケールダウンが増えると、すぐに管理不能になります。
一般的な解決策は、 Consulのような高可用性サービス レジストリを使用してシステム内のサービスを追跡することです。 新しいサービス インスタンスは、デプロイされると Consul に登録されます。 Consul は、定期的にヘルスチェックを送信してインスタンスのステータスを監視します。 インスタンスがヘルスチェックに失敗すると、利用可能なサービスのリストから削除されます。
NGINX は、さまざまな方法を使用して Consul などのレジストリを照会し、それに応じてルーティングを調整できます。 リバース プロキシまたはロード バランサーとして機能する場合、NGINX はトラフィックを「アップストリーム」サーバーにルーティングすることを思い出してください。 次の単純な構成を検討してください。
# 「messenger_service」というアップストリーム グループを定義します
upstream messenger_service {
server 172.18.0.7:4000;
server 172.18.0.8:4000;
}
server {
listen 80;
location /api {
# '/api' で始まるパスを持つ HTTP トラフィックを上記の 'upstream' ブロックにプロキシします。 デフォルトの負荷分散アルゴリズムである
# ラウンドロビンは、ブロック内の 2 つのサーバー間でリクエストを交互に送信します。
proxy_pass http://messenger_service;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
デフォルトでは、NGINX はトラフィックをルーティングするために、各メッセンジャーインスタンスの正確な IP アドレスとポートを認識する必要があります。 この場合、172.18.0.7 と 172.18.0.8 の両方のポート 4000 になります。
ここで Consul とConsul テンプレートが登場します。 Consul テンプレートは NGINX と同じコンテナー内で実行され、サービス レジストリを管理する Consul クライアントと通信します。
レジストリ情報が変更されると、Consul テンプレートは正しい IP アドレスとポートを含む NGINX 構成ファイルの新しいバージョンを生成し、それを NGINX 構成ディレクトリに書き込み、NGINX に構成を再読み込みするように指示します。 NGINX が設定を再読み込みするときにダウンタイムは発生せず、再読み込みが完了するとすぐに新しいインスタンスがトラフィックの受信を開始します。
このような状況では、NGINX などのリバース プロキシを使用すると、他のサービスがアクセスする場所としてシステムに登録するための単一のタッチ ポイントが存在します。 チームは、他のサービスがサービス全体にアクセスできなくなることを心配することなく、個々のサービス インスタンスを柔軟に管理できます。
確かに、マイクロサービスは、サービスの技術的観点でも、他のチームとの関係の組織的観点でも、複雑さを増大させます。 マイクロサービス アーキテクチャの利点を享受するには、モノリス向けに設計されたプラクティスを批判的に再検討し、まったく異なる環境に適用した場合にも同じ利点が得られることを確認することが重要です。 このブログでは、12 要素アプリの要素 3 がマイクロサービス コンテキストでも依然として価値を提供しながら、具体的な適用方法を少し変更することでメリットが得られることを説明しました。
マイクロサービス アーキテクチャへの Twelve-Factor App の適用について詳しくは、「Microservices March 2023」(ブログで近日公開) のユニット 1 をご覧ください。 無料で登録する このトピックに関するウェビナーとハンズオンラボにアクセスします。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"