- Preface
- Why I Wrote This Book
- What’s New and Different in This Edition
- What’s Next
- What This Book Is and Isn’t
- Some History of Infrastructure as Code
- Who This Book Is For
- Principles, Practices, and Patterns
- The ShopSpinner Examples
- Conventions Used in This Book
- O’Reilly Online Learning
- How to Contact Us
- Acknowledgments
- I. Foundations
- 1. What Is Infrastructure as Code?
- From the Iron Age to the Cloud Age
- Infrastructure as Code
- Benefits of Infrastructure as Code
- Use Infrastructure as Code to Optimize for Change
- Objection: “We don’t make changes often enough to justify automating them”
- Objection: “We should build first and automate later”
- Objection: “We must choose between speed and quality”
- The Four Key Metrics
- Three Core Practices for Infrastructure as Code
- Core Practice: Define Everything as Code
- Core Practice: Continuously Test and Deliver All Work in Progress
- Core Practice: Build Small, Simple Pieces That You Can Change Independently
- Conclusion
- 2. Principles of Cloud Age Infrastructure
- Principle: Assume Systems Are Unreliable
- Principle: Make Everything Reproducible
- Pitfall: Snowflake Systems
- Principle: Create Disposable Things
- Principle: Minimize Variation
- Configuration Drift
- Principle: Ensure That You Can Repeat Any Process
- Conclusion
- 3. Infrastructure Platforms
- The Parts of an Infrastructure System
- Infrastructure Platforms
- Infrastructure Resources
- Compute Resources
- Storage Resources
- Network Resources
- Conclusion
- 4. Core Practice: Define Everything as Code
- Why You Should Define Your Infrastructure as Code
- What You Can Define as Code
- Choose Tools with Externalized Configuration
- Manage Your Code in a Version Control System
- Infrastructure Coding Languages
- Infrastructure Scripting
- Declarative Infrastructure Languages
- Programmable, Imperative Infrastructure Languages
- Declarative Versus Imperative Languages for Infrastructure
- Domain-Specific Infrastructure Languages
- General-Purpose Languages Versus DSLs for Infrastructure
- Implementation Principles for Defining Infrastructure
as Code
- Separate Declarative and Imperative Code
- Treat Infrastructure Code Like Real Code
- Conclusion
- II. Working with Infrastructure Stacks
- 5. Building Infrastructure Stacks as Code
- What Is an Infrastructure Stack?
- Stack Code
- Stack Instance
- Configuring Servers in a Stack
- Low-Level Infrastructure Languages
- High-Level Infrastructure Languages
- Patterns and Antipatterns for Structuring Stacks
- Antipattern: Monolithic Stack
- Pattern: Application Group Stack
- Pattern: Service Stack
- Pattern: Micro Stack
- Conclusion
- 6. Building Environments with Stacks
- What Environments Are All About
- Delivery Environments
- Multiple Production Environments
- Environments, Consistency, and Configuration
- Patterns for Building Environments
- Antipattern: Multiple-Environment Stack
- Antipattern: Copy-Paste Environments
- Pattern: Reusable Stack
- Building Environments with Multiple Stacks
- Conclusion
- 7. Configuring Stack Instances
- Using Stack Parameters to Create Unique Identifiers
- Example Stack Parameters
- Patterns for Configuring Stacks
- Antipattern: Manual Stack Parameters
- Pattern: Stack Environment Variables
- Pattern: Scripted Parameters
- Pattern: Stack Configuration Files
- Pattern: Wrapper Stack
- Pattern: Pipeline Stack Parameters
- Pattern: Stack Parameter Registry
- Configuration Registry
- Implementing a Configuration Registry
- Single or Multiple Configuration Registries
- Handling Secrets as Parameters
- Encrypting Secrets
- Secretless Authorization
- Injecting Secrets at Runtime
- Disposable Secrets
- Conclusion
- 8. Core Practice:
Continuously Test and Deliver
- Why Continuously Test Infrastructure Code?
- What Continuous Testing Means
- What Should We Test with Infrastructure?
- Challenges with Testing Infrastructure Code
- Challenge: Tests for Declarative Code Often Have Low Value
- Challenge: Testing Infrastructure Code Is Slow
- Challenge: Dependencies Complicate Testing Infrastructure
- Progressive Testing
- Test Pyramid
- Swiss Cheese Testing Model
- Infrastructure Delivery Pipelines
- Pipeline Stages
- Scope of Components Tested in a Stage
- Scope of Dependencies Used for a Stage
- Platform Elements Needed for a Stage
- Delivery Pipeline Software and Services
- Testing in Production
- What You Can’t Replicate Outside Production
- Managing the Risks of Testing in Production
- Conclusion
- 9. Testing Infrastructure Stacks
- Example Infrastructure
- The Example Stack
- Pipeline for the Example Stack
- Offline Testing Stages for Stacks
- Syntax Checking
- Offline Static Code Analysis
- Static Code Analysis with API
- Testing with a Mock API
- Online Testing Stages for Stacks
- Preview: Seeing What Changes Will Be Made
- Verification: Making Assertions About Infrastructure Resources
- Outcomes: Proving Infrastructure Works Correctly
- Using Test Fixtures to Handle Dependencies
- Test Doubles for Upstream Dependencies
- Test Fixtures for Downstream Dependencies
- Refactor Components So They Can Be Isolated
- Life Cycle Patterns for Test Instances of Stacks
- Pattern: Persistent Test Stack
- Pattern: Ephemeral Test Stack
- Antipattern: Dual Persistent and Ephemeral Stack Stages
- Pattern: Periodic Stack Rebuild
- Pattern: Continuous Stack Reset
- Test Orchestration
- Support Local Testing
- Avoid Tight Coupling with Pipeline Tools
- Test Orchestration Tools
- Conclusion
- III. Working with Servers and Other Application Runtime Platforms
- 10. Application Runtimes
- Cloud Native and Application-Driven Infrastructure
- Application Runtime Targets
- Deployable Parts of an Application
- Deployment Packages
- Deploying Applications to Servers
- Packaging Applications in Containers
- Deploying Applications to Server Clusters
- Deploying Applications to Application Clusters
- Packages for Deploying Applications to Clusters
- Deploying FaaS Serverless Applications
- Application Data
- Data Schemas and Structures
- Cloud Native Application Storage Infrastructure
- Application Connectivity
- Service Discovery
- Conclusion
- 11. Building Servers as Code
- What’s on a Server
- Where Things Come From
- Server Configuration Code
- Server Configuration Code Modules
- Designing Server Configuration Code Modules
- Versioning and Promoting Server Code
- Server Roles
- Testing Server Code
- Progressively Testing Server Code
- What to Test with Server Code
- How to Test Server Code
- Creating a New Server Instance
- Hand-Building a New Server Instance
- Using a Script to Create a Server
- Using a Stack Management Tool to Create a Server
- Configuring the Platform to Automatically Create Servers
- Using a Networked Provisioning Tool to Build a Server
- Prebuilding Servers
- Hot-Cloning a Server
- Using a Server Snapshot
- Creating a Clean Server Image
- Configuring a New Server Instance
- Frying a Server Instance
- Baking Server Images
- Combining Baking and Frying
- Applying Server Configuration When Creating a Server
- Conclusion
- 12. Managing Changes to Servers
- Change Management Patterns: When to Apply Changes
- Antipattern: Apply On Change
- Pattern: Continuous Configuration Synchronization
- Pattern: Immutable Server
- How to Apply Server Configuration Code
- Pattern: Push Server Configuration
- Pattern: Pull Server Configuration
- Other Server Life Cycle Events
- Stopping and Restarting a Server Instance
- Replacing a Server Instance
- Recovering a Failed Server
- Conclusion
- 13. Server Images as Code
- Building a Server Image
- Why Build a Server Image?
- How to Build a Server Image
- Tools for Building Server Images
- Online Image Building Process
- Offline Image Building Process
- Origin Content for a Server Image
- Building from a Stock Server Image
- Building a Server Image from Scratch
- Provenance of a Server Image and its Content
- Changing a Server Image
- Reheating or Baking a Fresh Image
- Versioning a Server Image
- Updating Server Instances When an Image Changes
- Providing and Using a Server Image Across Teams
- Handling Major Changes to an Image
- Using a Pipeline to Test and Deliver a Server Image
- Build Stage for a Server Image
- Test Stage for a Server Image
- Delivery Stages for a Server Image
- Using Multiple Server Images
- Server Images for Different Infrastructure Platforms
- Server Images for Different Operating Systems
- Server Images for Different Hardware Architectures
- Server Images for Different Roles
- Layering Server Images
- Sharing Code Across Server Images
- Conclusion
- 14. Building Clusters as Code
- Application Cluster Solutions
- Cluster as a Service
- Packaged Cluster Distribution
- Stack Topologies for Application Clusters
- Monolithic Stack Using Cluster as a Service
- Monolithic Stack for a Packaged Cluster Solution
- Pipeline for a Monolithic Application Cluster Stack
- Example of Multiple Stacks for a Cluster
- Sharing Strategies for Application Clusters
- One Big Cluster for Everything
- Separate Clusters for Delivery Stages
- Clusters for Governance
- Clusters for Teams
- Service Mesh
- Infrastructure for FaaS Serverless
- Conclusion
- 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
- 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
- Create Boundaries That Support Resilience
- Create Boundaries That Support Scaling
- Align Boundaries to Security and Governance Concerns
- Conclusion
- 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
- Conclusion
- 17. Using Stacks as Components
- Discovering Dependencies Across Stacks
- Pattern: Resource Matching
- Pattern: Stack Data Lookup
- Pattern: Integration Registry Lookup
- Dependency Injection
- Conclusion
- V. Delivering Infrastructure
- 18. Organizing Infrastructure Code
- Organizing Projects and Repositories
- One Repository, or Many?
- One Repository for Everything
- A Separate Repository for Each Project (Microrepo)
- Multiple Repositories with Multiple Projects
- Organizing Different Types of Code
- Project Support Files
- Cross-Project Tests
- Dedicated Integration Test Projects
- Organize Code by Domain Concept
- Organizing Configuration Value Files
- Managing Infrastructure and Application Code
- Delivering Infrastructure and Applications
- Testing Applications with Infrastructure
- Testing Infrastructure Before Integrating
- Using Infrastructure Code to Deploy Applications
- Conclusion
- 19. Delivering Infrastructure Code
- Delivering Infrastructure Code
- Building an Infrastructure Project
- Packaging Infrastructure Code as an Artifact
- Using a Repository to Deliver Infrastructure Code
- Integrating Projects
- Pattern: Build-Time Project Integration
- Pattern: Delivery-Time Project Integration
- Pattern: Apply-Time Project Integration
- Using Scripts to Wrap Infrastructure Tools
- Assembling Configuration Values
- Simplifying Wrapper Scripts
- Conclusion
- 20. Team Workflows
- The People
- Who Writes Infrastructure Code?
- Applying Code to Infrastructure
- Applying Code from Your Local Workstation
- Applying Code from a Centralized Service
- Personal Infrastructure Instances
- Source Code Branches in Workflows
- Preventing Configuration Drift
- Minimize Automation Lag
- Avoid Ad Hoc Apply
- Apply Code Continuously
- Immutable Infrastructure
- Governance in a Pipeline-based Workflow
- Reshuffling Responsibilities
- Shift Left
- An Example Process for Infrastructure as Code with Governance
- Conclusion
- 21. Safely Changing Infrastructure
- Reduce the Scope of Change
- Small Changes
- Example of Refactoring
- Pushing Incomplete Changes to Production
- Parallel Instances
- Backward Compatible Transformations
- Feature Toggles
- Changing Live Infrastructure
- Infrastructure Surgery
- Expand and Contract
- Zero Downtime Changes
- Continuity
- Continuity by Preventing Errors
- Continuity by Fast Recovery
- Continuous Disaster Recovery
- Chaos Engineering
- Planning for Failure
- Data Continuity in a Changing System
- Lock
- Segregate
- Replicate
- Reload
- Mixing Data Continuity Approaches
- Conclusion
- Index