Spring Tips: Spring and Kubernetes, Redux (2022)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right the first thing we're going to do is we're going to build applications that work well considering the production environment obviously we're not talking about the application software here itself what we care about is how that application lives in production and there are two dimensions to that obviously we have to first get to production that means packaging your application in such a way that it can be used effectively in production and then there is the ongoing life of the application once in production and we support that ongoing life through observability so we'll look at those two dimensions but we'll do so by looking at a real application we're going to build a service which i'll just call service we'll use java 17. we're going to bring in the spring native support the actuator support the h2 in memory database r2dbc and the reactive web support we'll hit generate and open this up in our ide [Music] now this is just a traditional typical application we'll it's going to talk to a database so we'll create a type customer integer id string name and we'll have an id field and we'll have a reactive repository to manage data access with that type like so and you know that's pretty boring but that does give us most of what we want now what i want to also do is to support um writing some data into the database when the application is ready so i'll create a listener here and you'll see me do this or some variation of this a lot i'd like to initialize things when the application starts up and so here all i'm going to do is i'm going to say i'm going to put some people in the database some some friends of mine right so okay and then e is a long one there we go and we'll say for each name i want to turn it into a new customer like so and then for each one of those i just want to save the record in the database using the aforementioned uh just created customer repository okay so we'll say custom repository dot save c good and then we'll just subscribe to the results like so okay good there's our listener and um and that's basically i guess we can create a rest endpoint something very simple to show us the data so we'll say add controller at response body class customer http controller and you know i'm going to inject this repository and i could create the constructor myself but i prefer to use lombok for this kind of stuff to generate the constructors for me for all final fields uh and uh that's easy enough like just say at required args i have to refresh this of course good required args constructor alright and with that done we just create an endpoint customers get this.repository dot find all okay there's our basic repository now um this is it this is not important this is not interesting it's just a trivial boring application what i care about is observability first so i will enable certain endpoints that allow me to better understand how the application is doing now i won't revisit all the details of the actuator the actuator are a set of managed http endpoints you should use them they give you visibility into the state of the application in particular though what i care about are the kubernetes probes okay so i'm going to say i want to enable all the health details just for our demo here and i want to enable the probes which i've i can specify like so enable probes and then i'll restart the application the application is complaining because we don't have a database schema okay so easy enough schema. sql create table customer id serial primary key name var car 255 not no okay good there we are there's our application it's up and running we can see as much customers okay everything is fine what i care about like i say is the actuator there's all these different endpoints and we don't have time to look at all of them but what we care about here is the health endpoint which gives me information about the health of the application right so you click on health and you can see it shows you all the different sub systems that might fail your disk space your your connection factory to the database etc you also have readiness and liveness in kubernetes a liveness probe tells kubernetes whether the service the process the web app whatever is still running a readiness probe tells it whether it's ready to start running and so you can access these endpoints through the actuator health liveness and actuator health readiness probe endpoint your eyes all right that gives us the ability to ask is the service okay if something goes wrong and the service becomes sick the container orchestrator will kill our pod it'll destroy the pod and then deploy a new instance we want to make sure that if we are doing anything uh while the application is running and while the service is being shut down that we have some time to let that work finish okay so we will use graceful shutdown okay and of course we want to make sure that we have enough time so you can configure how much time to wait between the sig term and sig kill signal is sent to the pod you have to make sure that this value lines up with the configuration for the grace period in your kubernetes pod or service or deployment configuration let's now i want to be able to test all this out and so one thing that's very interesting here is that we can actually influence the probe uh by using events some of you may know that spring actually has an event bus where components are able to talk to each other uh one component to another in the same application context so let's create availability http controller and this will use private final application context and we're going to create an endpoint here called down when you call this endpoint we'll publish an event uh on in the context telling the rest of the application including the liveness probe that something is broken i also want to have an endpoint called slow and when you call this it'll create some work that takes a long time right i'll just arbitrary block and so i'll say down throws exception sorry slow okay thread dot sleep 10 so 10 milliseconds uh sorry 10 seconds 10 000 milliseconds okay let's now restart this okay we can go here we can see uh the liveness is up and if i go to the command line curl minus v i can see it says 200 now that we have the application up and running i'm going to call the slow endpoint and then i'm going to try and kill it as we imagine the kubernetes system would do and you can see it says commencing graceful shutdown waiting for the active request to complete a few seconds later the request completes and the application process stops normally now of course we have published an event to trigger a change in the state but it's also worth noting that you can listen for that change as well so availability change event return and here i'm just going to log out the results i'll say system out event okay let's restart that okay you can see it says correct accepting traffic now if we call down you can see it says broken right so it's good to be able to listen for those events to understand when the application is changing its health okay we've got an application that's observable and manageable in production let's talk about getting it packaged for production the first thing we want to do is to take advantage of graviem native image compilation growl vm is a just in time compiler but it also provides a component called the native image compiler that proactively compiles your application into operating system and architecture specific binary code we're going to use that to turn our application into a very lightweight application that takes very little ram at uh production time so we'll say maven skip tests using the native profile clean package now this will take about a minute remember behind the scenes the gravim native image compiler is doing an analysis on all the code in the application it's looking at all the types that we use in our code and all the types that they use and all the types that those types use and it's determining which types are to be kept and which should be thrown away from the class path and from the java runtime there is a bit of a problem here in that this static compile time analysis can't see all the dynamic things that you might do at runtime using reflection jni proxies serialization loading resources from jars etc so it's important that there be something to contribute configuration to tell the gravium native image compiler not to throw things away that thing is called spring native spring native is a project that we created to support graviam native image compilation for spring framework five based applications and workloads that said the real goal is to get to spring framework 6 which is due later this year in 2022 spring framework 6 has built-in support for graviem native image compilation you don't need a separate project like you do for spring framework 5 and springboot 2. all right there's our application we'll go to the target directory and you can see that the compiled application is not very big that is very quick to start as well um so you know if i control see it start again you know it's very very fast 50 57 thousandths of a second right very very quick and it's ready for packaging for production now of course i could build my own docker file but i much prefer to just let the tools do the job for me one great way to do that is to use build packs build packs are a great way to take an existing application like a java application and to turn it into a a container and in this case i'm on a macintosh m1 so i can't use the default build pack yet but you can if you're using linux or if you're in uh mac intel but for now i'm going to use a separate standalone builder that my colleague built to show you what happens maven skip the tests clean spring boot build image we'll let this run it'll take about three minutes and it'll create a linux it'll take the code compile it inside the linux dock or container and then give us a docker image that has a linux binary in it that we can then docker tag and docker publish and then use in our kubernetes cluster it is based on the amazing build packs technology ctrl c docker run and there's the application running inside of a linux container on mac right i'm running on docker desktop on a mac running linux over emulation on my m1 and it's still only 67 000 of a second all right so clearly there is a lot of potential here for getting an application to production all right we've got an application of course our clients are going to want to connect to it this isn't kubernetes specific per se but i do think it's a good testimony to some of the incredible capabilities that arise when you have a really really powerful container orchestrator like kubernetes that makes deploying applications a snap once you feel ease and comfort in deploying the applications then you have no fear in deploying lots of them and this is very useful because you will have lots of different clients and they will need to talk to those services and so in this section we're going to look at some integration technologies we're going to build a new service here called edge we use the spring cloud gateway the reactive web support the spring for graphql support and i think that is probably enough for now now an edge service is the first port of call for requests coming in from the outside world it's a logical uh place to adapt incoming requests to transform them to work with the downstream services so if you have android and iphone and all these different clients you can normalize those requests at the edge before forwarding them onto the downstream service the first thing we're going to use is spring cloud gateway spring cloud gateway is a great way to proxy or forward or work with the different kinds of payloads while operating on the envelope of the payload uh not usually the the the payload of the request right so you want to operate on the envelope of the request not the payload of the request you can operate on the payload request but when you're trying to do something that's aware of the payload that starts to become a more semantic thing so we're going to use a spring cloud gateway route and remember our edge service will live on port 99.99 and we're going to say that when requests come into force proxy they're going to we're going to forward them on to 8080 okay now i want to send them to 8084 customers so i'll use filters here to add the path the filters are one of the most powerful parts of spring cloud gateway i can add response headers i can do retries i can do uh retries 10 i can do timeouts i can do all sorts of things here i can do rate limiting i love that right i can do all sorts of different things here so let's let's just see what that looks like okay localhost 99.99 forward slash proxy oh did i forget to run the old service let's run this and then i'll run this one okay so now there we go there's the proxy data no problem at all ah because i'm using m1 i get these goofy errors uh you can fix that very easily by bringing in neti all and then reloading your build and restarting okay so we have a proxy this is good but a lot of times rather than having to add new views of the data it might be very useful to have a view that the client can consume and where the client can get schema introspection they can get uh the ability to ask for more or less of the data that they want and to support all these use cases i think graphql is quite nice so let's create a graphql service okay we'll call this graphql create a new file here called schema.graphql type query and uh you know we're gonna have an uh an endpoint here that gives us all the customer data well of course we need to actually enable the customer data so that we'll describe that type here and we're creating schema here to describe an endpoint that will live off of a well-known uh field called cust called that's a well-known type called query so let's create that graphql controller okay and i think we also want lum buck i don't think i brought it in by in my first generation okay required rx constructor and we're going to use the reactive non-blocking http web client okay builder.build and we're going to create a handler to resolve the um query that we are providing right so return all the customers now the customers we need to create a dto okay data transfer object so we'll say customer turn this dot http.git.uri http localhost 8080 forward slash customers retrieve the data body to flux customer.class okay so i'm just making a network call using my graphql endpoint and it's a resolver for a field in the well-known type called query i want to be able to interact with this data so i'll create i'll enable the graphical console i need to name this graphqls and then restart and there's our graphical endpoint and there's a graphical endpoint so now i can issue a query and i want to get all the ids and the names and there we go now of course the nice thing about graphql is i can easily add to it by changing the schema and the representation of the data in the schema can be separate or different from the the way that the data is stored in the backend services so for example i can add a new profile type that is a part of the customer but of course that could come from a separate micro service so for example schema mapping type name is equal to customer but i'm going to return a profile a new type now i don't have another microservice called uh profile service but you know you can imagine i did and then you would see that this is how you would resolve it you'd inject a reference to the root aggregate type you'd say customer to id and then restart okay so i want to now get all the profile data hit enter but if i if i hit control space and you can see there's a profile field if i don't ask for it great no problem i don't get it but if i want it i can ask for it as well and i get that data back so i don't pay the price of getting this data if i don't specify that i want the data so now if i want just the overview i can have that if i want the details i can have that graphql lets me have as much or as little of my data as i want and i don't have to build a new endpoint each time hopefully this has illuminated some possibilities for integration configuration is critical if you wanna have an application in production kubernetes provides two ways of communicating configuration to your application one is an object called a config map which is a dictionary of keys and values and another is a secret which is a dictionary of uh potentially hashed keys and values uh well values right both are work and are act the same for the purposes of what we're trying to do here today uh and that is to look at how the different ways that spring loop can read and work with configmaps in a spring boot and a spring cloud context when running on kubernetes so let's go ahead and build a new application here i'm going to call it configuration we're going to use the cloud bootstrap dependency the reactive web support lumbok and the spring boot actuator and we'll hit generate to generate a new project that we can open up in our ide [Music] so let's look at this application i'm just going to start up the application and i want this application when it starts up to listen for an event called application ready event and public void you know begin and we're just going to print out a message a key from the environment right so we're going to inject that environment here in the constructor and i'll just say the message is this.environment.getproperty message okay very good so there's my uh application but there is no message at the moment right i need to actually define a message so i could of course just say message equal message equals hello application.properties let's try that and that works just fine but what if i want to get configuration from a config map that's been connected to my application well the first most common ways to connect configmap configuration to an application are to either turn it into a uh as an environment in which case the keys and the config map just become environment environment variable names and the values of course become the values of those environment variables those just work by default so for example if i go to my program and go to environment variables message equals high env or evs environment variables okay hit apply hit ok i'm just going to restart okay so that worked right um of course you can do something similar with uh in program arguments you can say dash dash you know in the program argument section of your launch you would say hello pvs pa there you go right you could do that uh as well and that would go up here in in the program arguments they've made this menu so hard there it would go like that right now as environment variables this just works for free it's worked since springboot 1.0 uh you don't have to do anything special another alternative way to get that configuration is to get configuration uh in a config map turned into a volume a directory in which the uh contents are files and those whose files have as their names the config config map uh keys and whose values are the or whose contents are the the values of those uh config map entries until you can do that you can say i want to import config spring config import and you point it to a config tree structure so i'm going to pretend i'm running on kubernetes here and then i've got a config tree mounted on my desktop right so i'll make a directory here config cd config i'll say echo message sorry i'll say um yeah message uh it'll be hello config tree to message right so there's the file here's the directory the contents of the directory i'm going to connect my application thusly so that message will come from there instead of program arguments or environment variables or their application.properties file okay now this is i'm getting configuration as i would in kubernetes in a config tree i can in spring boot cordon off that kubernetes specific configuration and say that i want this to activate only when i'm in a uh part you know in a kubernetes context so activate on cloud platform kubernetes uh and so everything after this line you know and after the dash triple hash will be only activated when i'm inside of kubernetes i can also do this for objects for example here i can create a bean and i can say conditional on cloud platform cloud platform is kubernetes so application listener ready event turn args system out the application is running right uh something like that right i can i can create a bean um that is only active when boomer and kubernetes context now you are running locally uh obviously we're running locally on our machines here what we want to do is want to make our application behave as though it was running in production and i can do that by specifying the main cloud platform you can do this as an environment variable or program argument of course i wouldn't leave it here in application of properties for your production code obviously but um because it'll automatically detect whether it's in kubernetes or not and you may not want it to act like it's in kubernetes when you're not in kubernetes but for our demo i'm just going to leave that there just keep things simpler because everything i'm showing you henceforth depends upon us uh you know writing code in such a way that it thinks it's in kubernetes okay so we've got this event we've got this um specific thing that'll get run when we're in a kubernetes context let's restart okay so this application is running on kate thank you very much we know that now uh if we get this and we um by the way notice that we don't it didn't pay attention to that which is nice uh hit okay hit apply go back here and we can comment this out restart and there we go we have no message right and because it's not getting the config tree there's no built-in default value so we're just getting null there now i also want to get sometimes a configuration in a lively fashion right more dynamically right when i've shown you the config map and the environment variables works the first time you start the application but what if you change the config map whilst the application is running in this case i will uh want something a little bit more intelligent spring cloud has a module called spring cloud kubernetes and that module in turn contains support for both config maps you know getting config from configmaps directly and for using spring for using a kubernetes as a service registry so spring cloud starter kubernetes client config i'm just going to bring that into the class path and for it to do its work it's going to need configuration to tell it where to find the kubernetes cluster and what to look for once it gets there that needs to be resolved obviously before we actually get the rest of the configuration for the application that is we need to first connect to kubernetes and then once we've done that we get configuration from kubernetes that might then inform things like which port we're running on and so on so there's a life cycle here we need to do one thing before the other so to bootstrap that configuration resolution we typically use a file called bootstrap.properties and here we put things that we can use to identify the source of that configuration the first is that the application has a name called edge um we wanted to tell it to that we are going to monitor configmaps that's optional it's true by default but there's a similar property for monitoring secrets and that's false by default so if you want that make sure to enable this okay we are going to enable the reload of configuration it's off by default okay and we want to um tell it to use a particular mode right i think it's i don't match what the default is but it doesn't hurt dispatch but specify event and then we need to specify the namespace right and the name space is for both the config module as well as just a generic api client that we've got uh that that's fed into and that bootstraps the config line so kubernetes has there's two different connections here one to kubernetes and one two kubernetes for the config module in particular and so we need to specify the namespace okay so i'll say default and client namespace is true or just say default okay and that should be everything we need let's now try it out okay so we're expecting to get configuration from our kubernetes cluster we're going to apply this config map okay good okay get uh config maps edge minus o json and you can see it's got application of properties equals and it's got a single string which is a property file as we could use in spring where the message has a value of ninhau so we want to connect and see ninhau print it out in the application so let's restart the application okay it has failed here um because it doesn't think we're running in kubernetes i forgot to restore that and actually we probably want to make that clear even earlier since we're doing this now okay so ninhau is the value but now i want to get the value after i've changed it i don't want to have to reload everything so springboot has a mechanism for that called the refresh event and so here i want to say i want to get notified of both when the application starts and when configuration has been refreshed okay so and you can actually use a an annotation a scope spring bean scope called refresh scope on beans and they'll get recreated with new configuration when this event happens but you can also just listen for the event directly so i'm going to go ahead and restart and uh now we've got you know the ability to talk to the config config map we also want to be able to see if the value has changed and see if see if that works so let's go back to our desktop config map and you know just um make this a little bit more interesting i'll add some extra exclamation marks i'm going to save the file so just to confirm that everything has worked right so i'm going to now apply that but before i do let's make visible the console of my kubernetes app so desktop config map yamo applying and as soon as i did that the new value was immediately visible and it only restarted the info you know it triggered that event it took eight milliseconds uh to to do that work okay all right so we've looked at a number of different ways to get configuration into your springboard app in a dynamic way right when running in kubernetes you know kubernetes is an amazing place to run your spring boot applications but i also think spring boot is a great way to run your kubernetes cluster and what i mean by that is uh we have a unique opportunity with the arrival of native images in the spring boot ecosystem to build controllers operators things that actually manipulate the cluster uh for you there are metas meta applications that control the cluster that help do provisioning and deployment and things like that and there's no shortage of examples of clusters or or custom controllers that were written to support some of the use cases for which kubernetes just isn't you know well suited out of the box and it's a testament to kubernetes uh strengths that it is composable and flexible enough to support all these amazing extensions through uh its you know primitive atoms the building blocks of a given cluster like a pod and a deployment and through its very very impressive very extendable api that api is just that though it's just an api server it's just an api that manages it stores data basically you give it a yaml object and it writes it to a disk somewhere writes it to a cache and then you can look up that object via query you can actually say given this key give me the object it's kind of like a database and when people uh think of that they're like well that's that's ridiculous you know but actually it really is true it it's a database that stores data and then programs very much in the same way that jdbc clients connect to data sources like uh postgres or mysql or oracle database or whatever they connect to the api server and they look at the state of things and they do things in response to it right and this may seem magical or mysterious but it's really just a client service architecture or program sitting on your laptop can talk to kubernetes and ask it questions and it can create new things all programmatically right so you can actually create new deployments new pods do services new anything right you can you can also through this api server you can also define your custom objects your own objects these these are called custom resource definitions but again that's just another object of your own specification that doesn't mean anything unless a program acts on it right so if you create if you create an instance of an object in java but nothing does anything with that variable is that really interesting same thing for uh custom resource definitions you can create millions of instances of that but if no software is actually looking at that state and responding to it then nothing interesting happens and so when you see a controller when you see somebody who in that in you know that fraxel of a landscape page and the cncf landscape page has written their own custom controller what they're doing is they're just programmatically creating things for you in a way that makes sense for the infrastructure that they're trying to that they're trying to uh land and make work on uh kubernetes so if you understand that then it's pretty easy to move forward what we're going to do is we're going to build a simple program you know i wouldn't even call it really a controller but you know just for now we need spring native um and lumbar and that's i think everything we need let me bring in the cloud bootstrap here just in case um and we're going to hit generate i'll open this up in my ide [Music] and in kubernetes there's an api and so obviously there are clients that you can use to talk to that in the uh and for us to talk to we need to have a client on the class path and there are two well-known implementations one is the client javas uh the official kubernetes java client for which by the way there is a spring boot integration so you know naturally i'm going to use that one but i also want to be able to turn applications written using this api into native images so to support that use case i'm going to use a library that i wrote called hints okay and all it does is it provides spring native hints for various libraries you can use when it doesn't matter how you write these kubernetes clients you can use the official java client and its spring boot integration you can use the fabricate client from red hat uh either way there are supported hints here so that you can turn those applications so written into gravium native images so okay i've got now all the dependencies i care about on the class path um i think i've got everything i need so here's our springboard application and basically i'm going to create a um a application listener application ready event runner return new application listener and we're going to use a generic kubernetes api i'm going to modify instances of v1 pod which is the uh the java client representation of the object uh in the v1 core api called a pod right so we're going to inject a reference to a generic kubernetes api that manages instances of pod whose plural form is v1 pod list and we have to define this bean right somebody has to define that um before we do that let's just write the code here okay so uh response will be apis.get i'm going to pass in the namespace of the thing that we want to look up so default um and and then the instance will be just i have a nginx instance running on the computer so if i say k get pods you can see i've got nginx right okay get pods there it is okay so i want to get that and i say okay well assert uh respond is state i guess and then i can say response dot is success the call to query a pod was not successful okay and then i'll get the actual pod itself so um say response dot get object and that's a v1 pod and i'll just log out the results and to log that out i'll just use lum buck here okay info pod pod okay tostring now that's the basic framing of my client not a big deal nothing fancy i'm not doing a full-blown controller here but you can see how easy it is to do what you're trying to do right you just create a generic uh kubernetes api implementation for a given type and then use it now i'm simplifying things considerably here right actually there's a lot more nuance that you really do need to sort of dive into but suffice it to say the kubernetes api in turn delegates to something called a api client um and you know that's that's the level of con that's the thing that the generic kubernetes api uses uh but it in turn gets used by um a whole interesting thing called a a shared index informer and and so there's there's basically three more specialized types there's an an informer which is like a cache of data from the api so that the client doesn't have to constantly make calls to the to the api proper because there's a lot of back and forth with a given operator and then there's a specialization of an informer that can be shared across multiple controllers and multiple concurrent accesses that's called a shared informer and if you want to query that shared informer and get objects from it by a type by a key that's called a shared index informer and so those things are usually you don't talk to the api directly if you can avoid it you talk to a shared in former right shared index informer but for our little example here we'll just do this right so v1 uh pods and of course we're going to use the api client which the springboot auto configuration will give us for free okay okay good so there's our um basic client let's go ahead and run this see what we get all right look at that we ran the application uh it started up and it gave us the objects right it gave us the v1 pod the v1 status and you know there's other methods here to create to delete etc you get the basic idea i'm not going to spend much more time on this the only thing i want to do is to turn this into a native image using spring native okay so maven minus p native d skip tests clean package [Music] all right it's compiled let's go to the target directory here and controllers there we are run again you can see it's lightning fast takes up almost no ram you can deploy this to a cluster let me see it started up in 33 thousandths of a of a second so pretty great right and it'll take up you know 50 100 megs of ram whatever that's infrastructure you can deploy confidently alongside other things in the cluster and know that it's not weighing the cluster down and it can do anything that spring can do right it's a good fit you
Info
Channel: SpringDeveloper
Views: 31,911
Rating: undefined out of 5
Keywords: Web Development (Interest), spring, pivotal, Web Application (Industry) Web Application Framework (Software Genre), Java (Programming Language), Spring Framework, Software Developer (Project Role), Java (Software), Weblogic, IBM WebSphere Application Server (Software), IBM WebSphere (Software), WildFly (Software), JBoss (Venture Funded Company), cloud foundry, spring boot, spring cloud
Id: Xe7K1biKcs0
Channel Id: undefined
Length: 44min 12sec (2652 seconds)
Published: Wed Jul 20 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.