マルチテナント機能と同時開発について understanding-multitenancy-and-concurrent-development
はじめに introduction
複数のチームが同じ AEM 環境にコードをデプロイする場合、チームが他のチームの邪魔をすることなく、できるだけ独立して作業できるようにするために従うべきプラクティスがあります。 これらのテクニックは、チーム間の依存関係を完全には解消できないものの、最小限に抑えます。 同時開発モデルを成功させるには、開発チーム間の良好なコミュニケーションが不可欠です。
さらに、複数の開発チームが同じ AEM 環境で作業する場合は、ある程度のマルチテナント機能が効果を発揮する可能性が高くなります。 AEM 環境で複数のテナントをサポートしようとする際に実際に役立つ考慮事項、特にガバナンス、運用および開発を管理する際に直面する課題に関して、多くのことを説明してきました。 ここでは、マルチテナント環境での AEM の実装に関する技術的な課題をいくつか紹介しますが、これらのレコメンデーションの多くは複数の開発チームを持つあらゆる組織に当てはまります。
AEM では、1 つの環境に複数のサイトや複数のブランドをデプロイできるとは言え、真のマルチテナント機能が提供されないという点に前もって留意しておくことが重要です。 一部の環境設定およびシステムリソースは、環境にデプロイされているすべてのサイトで常に共有されます。 ここでは、これらの共有リソースの影響を最小限に抑えるためのガイダンスと、これらの分野でのコミュニケーションと共同作業を効率化するための提案を示しています。
メリットと課題 benefits-and-challenges
マルチテナント環境の実装には、多くの課題があります。
次のものが含まれます。
- 高まる技術的な複雑さ
- 開発のオーバーヘッドの増加
- 共有リソースへの組織横断的な依存関係
- 運用の複雑さの増大
課題はあるものの、マルチテナントアプリケーションの実行には次のようなメリットがあります。
- ハードウェアコストの削減
- 将来のサイトの市場投入までの時間の短縮
- 将来のテナントの実装コストの削減
- ビジネス全体でのアーキテクチャと開発プラクティスの標準化
- 共通のコードベース
ビジネスで真のマルチテナント機能が必要であるにもかかわらず、他のテナントの知識がなく、共有コード、コンテンツまたは共通のオーサーがない場合は、個別のオーサーインスタンスが実行可能な唯一の選択肢となります。 開発作業の全体的な増加をインフラストラクチャコストおよびライセンスコストの削減と比較して、アプローチが最適な方法かどうかを判断する必要があります。
開発手法 development-techniques
依存関係の管理 managing-dependencies
Maven プロジェクトの依存関係を管理する際は、すべてのチームがサーバー上の特定の OSGi バンドルの同じバージョンを使用することが重要です。 次の例では、Maven プロジェクトの管理が誤っている場合にどうなるかを示します。
プロジェクト A は、ライブラリ foo のバージョン 1.0 に基づいています。foo バージョン 1.0 は、サーバーへのデプロイメントに組み込まれています。 プロジェクト B は、ライブラリ foo のバージョン 1.1 に基づいています。foo version 1.1 は、デプロイメントに組み込まれています。
さらに、このライブラリで API がバージョン 1.0 と 1.1 の間で変更されたとすると、これら 2 つのプロジェクトのどちらかが正しく動作しなくなります。
この問題に対処するために、すべての Maven プロジェクトを 1 つの親リアクタープロジェクトの子にすることをお勧めします。 このリアクタープロジェクトは、必要に応じてすべてのプロジェクトの作成とデプロイを一緒に行えるようにすることと、すべての子プロジェクトの依存関係宣言を含んでいること、という 2 つの目的を果たします。親プロジェクトは依存関係とそのバージョンを定義するのに対して、子プロジェクトは必要な依存関係のみを宣言し、親プロジェクトからバージョンを継承します。
このシナリオでは、プロジェクト B で作業しているチームが foo のバージョン 1.1 の機能を必要とする場合、この変更によってプロジェクト A が機能しなくなることが開発環境ですぐに明らかになります。この時点で、チームはこの変更について話し合い、プロジェクト A と新しいバージョンに互換性を持たせるか、プロジェクト B の代替ソリューションを探すことができます。
なお、これにより、これらのチームでこの依存関係を共有する必要がなくなるわけではありません。問題を迅速かつ早期に特定されることで、チームがリスクについて話し合い、解決策について合意できるようになるだけです。
コードの重複の防止 preventing-code-duplication-nbsp-br
複数のプロジェクトで作業する場合は、コードが重複しないようにすることが重要です。 コードの重複により、欠陥の発生の可能性が高くなり、システムの変更に伴うコストが増え、コードベースの全体的な柔軟性が低くなります。重複を防ぐためには、共通のロジックを、複数のプロジェクトで再利用可能なライブラリにリファクタリングします。
このニーズに対応するために、すべてのチームが基盤とし寄与することができるコアプロジェクトの開発と維持管理をお勧めします。 その場合は、このコアプロジェクトが個々のチームのプロジェクトに依存しないようにすることが重要です。これにより、コードの再利用を促進しながら、独立したデプロイメントが可能になります。
コアモジュールで一般に使用されるコードの例をいくつか以下に示します。
-
システム全体の設定(例えば以下のもの)
- OSGi 設定
- サーブレットフィルター
- ResourceResolver マッピング
- Sling Transformer パイプライン
- エラーハンドラー(または ACS AEM Commons Error Page Handler1 を使用)
- 権限対応キャッシュの認証サーブレット
-
ユーティリティクラス
-
コアビジネスロジック
-
サードパーティ統合ロジック
-
オーサリング UI オーバーレイ
-
カスタムウィジェットなど、オーサリングに必要なその他のカスタマイズ
-
ワークフローランチャー
-
サイト間で共通に使用されるデザイン要素
モジュール型プロジェクトアーキテクチャ
これにより、複数のチームが同じコードセットをベースとし、場合によって同じコードセットを更新する必要がなくなるわけではありません。 コアプロジェクトを作成することで、チーム間で共有するコードベースのサイズを小さくし、共有リソースの必要性を減らすことはできましたが、なくすことはできませんでした。
このコアパッケージに加えた変更がシステムの機能を妨げないように、シニアデベロッパーまたはデベロッパーチームが管理を維持することをお勧めします。 一つの選択肢は、このパッケージに対するすべての変更を管理する 1 つのチームを持つことです。もう一つは、チームがプルリクエストを提出し、それをこれらのリソースがレビューしマージすることです。ガバナンスモデルは、チームが設計し合意したうえで、デベロッパーがそれに従うことが重要です。
デプロイメント範囲の管理 managing-deployment-scope
異なるチームが同じリポジトリにコードをデプロイする場合、互いの変更点を上書きしないことが重要です。AEM には、コンテンツパッケージのデプロイ時にこれを制御するためのメカニズムがあります。つまり、filter.xml ファイルです。重要なのは、filter.xml ファイル間に重複がないことがです。重複がある場合、あるチームのデプロイメントによって、別のチームの以前のデプロイメントが消去される可能性があります。 この点を説明するために、適切に作成されたフィルターファイルと問題のあるフィルターファイルを対比した例を以下に示します。
/apps/my-company と /apps/my-company/my-site
/etc/clientlibs/my-company と /etc/clientlibs/my-company/my-site
/etc/designs/my-company と /etc/designs/my-company/my-site
各チームが作業中のサイトまでフィルターファイルを明示的に設定すれば、各チームは、互いの変更を消去することなく、コンポーネント、クライアントライブラリおよびサイトデザインを独立してデプロイすることができます。
これはグローバルなシステムパスであり、あるサイトに固有のものではないので、以下のサーブレットはコアプロジェクトに含まれるべきです。ここでの変更はどのチームにも影響を与える可能性があるからです。
/apps/sling/servlet/errorhandler
オーバーレイ overlays
オーバーレイは、AEM の標準機能を拡張または置き換えるためによく使用されますが、オーバーレイの使用は AEM アプリケーション全体に影響します(つまり、オーバーレイされた機能の変更はすべてのテナントで利用可能になります)。 オーバーレイの要件がテナント間で異なる場合、状況はさらに複雑になります。 ビジネスグループが連携して、AEM 管理コンソールの機能と外観について合意するのが理想です。
様々なビジネスユニットの間で合意が得られない場合、考えられる解決策は、オーバーレイを使用しないことです。代わりに、機能のカスタムコピーを作成し、テナントごとに異なるパスを使用して公開します。 これにより、テナントごとに完全に異なるユーザーエクスペリエンスにすることができますが、このアプローチでは、実装やその後のアップグレード作業のコストが増加します。
ワークフローランチャー workflow-launchers
AEM では、指定された変更がリポジトリで行われた場合、ワークフローランチャーを使用して、ワークフローの実行を自動的にトリガーします。 AEM には、例えば、新規アセットおよび更新されたアセットに対してレンディション生成やメタデータ抽出の処理を実行するためのランチャーなど、標準のランチャーがいくつか用意されています。マルチテナント環境では、これらのランチャーをそのままの状態にしておくことは可能ですが、テナントごとにランチャーやワークフローモデルの要件が異なる場合、テナントごとに個別のランチャーを作成し管理する必要が生じる可能性が高くなります。 これらのランチャーは、他のテナントのコンテンツはそのままにして、自分のテナントの更新を実行するように設定する必要があります。これを簡単に実現するには、テナントごとに指定されたリポジトリパスにランチャーを適用することです。
バニティ URL vanity-urls
AEM には、ページ単位で設定できるバニティ URL 機能があります。マルチテナントシナリオにおけるこのアプローチの懸念事項は、この方法で設定されたバニティ URL 間の一意性が AEM で確保されないことです。2 人の異なるユーザーが異なるページに同じバニティパスを設定した場合、予期しない動作が発生する可能性があります。このため、Apache Dispatcher インスタンスでは mod_rewrite ルールを使用することをお勧めします。このルールは、アウトバウンド専用のリソースリゾルバールールと連携して設定の中心点となることができます。
コンポーネントグループ component-groups
複数のオーサリンググループ用のコンポーネントやテンプレートを開発する場合は、componentGroup プロパティと allowedPaths プロパティを有効活用することが重要です。 これらをサイトデザインと共に効果的に活用することで、ブランド A の作成者には、自分のサイト用に作成されたコンポーネントやテンプレートのみが表示されるのに対して、ブランド B の作成者には自分のサイトのコンポーネントやテンプレートのみが表示されます。
テスト testing
優れたアーキテクチャとオープンなコミュニケーションチャネルは、サイトの予期しない領域に欠陥が生じるのを防ぐのに役立ちますが、これらのアプローチは絶対確実というわけではありません。 このため、何かを実稼動環境にリリースする前に、プラットフォームに何がデプロイされようとしているかを十分にテストしておくことが重要です。 それには、リリースサイクルでチーム間の調整が必要です。また、可能な限り多くの機能に対応する自動テストスイートの必要性も高まります。 さらに、1 つのシステムが複数のチームで共有されるので、パフォーマンス、セキュリティおよび負荷テストがこれまで以上に重要になります。
運用上の考慮事項 operational-considerations
共有リソース shared-resources
AEM は単一の JVM 内で動作します。デプロイされた AEM アプリケーションは、AEM の通常の実行で既に使用されているリソースに加えて、本質的にリソースを互いに共有します。JVM 空間自体の内部ではスレッドは論理的に分離されず、AEM で使用可能な有限リソース(メモリ、CPU、ディスク I/O など)も共有されます。リソースを消費するテナントは、必然的に他のシステムテナントに影響を与えます。
パフォーマンス performance
AEM のベストプラクティスに従わない場合は、正常と見なされる範囲を超えてリソースを消費するアプリケーションを開発することが可能です。この例としては、処理の重い多くのワークフロー操作(DAM アセットの更新など)のトリガー、多数のノードに対する MSM の変更時プッシュ操作の使用、高負荷の JCR クエリを使用したコンテンツのリアルタイムレンダリングなどがあります。これらは、必ず他のテナントアプリケーションのパフォーマンスに影響を与えます。
ログ logging
AEM は、堅牢なロガー設定に対応した標準のインターフェイスを備えており、共有開発シナリオで有効に利用できます。ブランドごとに別個のロガーをパッケージ名で指定することで、ある程度のログ分離を実現できます。レプリケーションや認証などのシステム全体の操作は引き続き一元的に記録されますが、非共有のカスタムコードは個別に記録することができ、各ブランドの技術チームによる監視やデバッグの作業が容易になります。
バックアップと復元 backup-and-restore
JCR リポジトリの性質上、従来のバックアップは、個々のコンテンツパスに対してではなく、リポジトリ全体で機能します。したがって、テナントごとにバックアップを分離することは容易にはできません。 逆に、バックアップから復元すると、システム上のすべてのテナントのコンテンツノードとリポジトリノードがロールバックされます。VLT などのツールを使用してターゲットコンテンツのバックアップを実行したり、別の環境でパッケージを作成することで復元するコンテンツを慎重に選ぶことは可能ですが、これらの
アプローチは、設定やアプリケーションロジックを包括的に扱うことは容易ではなく、管理が煩雑になる場合があります。
セキュリティに関する考慮事項 security-considerations
ACL acls
もちろん、アクセス制御リスト(ACL)を使用して、コンテンツを表示、作成および削除できるユーザーをコンテンツパスに基づいて制御することは可能ですが、ユーザーグループの作成と管理が必要になります。ACL とグループの管理の困難さは、各テナントが他のテナントのことをまったく知らなくてよいことが重視されているかどうかと、デプロイされたアプリケーションが共有リソースに依存しているかどうかに左右されます。ACL、ユーザーおよびグループの効率的な管理を実現するために、必要な管理を行う一元化されたグループを用意して、効率とセキュリティが向上する方法で、これらのアクセス制御とプリンシパルが重複するように(または重複しないように)することをお勧めします。