Slide 1

Slide 1 text

CLOUD NATIVE & JEE FREUND ODER FEIND? Josef Adersberger ( @adersberger)

Slide 2

Slide 2 text

★ Design for Distribution: Container, Microservices, API-getriebene Entwicklung ★ Design for Performance: Responsive, nebenläufig, ressourcenschonend ★ Design for Automation: Dev- und Ops-Aufgaben automatisieren ★ Design for Resiliency: Fehlertolerant und selbst-heilend ★ Design for Elasticity: Dynamisch skalierbar und reaktiv ★ Design for Delivery: Kurze Roundtrips und automatisierte Provisionierung ★ Design for Diagnosability: Cluster-weite Logs, Metriken und Traces 2 Mehr Ordnung: Cloud Native Maturity Model (https://de.slideshare.net/Pivotal/the-cloud-native-journey-58445711) Mehr Lösung: The Twelve-Factor App Methodology (https://12factor.net) Design-Prinzipien für Cloud Native Applications

Slide 3

Slide 3 text

JEE CLOUD
 NATIVE + = CLOUD
 NATIVE
 JEE instagram.com/toby_littledude

Slide 4

Slide 4 text

JEE IST AKTUELL WEDER HÄSSLICH NOCH REIN AUF MONOLITHEN AUSGELEGT ▸ Die JEE API ist modular und in vielen Teilen leichtgewichtig
 (unter uns: manchmal fühlt sich Spring schwergewichtiger an) ▸ Neben den klassischen Application Servern sind nun auch JEE Micro Container verfügbar, mit denen JEE-Anwendungen in Microservices geschnitten werden können ▸ Das Wissen ist breit verteilt, wie Enterprise Anwendungen auf Basis JEE entwickelt werden können ▸ Die JEE API ist standardisiert und reif. Die Implementierungen sind “battle proven”. ▸ … aber leider werden auch in JEE 8 die Design-Prinzipien von Cloud Native Applications noch zu wenig berücksichtigt. Mit Hilfe von Open-Source- Bausteinen gelingt dies aber trotzdem.

Slide 5

Slide 5 text

JEE CLOUD
 NATIVE + = CLOUD
 NATIVE
 JEE instagram.com/toby_littledude

Slide 6

Slide 6 text

SICHT DES ARCHITEKTENS CLOUD NATIVE JEE

Slide 7

Slide 7 text

WIR NEIGEN DAZU, JEE-ANWENDUNGEN IN SCHWERGEWICHTIGEN APPSERVERN ALS AUSFÜHRUNGSMONOLITHEN LAUFEN ZU LASSEN DESIGN CODE RUN startup-cluster.sh JEE Impl. JEE App Server

Slide 8

Slide 8 text

JEE MICRO CONTAINER ERMÖGLICHEN JEE AUSFÜHRUNGSKOMPONENTEN (MICROSERVICES) IN EINER MAßGESCHNEIDERTEN AUSFÜHRUNGSUMGEBUNG DESIGN CODE RUN main() JEE impl. parts

Slide 9

Slide 9 text

EINE CLOUD AN JEE MICROSERVICES! MISSION ERFOLGREICH? image: https://www.dreamstime.com

Slide 10

Slide 10 text

eine Cloud ist nicht nichts

Slide 11

Slide 11 text

CLOUD NATIVE STACK JEE MICROSERVICES

Slide 12

Slide 12 text

CLUSTER VIRTUALIZATION (computing, network, storage, memory) CLUSTER RESOURCE MANAGER CLUSTER ORCHESTRATOR APPLICATIONS CONTAINER CLUSTER RESOURCES MICROSERVICE PLATFORM
 API Gateway Micro Container Configuration & Coordination Diagnosability &
 Monitoring Infrastructure-as-a-Service Bare Metal Local Host Service
 Client Service Discovery DevOps Interface •deploy •rollback •scale •configure Diagnose Interface •analyze logs •analyze metrics •analyze traces Edge Interface •request service •authenticate

Slide 13

Slide 13 text

CLUSTER VIRTUALIZATION (computing, network, storage, memory) CLUSTER RESOURCE MANAGER CLUSTER ORCHESTRATOR APPLICATIONS CONTAINER CLUSTER RESOURCES MICROSERVICE PLATFORM
 API Gateway Micro Container Configuration & Coordination Diagnosability &
 Monitoring Infrastructure-as-a-Service Bare Metal Local Host Service
 Client Service Discovery DevOps Interface •deploy •rollback •scale •configure Diagnose Interface •analyze logs •analyze metrics •analyze traces Edge Interface •request service •authenticate Wie können den Containern die passenden Ressourcen zugewiesen werden? Wie entkoppelt man von physischer Hardware? Wie führt man containerisierte Anwendungen im Cluster aus? Wie entdeckt man Laufzeitanomalien und ihre Ursachen? Wie ruft man resilient und responsive andere Microservices auf? Wie führt man einen Microservice aus? Wie erzeugt man cluster-weiten Konsens zu Konfigurationswerten
 etc. Wie exponiert man Services in das Internet? Wie verwaltet man zentral die verfügbaren Services und Endpunkte?

Slide 14

Slide 14 text

goo.gl/xrVg3J

Slide 15

Slide 15 text

CLUSTER VIRTUALIZATION (computing, network, storage, memory) CLUSTER RESOURCE MANAGER CLUSTER ORCHESTRATOR APPLICATIONS CONTAINER CLUSTER RESOURCES MICROSERVICE PLATFORM
 API Gateway Micro Container Configuration & Coordination Diagnosability &
 Monitoring Infrastructure-as-a-Service Bare Metal Local Host Service
 Client Service Discovery DevOps Interface Diagnose Interface Edge Interface

Slide 16

Slide 16 text

DER ZWITSCHER CLOUD NATIVE JEE SHOWCASE ZWITSCHER-APP-HISTORY ZWITSCHER-APP-WIKIPEDIA ZWITSCHER-APP-CHUCK ZWITSCHER-APP-BOARD Keyword Resultat + Keyword-Historie random joke
 (weil jeder Chuck Norris Witz auf jedes Keyword matched) JDBC Open API ZWITSCHER-APP-CHUCK fallback https://github.com/adersberger/cloud-native-zwitscher-jee

Slide 17

Slide 17 text

SICHT DES ENTWICKLERS CLOUD NATIVE JEE

Slide 18

Slide 18 text

MICRO CONTAINER DEPENDENCY INJECTION APPLICATION LIFECYCLE ENDPOINT EXPOSITION

Slide 19

Slide 19 text

DAS JEE MICRO CONTAINER ÖKOSYSTEM http://wildfly-swarm.io https://ee.kumuluz.com http://microprofile.io (to come) http://tomee.apache.org http://www.payara.fish/payara_micro EMBEDDED implements (unstable)

Slide 20

Slide 20 text

JEE MICRO CONTAINER IM VERGLEICH Payara Micro Wildfly Swarm TomEE kumuluzEE Servlets et al. x x x x WebSockets x x x JSF x x x JAX-RS x x x (TomEE+) x JAX-WS x x (TomEE+) EJB *2 x x x CDI x x x x JTA x x x JCA x x (TomEE+) JMS x x (TomEE+) JPA x x x x Bean Validation x x x x JBatch x x Concurrency x x JCache x x JEE Version JEE 7 Web Profile JEE 7 JEE 6, JEE 7 teilw.*1 JEE 7 teilw. Packetierung WAR + JAR JAR JAR classes + JARs Startup-Dauer 4s 4s 2s 1s Referenz-Größe 57MB 83 MB 44 MB 15 MB *1) http://tomee.apache.org/javaee7-status.html *2) http://stackoverflow.com/questions/13487987/where-to-use-ejb-3-1-and-cdi

