What are Microservices?
- Build systems of small composable parts
- Interact through defined channels
1. Small systems of composable parts
2. Each app has its own database/deployment etc.
3. interact through defined channelsWhy?
Ease of mainteance
Scalability
Autonomous Teams
1. Start with why Herrmann is interested
a. Have a bunch of apps/potential apps with different purposes
b. Didn't want all of this sharing a single hard to managment monolith
c. Transition to general explanation
1. Ease of maintenance - Each app has it's own database and smaller codebase
2. Scalability, give example of order vs pricing, why its' harder to scale monoliths.
Say it's not our primary concern
3. Teams can behave autonmously.Microservices Concerns
- Client Routing/Discovery
- Common Client/Protool Concerns
- Cross service communication
-
Deployment
As you might expect it means setting up some infrastructure
so that what is really a bunch of little apps still ensures an
integrated user experience, and that everything plays nicely.
In general there are 4 real concerns
Routing
How does a client get to the right service?
Api gateway takes client requests and ensure they go to the correct application.
Generally it's a webserver, with some sort of logic to map a url to an application and then proxy the content
The hard way
Dedicated service discovery tools + webproxy
In enterprise scenarios companies will use dedicated service discovery
tools like consul and zookepper. Track multiple instances of the same server,
do health checks, load balancing, etc. These tools are hooked into the
webproxy through custom scripts, and the webrpoxy may even be load balanced itself
Benefits
-
Load Balancing
-
Complex routing scenarios
Drawbacks
-
Complex setup and maintenance
The easy way
Docker + nginx + nginx-proxy
Docker is containerization tech that can run multiple apps on a single server
in an isolated fashion. Nginx is a webserver. Docker nginx is a program that
runs in a docker container and builds dynamic config files for nginx based on running docker containers.
Basically we build a template for docker-nginx that will generate our nginx
config based on our running services.
Benefits
-
Easy setup
-
All pieces required for our infrastructure anyways
Drawbacks
-
Single server
-
Redeploy for each new service
This setup is easy to put together, since it's a just s series of docker containers
and a single template for the dynamically generated configuration part.
All the components were already a required part of our infrastructure,
and don't require any additional apps or processes
The main drawback is that you can't scale beyond a single server/single process
per application, but that's a huge problem for us, because we're not at that scale.Common Client Concerns
HTTPS
Authentication
Error Handling
The next thing we'll look at are some common client concerns.
This is about how the client connects to a service, how errors are
handled, etc
1. Some of these, like HTTPS and error handling are easy, just handle
at the api gateway. There really isn't anything tricky or unique about
our approach there
2. What about Auth though? The hard way
OAuth
One approach I've seen alot of companies reccomend is OAuth. If you aren't
familiar with OAuth, it's an authentication/delegation protocol used by
companies like twitter and facebook. If you've ever signed into a third party
web app using a "facebook", "twitter", or "google" login, you've used OAuth
It delegates authentication to another service, which then may choose to
share some data with the requesting app.
Benefits
-
Allows an "app" ecosystem
Drawbacks
-
Complex setup
-
Every app has to be oauth aware
-
Every app has to know how to find the auth service
-
Lots more cross service chatter
This setup is easy to put together, since it's a just s series of docker containers
and a single template for the dynamically generated configuration part.
All the components were already a required part of our infrastructure,
and don't require any additional apps or processes
The main drawback is that you can't scale beyond a single server/single process
per application, but that's a huge problem for us, because we're not at that scale. The easy way
Signed cookies generated by the gateway
We go with a very different approach. Instead, all of our auth logic lives
at the api gateway. No auth in individual applications, just get user data in custom http headers
1. On login we proxy credential request to an auth service
2. Auth service checks credentials and if good generates a simple json token
3. This json token is cryptographically signed with a secret key and then sent to the client
in the form of a cookie, or just a string.
4. The token is included for subsequent requests, the gateway decrypts the token.
5. If decryption is successfully, the gateway adds token data to http headers
6. If it fails, we give the user a 401.
Benefits
-
We only hit the database on login, not on later requests
-
Apps implement no auth logic. Devs don't have to worry about it.
Drawbacks
-
Any mobile apps have to be spefically written to store and manage the token
This setup is easy to put together, since it's a just s series of docker containers
and a single template for the dynamically generated configuration part.
All the components were already a required part of our infrastructure,
and don't require any additional apps or processes
The main drawback is that you can't scale beyond a single server/single process
per application, but that's a huge problem for us, because we're not at that scale.Cross Service Communication
How do services talk and share data?
Next we're going to look at cross service communication, how do services talk a share data? The ugly way
Synchronous request/response
We're going to break format here, and before I talk about the hard way, I'm going to
talk about the ugly way. This is a really common approach that's got alot of flaws,
and should really be avoided because of it's problems. That's synchronous request/response
usually over http. This is where service A sends Service B request on http, and then service B sends back data
Problems
1. Really tight coupling. Failure of 1 = failure of all Ex: Price management and ordering system.
2. Hard to trace where requests go, and which systems are hit
3. Hard to scale, since every service might be involved in every requestAsync
Data duplication over events
No coupling
Better traceability
Better scalability
If no sync, then what. After all that ordering application still needs the price data
It helps to think about how we handled this sort of stuff before computers
CSR taking orders over the phone. Wouldn't call up the sales person and ask the price
of every product while they're taking. Instead each CSR would have a list. If the price
changes, somebody distributes a new list. The CSR has everything he or she needs to <!DOCTYPE html>
their job, without having to request data from anyone else.
We can use the same model, and in fact it has a very computery sounding name. Data duplication over
events. In this model, everytime someone updates a price in the price management system,
we publish an event with data about the change. The online store listens for these events
and uses the data to update it's own cache of product prices.
This solves all the problems we outlined with synchronous communication.
How does this work? We'll talk about the hard way and then the easy way The hard way
Dedicated event bus/messaging system
Kafka
Rabbit MQ
Zero MQ
Etc.
Dedicated system that only does event management/pub sub.
Benefits
-
Fault tolerant
-
Optimized for high loads
Drawbacks
-
Another system to support and maintain
-
Dual Writes
The easy way
Database based message system
What this means is that we have our pub sub messaging system built directly into a shared databaseQueBus
Built on top of background job library
Add a subscribers table
Publishing application loops through subscribers and queues up jobs
We built a custom pub sub system that uses the database directly called QueBus
It may sound like alot, but it's mostly but on top of an existing background job library
explain background jobs
So on top of this background job library, we add a table of subscribers
Stores who is listening for what kind of events.
So when the price management app goes to publish a price changed event
it looks at the subscribers table, sees a row in there for the online store
and then queues up a background job for the online store.
The online store grabs this background job, and processes it, adding the price
change data to it's own database
Most of the heavy lifiting is this existing background job library
Benefits
-
No new system, built into existing database
-
No dual writes
Drawbacks
-
All apps share a database (mitigated by postgres schema)
-
Logic for using the subscribers and jobs tables must be implemented in every app
This setup is easy to put together, since it's a just s series of docker containers
and a single template for the dynamically generated configuration part.
All the components were already a required part of our infrastructure,
and don't require any additional apps or processes
The main drawback is that you can't scale beyond a single server/single process
per application, but that's a huge problem for us, because we're not at that scale.Deployment
Autonomous teams = Automated deploys
Deploy on checkin
Isolated apps
How does your code get into production. Remember one of the original goals was
autonomous teams. Autonomous teams need to be apply to deploy on their own
schedule, and without depending on other teams. That means automated deployment
Generally this means you build a Heroku like setup. If you aren't familar with
Heroku, the way you deploy code is by checking your latest code into a specific
git repository. From their an automated process takes over and publishes the code you checkin
Different branches or repositories are used for different environments The hard way
Dedicated Build server
Standalone server/app for builds and deployments The easy way
Cloud build services
Codeship
Drone.io
Travis CI
In this case, the hard way and the easy way aren't too different. It's
mostly the same process just one uses the cloud versus setting up and configuring
a dedicated system. The cloud might cost a little more, but it gives you
convinence and ease of setup.Summary
-
Small applications that talk asynchronously
-
Automated deployments tied to source control
-
Easy to add new features via seperate apps
-
Easy to experiment with different approachs from app to app
-
Single server, but ready to scale with minimal app changes
-
Single database but ready to scale with minimal app changes
Microservices
The Easy way
Created by Andrew Swerlick / @thirdthoughts
Straight to what are microservices