Home Bookclub Episodes Cloud Native Spr...

Cloud Native Spring in Action: With Spring Boot and Kubernetes

Thomas Vitale • Josh Long | Gotopia Bookclub Episode • February 2024

You need to be signed in to add a collection

Share on:
linkedin facebook
Copied!

Transcript

Introduction

Josh Long: Hi, I'm Spring Developer Advocate Josh Long, and I'm joined here for the "GOTO Book Club" with the illustrious, the amazing, the inimitable, the really, really great, Thomas Vitale. How are you, my friend?

Thomas Vitale: Hey, Josh. I'm doing great. Thank you. It's great to be here with you.

Josh Long: My pleasure, honestly. I wish that we could actually be in the same place having this conversation, because then I'm sure it would be we could have a longer one. But it is good to talk to you, and about a very interesting thing no less, a topic that I think we both appreciate. Something on which both of us have written, you far more recently than I, on cloud-native Java with Spring, right? I love your book. I just love it. I'm a huge fan. I read it and I've recommended it to people, and I'm just glad we could have this conversation. I think it'll be good. So, thank you for that.

Thomas Vitale: It means a lot that you like the book and you wrote the foreword.

Josh Long: There's a lot of topics, though, that I kinda wanted to talk about because I just think, I mean, I'd just like to get you on the record here and just to have this conversation. So, I was wondering if we could talk about some of the things that are in the book just superficially. And bear in mind, this book is now already, what, six months old? You know, like, I don't know, when did it come out? Feels like...

Thomas Vitale: One year now, actually. It was November of 2022, it was officially released. 

What is Cloud-Native and Its Benefits

Josh Long: Wow. Congrats. Okay. So, one year, here we are. And there's just a lot of stuff. What I'm trying to say is in this ecosystem one year is a long time, right? A lot of things are already changing. And so, I just wanna have a conversation. Maybe we can see if things have changed in your perspective, or, you know, I don't know. Just let's have a conversation. So, first things first we talk about cloud native. What is cloud native? Like, when you say the cloud-native software, what does that mean to you?

Thomas Vitale: That is a very interesting question. Actually, I spent a whole chapter on that because I really felt like defining the term was important because probably means different things to different people. Like, is it containers, is it Kubernetes? And the bottom line is cloud-native development is about architecture. So, how we build applications so that they run better in the cloud. So, from that perspective. But it doesn't have to be containers, in my opinion. I like what Cornelia Davis wrote in her book about cloud native patterns that cloud is where the computation happens, and cloud native is the how, how we build applications to run smoothly in the cloud. So, that's one of my favorite definitions for cloud native.

Josh Long: She's a genius. I don't know if you know this, but she used to work at Pivotal. I used to work with her in the same company. And when I launched my podcast, she was the first guest. Of all the people I could have asked to be my first guest, I was like, "I wanna aim as high as I can." And so, she was my first guest.

Thomas Vitale: Wow.

Josh Long: She's a legend. I love that answer. You're right. We cannot improve upon that answer, so let's not even try. So, it is the how you build software, right? It's the how. And I like that you went out of your way to disconnect it from the idea of containers. I think that whole thing is very confusing to a lot of people, and I tend to think that, you know, you don't need containers. You need automation more than you need containers. A lot of times automation today is containers, but it doesn't have to be. If you have an API-driven infrastructure layer, you can get that result. It may not be as efficient, but you can get the same result, you know? And also, you know, containers are kind of the middle ground today, aren't they? You've got VMs below that in terms of like bigger, bulkier deployment units, but above that you've got serverless. So, like, even in that, even if you're looking at the new stuff, like, tying it to the containers just makes no sense, you know?

Thomas Vitale: Exactly. It might be just an implementation detail, perhaps. With serverless platform, we just push our code, and then if behind the scenes it's wrapped in a container, we don't even know or care for that matter as developers. Yeah.

