じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

Infrastructure as Code, 2nd Edition のIV. Designing Infrastructure 読書感想文

はじめに

前回の続きで第四部のIV. Designing Infrastructure (インフラストラクチャの設計)という部の読書感想文になります。

前回の記事 syu-m-5151.hatenablog.com

次回の記事 * Infrastructure as Code, 2nd Edition のV. Delivering Infrastructure 読書感想文 - じゃあ、おうちで学べる

書籍のリンク

第四部 目次

IV. Designing Infrastructure (インフラストラクチャの設計)
15. Core Practice: Small, Simple Pieces (コアプラクティス:小さく、単純な部品)
    - 小さく単純な部品を使用してインフラストラクチャを設計する方法に焦点を当てます。
16. Building Stacks From Components (コンポーネントからスタックを構築する)
    - 個々のコンポーネントから効果的なスタックを構築するアプローチを提供します。
17. Using Stacks As Components (スタックをコンポーネントとして使用する)
    - スタックをコンポーネントとして活用するための戦略について説明します。

IV. Designing Infrastructure (インフラストラクチャの設計)

15. Core Practice: Small, Simple Pieces (コアプラクティス:小さく、単純な部品)

Designing for Modularity

モジュラリティの設計は、システムの変更を安全かつ容易にすることを目的としています。これは、ソフトウェア開発経験においても非常に重要です。モジュール式の設計は変更管理を簡素化し、技術的負債の蓄積を防ぐ効果があります。インフラコードにおいても、このアプローチは同様に有効であり、システムの成長に伴う複雑さとリスクを管理するための鍵となります。コンポーネントをより小さく、単純に保つことで、システム全体の品質と反応性が向上します。

Characteristics of Well-Designed Components

良く設計されたコンポーネントは、低い結合度と高い凝集度を持ちます。これは、各コンポーネントが独立して機能し、他の部分に影響を及ぼすことなく変更可能であることを意味します。これらの特徴はシステムの長期的な安定性とメンテナンスの容易さに大きく貢献します。低結合度は、一部の変更が全体のシステムに広範な影響を与えるリスクを最小限に抑え、高凝集度は、コンポーネントがその機能に集中し、より効率的に動作することを可能にします。

Rules for Designing Components

コンポーネントの設計におけるルールには、重複の排除や単一責任原則などが含まれます。これらはコードの可読性と保守性を高め、変更を容易にします。重複の排除は、同じ機能やデータの複数のコピーを避けることで、変更時の労力を減らし、エラーの可能性を下げます。一方、単一責任原則は、各コンポーネントが一つの機能または責任を持つべきであるという原則です。これにより、システムはより整理され、理解しやすくなります。

Use Testing to Drive Design Decisions

テスト駆動設計は、インフラコードの品質を向上させます。テストは、コードの継続的な改善を促進し、設計の効率化に寄与します。テスト可能なコードは、自然とより良い設計に導かれます。テストを重視することで、コードの変更が容易になり、新しい機能の追加や既存機能の改善がスムーズに行えるようになります。また、自動化されたテストは、システムの信頼性を確保し、デプロイメントプロセスを加速します。

Modularizing Infrastructure

インフラのモジュラー化は、システムの柔軟性とスケールアップを促進します。構成要素を効果的に分割することで、変更が容易になり、システムの拡張がスムーズに行えます。このアプローチは、インフラストラクチャの管理と運用においても有効であり、特に大規模なシステムでは、異なる部分を個別に更新、拡張、または縮小できる柔軟性が重要です。モジュール化されたインフラストラクチャは、変更のスピードを高め、システムの全体的な効率を改善します。

Stack Components Versus Stacks as Components

スタックコンポーネントとしてのスタックは、独立性を提供し、変更を容易にします。分離されたスタックは変更管理とスケーラビリティにおいて重要な役割を果たします。スタックとしてのコンポーネントは、システムの一部として独立してデプロイおよび管理することができ、これにより大規模な変更や障害が他の部分に波及するリスクを最小限に抑えます。

Figure 15-1. Shared code module used by two stacks より引用

Using a Server in a Stack

