Preface 1. Introduction to Cloud Native Distributed Systems Fallacies of Distributed Systems CAP Theorem The Twelve-Factor App Availability and Service-Level Agreements Summary 2. Fundamentals Containers Container Isolation Levels Container Orchestration Kubernetes Overview Kubernetes and Containers Serverless Computing Functions From VMs to Cloud Native Lift-and-Shift Application Modernization Application Optimization Microservices Benefits of a Microservices Architecture Challenges with a Microservices Architecture Summary 3. Designing Cloud Native Applications Fundamentals of Cloud Native Applications Operational Excellence Security Reliability and Availability Scalability and Cost Cloud Native versus Traditional Architectures Functions versus Services Function Scenarios Considerations for Using Functions Composite of Functions and Services API Design and Versioning API Backward and Forward Compatibility Semantic Versioning Service Communication Protocols Messaging Protocols Serialization Considerations Idempotency Request/Response Publisher/Subscriber Choosing Between Pub/Sub and Request Response Synchronous versus Asynchronous Gateways Routing Aggregation Offloading Implementing Gateways Egress Service Mesh Example Architecture Summary 4. Working with Data Data Storage Systems Objects, Files, and Disks Databases Streams and Queues Blockchain Selecting a Datastore Data in Multiple Datastores Change Data Capture Write Changes as an Event to a Change Log Transaction Supervisor Compensating Transactions Extract, Transform, and Load Microservices and Data Lakes Client Access to Data Restricted Client Tokens (Valet-Key) Database Services with Fine-Grained Access Control GraphQL Data Service Fast Scalable Data Sharding Data Caching Data Content Delivery Networks Analyzing Data Streams Batch Data Lakes on Object Storage Data Lakes and Data Warehouses Distributed Query Engines Databases on Kubernetes Storage Volumes StatefulSets DaemonSets Summary 5. DevOps What Is DevOps? Collaboration Automation Lean Principles and Processes Measurement Sharing Testing Test Doubles Test Automation Pyramid When to Run Which Types of Tests Testing Cadence Testing in Production Development Environments and Tools Development Tools Development Environments Local Development Environments Local Development with a Remote Cluster Skaffold Development Workflow Remote Cluster Routed to Local Development Cloud Development Environments CI/CD Source Code Control Build Stage (CI) Test Stage (CI) Deploy Stage (CD) Release Stage (CD) Post-Release Stage Monitoring Collecting Metrics Observable Services Confguration Management Single-Environment Variable Multiple-Environment Variables Adding ConfigMap Data to a Volume Storing Secrets Deployment Configuration Sample CI/CD Flows Summary 6. Best Practices Moving to Cloud Native Breaking Up the Monolith for the Right Reasons Decouple Simple Services First Learn to Operate on a Small Scale Use an Anticorruption Layer Pattern Use a Strangler Pattern Come Up with a Data Migration Strategy Rewrite Any Boilerplate Code Reconsider Frameworks, Languages, Data Structures, and Datastores Retire Code Ensuring Resiliency Handle Transient Failures with Retries Use a Finite Number of Retries Use Circuit Breakers for Nontransient Failures Graceful Degradation Use a Bulkhead Pattern Implement Health Checks and Readiness Checks Define CPU and Memory Limits for Your Containers Implement Rate Limiting and Throttling Ensuring Security Treat Security Requirements the Same as Any Other Requirements Incorporate Security in Your Designs Grant Least-Privileged Access Use Separate Accounts/Subscriptions/Tenants Securely Store All Secrets Obfuscate Data Encrypt Data in Transit Use Federated Identity Management Use Role-Based Access Control Isolate Kubernetes Pods Working with Data Use Managed Databases and Analytics Services Use a Datastore That Best Fits Data Requirements Keep Data in Multiple Regions or Zones Use Data Partitioning and Replication for Scale Avoid Overfetching and Chatty I/O Don't Put Business Logic in the Database Test with Production-like Data Handle Transient Failures Performance and Scalability Design Stateless Services That Scale Out Use Platform Autoscaling Features Use Caching Use Partitioning to Scale Beyond Service Limits Functions Write Single-Purpose Functions Don't Chain Functions Keep Functions Light and Simple Make Functions Stateless Separate Function Entry Point from the Function Logic Avoid Long-Running Functions Use Queues for Cross-Function Communication Operations Deployments and Releases Are Separate Activities Keep Deployments Small CI/CD Definition Lives with the Component Consistent Application Deployment Use Zero-Downtime Releases Don't Modify Deployed Infrastructure Use Containerized Build Describe Infrastructure Using Code Use Namespaces to Organize Services in Kubernetes Isolate the Environments Separate Function Source Code Correlate Deployments with Commits Logging, Monitoring, and Alerting Use a Unified Logging System Use Correlation IDs Include Context with Log Entries Common and Structured Logging Format Tag Your Metrics Appropriately Avoid Alert Fatigue Define and Alert on Key Performance Indicators Continuous Testing in Production Start with Basic Metrics Service Communication Design for Backward and Forward Compatibility Define Service Contracts That Do Not Leak Internal Details Prefer Asynchronous Communication Use Efficient Serialization Techniques Use Queues or Streams to Handle Heavy Loads and Traffic Spikes Batch Requests for Efficiency Split Up Large Messages Containers Store Images in a Trusted Registry Utilize the Docker Build Cache Don't Run Containers in Privileged Mode Use Explicit Container Image Tags Keep Container Images Small Run One Application per Container Use Verified Images from Trusted Repositories Use Vulnerability Scanning Tools on Images Don't Store Data in Containers Never Store Secrets or Configuration Inside an Image Summary 7. Portability Why Make Applications Portable? The Costs of Portability Data Gravity and Portability When and How to Implement Portability Standardized Interfaces Common Services and Features Abstractions and Layers Managed Services from Other Vendors Portability Tooling Kubernetes as a Portability Layer Summary Index