Slide 21

Slide 21 text

@Path(“/joke") @RequestScoped
 public class ChuckJokeResource {
 @Inject
 private IcndbIntegration icndb;
 @GET
 @Produces("application/json")
 public Response getJoke() {
 Map json = new HashMap<>();
 json.put("message", icndb.getRandomJoke());
 return Response.ok(json).build();
 }
 } @ApplicationPath("/chuck")
 public class ChuckJokeApplication extends ResourceConfig {
 public ChuckJokeApplication() {
 super(); register(ChuckJokeResource.class);
 }
 } JAX-RS MIT CDI bean-discovery-mode="all"

Slide 22

Slide 22 text

public class Main {
 
 /**
 * Starts the microservice container.
 *
 * Requires environment variable "PORT" according
 * on what port to listen.
 *
 * @param args no arguments evaluated
 */
 public static void main(String[] args) {
 com.kumuluz.ee.EeApplication.main(args);
 }
 } JEE MICRO CONTAINER STARTUP

Slide 23

Slide 23 text

SERVICE CLIENT SERVICE DISCOVERY LOAD BALANCING REQUEST MONITORING CIRCUIT BREAKING

Slide 24

Slide 24 text

SERVICE CLIENT MIT JERSEY, RXJAVA UND HYSTRIX •Circuit Breaker (Resiliency) •Request Monitoring •JAX-RS 2.0 REST Client •Parallele und asynchrone
 Ausführung 
 (Responsive)

Slide 25

Slide 25 text

SERVICE CLIENT MIT JERSEY, RXJAVA AND HYSTRIX @RequestScoped
 public class IcndbIntegration implements IChuckNorrisJokes {
 
 @Override public String getRandomJoke() {
 IcndbIntegrationCommand cmd = new IcndbIntegrationCommand();
 return cmd.observe().toBlocking().toFuture().get();
 }
 
 private class IcndbIntegrationCommand extends HystrixObservableCommand {
 
 IcndbIntegrationCommand() {
 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("zwitscher"))
 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
 .withExecutionTimeoutInMilliseconds(3000)));
 }
 
 @Override protected Observable construct() {
 return RxObservable.newClient()
 .target("http://api.icndb.com").path("jokes/random").request(MediaType.APPLICATION_JSON_TYPE)
 .rx().get()
 .map(response -> {
 Map> json = response.readEntity(Map.class);
 return json.get("value").get("joke");
 });
 }
 }
 } •Asnc bis zum Client mit dem Async-Support ab Servlet API 3 •Mehr Nebenläufigkeit ein Y-Stück Observable

