Microservices: Klein, aber oho!
Stephan Hochdörfer // 31.05.2016
Über mich
Stephan Hochdörfer
Head of Technology , bitExpert AG Mannheim
S.Hochdoerfer@bitExpert.de
@shochdoerfer
#PHP, #DevOps, #Automation
#phpugffm, #phpugmrn, #unKonf
Der Fluch des Monolithen
Einschüchternde Codebasis
Komplexer Code der schwer zu verstehen ist, überladene IDE, viele Klassen, viele Methoden, Benennung die
keinen Sinn ergeben
Entwicklung langsam, erhöht die Wahrscheinlichkeit Fehler zu machen (Natur der Sache)
Verhindert dass wir schnell vorhan kommen (Agilität leidet)
Fragiles System
Jede noch so keine Änderung sorgt für einen kompletten Rebuild. Dauert lange (langer Test-Zyklus,
Integrationstests, Deployment)
Hintert uns daran oft Änderungen zu machen und zu deployen! Continious Deployment nicht wirklich
machbar!
Langfristige Verpflichtung
Wir sind an "alte" Technologien gebunden die vor 10 Jahren "aktuell" waren. Schwer neues zu integrieren.
Versuch eines Rewrites?
Den Tech-Stack auszutauschen heißt ein komplettes Rewrite versuchen. Es gibt kein partielles Rewrite und
das Rewrite wird mit ziemlich großer Wahrscheinlichkeit nach hinten los gehen.
Netscape 4 Rewrite - Misslungen (IE hat gewonnen)
Borland dBase - Misslungen (Access hat gewonnen)
Borland Quattro Pro - Misslungen (Excel hat gewonnen)
Die Legacy Wahrheit
« Legacy code is a business asset that helped
the business get where it is. Treat it with respect. »
- @ylarrivee
Behandle den Legacy Code / den Monolithen mit Respekt: behalte ihn und "modernisiere" ihn langsam nach
und nach.
Die LTS Wahrheit
« good long term software is not commonly about reuse,
its about the ability to rewrite completely isolated bits
without hitting the big bang » - @gregyoung
LTS = langlebige Software
Die Lösung?
« [...] This is the Unix philosophy: Write programs that do one thing and do it well. »
- Doug McIlroy
Doug McIlroy, inventor of the Unix pipe
Zusammenarbeit
« [...] Write programs to work together. Write programs to handle text streams, because that
is a universal interface. » - Doug McIlroy
Doug McIlroy, inventor of the Unix pipe
Was sind Microservices?
« [...] approach to developing a single application as a suite of small services, each running in
its own process and communicating with lightweight mechanisms. » - James Lewis, Martin
Fowler
Blogpost geschrieben im März 2014
Unternehmen die verstärkt auf Microservices setzen: Amazon, Netflix, Soundcloud, ...
Hatten wir das nicht schon mal? (SOA)
"Nur" SOA? Oder besser?
« I guess it is easier to use a new name (Microservices)
rather than say that this is what SOA actually meant [...] » - @arnonrgo
Kleine Problemdomäne
Bounded Context in DDD Sprache
Gleiches Vorgehen dass wir verwenden um Klassen und Methoden zu strukturieren.
Single Responsibility
« A class should have one, and only one, reason to change. » - Robert C. Martin
"Großes Problem" aufsplitten in kleinere Stücke und diese unabhängig davon lösen.
Isolierter Prozess
Nicht ein Applikation-Server auf dem alles läuft, sondern ein Applikation-Server für eine Applikation.
Docker
Docker is an open platform for developers and sysadmins to build, ship, and run distributed
applications.
Microservices Plattform
Alternativen:
https://tsuru.io/
https://flynn.io/
Deployment Automatisierung
Alternatives:
http://deployer.org/
http://rocketeer.autopergamene.eu/
http://kohkimakimoto.github.io/altax/
Build + Deployment isolieren
Sicherstellen dass es beim Bau und Deployment nicht zu Seiteneffekten kommt.
Idealerweise wenige dependencies, idealerweise keine Dependencies unter den Services
Separater Datenspeicher
MySQL, PostgreSQL, NoSQL Datenbank, Dateisystem, ...
Separater Datenspeicher macht das spätere Skalieren viel einfacher.
Einfache Kommunikation
Standardized protocols, e.g HTTP
Smart endpoints, dumb pipes
« Be of the web, not behind the web. » - Ian Robinson
SOA: Dump endpoints and smart pipes
Open/closed principle
« You should be able to extend a classes behavior, without modifying it. » -
Robert C. Martin
Gilt nicht nur für die Logik in den Services, genau so auch für die Datenstrukturen die ausgetauscht
werden
Datenstruktur-Evolution
{
"id" : 123 ,
"name" : "My Product" ,
"categories" : [{
"id" : 12 ,
"name" : "My category"
}],
"price" : 12.34
"currency" : "EUR"
}
{
"id" : 123 ,
"name" : "My Product" ,
"categories" : [{
"id" : 12 ,
"name" : "My category"
}],
"price" : [ 12.34 , 14.26 ]
"currency" : [ "EUR" , "USD" ]
}
Open/closed principle gilt auch für Datenstrukturen
Datenstruktur-Evolution
{
"id" : 123 ,
"name" : "My Product" ,
"categories" : [{
"id" : 12 ,
"name" : "My category"
}],
"prices" : [{
"price" : 12.34 ,
"currency" : "EUR"
}, {
"price" : 14.26 ,
"currency" : "USD"
}]
}
Vorrausschauend planen / arbeiten. Ähnlich wie bei REST Schnittstellen, ggf. mit Versionierung arbeiten,
kann aber Wartung verkomplizieren!
Bauen, Deployen, Supporten
You build it, you run it!
Keine Trennung zwischen Entwickung und Operations - DevOps. Jeder Micoservice ist anders, benutzt andere
Technologien. Eine zentrale IT Abteilung kann das nicht mehr leisten, auch nicht das permante Deployment
von Entwicklungen durch mehrere Teams.
Warum Microservices?
« Microservices are organised around business capabilities. » - Martin Fowler
Alt: Technologische Trennung bei den Teams (UI team, DBA team)
Neu: Fokus auf Business Prozesse
Observe, Orient, Decide, Act
OODA: Observe - Orient - Decide - Act
Macht jedes Unternehmen, direkt oder indirekt. Je schneller man den Zyklus durchlaufen kann desto größer
ist der Vorteil gegenüber Mitbwerbern. Man lernt schneller was Kunden wollen, wie der Markt aussieht
und ist dadurch entsprechend stärker im Wettbewerb aufgestellt. Unternehmen die den Zyklus durchlaufen
verbesseren die Qualität der Software.
Team Organisation
Fundamentale Wahrheit: Es geht hier nicht um Code, es geht um die Optimierung der Zusammenarbeit.
SW-Architektur und Team Organisation hängen stark zusammen - Conways Law
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy
of the organization's communication structure
Kleine, fokussierte Teams
"Two pizza teams", Jeff Bezos (Amazon)
Mehr Macht den Entwicklern!
Vertraue deinen Entwicklern. Entwickle eine Kultur von Freiheit und Verantwortung, dies wird die
Software verbessern, Deployment-Zeiten minimieren und die Erfolgsrate erhöhen.
Nutze das "passende" Tool...
Backend: Austausch einfacher, da nach "außen" ja nur HTTP Requests / Responses eine Rolle spielen
Frontend: Ggf. schwieriger wenn Frontend-Framework fixe HTML Strukturen vorsieht (bsp. Sencha)
...oder tausche es aus!
Ersetze Tools / Frameworks die nicht so funktionieren wie sie sollen.
Liskov substitution principle
« Derived classes must be substitutable for their base classes. » - Robert C. Martin
Skalierung wird einfacher!
Microservices sind klein, kleine Services sind einfacher zu skalieren als ein großer Monolith.
Einzelne Services können unabhängig von einander skaliert werden!
Entwicklung wird schneller...
...je nach Bedarf!
A/B Testing für Services
Feature toggles
Herausforderung
« There ain’t no such thing as a free lunch. » - Robert A. Heinlein
Robert A. Heinlein, The Moon Is a Harsh Mistress, 1966
Nicht so einfach!
« If you can’t build a monolith, what makes you think
microservices are the answer? » - Simon Brown
Disziplin notwendig, noch mehr als ohnehin schon nötig.
Kleine Services FTW!
« Small teams typically work better than big teams.
Small services typically work better than monoliths.
#microservices #microservice » - @microsvcs
Was heißt denn "klein"?
« @olivergierke Micro Services are neither
micro nor service. :-) » - @ewolff
Microservices, Nanoservices - Größe spielt keine Rolle!
Interface Segregation
« Make fine grained interfaces that are client specific. » - Robert C. Martin
Größe spielt keine Rolle, Funktionalität ist wichtig!
Standardisierung notwendig!
Entwickler haben die Freiheit zu entscheiden wie Services gebaut werden, welche Frameworks, Tools,
Datastores verwendet werden.
Alles andere muss standardistert werden: Deployment, Monitoring/Logging, Fehlerbehandlungen, Errorcodes
Monitoring und Logging
Monitoring und Logging ist wichtiger denn je. Es muss direkt sichtbar sein wenn ein Miroservice ein
Problem verursacht und ich muss nachverfolgen können woher das Problem stammt (Microservice-übergreifend
Logfiles analysieren können!). Wie vieles im Kontext von Microservices ist das nicht einfach und
resultiert in einem komplexeren Setup was die eigene Infrastruktur und das Tooling angeht!
Keine verteilte Transaktionen!
Verteilte Transaktionen verhindern!
Keine kaskadierende Fehler!
Intensiv mit Timeouts und circuit breakers arbeiten um kaskadierende Fehler vermeiden.
Design for failure
Chaos Monkey - Randomly terminates one of the systems in a group
Janitor Monkey - Clean up AWS instances
Conformity Monkey - Looks for instances that are not conforming to predefined rules for the best
practices.
Design for failure
« @olivergierke No. I meant to says that REST is not the
most difficult problem for Microservices. E.g. resilience
is probably harder. » - @ewolff
Resilience
Port of Nextflix Hystrix to PHP.
Früh und oft releasen!
Don’t let changes build up - release as soon as you can, and preferably one at a time.
Automatisierung FTW!
Automation is the glue that holds everything together.
In a world where microservices come and go, grow and shrink, and migrate around racks and data centers
in seconds - there's absolutely no room for manual intervention. All aspects of deployment, monitoring,
testing, and recovery must be fully automated. For example, monitoring a service should occur instantly
and automatically by virtue of it being deployed, not requiring a separate manual step.
Dependency Inversion
« Depend on abstractions, not on concretions. » - Robert C. Martin
Lookup services to not explicitly communicate with a specific instance!
Service Discovery
Service discovery and configuration made easy. Distributed, highly available and datacenter aware.
Service Discovery (e.g. mysql, web), Key/Value Store for application configuration.
Service Discovery Beispiel
{
"require" : {
"sensiolabs/consul-php-sdk" : "1.0.*@dev"
}
}
< ?php
require_once __DIR__. '/vendor/autoload.php' ;
$sf = new SensioLabs\Consul\ServiceFactory ();
$kv = $sf -> get ( 'kv' );
$kv -> put ( 'test/foo/bar' , 'bazinga' );
$kv -> get ( 'test/foo/bar' , [ 'raw' => true]);
$kv -> delete ( 'test/foo/bar' );
Dokumentation FTW!
Änderungen dokumentieren!
API Konsumenten wie Kunden behandeln!
Tools: https://apiblueprint.org, http://swagger.io,
Microservices Beispiel
Microservices Beispiel
$> composer.phar require zendframework/ zend- diactoros
$> composer.phar require zendframework/ zend- stratigility
use Zend\Stratigility\MiddlewarePipe;
use Zend\Stratigility\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Zend\Diactoros\Response\JsonResponse;
class SearchMiddleware implements MiddlewareInterface
{
public function __invoke ( Request $request , Response $response ,
callable $next = null)
{
$params = $request -> getQueryParams ();
$service = new SearchService ();
return new JsonResponse ( $service -> search ( $params [ 'search' ]));
}
}
Acts as an abstraction for 3rd party services using search. Does not matter if in the first place a
PostgreSQL database with full-text search capabilities is used or later on elasticsearch. The
"interface" stays the same. Makes things easier to change.
Microservices Beispiel
use Zend\Stratigility\MiddlewarePipe;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Response;
$api = new MiddlewarePipe ();
$api -> pipe ( new SearchMiddleware ());
$response = $api ( ServerRequestFactory:: fromGlobals (), new Response ());
Acts as an abstraction for 3rd party services using search. Does not matter if in the first place a
PostgreSQL database with full-text search capabilities is used or later on elasticsearch. The
"interface" stays the same. Makes things easier to change.
Extending your Microservice
$> composer.phar require oscarotero/ psr7- middlewares
use Zend\Stratigility\MiddlewarePipe;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Response;
use Psr7Middlewares\Middleware\Firewall;
$api = new MiddlewarePipe ();
$api -> pipe ( new Firewall ([ '127.0.0.1' ]));
$api -> pipe ( new SearchMiddleware ());
$response = $api ( ServerRequestFactory:: fromGlobals (), new Response ());