Josh Long: Exactly. Love that. What makes people build cloud-native software? Why would I do that instead of staying home and having an apple? You have a choice between I'm gonna build some cloud-native software versus, I don't know, whatever else you might do instead. You know, like, why do people do that?

Recommended talk: Next-Generation Cloud Native Apps with Spring Boot 3 • Thomas Vitale • GOTO 2023

Thomas Vitale: There are some goals that we have when building cloud-native applications, or at least wishes, so that is the reason why we start adopting cloud-native techniques. For example, we want to build software faster. So, like, the speed of development, time to market sometimes is critical. It provides a competitive advantage or we want to scale our software across different devices around the world, so cloud-native technologies can help with that, the scalability part with resilience.

In general, it could be also for cost optimization. Of course, the last one depends on the use case. It's not always true, I would say. Like, for example, in an organization working on more legacy technologies, if you start onboarding cloud-native techniques, perhaps you don't get right away cost optimization because you need time to train the staff, you need time to learn about new technologies. So, that last one depends, but in general, I think those are the main reasons why people look into cloud-native technologies.

Josh Long: Absolutely. And I love that you talked about the organization there, right? Like, it's not just the software programmers, it's not just the programmers, it's everybody. If they're not all playing in this sort of continuous improvement mindset, then it's not gonna work, you know? So, we talk about the cloud-native, and we talk about...Does cloud-native, when we talk about building cloud-native systems, does that imply microservices or can you build a majestic monolith?

Thomas Vitale: Just like with containers, I think microservices are quite popular for cloud-native applications, but it's not a requirement. I would say it's not part of the definition. Like, when tracing back to the term cloud native, I was researching for the book, and then I found a definition from 2010 by Paul Fremantle. And at that time Docker didn't exist. We were not talking about microservices yet, we were talking about cloud-native applications. So, usually, that's my proof that it's not a requirement.

Microservices vs. Monoliths

Thomas Vitale: I think that the discussion around microservices and monoliths depends. So, you can have cloud-native applications in both cases. Probably, I think the best or the better point of view in that regard is probably looking at the domain or domains within your software and how you design them using domain-driven design techniques, and looking at the bounded context to have more modularized and self-contained modules. Now, if those modules are then deployed independently as microservices or are part of a monolith, I think that shouldn't matter that much.

It's important to look at the organizational needs and requirements, I think, and the deployment needs, because sometimes organizations adopt microservices, but the organization is not ready to work with microservices, and then you don't get the benefits that a microservices architecture can provide. So, it's a mix of technologies, organizational structure and culture, and overall requirements for the final system.

Josh Long: I think you mentioned the bounded context, right? The domain of the application, you know, the types, the data, the records, the things we're moving around the system, you know, that has to dictate what we build. And if you build it correctly, it doesn't matter if it's a microservice or a monolith, right, you can keep it clean in both cases. One thing that's kind of interesting to me is that, like, I don't know, do you have a specific section on remote procedure call or SOAP or REST or...versus messaging or anything? Is there a discussion in there that I've forgotten about in the book? Something about is there...?

Thomas Vitale: So, I cover both, like, REST interactions, more traditional ones, but also messaging. And in that sense, when working with distributed systems, like usually cloud-native applications are, then the challenge is like, how do we design a transaction? So, we don't want to have distributed transactions, so then we go into more, like, event-driven strategies patterns like SAGA, so that we can build an event-based system and ensure eventual consistency across different services in a distributed system.

I like, for example, the, well, very new project in the Spring portfolio, Spring Modulith where you can build a modular monolith. So, it's based on these domain-driven design principles. So, you get all these very nicely self-contained modules, and at any point in time you can just extract the module and make it an independent application without any effort because it's already designed to be self-contained, to be...Yeah, it's built around this concept of bounded context and the principles of domain-driven design. So, I think it's a really powerful concept that one. I like that project. It's great to have the new addition in the Spring portfolio.

