Crossplane - The Cloud Native Framework for Platform Engineering - Jared Watts & Adam Wolfe Gordon
About this talk
In this talk, the speakers introduce Crossplane, a control plane framework that enables developers to build platforms and control planes efficiently. They explain how Crossplane simplifies infrastructure deployment through a self-service API that allows developers to request resources without waiting for DevOps teams, thereby accelerating the deployment process. The session also highlights the recent advancements in Crossplane, including its graduation to a CNCF project and the release of Crossplane V2, which brought significant user-focused improvements. Additionally, the talk covers the implementation of Crossplane's compositions, managed resources, and functions, which can be developed in various programming languages and are essential for customizing resource definitions and interactions. The speakers emphasize the importance of a cohesive developer experience and introduce features that allow for better monitoring and testing of Crossplane deployments, fostering community collaboration.
Full transcript
All right, everybody, we're going to get started here now. Thanks for everybody for coming. So, my name is Jared. This is Adam. We're both core maintainers on the Crossplane project. Uh and we're going to basically let you know everything you need to know about Crossplane in this session right here. Okay, so we know that's um you know, there's going to be lots of people here who have
never even used Crossplane before. So, as usual, we're going to start with some foundational knowledge, explain what Crossplane is, and then we'll get into the details about latest things we've been building and all that sort of stuff. So, let's start with a platform story. So, imagine a developer, they or an AI agent, whatever, that builds a new service and they want to deploy it. They want to
get it out there. That service probably needs some infrastructure. Uh it's probably going to need to do some compliance and some sort of like scaling and you know, best practices and all sorts of complicated things. Um so, they've got a lot of things in front of them still to get out there and deployed. And if they have some sort of DevOps team or platform team, whatever, maybe
they'll file a ticket and they'll wait to get all that complexity. Days, maybe weeks sometimes. And we know that's true because we've talked to many Crossplane users who have basically told us that before they started using Crossplane, they would have to wait for weeks in order to be able to deploy things and get infrastructure. So, we know that's a thing. So, basically, we have this developer, they
just want to ship something, but they're blocked by not only process, but complexity as well. They don't necessarily even have the skills to do this stuff anyways, right? So, they're waiting. We would like to say that there's a better way to do this. So, think about a platform, building a platform, and then exposing or offering an API to your developers so that they can do what we
just talked about them doing on the last slide, uh but do it in a very easy way. So, here's maybe an API for an app. Um this is not a Crossplane concept. This is something you can do, you can build this yourself with Crossplane. But your developer could just say, "Hey, here's my container image. Um I need database. I want Postgres, make it 100 gigs." This is
the simple interface that they get. And from this very simple platform API, they get all this instead. Uh so, they you know, start with an app and then it it fans out and creates a deployment for the container, a service for the networking, and maybe a Google Cloud SQL instance for the database. But we started with this very simple API that they know they can use and
they can get what they want, and they get this more complicated infrastructure that's been blessed by the platform team. That's much better than waiting for a number of weeks, right? So, that's what Crossplane is. Crossplane is a control plane framework that lets you build a platforms and control planes like we just showed. Self-service is a hugely important part of that. The whole goal of this is for
our developers to be able to get what they need when they need it, but with safeguard rails around there that the platform team has figured out and you know, constrained so they're not just doing, you know, anything, but they're going along a golden path. You want to be able to declare it of say, "Hey, I want this is this is what I want and not necessarily how."
And then we're going to want this control plane to be continuously reconciling it, making sure it's staying up to date and fixing any drift that may be coming along the way. Um we've heard of control planes before. We know control planes. Kubernetes is a control plane and it's fantastic at orchestrating containers, right? So, Crossplane basically extends Kubernetes to be a control plane for everything else. Uh another
thing that Crossplane is is it now it's now a CNCF graduated project. This isn't fresh fresh news, but it is news since we were here with you all in Europe last time. Um so, we're happy to get to that maturity level with the CNCF. Uh you know, we think the project is at a great place after going through that process. And the key takeaway here for me
though, particularly, is that over 3,000 people have contributed in some way to the project. So, it's amazing to see a community helping this project get to where it is today. Another major milestone to be excited about that is not fresh fresh news, but it's new since the last time we were in Europe together, is that we did release a Crossplane V2, which is a major release for
Crossplane. Um there's it was a great opportunity for us to learn from uh a number of years of running Crossplane, getting feedback from users, and you know, basically fixing some of the things that we didn't get right the first time, and to make it easier for everybody to use. So, for instance, you know, resources are namespaced by default now. That was a major feedback point. And then
you can compose anything. When you're building your platform, it doesn't matter. If it's part of the Kubernetes API, you can use it with Crossplane now. Uh and then a big note there is that of the V.1.X stuff, Crossplane 1.20 is the last supported release of Crossplane. It's still supported. If there's a critical bug fix is needed, we will patch it, we will release it, but that is
the last release of the 1.X's. Okay. So, let's dive in to this platform that we've been talking about and let's focus on the specific Crossplane terminology and concepts that enables it to happen. So, starting at the top, this whole platform API, what we're exposing to our developers, we do that in Crossplane with what we call an a composite resource definition, an XRD. That's the shape of our
platform API we're exposing. Underneath that, we have to have some implementation. That's the logic of, "Okay, when developer uses this API, what resources do they get? How do I configure them, transform the data?" That's called a composition. And then underneath that are the resources that you're composing. These managed resources. They could be in Kubernetes, they could be uh you know, cloud resources, um Google, AWS, Azure, Alibaba,
whatever. Doesn't matter. But this is the the main components of what it takes to build a platform using So, let's talk about the leaf level there, the bottom the building blocks, the managed resources. So, think about everything in the cloud providers. Like Amazon has like 900 something uh various services that they offer. We've basically taken all of those and then transformed them into Kubernetes API objects that
will be managed and reconciled by the control plane. So, for instance, an S3 bucket, um that's represented as a Kubernetes API object now. It's got a spec, it's got status, and then when you create one of these resources out in the real world, the real S3 bucket will go and get created And you know, the way that works is that, you know, somebody applies a manifest to
the Kubernetes API server that says I want a bucket. Crossplane's got a bunch of controllers running. They reconcile that desired state with the real state in AWS by calling the AWS APIs, and voila, an S3 bucket is created for you. So, the schema, the API the platform API, we keep talking about that. Keep talking about that. Super important. We we create those with XRDs in Crossplane. And
like the key takeaway here is that this is the the shape of the this is what you want to expose to your developers. You want a curated experience. You want to you know, constrain down what you expose to them. So, the specific configuration knobs that you're exposing, you define that schema via a Crossplane XRD. And then finally, the composition. Uh you know, as we said that's how
you define your logic of like you know, developer asks for app, and then okay, what what does that mean? What do you do? This is all captured in a composition, which is basically a pipeline of functions that run to configure and declare the resources that's needed. And so, let's move on to functions and let Adam take over for now. Great, thanks. So, we just talked about how
a Crossplane composition is a pipeline of functions. Let's talk about what that actually means. The cool thing is you can write your functions in any language you want. Crossplane calls them by GRPC. So, any language that supports GRPC, you can write a function in. And functions are only concerned with one thing. They take input. The input is your XR, that composite resource, the output of any previous
functions in the pipeline, and any other resources that they want to request from the cluster. And they produce output. And that output is what should be composed. So, they're just focused on transforming your XR into a set of composed resources. And Crossplane takes care of the rest. It takes care of calling all the functions, passing their input and output along, and then finally, managing all the resources
that come out the end of the pipeline. So, that means you don't have to take care of any of the hard parts of writing a Kubernetes controller. Uh you don't have to worry about server-side apply or finalizers or owner refs or retries or any of that stuff. Crossplane's doing all of that. Your function is just focused on the business logic that you have of composing resources. So,
here's a a few examples of what a a function might look like. You can use Go templating to write a function if you want. So, really simple one. You can use a full general-purpose programming language like Python, and you can see a little snippet of Python Somewhere in between, we have configuration languages like KCL or Q. Or if you are migrating from KRO or you like their
YAML and cell syntax, you can use that. And if you're really comfortable with Terraform and you like HCL, that's an option as well. And like I said, the the cool thing is like these are just GRPC endpoints. So, if you want to write your whole function as a full-on Go project and make it a real piece of software, use external libraries, that's definitely possible. And on the
other end of the spectrum, you can just write it in plain YAML. Crossplane started out with a language we call patch and transform, and that's just a YAML transformation language. And that kind of leads me to what we're working on in Crossplane right now, which is developer experience. Something that we know about building on top of Crossplane is that it's complex. I it is a lot it's
a big learning curve for people to go through. And that's been a blocker for people adopting it. So, we've been working on making this better I as part of this developer experience initiative we have. Like I said, pipelines are powerful, but they're complex to build. So, you've got multiple source source repositories to manage if you're building a pipeline of functions. You've got repositories for all your functions,
then you've got one that holds your configuration, those XRDs and compositions that we talked about. And there's dependencies between them. So, when you want to make an update, you've got to update multiple places and propagate it. Um and a lot of users don't want to deal with that. So, they go with these generic functions like function Go templating or function Python, because they let them just write
that code in their composition. great, except it means you're writing code inside a YAML document, and that is gross. I you're putting a a block of potentially white space sensitive code like Python into a YAML string literal, and YAML's also white space sensitive. and And just it's really easy to get it wrong. It's not very ergonomic. You don't get any of the advantages of writing code in
a real language. You don't get nice IDE features and it's hard for LLMs to write these cuz they're they're weird. Uh you can't use external libraries very easily. So uh some users worked around this. We did the setup down for a while where we'd write code in separate files and then we'd inject it into the composition as part of like a build process. But that was kind
of fragile. Everybody had to build it themselves. So the solution that we've come up with is what we call control plane projects. This is something we built at Upbound as part of our proprietary CLI a couple years ago. And over the past 6 months or so we've been working with the Crossplane community to take this upstream. Uh and we're in the process of of taking it upstream.
Because we think it's it's pretty foundational capability for uh Crossplane users to have to be able to build easily on the top of the platform. Uh so you can think of a project as a development artifact for your control plane or your platform. Just like when you're building software you have a source repository that's your development artifact. And then you build runtime artifacts which are binaries or
container images or whatever. Uh a project is the same idea. This is your your source uh dev artifact. And then you compile it into a set of Crossplane packages that you're going to uh install into your into your cluster. So that sounds great. Um but I think it's it's more fun if we actually look at how it works. So we're going to do a little live demo
here. And I'm just going to do this so we get get my screen mirrored here so I can see what I'm typing. All right. Great. So um what [clears throat] I'm going to use today is a a version of the Crossplane CLI that I've built out of a local branch. Uh and this branch is available for everybody to try. So you can try it out after we
get a link to the slides. Um but it's not merged into a Crossplane yet. So this isn't functionality that's available and released just yet. But the first thing we're going to do is we're going to initialize a a new And if we look at what's in our project it's got some empty directories and it's got this Crossplane Crossplane project.yaml file. And this has a a little bit
of metadata about our project. So it has a name and it's got the OCI repository that we're going to push our images to. And now that we have that um we can declare our API. And we're going to use simple schema for that today because I like writing simple schema better than I like writing OpenAPI. Um and so we're declaring here a type kind called web app.
This is going to be like an abstraction for a web application. It's got a few properties. It's got an image, a number of replicas, and some ports to expose. Um and now that we have that simple schema we can convert that into an XRD with that command. and we can see this kind of expanded it a little bit, gave us that additional XRD metadata that that Jared
showed before. Uh it has different API versions cuz this turns into a CRD at the end of the day and it expanded this whole OpenAPI schema. Now that we've done that we can generate a composition. So here's our sort of empty no-op composition. Right now it just calls this function auto ready which is kind of an off-the-shelf piece that Crossplane has to uh mark your XR ready
when your resources are ready. And now we need to think about the what we're going to compose. And we're going to compose some core Kubernetes So to do that we're going to add what we call an API dependency to our project. And this is going to give us language bindings for the core Kubernetes APIs. Um you can also add API dependencies on any uh other Crossplane provider.
So if you want your AWS resources or whatever. Uh and on any generic CRD that you want. And the the CLI is going to generate language bindings for us so that we can write it in a real language. Uh and we having done that we can generate a function. So functions live inside of projects alongside the compositions and the XRDs. And that's kind of what makes this
a powerful concept. We have everything in one place and we think of it as being all versioned together. So you can see we generated this uh this function. It's got a main.py. We used Python today but we support different languages. and I have a pre-populated version we're going to over to so that you don't have to watch me write Python code. but we can see now the
the power of those uh language bindings that we generated. If I mouse over my my web app type this came from that simple schema that I wrote. We can click to it. We can see the the Python types here. And we get all the nice LLM and and uh editor features that we're used to when we're writing code. we get the little docstrings that we wrote um
and I auto completion and stuff. And same for uh core Kubernetes resources. We we can get all the nice editor support with those. So now we can create a an example. we're going to use the render command to run our function pipeline. And um oh I'm not logged in so it's not going to do it today. I didn't anticipate that. But I what this should do is
um I run our function pipeline locally and it's going to show us the resources that would be composed by the function pipeline. Uh and when you all try it out at home you'll you'll see a bunch of YAML pop out here with a service and a deployment and all that stuff. we'd probably need to let's just do that. So that the next project next build work. So
the next thing I can do is I can use this Crossplane beta project run command. And what this is going to do is spin up a local kind cluster and install Crossplane into it. And then it's going to install this project that I that it's built into that control plane into that that cluster. So this lets me uh play around with it locally, experiment with it locally
without installing it into a Kubernetes cluster somewhere else. Um so it'll take a moment to do that. And now we can see our functions got installed. So that the function auto ready and the one that we wrote as part of our project. And we've got this XRD that we defined available as a a type in our API server. And now we can apply that example. And we
should see it get uh composed. So there's our uh web app that we created. And we should see the the service and the deployment that we created will become ready here. So those are what we composed in our function. Um we've got our our our deployment there and our service down here. Um and if I port forward to it we can see there there it is running
on on localhost. So it did actually work. So the the last thing I wanted to show here is um some test functionality. And for this we are going to use um a project called XPRN. This is was built by our friend Theo who works at Elastic. Um but has been donated to Crossplane as well. So this is part of the Crossplane contrib organization on GitHub. Um and
XPRN is a a test framework for writing tests for your compositions. we're going to take a look at what that test looks like. We have a an XPRN test here that's built um written And it says we're going to here's our XRD. We're going to do a here's our composition. We're going to apply this XR. And then we're going to run that render command that I showed
and it it failed but it'll pass this time I promise. Um and we're going to make some assertions on the output. So we're going to say the deployment should look like this golden test file that I have. The service should look like this other one. and if I run that with the the XPRN tool um it's going to actually just spit out some really boring output. It's
going to say okay because my test should pass. Um but if it failed it would spit out a diff of what was different. So what this gives us is a really nice local development loop where you can uh update your functions, update your compositions. You can test them locally with these composition tests. You can spin them up in a local cluster and and test them for real.
Um and then you can push them to production. So it gives us a a really nice uh development life cycle for building on top of Crossplane. And uh that's the whole demo I wanted to go through today. I'm going to hand it back. Oh and I guess one note is if you click get these slides and click through to this uh you can run the demo on
your own machine too. It is all available. So I would love to get feedback on this before we start merging it upstream. Um come and chat with us at the Crossplane booth about it. More feedback we get the the better thing we'll build. And I'll hand it over to Jared for another demo. What's this one here? That's power. There you go. Thank you, buddy. All right. Let's
switch laptops. See if this is still projecting like I had it working before. Please show something. Yay! All right, All right. So I'm going to find my mouse first. All right. I'm going to talk about metrics in Crossplane and basically every single Crossplane metric you've ever wanted to care about. That's what we're going to cover. Uh so right now nothing. I got nothing on this cluster here
whatsoever. Um so I'm going to go ahead and kick off something while we start talking. Um yeah, run that. Go. Okay. So we're going to just like we're in the background here like three different EKS clusters are going to start coming up dev staging prod Um things are going to start happening. But first background information. Okay, so let's talk about history of metrics and crossplane. So we
Crossplane has complexity to it. It lets you do a ton of very powerful things and with that comes some complexity. A common then question from a lot of the adopters is Crossplane's complex. I need more metrics to be able to be you know observant and understand it and get it get insight into it. Very reasonable request that comes at like all levels of the Crossplane object model
all fields, you know, status like annotations like people want to know more about everything in Crossplane and what's happening. Once again, very reasonable request. Historically, we've been able to tell you lots of things. So we have metrics in Crossplane. We've been able to tell you things like you've got 15 GKE clusters that are not healthy. But what you really want to know is more things like well,
which clusters aren't healthy? Who what team is affected by and how long has this been happening for? And we have not been able to answer questions like that. And there's there's a couple good reasons for that. We've been hesitant to add these type of metrics into Crossplane for two main reasons. One is that you know, if somebody want request a metric, we add it into core Crossplane
and we ship a release that goes out there. Somebody asked for another metric, we ship it you know, have to ship another release. Like we don't want to tie that to releases of core Crossplane. We want to move it outside of Crossplane. And then also the second reason is card not cardinality explosions. If you're not familiar with that, if you create a metric and then you have
it like labels on that metric tied to like the names of instances or something like that basically tied to the number of instances you have, that means you get a time series in Prometheus for every single instance in the cluster. If you've got thousands of those, you're going to your Prometheus is going to start having some bad days because your cardinality of your your metrics is exploding.
So we don't want to make enable people to do that by default out of the box. Like that's not a good experience either. So we sat down and we tried to design an experience here of okay, how can we do metrics in Crossplane that is super powerful, super flexible. And then we realized as well like we kind of got a design together based on cube state metrics
that there's an upstream effort around this as well. Resource state metrics is the name of the project. So we've kind of joined forces with that upstream effort and then we've made available to you like a package sort of experience for Crossplane specifically based on the resource state metrics project there that gives you the ability to basically monitor and and get metrics for anything you want in Crossplane
like really anything basically. And of course, thank you to Chris Christopher Harkey who is who has been driving this effort. He's done an excellent job on it as always with everything he touches in the Crossplane ecosystem. So thanks Chris if you're here or if you watch the recording later. But let's go to the actual demo now. So if you give me that background information, we started with
seeing that there was nothing in the cluster whatsoever. We applied some stuff get resources going and metrics going and stuff like that. And so I'm hoping yeah, okay. So good. We're seeing some stuff happening. So Basically what I was saying is that we kicked off the creation of not only three EKS clusters for dev staging and prod, but also all of like their what like the IM
roles, you know, VPCs, subnets, security groups, route tables, blah blah blah. Like so we've got all sorts of infrastructure resources going on right now. And this is a little bit further than what we showed earlier, right? We showed like a simple app and like it's a deployment and a database. Like this is starting to do real stuff in front of our eyes here. But how do we
monitor it? How do we get metrics about it, right? And so these are the sort of the things that you can start doing in Crossplane that you'd never been able to do before. So not only can we see sort of you know, high level like you know, of my providers, my functions, my EKS clusters, how many of them are ready and healthy and all that jazz. We
can start doing things more like okay, but which ones? That's the really important part here is that you know, on the per resource per instance level, we can start carrying and alerting and you know, monitoring you know, which instances are actually having problems. So I know that the not going to be able to see labels really good in the Grafana dashboard. That's okay. I'm going to tell
you. But so for instance, like we're starting to track here like of all my EKS clusters, which ones are ready? How long have they been ready for? How long have they not been ready for? So we have like different time series for dev, for production, for staging. So we can start breaking out things you know, on individual clusters. Then we can see for instance like how long
they've been in that not ready state. So you can kind of alert on that. Like okay, if it's not ready for a certain amount of time, then we can start alerting on it. We get all sorts of interesting metrics here as things are continuing to progress. And then like even like for instance cardinality too, right? We we are worried about cardinality because like it will absolutely destroy
Prometheus. So there's kind of built-in monitoring of that as well of if you're these metrics you're creating, if they're creating too many time series in the background, you can be alerted on that So let's look at details. We've talked about the concepts here, but I want to start showing you details about this stuff. So the key element here, the key object you need to worry about is
the resource metrics monitor. So we've created a whole bunch of them for like node health and you know, cluster status and providers and all that jazz. So those are the things that we create those and then they start creating metrics on our behalf. So this is what one looks like. I'm definitely going to have to make this bigger. Here we go. Here is a resource metrics monitor
for providers. So it's saying basically look at all the providers on the system and use a cell expressions to give me generate metrics for how many of them are healthy, how many of them are installed. So you can you know, reach down into like the status conditions, do checks of like find the healthy condition, if it's true, start counting that etc. But then the key part here
is that not only can you use like a you know, an expression language or rich expression language to generate your own metrics, but you can start adding labels to them too. So this is how you know like which objects are healthy and which objects aren't We can go further than that though actually let's look at yeah, let's look at cardinality as well. So for that provider health
we were just looking at. Now I'm looking at that resource metrics monitor there and looking at the status, the cardinality part of it. So this you know, it's saying for every provider in the system create a time series So now we can see that we're up to 12. Like we've created 12 new time series entries in the Prometheus database, but that doesn't exceed the threshold yet because
you can put limits on these things. You can say yes, start giving me these metrics, but if it gets past a hundred, stop. Don't do it anymore. Please don't make my Prometheus unhappy. So you get this flexibility of you know, monitoring and getting observability about whatever you want, but not necessarily exploding your Prometheus at the same time. And then the last thing to focus on here and
to show you that you definitely could not do before is here's a resource metrics monitor for all the EKS clusters in here. And then what I'm going to do is I'm going to extract some really important information off of those clusters. So for instance, I want to see like which XR which composite resource is this cluster a part of by extracting some of the the labels information.
I want to see some annotations to see like which resource in the composition it is and what's what's the name of it in AWS etc. And so once you get those that information those labels there, then you can do really interesting queries around like you know, find all of the resources that belong to this production EKS cluster or you know, give me all the resources that belong
to an unhealthy EKS cluster. And you know, before we could just tell you right how many clusters are unhealthy. Now you can start seeing like okay, these are the affected resources, these are the affected teams. Like this is the scope of the problem that I'm dealing with. All from your metrics. If you choose to do that, remember. If you don't want to pick up all that cardinality
and those time series throwing them into your Prometheus, you don't have to do all that. But the power the point is that you have the flexibility and the power to do that to do the monitoring the way that you want to. So excellent work for for what Chris has been doing and also upstream in the resource state metrics project as well. So let's close this thing out
here. Just one more slide I think. Where you know, the the whole point here about this project and its success is because of the you know, the contributor community, right? We've had over 3,000 people contribute like I said earlier. And so we want more people to do that as well. So go to Crossplane/Crossplane repo. There's a contributing directory there and there is a contributing getting started guide
where the very first couple of paragraphs tell you like how to get involved you know, in the core in the ecosystem functions, providers, the docs, all that sort of stuff. It kind of gives you your first steps about how to start contributing to the project and we would welcome you there with open arms. That's all. Thank you very much everybody. >> [applause]
More from this event
See all 436 talks →
Best of KubeCon + CloudNativeCon Amsterdam 2026
2:17
The Quiet Work of Forever: Sustaining Open Source Communities - O. Hope Amaechi-Okorie, JSON Schema
26:24
Evolving KServe: The Unified Model Inference Platform for Both Predictive and... F. Spolti & J. Lee
32:40
Preventing S3 Cost Storms: Applying Cortex’s Efficiency Lessons to I/O-Heav... A. Fishman-Lichterman
5:32