 By the way, this is a big picture that took about five minutes to download on the Wi-Fi outside. So appreciate it, please All right experimenting with Q and Carvel So this is us. I'm Dmitri. I've been working at VMware for quite a while now Infrastructure related stuff Kubernetes related stuff a little bit of Carvel nowadays And I'm Rupa. I also have been working with Dmitri for a while at VMware We work on time to application platform and on project Carvel and previously we were doing some interesting cloud foundry stuff And today we'll talk a little bit about Q and Carvel This is more of us sharing our learnings We've been playing around a little bit with Q and so we wanted to share, you know What we've learned about Q what we've learned about how to use it share a little bit about Carvel and cap controller and Talk a little bit about how these two projects fit together and you could potentially use them together So just to set the context we'll kind of talk about it in in this idea of how to deploy a change So, you know thinking about making a change whether that's a change in your source code or in your Kubernetes configuration You typically after you make that change you want to create a PR to get repo. You have various sorts of verifications tests running Then after all that is clear you've passed your checks the PR is merged And then once it's merged, there's something sitting on your cluster. That's continuously reconciling So it picks up this change and the change is deployed So that's a broad context that we'll go through in the stock and also in the demo later All right, so jumping in Q Configure, Unify, Execute So I've been in a configuration land for quite a while Started out with you know playing around with various ways of how to augment configuration that you don't really own You know, there's there's been name thrown around like ops files A lot of you are familiar probably with projects like customize similar space, right? Then there's obviously a lot of tools out there templating tools like helm Json it right so there's all these tools out there that allow you to do this different Types of kind of a configuration building right Q Seems to be the the latest one But it has a very unique twist So maybe a first question to the audience raise your hand if you actually heard about Q before All right, quite a few people and then maybe people who used it. Please raise their hands. Oh Quite a few people too. All right, and then maybe how many people use that in production? So production will break if Q breaks one person right Come up come up come on up here help us out So anyway, so Q right What's interesting about Q to me is that it doesn't just focus on configuration building, right? So this tool is so versatile and the the way that it builds up It's Kind of a semantics around working with data is you're able to quit it the data You're able to validate the data. You're really able to Work with the data as if it was a you know full-blown programming language, right? And that's to me at least quite exciting because in a lot of these other tools, right? You're able to go so far, but then at some point It's kind of it's maybe not enough or maybe it doesn't quite fit the ideas that you kind of want to express Right and so in our case what we're mostly interested in is using Q with Kubernetes, right? specifically, how do you define various Kubernetes configuration, you know, how do you? You know mangle it maybe how do you? Allow your application teams to express some kind of our application related definition, right? So that that's kind of the space that we're most interested in that's obviously what you know this kind of what talk is about So here's a little example just to dive in deep So we have a YAML obviously everybody knows YAML on the left side and we have Q on the right side So you may notice it's actually quite quite similar, right? But there seems to be a little bit of more Maybe you can argue noise on the right side, but maybe to some people it's actually not noise, right? It's those very important differences that make YAML Sometimes ambiguous, right? So for me, for example, I'm a big fan of being explicit with what type of data it is, right? Now in YAML, that's a little bit vague, right? So, you know, if you look on the side over here, right, you end up with actually is my point of yet You can see my point or so you can see over here, right that config maps, right? They store string to string and you have to really remember to quote your values, right? So that's potentially a common pitfall that somebody might run into and actually, you know, cause some kind of a problem, right? Now in this case, the problem would show up when you submit this resource to Kubernetes, but Seems like it's a little bit too late, right? Maybe it would be nicer to get that error message much much earlier now on the Q side if we're taking a look at it In this example at least, right? You do also have to quote things, but you have to quote every single string, right? So that means that you can't have strings that are unambiguous and not quoted, right? They always have to be quoted Now the other thing here is that instead of indentation You do have to use the curly braces, right? Curly braces really allow you to specify that Explicit like what is this data really related to now funny enough though? You might think that hey, this is the you know explicit nature is actually quite helpful But a few days back, I was trying to figure out staring at my Q configuration I was trying to figure out what was wrong with it and turns out I actually forgot to put the curly braces around But visually it looked like the name under metadata actually did belong in that section But it actually was right. It was name and the metadata were in the same section Just because the curly braces are we're not there, right? So I don't know if there is some interesting question to Q developers You know, how can we prevent cases like that and maybe it actually dance where will be a little bit more obvious down Down to the next few slides By the way, there is a command called Q import. You can import all your stuff into this Q format, right? And then you know supposedly you can do more Usage of Q with it directly So here's the first kind of idea within Q that to me really made Q really unique types are values, right? What I mean by that here is that Going back to our example of the config map, right? We have a data which is a string It's a map of string to a string, right? And so in Q you're able to express that directly in your configuration, right? And the following section for example, or actually technically it doesn't matter which way you order this But the following section will fail with an error message was in Q if you actually throw in an integer over here, right? Because Q knows that the type of value that should be coming in here is actually a string Or a string That you know, maybe a little bit more specific. So we'll get to that in a second Now I did throw in a little hint over here values are ordered and that's actually going to help us in the next slide over here so This is actually a little screenshot from the Q documentation by Q documentation might seem a little bit You know rich and a little bit hard to navigate at least for me when I started out, you know playing with Q a while ago But once you kind of a get the core concepts down It really becomes much much easier to Just orient it within the Q landscape. So what is this thing? So Going back to our example over here, right? When you define The type for this values over here a string, right? What's happening here is Q has this unique system where it's able to describe the values in the lattice now, I'm not a mathematician or Related to mathematician in any way. So I don't know if that's actually some proper terminology related to mathematics Probably is pretty sure is But the way this works out, right is that and maybe let's go with the most simplest example, right? So Boolean imagine as a big group, right? What is the subset of that group, right? Subset of that group is true and another subset of that group is false, right? And there's nothing else inside Booleans, right? So that means that if you define something of value Boolean You can only have either Boolean as just it's still a Boolean, right? So we don't know we can't really refine it further or It can be one of the children effectively is it either true or false, right? Now with numbers, of course, it gets a lot more complicated, right? Because you know, there's lots of different types of numbers, right? There's ins floats You know, you can potentially define some rules around it, right? Hey is some number bigger than 10 Let's say less than 10, right? And so Q builds up for each one of the values a system like that, right? And you can actually get as complex as you want, right? You can have a Boolean or a number for example, right? So Q is able to figure out how to kind of a boil it down to this what's possible and What's concrete versus more or less concrete? I guess, right? And so one of the goals for you as a configuration author, right at the end of your Q evaluation Is to get to a point where all of the values are concrete, right? So if I generate myself, you know a config map, right and half of my values are Just type string, right? That's totally meaningless to Kubernetes, right? You need to have something within that string value, right? Either maybe empty string or maybe some non-empty string, right? But something right so just the type is not good enough right now to Q That's perfectly fine, right? But to us as humans, let's say working with Kubernetes definitions, right? That's just not good enough and so you want to be able to get to those concrete values So that's where the power of Q comes in is that to get to those concrete values You're able to fill them in from multiple places, right? And so in this example over here, this value string is not concrete, right? But three over here is obviously concrete, right? So for this value, you're able to get to the concrete, you know, point, right? And so going a little bit further Going a little bit further to the next slide, right? If you just think of, hey, you know, I got Booleans, I got numbers, great, right? But then you actually want to be able to combine them into more complex types, right? And hopefully this slide is interesting enough to look at, right? Because you have a lot of these basic concepts, right? Like strings and maybe some maps, right? Actually pretty much strings and maps in this case, right? And so you're able to build up the definition of something higher level, right? So a config map in our case, this is my own shared definition of what a config map is is actually not complete, right? As you all may know, Kubernetes metadata is quite elaborate. But once you're able to define this more complex types, right? You're able to reuse them and you're able to make certain parts of those types concrete, right? So for example, on the right, you're saying that this section is of type config map and you're able to fill out some of those things with concrete values, right? And so what we end up with is if we'll take a look on the left over here, right? So API version, right? This little star, by the way, means that it's a default value, right? So if you don't specify something explicitly, like you don't specify an explicit string saying, oh, this is whatever apps V2 or something like that, right? It's going to be V1, right? So this is concrete, kind, config map, that's concrete, metadata, right? Well, the name here is actually a regex definition. So that's a type of string, right? And so you should be able to get to something more concrete by the end of the invocation, right? And so in our case, so here, metadata name, again, we're filling in a concrete value. The labels and annotations here are, you know, maps on string to string. We don't really care about those. And by the way, actually here, I would argue that this should be marked with a little question mark to mark this field as optional, right? So you don't really have to have it. And then finally, data string to string, again, we already kind of looked at that. And so here, we'll make those things concrete, right? Or we technically could leave it empty as well if we mark that as optional. If we mark that as optional. So this is my own kind of a hand coded example, probably very incomplete, probably slightly incorrect. But can you imagine if all of the configuration for the Kubernetes that you write, right, would have this level of typing in much more detail, right, based on the go types that the Kubernetes service provides, right? And so if you go and look at, for example, Kubernetes flash API, Git repo I think, where I think it's Kubernetes slash API, right? You'll see that core V1, apps V1, et cetera, et cetera, all the built-in stuff, right, is all in that repository, all of the go definitions, right? So wouldn't it be nice to be able to use this kind of type information when you're authoring your configuration so that you don't end up making the mistakes that potentially either get swallowed by a Kubernetes cluster, or maybe just get it, you know, end up, you know, catching maybe downstream where it's maybe too late or just too inconvenient. So it is possible, right? And possible due to this type of level of kind of detail that you can specify, right? Now, obviously, you don't want to copy that stuff every single time when Kubernetes updates, right, just too much work. And so you hear you end up using one of the features of Q where you're able to pull in the go definition, and Q will generate the Q definition for you, right? And now you're able to take advantage of those types when you're building up your objects. And so this, it looks something like this over here as I was showing on the right, but obviously it was a much more detail to what you can fill in. And, you know, there's a concept of, actually, I just remembered, there's a slide, modules and packages. So when you do import Kubernetes API definitions, you are able to reference to them uniquely, right? And for those who are familiar with Go programming language, Q, I think, is in a lot of ways inspired by the Go team. Actually, the creator of Q might have been on the Go team at some point. And so there's a lot of kind of a same type of feel for packages and modules that's taken and that you can use in Q. Now, actually, the reason why I put in this slide is that because we have this common problem of being able to define configuration per type of environment, so like development environment, stage environment, production environment, maybe you have five, 10 production environments, right? So how do you actually cope with some of that stuff? And so with Q, once you kind of get to the concrete value, you can't really override it. And that's actually what makes this a little bit less aeropron from a usage perspective, right? Is once you get to the concrete value, it's probably there's some duplicate configuration somewhere that's sitting that's maybe setting it to another concrete value. And that potentially you didn't intend, right? Like we want to be able to capture that explicitness between, hey, I actually have this field that I'm intending for somebody to configure versus here I have this field that's not really meant to be configured or maybe configured in two different places. And so that's what Q actually by default gives you. And what was interesting that I kind of stumbled upon at some point which I didn't realize was the feature is that when you evaluate Q package or module, I guess, in a particular way, right? So in this example, I'm Q export dot for example, right? It's actually going to only evaluate the kind of the first depth of your module. And that actually surprised me a little bit. But then I actually realized that, you know what? The reason why they've done that, right? Is that you want to be able to kind of nest your further configuration, right? So for example, for a staging environment, for a development environment, in the same kind, in this kind of hierarchy, right? So when you execute Q export dot dev, what's happening is it's going to evaluate the module plus this kind of a nested thing. And apparently you can kind of go as deep as you want. And as long as you're not specifying duplicate concrete values, right? You're actually adding in more to your kind of a base configuration. And so really you end up with this, you know, final configuration that has a bunch of concrete values that are specified once, right? Now you can refine each value through this layers, I think. So maybe more concretely, right? You can start with saying, hey, this field needs to be a string, but then down one layer in, you can say, okay, well this string actually needs to be at least 10 characters long. And then finally, maybe in the deepest layer, you can say, okay, this string needs to be, I don't know, my favorite password or something like that, right? So you can kind of refine this value, right? Through this nesting hierarchies, right? Or you can just go straight to concrete value. But this does give you this way to kind of structure the modules such that you can reuse them. Now this is not the only way to kind of refine the configuration. You can also import modules from one module to another. And so you can kind of imagine how you can reuse the types and rebuild kind of certain pieces of configuration in a composable way. But that's kind of a little bit of an introduction to kind of modules and packages there. All right. Okay, so we are after lunch. So I'm gonna try to raise the energy in the room a little bit. We'll do some questions. How many of us here are running Kubernetes? All right, that's a good number. And how many of us are running Kubernetes in production? Okay, how many of us are familiar with Carvel? Okay, how many of us are familiar with like the UNIX philosophy? Great, okay, okay. I think I know where we're at. And hopefully you had a chance to move your hands. You have a little bit more blood flowing. So Carvel is kind of trying to bring that UNIX philosophy to running applications on Kubernetes. So we wanna be building like a reliable, single-purpose, composable tool chain. And you can use that for building your applications, configuring your applications, deploying your applications. Really the idea is taken from how you might be used to, how you might be used to, okay, I do this thing, I pipe it to the next command. I pipe it to the next command. That's what Carvel tries to bring to application deployment on Kubernetes. So the tool chain is made up of a few different tools and we'll talk about all of them today. So there's vendor, there's image package, cap controller, cap, kbuild, secret gen controller, and YTT. So let's talk about the local development workflow using Carvel. And so let's say I've made some changes and I maybe have an application which has configuration in different sources. So I may have a home chart in a Git repo, I may have some YTT configuration somewhere, I may have some Q configuration in different repo. But my application uses all of that. So I want a tool which allows me to sync this configuration from different sources. And that's where I use vendor. So this is my tool to get everything and get all the configuration that I need into a single directory. Then the next thing I want to do is I've got all these dependencies but I want to customize them. Like I want to provide certain values, maybe some things, some home charts are not exactly exposing the configuration I want so I may want to write my own overlays. And so that's where I use YTT to do that. The next thing I want to do is I really want to lock down the images that I want to use. So maybe I'm consuming an open source home chart and it's tagged by a particular, the image is tagged with a version number. So it may be like v1.2.0. But I don't want to consume an image with a tag, I want to consume it with a digest. So I know exactly what I'm getting and I know that that digest will change when that image changes. So that's where I'll use Cable to resolve my image reference and convert it to being referenced by digest. Then I have a bunch of this different dependencies. So I talked about my application being made up of having a dependency on a home chart. Maybe I have some Q dependencies. So I want to bundle up my application's configuration and all of the dependent images. I want to treat them as one OCI artifact. It may not be one giant image, but I do want a way for me to declare that these are all the things that I'm dependent on and I want to treat it as a single OCI artifact. That's where I use Image Package to create that bundle. And then finally, I have everything I need. I have my configuration. I've done the customization I need. I've locked down my images. So I'm going to deploy it using CAP. Now, these steps, I could run these as is, but with the CNCF ecosystem, one of the great things we have is we have a lot of variety of tools and each of the tools are good at doing different things. So I want something which is composable and that I can replace any of these with a tool of my choice, which means that I could use YTT for templating or maybe I use Q here. And that's kind of what this UNIX philosophy and the composability gives you is you can leverage the goodness in the ecosystem and use different tools of your choice when you want to. So let's look at some example local development workflows. Again, looking at UNIX, this may look familiar to you. I could be using Q export and then getting that output in YAML and then using Kupcarol to apply it. Or I could be pulling down an image that I've bundled using image package and I could be then templating it with Q, deploying it with CAP. Or the final one, the last one that I have here is really interesting because I am using Q, but I'm also using YTT to apply a particular overlay on top of it. So that kind of shows you how you can mix and match these different things to really suit your environment and what's best for that workflow. So let's talk about how we take this to a cluster. We're all running Kubernetes. A lot of us are running it in production. So what do we do on cluster? So we talked about before, basically, your steps looking a little bit like, okay, I'm fetching some images, then I'm templating that and I'm deploying it. That's the three steps you're going through. So it would be great if we could express exactly that to run on a cluster. And that's what we did. So also we want to be able to sort out these, you know, have various fetch strategies, have various templating strategies. But that's exactly what we did with CAP controller. So with the CAP controller's app CR, you're able to define, here's what I'm fetching, here's what I'm templating, here's what I'm deploying. And you're able to swap out at each of these steps, just like we talked about in the local development workflow. So let's talk a little bit about what this could look like. So this is what your local development, your piping, your commands, you have that quick feedback loop. But then on the cluster, you have CAP controller, which is syncing to your Git repo or your fetching, your fetch source of choice every 10 minutes. So it's continuously reconciling every 10 minutes. It's configurable, so you could change that. But every 10 minutes, preventing any kind of configuration drift. But also it gives you that same feeling of what you're doing in development, right? So the idea here is to really showcase how, if in development you're used to doing vendor and then queue and then kbuild, image package and CAP, that's exactly what's going to happen on your cluster. So it makes it really easy for you to follow through debug, kind of logically understand what's going on. But you also get the goodness of it being continuously reconciling and it happening on your cluster. Here's a quick example of what using queue with an app CR looks like. So here we have, we're fetching from a config map. You could be fetching from a Git repo or you could be fetching from a OCI bundle. But here we're just, maybe we're doing local development and we just want to fetch from this config map. We're templating using queue and then we're deploying using CAP. So again, the idea being you could run exactly these commands in your local development flow and now you've taken that on cluster. Here's what some of the CAP controller CRs look like and we've talked a lot about the app CR, which is really the core API in CAP controller. Everything builds on top of it. And then Dmitri, we'll talk a little bit about the packaging CRs and then we'll also do a quick demo using queue. Yeah, apparently we're pretty bad on time. I think there was a five minute reminder. So yeah, the core API's app CRs, you already seen an example in the slide, right? Package install, package and package repository is really an optional layer that allows you to take that app CR and maybe distribute it to somebody else. But when you do distribute it, you don't really have to know what exactly is happening within the app CR, right? So you don't really have to know that the templating is based on queue or YTT or Helm template, et cetera, right? So it's kind of a hidden away from the consumer. But at the core with app CR, so let's actually real quickly, let's see a speed demo over here. I don't know, is this large enough here for us? We have one minute. One minute, perfect. So let's actually take a pretty complicated piece of code that somebody else have written and I found somebody did it with queue. It's a repository by Thomas. Thank you, Thomas. What we'll do here real quick is we're gonna say cab deploy dash a example dash f and we'll throw in cert manager here and I'll close this. So we're about to install an app CR. We'll click on yes here. And so what's happening here while this is gonna tell us things are reconciling, we're actually fetching a module of queue from GitHub, right? This is the person who's authored a bunch of queue configuration for all of their services that they run on their clusters. And we're telling queue, hey, let's use this module and this particular package under the module, which is cert manager here. And we're also saying, hey, please get us only the least portion of the output from queue and the least portion is a little bit funny. Ask us question about it later if you want. So we actually did deploy cert manager here and you can actually see that cert manager namespace ended up here. And then if we take a look at pods over here, so we got a pod for cert manager, right? And so all we've done is we just said, hey, we got some queue configuration in this git place. Let's execute it with queue and then let's deploy it with cap. And this is going to happen in reconciling continuously on a cluster, walk away, come back, still there, still being deployed. So I think we're out of time. But thank you very much. We'll actually put this one more slide here. Go to queuelink.org, great project. Go to carvel.dev, also great project. And please visit our Slack channel, we'll love to chat.