Josh Long: I couldn't agree more. One of the nice things I love about it is it lets you think in terms of events, right, application context events. But now with the new 1.1, you can just take an event and have it automatically distributed over a messaging technology like RabbitMQ or Kafka. So, you can have the event within the same monolith, and then for the interested services that exist outside that monolith, you know, maybe you still have some that can get messages via RabbitMQ or Kafka, or whatever. And so, you can have this event-centric approach and it distributes it or it stays local kind of like Erlang/OTP, you know? Okay.

Thomas Vitale: That's really powerful.

Recommended talk: When To Use Microservices (And When Not To!) • Sam Newman & Martin Fowler • GOTO 2020

Josh Long: It's good stuff. It just goes to show, like what you were saying earlier, it just goes to show you, you can build clean code. Whether it's a microservice or a monolith, you can get good results. Okay. So, this sort of, if you do build microservices, and if you do build, you know, smaller self-contained services, then probably you've got a lot of them, right? Or you've got more than one. And any engineer that sees more than one of something is gonna wanna turn it into a for loop instead of dealing with each one uniquely, right? You don't wanna have special case handling for each thing in the system. You wanna automate, right?

DevEx: Dealing with Resilience Infrastructure

Josh Long: We get into things like how to deal with resilience infrastructure and development. And so, I guess, the first question then is, like, if I'm dealing with lots of moving parts, for example, databases, message queues, and other services, no less, I think today during development, developers have a couple of really good choices. One is test containers and the other is Docker Compose. How's a developer supposed to pick and choose? What's your insight on that?

Thomas Vitale: That's a really good point about the consequences of going distributed or going cloud-native. You might end up having all these services, and it's important to take care of the developer experience. So, of course, we don't want to make things more difficult for developers and have them spend too much time on infrastructure concerns. Because especially when using Spring Boot, the focus is like you don't need to write all the boilerplate code. You get all the infrastructure concerns taken care of by the framework. You can focus on the business logic. But now we risk adding more complexity outside the application like we need to spin up all these other dependencies.

So, having something like Docker Compose and test containers helps. And since Spring Boot 3.1, both of them are supported by Spring. So, if you go to start the spring.io and you generate a new project and you're using Postgres with Spring data, then you get automatically either a Docker Compose file with PostgreSQL information to spin up a container, and that happens automatically when you run your app or test containers.

I love test containers specifically because I mean, it's so great and you can use it both for automated testing. So, when you run your test, you can have testing against the real dependency. I don't want to use mocks. If I am using Postgres in production, I want to use Postgres also during testing so I can really trust my test because I have environment parity. And then I can use the same configuration to have Postgres also spin up automatically when I'm developing. I mean, that's amazing. So, I do Spring Boot run, and then I get both the application and all the external dependencies with the lifecycle managed by Spring Boot itself. So, I can spin up all together. Yeah.

Josh Long: I love that. Absolutely. So, you use test containers first then? Is that the idea?

Thomas Vitale: I think it depends on the use cases here and perhaps a bit of personal preferences. What I like about test containers is that I have them both for my auto-test and development. So, if I have to specify a dependency on Postgres version 15, I only have to write that once. So, if I use, for example, test containers for testing and Docker Compose for development, then I have that container defined in two places. So, if I want to update the Postgres dependency, then I have to do it twice. So, I tend to prefer test containers because it cover my full development workflow.

Recommended talk: Bringing EDA to the Containers World • Jessica Deen • GOTO 2023

Josh Long: I agree. I quite like it. We've got this ability. And by the way, Docker, that's another thing is I think Docker's become...as a company, they've sort of reoriented themselves to focus on the developer. They are not the cloud runtime company as much as they are it seems a developer, sort of, the path to production-focused company, right? So, they have tools to audit your code, and you have Docker Compose, and all these kinds of things. So, I think that kinda stuff is cool as well, but yeah, I do quite love the, you know, granular nature of test containers. It lets me compose very nicely, very discreet little things, you know?

Development Challenges in Cloud-Native Systems