Slide 26

Slide 26 text

FALLBACK WITZE @RequestScoped
 public class IcndbIntegration implements IChuckNorrisJokes { @Inject @Named("chucknorrisjoke-chucknorrisio")
 private IChuckNorrisJokes fallback; @Override public Observable getRandomJokeObservable() { 
 return new IcndbIntegrationCommand().observe(); }
 
 //…
 
 private class IcndbIntegrationCommand extends HystrixObservableCommand {
 
 //…
 
 @Override
 protected Observable resumeWithFallback() {
 return fallback.getRandomJokeObservable();
 }
 }
 }

Slide 27

Slide 27 text

MONITORING & DIAGNOSABILITY METRIKEN LOGS TRACES DASHBOARDS & ALERTING

Slide 28

Slide 28 text

DAS MAGISCHE DREIECK DER DIAGNOSTIZIERBARKEIT Metriken Logs Traces Cluster-wide
 Diagnosis

Slide 29

Slide 29 text

DIAGNOSTIZERBARKEIT: METRIKEN DROPWIZARD METRICS JERSEY HYSTRIX MICRO SERVICE PROMETHEUS SERVLET METRICS SERVLET HYSTRIX SERVLET Dashboard / Turbine Health 
 Checks Service
 Lookup

Slide 30

Slide 30 text

INSTRUMENTIERUNG @ApplicationPath("/chuck")
 public class ChuckJokeApplication extends ResourceConfig {
 public ChuckJokeApplication() {
 super();
 //Instrument application with metrics
 MetricRegistry METRIC_REGISTRY = MetricsProvider.getMetricRegistryInstance();
 register(new InstrumentedResourceMethodApplicationListener(METRIC_REGISTRY));
 HystrixPlugins.getInstance().registerMetricsPublisher(
 new HystrixCodaHaleMetricsPublisher(METRIC_REGISTRY));
 //Register Prometheus metric exporter
 CollectorRegistry.defaultRegistry.register(new DropwizardExports(METRIC_REGISTRY));
 }
 } Die eingehenden REST-Aufrufe instrumentieren Die ausgehenden REST-Aufrufe instrumentieren Alle Metriken für Prometheus bereitstellen Die MetricRegistry holen (Singleton)

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