スタック内でサーバーを使用することは、設定変更の容易さを提供します。これにより、運用上の柔軟性が高まります。サーバーをスタックの一部として扱うことで、サーバーの設定やソフトウェアの更新が簡単になり、システム全体のメンテナンスが容易になります。また、サーバーの迅速な追加や削除が可能となり、システムのスケーラビリティが向上します。

Drawing Boundaries Between Components

コンポーネント間の境界を適切に設定することは、システムの成長と変更を管理する上で重要です。これはシステムの安定性と拡張性を支えます。境界線を引くことで、システムの異なる部分を明確に区分し、それぞれが独立して機能し、互いに干渉しないようにします。これにより、システムの一部を変更しても、他の部分に予期しない影響を与えるリスクが減少します。

Align Boundaries with Natural Change Patterns

変更パターンに合わせた境界線は、システムの自然な進化を促進します。これにより、継続的な改善が可能になります。システムの異なる部分がどのように変化し、成長するかを理解することで、それらの部分を適切に区分することができます。これは、変更の管理を容易にし、システム全体の効率を高めます。

Align Boundaries with Component Life Cycles

コンポーネントのライフサイクルに合わせた境界線は、管理の簡素化をもたらします。特定のコンポーネントの更新や交換が容易になります。例えば、頻繁に更新が必要なコンポーネントと、長期間安定して運用されるコンポーネントを区別することで、各コンポーネントをより効果的に管理することが可能になります。

Align Boundaries with Organizational Structures

組織構造に合わせた境界線の設定は、チーム間のコラボレーションを促進し、システムの全体的な一貫性を向上させます。Conwayの法則によれば、システムの設計はしばしばその開発を行う組織の構造を反映します。例えば、開発と運用が別々のチームによって行われる場合、それぞれのチームはシステムの異なる部分を管理することになり、結果としてシステム全体が分断されがちです。これを避けるためには、チームの組織構造をシステムのアーキテクチャに合わせて調整することが有効です。これにより、各チームは自分たちの責任範囲内で効率的に作業を進めることができ、全体としてのシステムの一貫性と効率が向上します。

Create Boundaries That Support Resilience

回復力を支持する境界線の設定は、システムの耐障害性と回復力を強化します。これは、特定のコンポーネントやサービスが障害に遭遇した場合に、システム全体が影響を受けるリスクを最小限に抑えることを意味します。例えば、システムの一部が故障した場合に、他の部分が正常に機能し続けるように設計することです。これにより、障害発生時にもシステムの主要な機能が維持され、迅速な回復が可能になります。また、このような設計は、障害発生時の影響範囲(ブラストラジアス)を小さくすることも目的としています。

Create Boundaries That Support Scaling

スケーリングを支持する境界線の設定は、システムの拡張性を高めることを目指します。これにより、需要の増大や減少に応じてシステムのリソースを柔軟に調整することが可能になります。例えば、特定のサービスやコンポーネントの利用が増加した場合に、追加のリソースを割り当てることで対応することができます。また、リソースの利用が減少した場合には、不要なリソースを削減してコストを節約することも可能です。このように、スケーリングを支持する境界線を設定することで、システムは変動する需要に柔軟に対応し、最適なパフォーマンスを維持することができます。

Align Boundaries to Security and Governance Concerns

セキュリティとガバナンスの懸念に合わせて境界線を設定することは、システムのセキュリティを強化し、規制遵守を容易にします。これは、異なるセキュリティ要件を持つシステムの部分に対して適切な保護措置を施すことを意味します。例えば、金融情報や個人データを扱う部分には、より厳格なセキュリティ対策が必要です。セキュリティとガバナンスに基づいて境界線を設定することにより、これらの要件を満たすための管理が容易になり、システム全体のセキュリティが向上します。

この章は、インフラストラクチャをコードとして定義する際の、より小さな部分への分割の重要性を強調しています。分割されたコンポーネントは、変更、スケーリング、回復力の向上に寄与し、システム全体の運用効率を高めます。また、組織構造、セキュリティ、ガバナンスの観点から適切に境界線を設定することで、システムはより安全で管理しやすい状態になります。

16. Building Stacks From Components (コンポーネントからスタックを構築する)

Infrastructure Languages for Stack Components