Josh Long: We talked about automation and I think consistency is a huge part of that. And so, you know, for 15 years, something like that, Heroku has had the 12-factor manifesto, right? This is the set of principles that they think they suggest will make it easier to build software that is compatible with a dynamic changing tumultuous cloud environment. And so, it's principles for architecture and application design that make your code, make your project more compatible with the cloud.

When we say cloud, you have to remember they're talking about a Heroku, a platform as a service. But I think generally, the rules are pretty much...you know. Whether you're running on a platform as a service, or whether you build all that stuff yourself, you're eventually gonna run on a platform as a service, right? So, they talk about the 12-factor manifesto, but you, in your book, you'll also talk about the 15-factor one. So, first of all, what is the 12-factor manifesto, and then what is this 15-factor manifesto? What's the delta, you know? I get there are...

Thomas Vitale: As you said, the 12-factor manifesto came from Heroku to help customers build applications that were more seamlessly deployable on their cloud platform, but in general they became very good principles to build cloud-native applications. Things like we want to externalize configuration so that if we have separate configurations for testing and production, we don't bake that into the application artifact. We want to keep it outside.

Over the years, I would say they are still relevant, but then I think back in 2016, Kevin Hoffman wrote a book called "Beyond the Twelve-Factor App", and introduced 3 new factors and review a bit some of the existing ones. So, for example, the configuration factor, he extended it so that it would distinguish between credentials and non-sensitive configuration code. We have three different parallel lines: the code, the credentials, and the configuration that you never cross, never meet. So, I think that it is very important to pay more attention to credentials. Like, we don't want to handle them just like we handle any other kind of configuration.

And introduced also new factors. My favorite one of the new additions is the one about telemetry because he wrote, like, "Treat your applications like space probes." And I think that's a very powerful image to have around observability and the fact that we want observability and not just monitoring. With observability, we want to be able to ask any kind of question without having to re-instrument the application as we do for monitoring. Because for monitoring, we look for known states. For observability, we don't. And if we consider the space probes, it's not like we can continuously add new sensors once the probe is out of the planet's orbit. So, I liked how, like, yeah, how he framed this new factor.

Josh Long: I quite like that as well. Can we talk briefly about some of the 12 factors?  I think they're so old I kinda wonder if people know them. But I mean, it's easy. It's 12factor.net, I think, right? That's the website. Let me see, 12factor...not factory, that's my Spring brain. Yeah. And so, they've got these pretty solid basic ideas, right? One codebase tracked in revision control, many deploys. So, whether you're going to development prod and Q&A, and all that, it's the same code base, not different builds for different environments, right?

Then dependencies, explicitly declare and isolate dependencies. Configuration, we just talked about that. You keep configuration in the environment. And you just made a great point. There's a distinction between credentials and non-sensitive values. Build, release, run. Strictly separate build and run stages. What is that? That's just basically building the jar here and then run it over there, right? As opposed to, you know, you put a thing together that's the packaged thing and then you deploy it somewhere else. Is that the idea?

Thomas Vitale: You build your application once as part of your pipeline, and then it's the same artifact that goes through all these stages. We don't want to build an artifact specifically for testing, and then we build another one for production, but it's the same one that needs to go through all these stages, otherwise, we cannot really qualify it and ensure that it's the...yeah, that it's behaving correctly. So, we want to build it once and then it must be immutable. So, if something changes, we have to go back to the build phase, build a new one, and then go through all the steps again with the same immutable activity.

Josh Long: Absolutely. One of the other ones that I loved is the idea of backing services. Practically speaking, all things will be a network hop away, right, from your service. And, you know, you'll treat them as things that can come and go and that your application should be aware of. They're attached resources, right? They could be unattached. That's kind of interesting, though. When they talk about that in Heroku and in Cloud Foundry, they're just talking about environment variables that have credentials that you can use to then connect to your data source or whatever. But, you know, in the Cloud Native Computing Foundation space they've got a...you know, there's service bindings, right, that came out of the work there, but that's available.

