Table of Contents Foreword. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi 1. Why Distributed?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 The Single-Threaded Nature of JavaScript Quick Node.js Overview The Node.js Event Loop Event Loop Phases Code Example Event Loop Tips Sample Applications Service Relationship Producer Service Consumer Service 1 6 9 10 11 14 15 16 17 18 2. Protocols. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Request and Response with HTTP HTTP Payloads HTTP Semantics HTTP Compression HTTPS / TLS JSON over HTTP The Dangers of Serializing POJOs API Facade with GraphQL GraphQL Schema Queries and Responses 22 23 25 26 29 34 35 36 37 38 iii GraphQL Producer GraphQL Consumer RPC with gRPC Protocol Buffers gRPC Producer gRPC Consumer 40 43 45 45 48 50 3. Scaling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 The Cluster Module A Simple Example Request Dispatching Cluster Shortcomings Reverse Proxies with HAProxy Introduction to HAProxy Load Balancing and Health Checks Compression TLS Termination Rate Limiting and Back Pressure SLA and Load Testing Introduction to Autocannon Running a Baseline Load Test Reverse Proxy Concerns Protocol Concerns Coming Up with SLOs 53 54 57 58 61 63 64 69 70 72 75 76 76 80 84 87 4. Observability. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Environments Logging with ELK Running ELK via Docker Transmitting Logs from Node.js Creating a Kibana Dashboard Running Ad-Hoc Queries Metrics with Graphite, StatsD, and Grafana Running via Docker Transmitting Metrics from Node.js Creating a Grafana Dashboard Node.js Health Indicators Distributed Request Tracing with Zipkin How Does Zipkin Work? Running Zipkin via Docker Transmitting Traces from Node.js iv | Table of Contents 92 93 94 95 98 100 102 103 104 106 108 111 112 115 115 Visualizing a Request Tree Visualizing Microservice Dependencies Health Checks Building a Health Check Testing the Health Check Alerting with Cabot Create a Twilio Trial Account Running Cabot via Docker Creating a Health Check 118 119 120 121 124 124 125 126 127 5. Containers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 Introduction to Docker Containerizing a Node.js Service Dependency Stage Release Stage From Image to Container Rebuilding and Versioning an Image Basic Orchestration with Docker Compose Composing Node.js Services Internal Docker Registry Running the Docker Registry Pushing and Pulling to the Registry Running a Docker Registry UI 133 140 141 143 146 148 151 152 156 157 158 160 6. Deployments. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Build Pipeline with Travis CI Creating a Basic Project Configuring Travis CI Testing a Pull Request Automated Testing Unit Tests Integration Tests Code Coverage Enforcement Deploying to Heroku Create a Heroku App Configure Travis CI Deploy Your Application Modules, Packages, and SemVer Node.js Modules SemVer (Semantic Versioning) npm Packages and the npm CLI 165 165 167 168 170 172 174 177 183 184 185 187 190 191 193 197 Table of Contents | v Internal npm Registry Running Verdaccio Configuring npm to Use Verdaccio Publishing to Verdaccio 204 205 205 205 7. Container Orchestration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Introduction to Kubernetes Kubernetes Overview Kubernetes Concepts Starting Kubernetes Getting Started Deploying an Application Kubectl Subcommands Kubectl Configuration Files Service Discovery Modifying Deployments Scaling Application Instances Deploying New Application Versions Rolling Back Application Deployments 210 210 211 214 214 219 219 222 226 232 232 233 235 8. Resilience. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 The Death of a Node.js Process Process Exit Exceptions, Rejections, and Emitted Errors Signals Building Stateless Services Avoiding Memory Leaks Bounded In-Process Caches External Caching with Memcached Introducing Memcached Running Memcached Caching Data with Memcached Data Structure Mutations Database Connection Resilience Running PostgreSQL Automatic Reconnection Connection Pooling Schema Migrations with Knex Configuring Knex Creating a Schema Migration Applying a Migration vi | Table of Contents 239 240 242 247 249 251 252 256 257 258 259 260 262 262 263 269 272 274 275 276 Rolling Back a Migration Live Migrations Idempotency and Messaging Resilience HTTP Retry Logic Circuit Breaker Pattern Exponential Backoff Resilience Testing Random Crashes Event Loop Pauses Random Failed Async Operations 279 280 284 286 289 289 293 294 294 295 9. Distributed Primitives. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 The ID Generation Problem Introduction to Redis Redis Operations Strings Lists Sets Hash Sorted Sets Generic Commands Other Types Seeking Atomicity Transactions Lua Scripting Writing a Lua Script File Loading the Lua Script Tying It All Together 298 301 302 304 305 307 308 310 311 312 313 315 317 318 320 322 10. Security. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 Wrangling Repositories Recognizing Attack Surface Parameter Checking and Deserialization Malicious npm Packages Application Configuration Environment Variables Configuration Files Secrets Management Upgrading Dependencies Automatic Upgrades with GitHub Dependabot Manual Upgrades with npm CLI 326 328 328 331 332 333 334 337 339 340 342 Table of Contents | vii Unpatched Vulnerabilities Upgrading Node.js Node.js LTS Schedule Upgrade Approach 344 346 346 347 A. Installing HAProxy. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 B. Installing Docker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 C. Installing Minikube & Kubectl. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 viii | Table of Contents