インフラストラクチャ言語の選択は、スタックコンポーネントの設計と実装において非常に重要です。宣言型言語は、その明確な構造と予測可能性により、特に大規模なシステムの設計において有効です。一方、命令型言語は、より動的で柔軟なシステムの構築に適しています。個人的な感覚では宣言型言語はインフラストラクチャの基本的な構造を定義するのに適しており、命令型言語はより複雑なロジックや条件分岐が必要な場面で役立ちます。

Reuse Declarative Code with Modules

宣言型コードのモジュール化による再利用は、システムの整合性を高め、変更の管理を容易にします。私は、モジュールを利用して共通の機能を効率的に管理し、コードベースの複雑さを減らすことができると感じています。宣言型言語で書かれたモジュールは、その明確さと一貫性により、特に大規模なプロジェクトや多くの開発者が関与する環境において有効です。

Dynamically Create Stack Elements with Libraries

ライブラリを利用した動的なスタック要素の作成は、システムの設計における柔軟性を大幅に向上させます。命令型言語を用いることで、条件に応じたリソースの動的な生成や複雑なロジックの実装が可能になり、システムのカスタマイズが容易になります。これは、特に要件が頻繁に変更されるプロジェクトや、特定の条件に基づいて異なる動作をさせる必要があるシステムにおいて有用です。

Patterns for Stack Components

スタックコンポーネントを設計する際には、適切なパターンの選択が重要です。これにより、システムの一貫性、再利用性、そして将来の拡張性が向上します。良い設計パターンを採用することで、システム全体の品質を高めることができます。

Pattern: Facade Module

ファサードモジュールは、複雑なリソースをよりシンプルに扱えるようにすることで、開発者の負担を軽減します。これは、複数のプロジェクトやチーム間で共通のリソースや設定を共有する際に特に有効で、一貫性のあるアプローチを提供します。ファサードモジュールを使用することで、開発者はより高度なタスクに集中でき、基盤となる複雑な詳細について心配する必要がなくなります。

Antipattern: Obfuscation Module

オブフスケーションモジュールは、実際には価値を追加せず、むしろシステムの複雑さを増加させるものです。このようなモジュールは、コードの可読性を低下させ、保守や拡張を困難にします。開発者がモジュールの背後にあるロジックを理解するのが難しくなり、結果として効率性が損なわれます。

Antipattern: Unshared Module

共有されていないモジュールは、その再利用性が低く、開発プロセスにおける効率性に欠けます。モジュール化の主な目的は、コードの再利用を促進することにありますが、この目的が達成されていない場合、モジュールの価値は大幅に低下します。このようなモジュールは、システム全体の一貫性を損なう可能性があります。

Pattern: Bundle Module

バンドルモジュールは、関連する複数のリソースを単一のインターフェースで管理することを可能にします。これにより、システムの一貫性と管理の容易さが向上します。特に、異なるリソースが密接に連携して動作する必要がある場合に有効で、開発者はより高度なタスクに集中できるようになります。

Antipattern: Spaghetti Module

スパゲッティモジュールは、パラメータに応じて大きく異なる結果を生み出すような複雑な設定が特徴です。これらのモジュールは、多くの動的な部分を含むため、実装が雑然として理解しにくくなりがちです。このようなモジュールはメンテナンスが困難で、変更を加える際には他の部分に予期せぬ影響を与えやすいことが分かっています。重要なのは、モジュールが単一の明確な目的を持ち、必要な機能だけを提供することです。複雑さを避けるためには、モジュールをより小さく、シンプルに保つことが重要です。

Pattern: Infrastructure Domain Entity

インフラストラクチャのドメインエンティティは、複数の低レベルのリソースを組み合わせて、より高度なスタックコンポーネントを実装するパターンです。このパターンは、特定のアプリケーションやサービスに必要なインフラストラクチャの全体像を捉え、その要件に基づいてリソースを動的に構築します。このアプローチは特に大規模で複雑な環境において効果的で、異なる要件に応じて柔軟にインフラストラクチャを構築できるようにします。しかし、これを実装するには、インフラストラクチャ自体をドメインとして捉え、その上で適切な設計を行う必要があります。