Thomas Vitale: Kubernetes. 

Josh Long: Kubernetes now as well. It's kind of interesting how that has evolved, right? I think people are codifying some of these good practices. Processes. Execute the app as one or more stateless processes. Yeah. When they say stateless, what are they talking about there? They're not talking about session state. Well, they maybe are talking about session state. They're saying if the app got killed, then nothing would be lost, right? I could restart the instance and then all the state that I had before would be somewhere else in…

Recommended talk: Kubernetes: Up & Running • Brendan Burns & Matt Turner • GOTO 2021

Thomas Vitale: Exactly.

Josh Long: ...Redis or your SQL database or whatever.

Thomas Vitale: That's the ultimate test. So, to ensure that you don't have any state in your app. You have multiple instances, you shut down one, if you lost data, then it's not stateless. So, there's something to fix. Yeah.

Josh Long: Right. Quickly. Okay. Port binding. Export services via port binding. So, this is the idea that the platform gives you the port, right? Like a port variable.

Thomas Vitale: I feel like Spring Boot is kind of out of the box. Many of these factors you get out of the box with the Spring Boot experience, because there were, I think back then we had these big application servers deploying multiple application packages, with files on it exposed through different endpoints. But now we just, like, with Spring Boot, we have separate applications, each application exposed through a different port. So, I think it's great that some of these became so, I don't know, part of the everyday life for a developer, because like Spring Boot already from version one, actually codified this, ensuring a nice cloud-native experience for developers.

Josh Long: I'm a big Spring Boot fan, but I'll also say Dropwizard did a lot of that stuff before Spring Boot and, you know, we owe a debt to them for what was done there. A lot of the innovation that we see now in the ecosystem started with Dropwizard. I think Spring Boot, obviously...you know, I'm a Spring developer advocate, you don't have to ask me, I think it did a great job. But there's some cool stuff. A lot of that initial wisdom, you know? Okay. Let's see. Anyway, there's just a good...Logs. Oh, treat logs as event streams. This goes back to your observability point, right? Like, this is another kind of observability, isn't it?

Thomas Vitale: The factor is about having logs just as a stream of events. So, just output them through the standard output and not deal with log files and rotation of log files, and having to scrape those log files so that, from a developer perspective, we just output the stream of events, and then we rely on the platform to scrape them, to collect them, and then make them available in some, yeah, GUI where we can query them. So, that moves some responsibility from applications to the underlying platform.

Spring Boot and Integration with Istio

Josh Long: Now this gets to another question. I hear this all the time. People ask me all the time, "Why do I need Spring Cloud if I have Istio, right?" This tells me that people don't understand either one of them, because, like, I feel like, you know, I can do 99% of what Istio does without Istio, but I can't do most of what Spring Cloud does without Spring Cloud. That is to say, you need the integration in the app to get that behavior even if you're using Istio, right?

So, I don't know. Like logs, you mentioned logs. The reason I'm talking about this is because that's a platform responsibility, right? And the platform is responsible for it runs your process and it then takes the logs and sends them off to, you know, a SIS log or something, right, or Splunk, or Elastic or whatever, right? It could be anything. But that's the contract, you don't care about that. All you know is you write the standard out, and then the platform takes care of taking those logs and then making them available somewhere for you later, right?

Thomas Vitale: Yes.

Josh Long: What other contracts does a platform in your, sort of, opinion kinda have? I'm thinking about things like service discovery, right? And configuration. What kind of things?

Thomas Vitale: Service discovery and load balancing, I guess., I also get those types of questions a lot, like around Spring Cloud and Kubernetes Istio. And I think it depends. So, for example, service discovery is something we get out of the box from Kubernetes, so we can just, like, if we want to call another service, we do it by name directly. We don't need any additional dependency on the application. So, for that specific use case, we don't need something like Spring Cloud Eureka, for example. But other things like, yeah, a question I get a lot is Spring Cloud Gateway and Istio or...Right? And I think the key part there is who is responsible for what? Because a developer probably shouldn't know or be responsible for working with Istio. That's the platform's responsibility.

