Monoliths to Microservices Migration

video locked
Enroll to watch

30 mins

Chapters
About
Discussion0
Notes
Be the first to start a conversation!

Motivation

A monolith keeps all the code of an application in a single repository, and a microservice architecture breaks it into services.

Microservices have three advantages:

  1. Separation of concerns. Accepting PayPal payments is a change in the payment service only.
  2. Engineering is easier, because the expectations and responses of a service are well defined.
  3. Deployments are easier. The authentication service is deployed separately.

Monoliths have two:

  1. They suit a small team. The move to microservices comes when the team has scaled, not the users.
  2. Communication is a function call with explicit parameters, and the compiler rejects a wrong call. In microservices, an ID that changes from an int to a string breaks every service that calls it: a breaking change.

One way to move is to build the whole microservice architecture and redirect 10% of the users to it. The engineering effort is massive.

The better approach starts with a new feature, such as analytics. It becomes a separate service, called over the network, with its own database and its own code repository.

Infrastructure Requirements

Adding microservices needs five infrastructure changes.

  1. Contracts. Each microservice defines how it is called, and ships a client: a library that other services download to make function calls such as get report. A library that is not up to date breaks the code.
  2. Routing. A load balancer or service registry routes requests from one service to another based on service capabilities.
  3. Deployments. Copying code to an AWS box with SCP and running a command does not hold as services increase. Jenkins deploys on every Git push to master, and containers replace boxes.
  4. Communication. The session service talks to analytics over a message queue, because a lost event only makes a report wrong. The profile service talks to payments with request response, for an immediate answer.
  5. Logging. A request crosses sessions, analytics, profiles and payments, so all logs go to a single repository. Kafka pushes them and the Elastic stack stores them.

Every log for a request ID is then pulled out in one shot, which makes debugging and root cause analysis easier.

Practical considerations

A move from a monolith to microservices depends on three things: who owns the data, where the business responsibilities sit, and the cost of the infrastructure.

  1. Every microservice encapsulates the data it is responsible for. The profile service is the single source of truth for profiles, with a dedicated data store. The payment service asks it for profile information.
  2. Business responsibilities are condensed to a single place. An intermediary that condenses information from Google, Facebook and LinkedIn is used only by the profile service and changes with its requirements, so it is a component of the profile service.
  3. The initial infrastructure cost is high: logging, deployment infrastructure, documentation. A small team sacrifices features for it.

The number of services per person:

  1. A startup: one person handles two services or fewer.
  2. A medium organization: one service or fewer, so two people handle a service and there is no single point of failure.
  3. A large organization: two to four people per service. Beyond four there is confusion, and the service is broken down.

Containers: A friendly Introduction

Capacity planning is a bunch of guesses about the compute, memory and storage an application needs, made before there is a computer for it.

Earlier, a DevOps team or a finance team asked which box to buy for a piece of code. The answer came from capacity planning, and then a computer was bought from the shop. That computer is an investment.

  1. A small computer does not hold when the business scales, and the time and effort are spent all over again.
  2. A large computer is reasonably safe, but the initial hardware investment is very large.
  3. Horizontal scaling on the basis of hardware alone means getting as many computers as possible.

One approach organizations took was to let their employees use a single computer. Multiple people using the same resource leads to contention, so resource usage is isolated as much as possible.

Containers: Usecases

A container is a lightweight form of virtualization that isolates one application from another.

A program needs four resources: memory, I/O, processing and disk. The operating system is the sole manager of these resources, and a program asks for them through an interface.

  1. Programs that share one operating system can still starve each other. If one takes all the memory, everyone else is upset.
  2. A virtual machine is a strong boundary: an operating system running on top of an operating system.
  3. Virtual machines make cloud computing possible. Amazon and Google rent out spare hardware. The code becomes platform independent, and a machine is replaced by shutting one down and starting another.
  4. Starting an entire virtual machine takes a few seconds. A container gives app isolation without the full operating system, so it is faster to build and tear down.

Docker lets the developer specify the operating system and the resources. Containers are still considered a little slow, and their management is not worth it for simple applications.

Service discovery and Heartbeats

A heartbeat is a regular message that tells a health service a server is alive, and service discovery is how the load balancer learns which boxes run a service.

  1. The health service asks S1 every five seconds whether it is alive, and S1 responds yes.
  2. If S1 misses one request it is marked critical. On the second miss it is assumed dead. The load balancer is told, and the service is run on another machine, S4.
  3. A machine sometimes responds yes while the application processes no requests. So the service itself sends a heartbeat every five seconds: a two-way heartbeat.
  4. A dead server with cron jobs still sends requests with stale data, a zombie. Once the health service marks it dead, it kills itself.

A new profile service tells the load balancer its IP addresses and ports. The load balancer persists a snapshot of all the services, and other services ask it for an address. The health service reads the diff in the snapshot and opens connections to the new boxes.