Building an Abstraction Layer

抽象化レイヤーを構築することで、より低レベルのリソースへの直接的なアクセスを抽象化し、より高レベルのタスクに集中できるようにします。これは、特に複数のチームが関わる大規模なプロジェクトにおいて有用です。抽象化レイヤーを使用することで、開発者はインフラストラクチャの詳細を気にせずに、アプリケーションの開発やビジネスロジックに集中できます。しかし、抽象化には適度なレベルが必要であり、過度な抽象化はシステムの理解を難しくし、問題の診断や解決を複雑化することもあります。

第16章では、コンポーネントからスタックを構築する方法とその利点について説明されていますが、同時に、抽象化のレイヤーやコンポーネントのライブラリがもたらす複雑さに注意する必要があるとも指摘しています。システムの規模や複雑さに応じて、これらの構造を適切に使用することが重要です。適切な抽象化レベルの選択は、システムの効率をあげることにつながります。

17. Using Stacks As Components (スタックをコンポーネントとして使用する)

17. Using Stacks As Components

Discovering Dependencies Across Stacks

スタック間の依存関係の発見は、インフラストラクチャの複雑な環境において、異なるスタック間の統合を容易にするために重要です。依存関係を発見する方法を選ぶ際には、システムの拡張性、メンテナンスの容易さ、そして再利用性のバランスを考慮することが必要です。スタック間の依存関係を効果的に管理することは、システム全体の効率を向上させることに繋がります。

Pattern: Resource Matching

リソースマッチングパターンは、名前、タグ、または他の識別特性を使用して、必要なリソースを発見する方法です。このパターンは、特に大規模なプロジェクトや、異なるチームや環境間での統合において有効です。実際、私が過去に関わったプロジェクトでは、リソースマッチングを使用することで、複数の環境やチーム間でのリソースの共有が容易になりました。

Figure 17-1. Resource matching for discovering dependencies より引用

Pattern: Stack Data Lookup

スタックデータルックアップパターンは、提供側スタックが管理するデータ構造に基づいて、必要なリソースを見つける方法です。このアプローチは、全てのインフラストラクチャが同じツールを使用して管理されている場合に特に効果的です。スタックデータルックアップは、依存関係を明確にし、統合を容易にするために役立ちます。

Pattern: Integration Registry Lookup

統合レジストリルックアップパターンは、両方のスタックが一つのレジストリを使用して値を保存し、それを読み取る方法です。これは、異なるツールを使用している複数のチーム間の統合に非常に適しています。私自身も、異なる技術スタックを持つチーム間での統合にこのパターンを利用したことがあり、その柔軟性と効率性に非常に満足しています。

Dependency Injection

依存性の注入は、スタック定義から依存性の発見を分離することで、スタックの再利用性と柔軟性を向上させるテクニックです。このアプローチにより、異なるプロバイダー実装を容易に切り替えることが可能になり、より包括的に統合されたシステムのテストが容易になります。依存性の注入を使用することで、スタックをよりモジュラー化し、システムの各部分を独立して開発し、テストすることが可能になります。

スタックをコンポーネントとして使用することは、システムの変更を容易にし、品質を向上させる効果的な方法です。このアプローチの成功は、スタックを適切に設計し、サイズを適切に保ち、スタック間の緩い結合を維持することに依存しています。スタックをコンポーネントとしてうまく利用することで、システム全体の可用性と拡張性が大幅に向上し、チームの生産性が向上しました。

まとめ

インフラストラクチャをコードとして扱う際のベストプラクティス、効果的な設計パターン、および一般的なアンチパターンに焦点を当てています。この部分は、インフラストラクチャのモジュラリティの重要性を強調し、スタックのデザインパターンアンチパターンを紹介します。依存関係の管理に関する方法論や依存性の注入の利点も説明されており、全体として、インフラストラクチャを効果的に設計し、管理するための重要な原則と方法論を提供しています。これらのガイドラインは、インフラストラクチャをコードとして扱う際に直面する一般的な課題に対する解決策を提示し、システムの効率性、拡張性、および信頼性を高めるための具体的な指針を提供しています。

Infrastructure as Code, 2nd Editionの読書感想文