HYSTRIX DASHBOARD

Slide 33

Slide 33 text

SERVICE DISCOVERY SERVICE REGISTRATION SERVICE LOOKUP SERVICE HEALTH CHECKING & API GATEWAY API EXPOSITION & REQUEST ROUTING AUTHENTICATION & AUTORISATION LOAD BALANCING & SHEDDING RATE LIMITING REQUEST MONITORING & AUDITING

Slide 34

Slide 34 text

DAS BIG PICTURE MICRO SERVICE SERVICE DISCOVERY Services
 registrieren Interne 
 Services
 registrieren &
 auflösen Web Requests Routen ermitteln
 (per Polling) API GATEWAY Health
 Checks java.security.Security.setProperty("networkaddress.cache.ttl", "3" ); Web Request

Slide 35

Slide 35 text

SERVICE REGISTRATION IN EINEM JEE MICROSERVICE @ApplicationPath("/chuck")
 public class ChuckJokeApplication extends ResourceConfig {
 
 @Inject
 public ChuckJokeApplication(
 @ConsulFabio IServiceDiscovery serviceDiscovery) {
 super();
 //Register service
 serviceDiscovery.registerService( "zwitscher-chuck", "/chuck/joke");
 }
 } Service-Name URL-Pfad zum Service-Endpunkt

Slide 36

Slide 36 text

