smart services & smart clients - how microservices change the way you build and deploy code
TRANSCRIPT
Smart Services & Smart Clients
How microservices change the way you build and deploy code
Neil Mansilla Developer Relations @mansillaDEV
Runscope
Presented at YAPC 2015Salt Lake City
Solve API Problems FastMonitor, log, measure & share your API usage.
65 services 9 engineers
Why microservices?
0
14,000,000
28,000,000
42,000,000
56,000,000
70,000,000
May 2013 Sep Jan May Sep Jan 2015
Scaling the infrastructure
Scaling the team
Microservices are highly independent
independent codebases independent deploys independent teams
There are a lot of moving parts…
If you don't invest in INFRASTRUCTURE
don't invest in MICROSERVICES
Microservices = SOA + DevOps
Dashboard Identity
get_teams()
Dashboard Identity
get_teams()
host001.runscope.com host002.runscope.com
import requests
def get_teams(): url = “http://host002.runscope.com/teams” resp = requests.get(url)
return resp.json()
Dashboard Identity
get_teams()
url = “http://host002.runscope.com/teams”url = “http://host003.runscope.com/teams”
Dashboard Identity
get_teams() XIdentity
if ENVIRONMENT == “local”:url = “http://localhost:5000/teams”
elif ENVIRONMENT == “test”:url = “http://test001.runscope.com/teams”
elif ENVIRONMENT == “prod”: url = “http://host003.runscope.com/teams”
Dashboard Identity
get_teams()
How do you find a service?
How do you find a service?
Smart Client
Smart Clientimport requests
def get_teams(): url = “http://host001.runscope.com/teams”
resp = requests.get(url)
return resp.json()
Smart Clientimport smart_client
def get_teams(): url = “http://host001.runscope.com/teams”
resp = smart_client.get(url)
return resp.json()
Smart Clientimport smart_client
def get_teams(): url = “service://identity/teams”
resp = smart_client.get(url)
return resp.json()
Smart ClientService discovery
service://identity/...
Retry failed GET/PUT/DELETE (idempotent) requests
Run HTTP requests asynchronously
Smart Client
Atlas
Smart Client
ZooKeeper
Atlas
DynamoDB
/hosts/identity/host001.runscope.com = 5000/hosts/identity/host001.runscope.com/enabled = True
Register service metadata in Zookeeper & DynamoDB
Smart Client
Atlas
Sidecar
Runs HAProxy on each host.
Reads cluster state from Atlas.
Watches for changes to services and updates the local configuration.
host001
host002
host003
Smart Client
Smart Client
Service AService A
Smart ClientService B
Service B
Smart Clientsmart_client.get(“service://identity/teams”)
requests.get(“http://localhost:4000/teams”, headers={“Runscope-Service”: “identity”})
haproxy.cfgfrontend http-in bind *:4000 mode http acl host_api hdr(runscope-service) -i api acl host_billing hdr(runscope-service) -i billing acl host_mission-control hdr(runscope-service) -i mission-control acl host_elasticsearch hdr(runscope-service) -i elasticsearch
use_backend api-backend if host_api use_backend billing-backend if host_billing use_backend mission-control-backend if host_mission-control use_backend elasticsearch-backend if host_elasticsearch
Smart Clientsmart_client.get(“service://identity/teams”)
requests.get(“http://localhost:4000/teams”, headers={“Runscope-Service”: “identity”})
How do you find a service?
Smart Client
How do you make building services easy?
How do you make building services easy?
Smart Service
Smart Service
Smart Service
Runscope/healthcheck
Smart Service
bugsnag runscope-daemon alchemist smart-config
Smart Service
Configuration Driven
defaults.py
REDIS_SERVICE = “redis-cluster-1”REDIS_DB = 2
BUGSNAG_API_KEY = “xyz123”
Smart Service
Configuration Driven
config = SmartConfig('example-service', defaults=example_service.defaults)
app = SmartService('example-service', config=config)
Smart Service
Various additions to the flask app object
from flask import current_app
current_app.realmcurrent_app.rediscurrent_app.databasecurrent_app.api
Smart Service
Common Logging Configuration
/var/log/runscope/<service>.access.log/var/log/runscope/<service>.error.log
current_app.logger.warn(“Uh oh!”)bugsnag.notify(e)
$> generate-service my-awesome-service
Creating a service skeleton with this configuration: Service name: my-awesome-service Destination directory: ~/runscope/my-awesome-service Python package name: my_awesome_service HTTP port: 5004
1. Generating files in ~/runscope/my-awesome-service2. Initializing Git repository in ~/runscope/my-awesome-service3. Creating virtualenv my-awesome-service4. Installing Python prerequisites (requirements.txt and setup.py)
All done! my-awesome-service source code can be found in ~/runscope/my-awesome-service
Smart ServiceAuto Generate Service Skeletons
Smart Service
Reduce Cognitive Overhead
How do you make building services easy?
Smart Service
What happens when you have so many services?
What happens when you have so many services?
A Lot :)
Automate everything
Language Agnostic
Language AgnosticHTTP is our common ‘language’
No shared databases
DashboardTraffic
GatewayAPI
Postgresql
DashboardTraffic
GatewayAPI
PostgresqlX
DashboardTraffic
GatewayAPI
Identity
Postgresql
Flask
Redis
A service owns it’s own datastore(s).
DashboardTraffic
GatewayAPI
And exposes an API for that data.
Identity
GET /teams/<id>
PUT /teams/<id>
GET /buckets
...
Make Deploying Code Really Easy
Make Deploying Code Really Easy
1-Click Deploys
0
10
20
30
40
50
Jun 2014 Jul Aug Sep Oct Nov Dec Jan Feb Mar 2015
Deploys per day
Test & Monitor your APIs
Test Lifecycle
Local Dev{{identity_host}} = http://localhost
Local Agents
Test Lifecycle
Test Realm
Local Dev
{{identity_host}} = http://test001.runscope.com
Local Agents
Trigger URLs
Test Lifecycle
Test Realm
Production
Local Dev
{{identity_host}} = http://host002.runscope.com
Local Agents
Trigger URLs
Schedules
0
10
20
30
40
50
Jun 2014 Jul Aug Sep Oct Nov Dec Jan Feb Mar 2015
Deploys per day
Thanks!
p.s. - we’re hiring :)
Runscope
https://www.runscope.com/jobs
Neil Mansilla@mansillaDEV