Planning to build microservices? The best practice of building a first-class Cloud Native Microservice is to follow12-Factor app (https://12factor.net/). But how do you fulfill the 12-factor app rules, e.g. how do you externalize the configuration, fault tolerance, etc?Come to this session to watch the live coding of building 12-factor microservices using MicroProfile programming mode (microprofile.io) and get them running Open Liberty (openliberty.io) and Quarkus (https://quarkus.io/). Finally, deploy them to Kubernetes and Istio to see how they function!
Talk Text
Emily Jiang (00:05):
Hello! Welcome to my session of Live Coding Twelve-Factor App. I am Emily Jiang from IBM. I am a Java Champion and Open Liberty Microservice Architect. In this session, I will briefly introduce the Twelve-Factor App, followed by live coding two 12-factor apps.
Emily Jiang (00:28):
Twelve-Factor App is a methodology based on practices from the manifesto published by Heroku. Let me first tell you about the Twelve-Factor App: Why, what, and how style. Why do we need the 12 factors? The Twelve-Factor defines the contract between applications and infrastructure, so that the application and infrastructure can each focus on their own domain, so that they don't step into each other's toes.
Emily Jiang (01:09):
What is Twelve-Factor App? Twelve-Factor App defines a clean contract between the application and the environment. Twelve-factor apps are suitable for deployment on modern cloud platforms. It minimizes divergences between development and production, enabling CI/CD.
Emily Jiang (01:33):
Twelve-factor apps can easily scale up or down without significant changes. The reason they called it "Twelve-Factor App" is because it has 12 factors, from the code-base to admin processes. I will briefly describe each of them later.
Emily Jiang (01:57):
How to build a 12-factor app? MicroProfile, and now Kubernetes, come to the rescue. In my demo, and also when I go through each individual factor, you will understand. Let me first tell you a little bit about MicroProfile.
Emily Jiang (02:17):
MicroProfile was established in 2016, by IBM, Red Hat, Payara, Tomitribe, the London Java Community, and later on, Oracle and Microsoft all joined. It's lightweight, iterative processes. It uses lightweight, iterative processes. It's completely community-driven: Spec, APIs, and TCKs are all freely available in the public domain.
Emily Jiang (02:51):
MicroProfile community has over a thousand Windows and Java user groups, around 169 individual contributors, and also, as a community, is still growing. About a dozen independent implementations, so here listed are these implementations: Open Liberty and WebSphere Liberty from IBM.
Emily Jiang (03:15):
Red Hat has Quarkus, WildFly, and Thorntail, and together with some other companies provides Payara, Apache TomEE, Helidon from Oracle, Launcher from Fujitsu. And also includes other communities such as, other companies, KumuluzEE, Hammock, and Piranha.
Emily Jiang (03:44):
So, MicroProfile adopts very iterative, rapid-growing processes. So, each year we have three releases. In February 2020, we released on the MicroProfile repository 3.3. So, in the next few slides I'll go through each individual factor, quickly, and give you a few quick overviews.
Emily Jiang (04:13):
Code-base: One code-base tracking in version control, and many deploys. The best practice is to use GitHub: One 12-factor app, one GitHub repo.
Emily Jiang (04:27):
The second one is dependencies. You should explicitly declare and isolate dependencies. You can use Maven and Gradle to specify your dependencies.
Emily Jiang (04:39):
The next one is the config. You should store the config in the environment. In this way, you can use MicroProfile Config to inject the configure property and then the property can be stored in the cloud infrastructure, such as Kubernetes ConfigMap or secrets, and et cetera.
Emily Jiang (05:03):
What is MicroProfile? MicroProfile provides several APIs for you to directly inject a set of properties. You can either use a programmatic lookup or by a CDI injection.
Emily Jiang (05:03):
When using a programmatic lookup, you can do a ConfigProvider.getConfig(), and then config.getValue(), and specify the configure property name, and then the type.
Emily Jiang (05:29):
Or, you can use CDI. You can use @Inject, @ConfigureProperty, and then property name, and it then uses a type.
Emily Jiang (05:41):
Backing services. You should always treat backing services as attached resources. In this way, you can easily swap with attaching resources. The result of this is to use a MicroProfile REST client.
Emily Jiang (05:57):
For example, microservice A needs to use service B. So, in service A you can specify service B's interface, and then you can annotate it with @RegisterRestClient. Then you can inject that REST client, and then you can freely pull any method on that class.
Emily Jiang (06:19):
And then, how can you find service B with service A? Let's use MicroProfile Config. That would be a fully qualified class name for that interface, say it's from a client, and then the value will be a service-based endpoint. I will demo it later, so you will understand it.
Emily Jiang (06:41):
So, the next one is build, release, and run. Strictly separate the build and release, and run stage. You should adopt, or consider adopting, a CI pipeline such as Tekton.
Emily Jiang (07:00):
The next one is processes. Execute an app as one or more stateless processes. You should consider adopting a RESTful API. So, in this way, all the processes are stateless, so you can easily scale up or down without losing any information.
Emily Jiang (07:21):
The next one is port binding. You should export services by port binding. Don't hard-code your port. Use MicroProfile Configure to inject the port, and then you can use cloud infrastructure to assign that particular port.
Emily Jiang (07:44):
The eighth one is concurrency. You can scale out via the process model. You can consider to use Kubernetes autoscaling to scale your processes, depending on the load.
Emily Jiang (08:01):
The next one is disposability. So, your microservices should start up fast, resist being shut down, and should be very resilient. For a fast startup, resisting shutdown, you should consider to use a very lightweight runtime such as Open Liberty or Quarkus, which I will demonstrate later.
Emily Jiang (08:24):
And for building the resilient microservices, you can adopt MicroProfile for Torrance. MicroProfile for Torrance offers these capabilities—such as retry, circuit breaker, bulkhead, timeout, fallback, and et cetera—to enable you to build a resilient microservice.
Emily Jiang (08:48):
So, the next one is dev/product parity. You should keep development, staging, and production as similar as possible in terms of code, people, and environment. You should consider using Operators to deploy in a repeatable manner.
Emily Jiang (09:07):
The next one is to treat logs as event streams. The best practice is that you should system-out the logs, and then you can attach a ELK, can directly streaming out.
Emily Jiang (09:22):
The next one is admin processes. You should run admin and management tasks as one-off processes. Utilize Kubernetes tooling, such as Kubernetes Jobs, and et cetera. Don't hard-code admin processes in your microservices.
Emily Jiang (09:43):
So, in summary, so, Twelve-Factor can be achieved by using GitHub, Maven, Gradle, MicroProfile, Kubernetes, and best practices. So, how can I create a 12-factor app? You could either use ... uh, is two available solutions, plus others, as well.
Emily Jiang (10:07):
But in this demo, I mention these two. In this session I mention these two. You can either use or start MicroProfile.io or use Appsody.dev. They are all open source. So, in the next 20 minutes, I will create two microservices.
Emily Jiang (10:24):
One is service A on Liberty. The other one is microservice B on Quarkus. Let me share my screen. And then try to ... and then they will talk to each other. So, service A will directly call into service B. Okay, screen.
Emily Jiang (10:52):
In order to save time, I already created service B. So, service B ... finding, so I can ... and then also I started service B, and this is service B. So, service B has an endpoint like doSomething, PathParam, and parameter, and also is ... as a client service, is JAX-RS as the resource.
Emily Jiang (11:24):
Service B is a database process. So, you can see, here is the application. So, the endpoint will be data on the new client service, and then you can pass in the parameter. Also, it is started in dev mode. You can see this started, and the port is 8080.
Emily Jiang (11:54):
Okay, let me create my service A. So, I go to start it on MicroProfile.io. I want this to be on Liberty. I will choose MicroProfile version 3.2 on Open Liberty. I want to choose the health and client-server architecture, and then Okay.
Emily Jiang (12:21):
So, actually ... I can go to the service A. Making it bigger, so I can show you the code. So, service A is quite simple. For this, I particularly want to show you this endpoint, since this is a client that directly calls back to the server.
Emily Jiang (13:22):
So, this is a ... I mentioned earlier, service B's interface is doSomething and the PathParam is a get operation. So, I define this interface annotation with Liberty's RestClient, and then I can inject RestClient into my JAX-RS resource, and then I can call the operation doSomething.
Emily Jiang (13:44):
And how can I wire it up? How do I know where the service B lives? That's a service binding. So, I use MicroProfile config. So, that would be a fully qualified class name for this interface, and then /mp-rest/url. Earlier, I said that service B lives on 8080/data/client/service, so I save that.
Emily Jiang (14:16):
So, now, I can start my service A on the dev mode, because it runs on Liberty. And, uh, Liberty offers the development mode. So, basically, if you want to change anything, without doing a change of anything, you can see the changes straightaway, without doing anything. So, it will automatically reflect the changes.
Emily Jiang (14:40):
So, as you can see, it's like which feature has been added, and also it tells you it lives on a local host on 9080. Now, I go to local host 9080. So, this is directly that endpoint. So, it is calling to service B. So, this is set at this is Quarkus service B, really.
Emily Jiang (15:06):
To demonstrate that it really is calling to service B, I can say on ... Thursday. You can see straightaway on Thursday. So, this is like ... I wired them up as a client-service architecture.
Emily Jiang (15:35):
The other thing I want to show you is, as you can see here, I put in some sleep and also the runtime exception to demonstrate that, uh, like, this service is not stable. It's sometimes not working, as I just tried to demonstrate that.
Emily Jiang (15:53):
Because this time is really slow, uh, and this is okay, and then it's not working. So this is the kind of setup, a scenario, realtime, real, uh realtime, scenario, and like this is not reliable.
Emily Jiang (16:12):
Okay, next one I want, like, an improved service A. So, for service A, so the first thing is, in my response here, actually I ... in order to retune this one, also I want to add something, and say Emily is speaking at a virtual conference.
Emily Jiang (16:51):
If I do something like this, like if later I will go to IBM, or IBM thing, or some other conference, and et cetera, I have to change this class and recompile it. So, luckily, MicroProfile Config can help you to externalize your configuration.
Emily Jiang (17:14):
So, basically, I want to put this into a variable, so I can use it to say in here ... is actually it's, uh, this one is a conference, equals virtual summit. So, in here, if I know how to code this I can use a variable. That variable, I want to inject a value, so I can use MicroProfile Config to directly inject a "conference" property, which is a String prompt.
Emily Jiang (18:03):
It's a con ... save this. So, I save this ... as I go back here. See, Emily is speaking at a virtual summit. So, the other thing is, as you can see, sometimes it's not working; like, uh, sometimes you get exceptions, and sometimes it is slow.
Emily Jiang (18:35):
Let's use ... let's improve the resilience aspect of this service A. So, what can I do, then? So, it's luckily MicroProfile for Torrance can help you to improve your resilience. So, in the service A I can ... insert a retry.
Emily Jiang (19:04):
Once I have a retry and I will be able to get a good response all the time. See, the response. But, occasionally, it is still slow. So, but in some, like, uh, situations—a mission-critical situation—you want your service to be fast. So, again, you use a MicroProfile for Torrance timeout.
Emily Jiang (19:38):
You say okay, I want a timeout if you cannot come back within, like, one hundred milliseconds. And, together with a timeout, and, uh, the best practice is to use asynchronous programming. So, I can annotate with @Asynchronous.
Emily Jiang (20:02):
So, here I will return our CompletionStage. And here, I will create our CompletionStage. And here is the CompletableFuture, put CompletableFuture. And then I can put the CompletionStage. And save it.
Emily Jiang (20:32):
Let's go back here. It's the same. So, the server is not up. So, service A threw an error, it said unable to ... See, this is the beauty of the ... This is the beauty of the dev mode, so if something not working, and you will get an error.
Emily Jiang (21:19):
So, here it says I need to insert a bracket. Let's go to here. So, it's, uh, complete. It is, and it is. Now, is everything compiled? I can see my ... You can see it's all worked out well. Beautifully.
Emily Jiang (21:51):
So, what happened and the learning? So, basically it's, one, have a timeout. And you will get a timeout if it doesn't return within 100 milliseconds, you will get a timeout exception, and then that will trigger retry, and then you get a quicker response back.
Emily Jiang (22:10):
The other best practice is to add a fallback, and provide some backup operation. So, for example, here I say fallback, and then I define the fallback method. And, uh, I can see here ... this is my fallback.
Emily Jiang (22:43):
So, this is very useful, because in the cloud infrastructure, and sometimes things will go wrong, and you don't know. It's always good to provide some contingency plan. So, this is briefly talk about wire the service together, and increase the resilience, and then config it.
Emily Jiang (23:07):
So, next thing is I want to Dockerize it, and then push it to the Kubernetes. So, in order to do that, I just build it, and then verify. And similarly for the ... microservice A, and microservice B ... I can do ... a build, as well.
Emily Jiang (23:23):
And then, after I build it, I can build the Docker image, and say Docker build ... build a Docker image. So, similarly for the service A, I can also do a Docker image. I can, uh, copy a Dockerfile. So, and then I can build a Docker image.
Emily Jiang (24:10):
So, the next thing is, while it's building, and then I should I look at the deployment. So, I created the deployment in advance, so I briefly discuss with you about this deployment.
Emily Jiang (24:10):
So, for two services you have one deployment each. So, for service A you have ... image: liberty-service-a, and with this is image on port 9080. And similarly for service B, is 8080. And service A is exposed by HTTP on the node port 380.
Emily Jiang (25:17):
Service B is using cluster IP, so its service name is factor-app-b-service, and it is on port 8080. So, the build is, I didn't know how to code the port and the host, and then again, my Kubernetes, the deployment YAML file, I can say the back end and the service B actually now lives in the factor-app-b-service at 8080.
Emily Jiang (25:44):
So, in this case, my service will directly call this back end. And the other thing, as you can see previously in my app, I have a hard code of the virtual conference, and now I can change it. In that case, in the future, if I go to a different conference, I can just change this value, without redeploying my application.
Emily Jiang (26:08):
And then this is MicroProfile Health. Expose this endpoint can be plugged in to serve as my readiness probe. Similarly, for this ready, start health is start-ready endpoint, as well.
Emily Jiang (26:24):
So, now, the all build okay. So, service A, finish building, and as well as service B. So, I can actually, in order to save time, I already started ... already deployed them. So, they are running and you can see is ... get services.
Emily Jiang (26:55):
You can see it is live on 380, right, 380. So, if you go here, 380 ... oh, co-host ... 380. So, it's up. The value specifying the YAML file is used, now it's working, service B.
Emily Jiang (27:37):
So, this is briefly a little demo to demonstrate how to build the Twelve-Factor. Let's go back to the slides. Let me stop screen share. Okay, so I demonstrated how to use MicroProfile in Kubernetes to build a Twelve-Factor App, and very flexible.
Emily Jiang (28:11):
So, as you can see, the first one is code base. So, service A will be one and service B will be another GitHub repo, and deployment will be checked in under its own separate GitHub repo.
Emily Jiang (28:26):
They are all under virtual control. Dependencies and all the dependencies for service A and B is all specified in the Maven file. And config, backend service, and port binding process is ... and this possibility I demonstrated using MicroProfile.
Emily Jiang (28:50):
Build, release, and run and dev/product parity. Yeah, I did all the development and I pushed to the cloud myself. And the logs, I didn't demonstrate, but if you have logs, use the system out.
Emily Jiang (29:06):
And concurrency. Concurrency, as you can see, like, I used the Kubernetes deployment and YAML. You see the two-ports setup per service, because a replica is two. So, you can use the Kubernetes auto-scaling to auto-scale it.
Emily Jiang (29:25):
So, admin process. If you have admin process, and remember to use Kubernetes Jobs. So, these are the references, the things I have demoed is in my personal GitHub: vsummit-12factor-app-a and b, and then deployment.
Emily Jiang (29:46):
And then some useful ... like, OpenLiberty.io has a lot of useful MicroProfile guides, if you want to know more about MicroProfile. I know Quarkas.io, similarly, has a lot of information, as well.
Emily Jiang (30:01):
So, that is ... If you have any questions, and et cetera, feel free to get in touch, to get connected with me. My Twitter handle is @emilyfhjiang. This concludes my presentation. Hope you enjoy it.
Emily Jiang (30:24):
Thank you for listening. Bye.
Session Speaker
Emily Jiang
IBM
Java Champion, MicroProfile guru, IBMer, passionate about Java +Jakarta EE, conference speaker.