SERVICE REGISTRATION: HEAVY LIFTING /**
 * Registeres a service
 *
 * see https://github.com/eBay/fabio/wiki/Service-Configuration
 */
 public synchronized void registerService(String serviceName, String servicePath) {
 
 String applicationHost = getOutboundHost();
 int applicationPort = getOutboundPort();
 HostAndPort consulEndpoint = getConsulHostAndPort();
 logger.info("Will register service on host {} and port {} at consul endpoint {}",
 applicationHost, applicationPort, consulEndpoint.toString());
 
 //generate unique serviceId
 String serviceId = serviceName + "-" + applicationHost + ":" + applicationPort;
 String fabioServiceTag = "urlprefix-" + servicePath;
 
 //point healthcheck URL to dropwizard metrics healthcheck servlet
 URL serviceUrl = UrlBuilder.empty()
 .withScheme("http")
 .withHost(applicationHost)
 .withPort(applicationPort)
 .withPath("/metrics/ping").toUrl();
 
 // Service bei Consul registrieren inklusive einem Health-Check auf die URL des REST-Endpunkts.
 logger.info("Registering service with ID {} and NAME {} with healthcheck URL {} and inbound ROUTE {}",
 serviceId, serviceName, serviceUrl, fabioServiceTag);
 
 //use consul API to register service
 ConsulClient client = new ConsulClient(consulEndpoint.toString());
 NewService service = new NewService();
 service.setId(serviceId);
 service.setName(serviceName);
 service.setPort(applicationPort);
 service.setAddress(applicationHost);
 List tags = new ArrayList<>();
 tags.add(fabioServiceTag);
 service.setTags(tags);
 //register health check
 NewService.Check check = new NewService.Check();
 check.setHttp(serviceUrl.toString());
 check.setInterval(ConsulFabioServiceDiscovery.HEALTHCHECK_INTERVAL + "s");
 service.setCheck(check);
 client.agentServiceRegister(service);
 }
 
 public static String getOutboundHost() {
 String hostName = System.getenv(HOSTNAME_ENVVAR);
 String host = System.getenv(HOST_ENVVAR);
 if (hostName == null && host == null) return DEFAULT_HOST;
 else if (host != null) return host;
 else {
 File etcHosts = new File("/etc/hosts");
 List lines;
 try {
 lines = Files.readLines(etcHosts, Charset.defaultCharset());
 } catch (IOException e) {
 return DEFAULT_HOST;
 }
 for (String line: lines){
 if (!line.trim().startsWith("#") && !line.trim().isEmpty()) {
 String[] etcEntry = line.split("\\s+");
 if (etcEntry[1].equals(hostName)) return etcEntry[0];
 }
 }
 return DEFAULT_HOST;
 }
 }
 
 public static int getOutboundPort() {
 String portEnv = System.getenv(PORT_ENVVAR);
 if (portEnv == null) return DEFAULT_PORT;
 return Integer.valueOf(portEnv);
 }
 
 public static HostAndPort getConsulHostAndPort() {
 String consulEnv = System.getenv(CONSUL_ENVVAR);
 if (consulEnv == null) return HostAndPort.fromString(CONSUL_DEFAULT_HOSTANDPORT);
 else return HostAndPort.fromString(consulEnv);
 } @ConsulFabio
 @ApplicationScoped
 public class ConsulFabioServiceDiscovery implements IServiceDiscovery { den Host-Namen und Port ermitteln, den Consul sieht den Health-Check Endpunkt ermitteln (MetricsServlet) Metadaten für Fabio erzeugen Service bei Consul registrieren
 (ebenso wie Diagnose- und Doku-Endpunkte)

Slide 37

Slide 37 text

ET VOILA: CONSUL MIT 3 REGISTRIERTEN SERVICE-INSTANZEN

Slide 38

Slide 38 text

ET VOILA: FABIO DIREKT AUS CONSUL BEDIENT

Slide 39

Slide 39 text

SICHT DES APPOPS CLOUD NATIVE JEE

Slide 40

Slide 40 text

DIE SICH SELBST AUSLIEFERNDE SOFTWARE RUNNING
 TESTED
 SOFTWARE
 INCREMENT EVERYTHING AS CODE: •die Anwendung •die Tests •die Infrastruktur •der CD-Workflow {

Slide 41

Slide 41 text

WICHTIG SIND KURZE ROUNDTRIP-ZYKLEN In-Process Local Cluster Remote Cluster •längere Roundtrips •aber: realistischer Vollständiges Deployment: Einzelne Services: https://github.com/qaware/gradle-cloud-deployer

Slide 42

Slide 42 text

MARATHON APP DEFINITION {
 "id": "/zwitscher",
 "groups": [
 {
 "id": "/zwitscher/infrastructure",
 "apps": [
 {
 "id": "consul",
 "cpus": 1,
 "mem": 256,
 "disk": 0,
 "instances": 1,
 "cmd": "/bin/consul agent -server -ui -advertise=$HOST -config-dir=/config -data-dir=/tmp/consul -bootstrap-expect=1 -node=consul-server -client=0.0.0.0",
 "container": {
 "docker": {
 "image": "gliderlabs/consul-server:0.6",
 "forcePullImage": true,
 "privileged": false,
 "network": "HOST",
 "portDefinitions": [
 { "port": 8300, "protocol": "tcp", "name": "server-rpc" },
 { "port": 8301, "protocol": "tcp", "name": "serf-lan" },
 { "port": 8302, "protocol": "tcp", "name": "serf-wan" },
 { "port": 8400, "protocol": "tcp", "name": "cli-rpc" },
 { "port": 8500, "protocol": "tcp", "name": "http-api" },
 { "port": 8600, "protocol": "udp", "name": "dns" }
 ],
 "requirePorts" : true
 }
 },
 "env": {
 "GOMAXPROCS": "10"
 },
 "healthChecks": [
 {
 "protocol": "HTTP",
 "port": 8500,
 "path": "/v1/status/leader",
 "intervalSeconds": 10,
 "timeoutSeconds": 10,
 "maxConsecutiveFailures": 3
 }
 ]
 },
 {
 "id": "fabio",
 "cpus": 1,
 "mem": 256,
 "disk": 0,
 "instances": 1,
 "env": {
 "registry_consul_addr": "consul.infrastructure.zwitscher.marathon.mesos:8500"
 },
 "container": {
 "docker": {
 "image": "magiconair/fabio:latest",
 "forcePullImage": true,
 "privileged": false,
 "network": "HOST",
 "portDefinitions": [
 { "port": 9998, "protocol": "tcp", "name": "web-ui" },
 { "port": 9999, "protocol": "tcp", "name": "proxy-port" }
 ],
 "requirePorts" : true
 }
 },
 "acceptedResourceRoles":["slave_public"],
 "healthChecks": [
 {
 "protocol": "HTTP",
 "port": 9998,
 "path": "/health",
 "intervalSeconds": 10,
 "timeoutSeconds": 10,
 "maxConsecutiveFailures": 3
 }
 ]
 }
 ]
 },
 {
 "id": "/zwitscher/services",
 "apps": [
 {
 "id": "zwitscher-chuck",
 "cpus": 1,
 "mem": 256,
 "disk": 0,
 "instances": 1,
 "container": {
 "docker": {
 "image": "adersberger/zwitscher-app-chuck:1.0.0-SNAPSHOT",
 "forcePullImage": true,
 "privileged": false,
 "network": "HOST",
 "portDefinitions": [
 { "port": 12340, "protocol": "tcp", "name": "rest-api" }
 ],
 "requirePorts" : true
 }
 },
 "env": {
 "PORT": "12340",
 "CONSUL": "consul.infrastructure.zwitscher.marathon.mesos:8500",
 "CONFIG_ENV" : "zwitscher"
 },
 "args": [
 "-Xmx256m"
 ],
 "healthChecks": [
 {
 "protocol": "HTTP",
 "port": 12340,
 "path": "/metrics/ping",
 "intervalSeconds": 10,
 "timeoutSeconds": 10,
 "maxConsecutiveFailures": 3
 }
 ],
 "dependencies": [
 "/zwitscher/infrastructure/consul"
 ]
 }
 ]
 }
 ]
 } marathon-appgroup.json /zwitscher /infrastructure /consul /fabio /service /zwitscher-chuck dependency "healthChecks": [
 {
 "protocol": “HTTP", "port": 9998, "path": "/health",
 "intervalSeconds": 10, "timeoutSeconds": 10, "maxConsecutiveFailures": 3
 }
 ] Health Checks: "network": "HOST",
 "ports": [9998, 9999],
 "requirePorts" : true Networking: "acceptedResourceRoles":["slave_public"] API Gateway auf den Public Slaves: "env": {
 "PORT": "12340",
 "CONSUL": "consul.infrastructure.zwitscher.marathon.mesos:8500"
 },
 "args": ["-Xmx256m"] Konfiguration: "container": { "docker": {
 "image": "adersberger/zwitscher-app-chuck:1.0.0-SNAPSHOT",
 "forcePullImage": true Immer das aktuelle Docker Image bekommen: "dependencies": [ "/zwitscher/infrastructure/consul"] Startup-Abhängigkeiten (eher zu vermeiden):

Slide 43

Slide 43 text

FAZIT CLOUD NATIVE JEE

Slide 44

Slide 44 text

FAZIT ▸ Cloud Native JEE Anwendungen entwickeln ist möglich und macht Spaß ▸ Die JEE API ist per se noch nicht Cloud Native und wird dies auf absehbare Zeit auch nicht sein, kann aber entsprechend ergänzt werden, z.B. mit unserem JCloudEE Mini-Framework:
 
 
 
 
 
 ▸ Kurze Roundtrip-Zeiten sind essenziell für die produktive Entwicklung von Anwendungen. Bei Cloud Native Applications ist dabei die volle Portabilität von lokaler Ausführung bis auf ein Cluster in der Cloud essenziell.

Slide 45

Slide 45 text

45 CompletableFuture ich = CompletableFuture.supplyAsync(() -> erfindergeist() .handwerksstolz() ); CompletableFuture qaware = CompletableFuture.supplyAsync(() -> professionalität() .lässigkeit() ); Erfolg start = qaware.thenCombine(ich, (i, q) -> i.sendeBewerbung(q)) .join(); Weiter Details unter http://www.qaware.de/karriere/#jobs

Slide 46

Slide 46 text

https://www.qaware.de https://slideshare.net/qaware https://github.com/qaware & [email protected] @adersberger https://github.com/adersberger/cloud-native-zwitscher-jee