I feel like at the platform level, the platform engineers use Istio to do things like, I don't know, service-to-service encrypted communication with MTLS, right? That's fine. That's the platform's responsibility. But then as a developer working with something like Spring Cloud Gateway, I can manage the routing and security that I as a developer know about the applications. So, it would be maybe not a good idea to push that down to the platform team as a responsibility. And so, I think that it's not an or/or. I think it's more of a coexistence. So, we have something like Spring Cloud Gateway that is very developer-focused, so it's application developers are working with it. And then other concerns come from the platform using either Kubernetes native functionality or using something like Istio.

Josh Long: Great answer. I think it works well when you consider that they both have to be there, you know, like we mentioned imagine you're doing fallbacks, you know, circuit breakers, right? Well, what happens when there's a failure with Istio? All you can get is a status code or something like that, right? But I wanna have a default fallback behavior. That's up to my code to do something interesting there. Okay. What about distributed tracing, right? If the platform supports propagating those trace headers, who cares? If your program doesn't know how to handle it, if there's nothing in your code to know what to do with those headers, to propagate them and to send them onward, then it's not gonna work, right? You need to have both sides there.

Federated authentication, right? Single sign-on, somebody's gotta provide the IDP, but again, that identity, that concept of identity doesn't mean anything unless my code knows to look for it, you know? So, it's sort of these two sides. And this is why I always get very confused when people say these things like, "I don't need Spring Cloud." "Oh? Okay. I think you're confused." 

API Gateways and Use Cases for Spring Cloud Gateway

Josh Long: You mentioned the gateway. So, I like API gateways in general and I love Spring Cloud Gateway, in particular. What are some of the use cases? What are some of the cool use cases you've seen for gateways? I just wonder.

Thomas Vitale: The first use case, which is I think one of the main reasons why we use this, we adopt the API gateway pattern, is if we have multiple services as part of our system, we don't want our clients to keep having to keep track of all those endpoints. So, we want one central entry point, ingress point to our system. So, that's the basic use case. So, all the requests goes to Spring Cloud Gateway, and then they are routed across different services based on some criteria. And that's the basic.

But then on top of that, there's so many powerful features that we can do on top of that. We can adopt different resilience patterns. For example, you mentioned the failover, so we can have circuit breakers in the gateway. So, if I call in a service to retrieve, I don't know, a list of books available in a catalog then and we go back to the application developer responsibility, I can define directly in the gateway that if the service is not available, maybe I cached the previous response in Redis and then I can return that. I can define some timeouts, retries, and then can do a lot of security stuff as well. I can have that single entry point to the system for doing authentication, delegating to OAuth and OpenID Connect. And on top of that, I can perhaps customize the way I want my tokens to be shaped, because I guess we have a very standard authentication mechanism, but then when it comes to authorization each application is different. So, we need that power of customization also in an easy way, and Spring Cloud Gateway provides that.

Josh Long: I love Spring Cloud Gateway.

Thomas Vitale: So, I think all these crosscutting concerns, security, resilience, and observability because then, at that point, we can keep track of all the traffic that enters the system from the gateway. So, we can start tracing from the gateway and have full visibility into everything that is going on. We can have rate limiting, another thing I really like. So, if we provide an API as a service with different tiers, then may be the free tier has a low rate limit, and if you buy a subscription, then it's a different rate limiting policy.

Josh Long: You've just mentioned resilience, and I think that kinda leads me into my next question. Well, not resilience. I'm thinking of, like, scalability here, you know? You've got the gateway, the gateway is sending traffic down to your backend services. How do I make sure that my service is gonna be able to meet that demand? Like, what are your thoughts on reactive programming versus Loom, versus, you know, just horizontal scaling? I mean, what is your average developer gonna do these days?

Recommended talk: The Busy Platform Engineers Guide to API Gateways • Daniel Bryant • GOTO 2023

Thomas Vitale: Spring Cloud Gateway originally was built with, like, on top of the reactive stack provided by Spring and Reactor. The reason was exactly about scalability because with a more traditional model, based on one thread, and one request, it would become a huge bottleneck having a gateway built with that pattern or architecture. With reactive programming, we get more scalability, and more resilience, because we can use just a few threads and then handle request, well, in a very asynchronous way. So, we don't have to assign a thread to a request, but if there's nothing to do, maybe we are waiting for another service or a database to reply, we can use that computational power to do something else. No need to wait around and waste resources. And that's one of the power of reactive programming about building more scalable and resilient applications.

The other side, I think, is the programming model. I really like the programming model where everything is a stream of data. We have this reactive stream, and you can combine different data sources together, but using the same APIs. So, I can have a flow of data with data coming from a database, from a message queue, from an HTTP API, and they all look the same in this reactive stream. I can apply resilience patterns to all of them. And I think it's a really powerful way of programming. Of course, it's very different from the more traditional imperative programming we are used to.

We have Loom, of course. One of the...probably the main thing in Java 21 now is that we get these mutual threads previously called Project Loom, where now we can have virtual threads exactly doing all the computation so we don't use, like, machine threads to wait and hang around if they're waiting for a request. But we're gonna spin up tons of different virtual threads that can take care of that. So, in a way, we also enter more into a synchronous fashion where we have these threads that are mounted and unmounted dynamically without having to block resources while they're waiting for something to do.

Josh Long: Yeah, it's the best of both worlds. Okay. So, I...

Thomas Vitale: That brings a lot of capabilities for also building scalable applications, also using a more traditional imperative way. For example, Spring Cloud Gateway now there's a new version of it that is based on virtual threads. So, instead of being based on the reactive stack, you can use virtual threats to reach that scalability demand that a gateway needs.

Josh Long: Right. It's so good.

Thomas Vitale: Yes.

Continuous Delivery and 12-Factor Manifesto

Josh Long: We've talked about several things, several practices that we can use to build robust services, right, in Spring. But, you know it's...You know that M.C. Escher...you know M.C. Escher, the artist? He's got a picture of a hand drawing itself, right? He drew a picture of a hand drawing a picture of the hand, drawing the picture of itself. And I think about that picture a lot because that, to me, describes the cloud as useful as it is to build robust, scalable services, without the infrastructure and the pipeline it's sort of, it just doesn't matter, does it? So, this brings us to continuous delivery and GitOps, and how would you distinguish one from the other? What is GitOps compared to continuous delivery in the modern world, you know, in the modern thinking of it?

Thomas Vitale: Continuous delivery, I see it as that way, that mindset, yeah, that process that we use to build software so that it's always in a releasable state. And this is the main definition also from the awesome continuous delivery book. Now, I know that the term is used to mean different things. We always have this problem, I feel, in our industry with terminology, but I feel like continuous delivery encompasses the whole process and mindset. And then, as part of continuous delivery, we can have different approaches and techniques. For example, continuous integration so that we continuously push our changes to the Git repo. We are not waiting around on a separate branch because otherwise, then merging all together will be more difficult, we're gonna get conflicts. And continuous delivery actually gives control of when we release software to the business rather than to be bound by a technical concern.

We don't necessarily deploy, but we always have a release candidate, that is approved, which is qualified, went through the pipeline. And then, if we also want to every time we build a new release, we want to deploy, then we reach the continuous deployment. So, kind of an extension to our pipeline. And GitOps is actually a way to implement continuous deployment, because before GitOps what we would do is as final step in our pipeline, then we would authenticate, I don't know, with the production environment, and then the pipeline would deploy the application directly in production. With GitOps we're kind of reversing control. It's the production environment that pulls changes. So, whenever there's a new release, then it pulls down the new release and updates the deployment relying on the continuous reconciliation capabilities within Kubernetes.

So, the pipeline is not responsible anymore for the deployment. That gives several advantages, one of them being that the pipeline doesn't need access to all these environments, but we can have a more decoupled system. I see it as an event-driven system because we don't have...Like, it's the GitOps agent that listens to events, but when there's a new version of the application, then it acts upon it. It doesn't need to know how that new jar file or container image has been built. So, kind of separation of concerns there.

Josh Long: I quite like that. That's a good point. Everything before the platform has to be built into the pipeline otherwise. Whereas if you just use GitOps, you're just thinking about the thing that the platform takes in. You don't care about everything before that, you know? Okay. So, that's fantastic. 

Recommended talk: Continuous Delivery Pipelines: How to Build Better Software Faster • Dave Farley • GOTO 2021

Continuous Delivery vs. GitOps

Josh Long: So, these are different ways to get to production. They're all based on the idea that you're constantly shipping, right? The goal is to be able to get to production consistently and easily, and without intervention from a human being as much as possible. But that does bring the question of, okay, suppose I'm a developer and I'm making changes to my code, and suddenly we have to go to production. Should I use a branch? What's your opinion on this stuff? Like, or should I do a feature flag? Or, you know, where do you stand on this discussion?

Thomas Vitale: If possible, my preference would be towards trunk-based development where we have only one main branch, and using continuous integration we continuously push changes to that branch. Of course, doing continuous integration means that we won't get, like if it's a big feature we're implementing, maybe we don't want to enable part of the code until it's fully complete. So, feature flags are a great way of dealing with this, like reconciling continuous integration with the feature that is not ready to be out there. So, we put a feature flag. Once all the code is implemented for that feature, then we can perhaps enable that feature flag on only certain environments or doing some A/B testing, enable it just for some users to try it out. And so, we can adopt different advanced deployment patterns there.

But I think the key part for me is not keeping long-lived branches. I mean, it's fine to create a branch but if it's short-lived, otherwise, I feel like it creates too many problems. One I mentioned earlier with conflicts and not having the right state in the main branch, if we're testing that. So, we find problems later. That's never good. And then the other one is more about, yeah, this divergence, Like, we have code that lives in a separate branch for months, and if that code is not in the main branch and it's not in production, it's basically useless, doesn't provide any value. So, I'm always pushing fast and actuate that value, because that's why we write code. Yeah.

Josh Long: Absolutely. It's not real until it's in production.

Thomas Vitale: Yes.

Josh Long: I think we did it. I think we covered...I think if I acted on all the things that we talked about today, I think I would have most, probably not all, but most of what I would need to get to production. And, you know, obviously, there's a lot of details that we've left out. We don't have 20 hours to have this conversation, or 30, or 40, or 100. But I think if people really wanna spend a few more hours and really learn a lot more, I think that book of yours is a fantastic, fantastic way to do it and to help them get to production. So, I guess, I kinda wanna close down and, you know, just thank you for the really cool discussion. Is there anything you wanna leave people with that hasn't been said?

Thomas Vitale: Yeah, just invite people to read the book if you're interested. I am always open to receiving some feedback, so please reach out on Twitter or LinkedIn. The book is called "Cloud Native Spring in Action: With Spring Boot and Kubernetes." So, if you read it, let me know. I love getting some feedback, also suggestions for improvements. You know, maybe second edition will arrive at some point. So, let me know what you think about the book. And I want to thank you, Josh Long, very much for this conversation. It's always very interesting talking with you.

Josh Long: Oh, my pleasure, my friend. Like I said, we'll continue at some point soon, I hope. All right. Good stuff. Thank you, my friend.

Thomas Vitale: Thank you. Bye.

About the speakers

Thomas Vitale

Thomas Vitale ( author )

Software Architect, author of "Cloud Native Spring in Action", CNCF Ambassador, Oracle ACE Pro

Josh Long

Josh Long ( author )

Spring Developer Advocate at Pivotal, Java Champion and author of 6 books