The Open Application Model and Crossplane - Part 2
An agnostic way to build application centric platforms and infrastructure in a team centric way, across clouds
The Open Application Model and Crossplane - Part 2
By Leonardo Murillo
- 12 minutes read- 2494 words
Let’s get experimenting
Welcome to Part 2 of my series on the Open Application Model and Crossplane. If you just got here and want to learn what OAM and CrossPlane are, start with part 1 of this series for an overview of both.
In this experiment we will take Gooogle’s “Online Boutique” and transform one of its services into a OAM / Crossplane framework based solution!
Before we dive into the technical, I want to share with you a quick story about my experience experimenting with CrossPlane and OAM.
It took me a lot longer to get this solution working than I had anticipated - of course I dedicate an hour or so a day at most to this type of endeavour, nevertheless, finding information and troubleshooting problems took considerable effort mostly due to the relatively still immature amount of documentation and documented “experiences” out there.
Things go rather smoothly when you follow the documented process, but once you start mixing things up and run into issues it took me quite a bit of googling and relatively fragmented documentation reading to get to the bottom of things.
But there is a community! When you get to the point of reaching out to the community usually means you’ve gotten to a point of resource exhaustion, in other words, you don’t know where else to look. I hit that point trying to get connection data to propagate to the right namespace, an issue that blocked me for over a week.
And that’s the beauty of open source: there is a community to reach out to, and there’s no better help than that which is given out of technical curiosity and the will to help one another.
I’d like to give a shout out to Krish Chowdhary, a Software Engineer with the RedHat Emerging Technologies team that, in the spirit of community, shared his own experimentation and pointed me in the right direction.
Our starting point
Google has put together a very useful project to demonstrate how solutions built using a microservice architecture actually work. It has evolved over multiple iterations and in its latest version it is known as “Online Catalog”.
It’s a fantastic resource for training and experimentation. The solution is super diverse, with services written in five different languages! This fully containerized solution is ready for deployment onto a Kubernetes cluster using Skaffold.
Microservices communicate with one another using gRPC and each one gets deployed as a Kubernetes Deployment with a Service in front of it.
The source code for each service can be found in a subdirectory inside the src/ directory at the root of the repository:
In this experiment we are specifically not going to modify the source code of the services, considering one of the objectives is to demonstrate the implementation of OAM patterns without impacting existing application code.
This is important since, were there a need to modify the applications, the effort and risk of adopting the Open Application Model would dramatically increase.
Kubernetes manifests defining each service can be found inside the aptly named directory kubernetes-manifests:
In actual scenarios, these different manifests may be located in different repositories, and they may be managed by different teams.
Step No. 1 - Platform builder resources
We will start working inside the platform-builders directory, this is where we will place all manifests for creating and publishing the platform components that will provide cloud managed resources, provided by CrossPlane and OAM, to teams that require them when building their solutions.
ContainerizedWorkload workload definition and traits
In its original version, both cartservice and redis are Deployment kind of objects, we will be turning them into OAM object kinds.
There are two main benefits of using the ContainerizedWorkload object as opposed to the native Deployment kind:
Abstraction: The ContainerizedWorkload kind “hides” a lot of the complexity of a deployment, and provides a layer of abstraction between development and deployment.
Configuration: By using a ContainerizedWorkload you enable application operators to use Traits to modify specific parameters of the solution during release.
As per redis, since we will now be providing this service as a managed cloud resource via CrossPlane, we will also create a WorkloadDefinition that refers to the custom Redis requirement definition that we will be publishing. redisinstancerequirements.infrastructure.murillodigital.com is a CRD that CrossPlane will create for us as we define and publish our custom infrastructure.
In order for us to enable this transformation, we of course need to have the necessary OAM resources deployed to the cluster. For that purpose we’ve added two definitions:
CrossPlane will automatically create a CRD using the template defined in crdSpecTemplate and use name.Kind property as name, in this case we’re defining a RedisInstance CRD that requires two properties defined, tier and storage which we will pass to the underlying managed service resource when we create a Composition that “maps” this kind to a CrossPlane managed cloud resource.
Another important detail here is the connectionSecretKeys list. One of the more convenient (and secure) features of having CrossPlane provision your cloud resources, is the automated way in which it manages connection details for you, to enable and use this feature, you must specify which keys you will be using for connection details from the underlying CrossPlane object. To understand which connection keys are exposed by each cloud provider resource, refer to the API documentation for your provider of choice.
A InfrastructurePublication: Defining a specific type of custom infrastructure as we did does not mean this new type of resource will automatically be referable for consumption. Before we can use it, it must be published:
A Composition: The composition is used by CrossPlane to map your custom resource to a Cloud Provided resource, in our case we are “mapping” our RedisInstance custom resource to a GCP CloudMemoryStoreInstance CrossPlane resource.
Pay special attention to the composition labels, your application developers can use them to choose which specific composition they want to use, to satisfy a specific service requirement.
Pay attention to your patches. These items will define, when using this composition, which parameters from your Component will be applied as overrides to resources being created by your Composition.
Step No. 2 - Application Developer Resources
Now that our Platform Builder team has created and published all managed resources that our Application Developers will need for their cartservice, we’re going to rewrite the cart service definition to match.
As you learned in part 1, application developers create components, which represent the functional units (eg. services) of their solution.
Since we got rid of our redis.yaml manifest now we will add Redis as a component of our solution, and will convert the cartservice service into a ContainerizedWorkload.
Note how our cartservice-redis-component points to a kind of workload named RedisInstanceRequirement, which is a CRD automatically created by CrossPlane that we use to indicate that we need a RedisInstance created to satisfy the requirement.
Also note that no RedisInstance nor ContainerizedWorkload will be created simply by applying these objects, they will need to be used by our application operator in the context of an ApplicationConfiguration. And this takes us to our final step, the application operator actually releasing this as a runtime solution.
Step No. 3 - Application Operator Resources
The application operator will now use the components defined by the application developers to provision a configured release of the solution. We are going to create a single ApplicationConfiguration that ties together both Components into a single unit, and we will use Traits to define runtime parameter of our ContainerizedWorkload
And with this we are done, now the application operator can go and apply the ApplicationConfiguration and this will create all resources, including those that exist as managed services in the cloud - it will take a few minutes for resources to be created and reach a Ready state, considering the time it takes for cloud managed resources to be provisioned:
$ kubectl get RedisInstanceRequirement
NAME READY SYNCED CONNECTION-SECRET
cartservice-redis-req True True online-catalog-redis-secret
$ kubectl get RedisInstance
NAME READY SYNCED COMPOSITION
cartservice-redis-req-f8st8 True True redisinstances.gcp.infrastructure.murillodigital.com
$ kubectl get secret
NAME TYPE DATA AGE
default-token-zndtk kubernetes.io/service-account-token 3 17d
online-catalog-redis-secret connection.crossplane.io/v1alpha1 2 54s
$ kubectl get secret online-catalog-redis-secret -o yaml
- apiVersion: v1
- apiVersion: infrastructure.murillodigital.com/v1alpha1
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/cartservice-workload-85fc7b46d6-pn7nb 1/1 Running 6 6m21s
pod/cartservice-workload-85fc7b46d6-zmg5x 1/1 Running 5 5m51s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cartservice-workload LoadBalancer 10.103.247.214 <pending> 7070:31337/TCP 6m21s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 17d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/cartservice-workload 2/2 22 6m21s
NAME DESIRED CURRENT READY AGE
replicaset.apps/cartservice-workload-85fc7b46d6 220 6m21s
And we will find the corresponding Redis instance provisioned in GCP:
I love the ability of managing cloud resources within the same context as the rest of my Kubernetes native solution, the fact you get full solution visibility and you can integrate the lifecycle of all components is really effective and powerful.
Having the ability to manage these resources across clouds makes the approach even richer.
The Open Application Model I think likely works best in enterprise contexts with large scale solutions and organized cloud initiatives, as powerful as the separation of concerns is, I believe for a lot of corporate solutions or relatively small sized projects it may be overkill.
In terms of adoption, as I mentioned it was not without effort, we are definitely looking at a model and suite of technologies that are in early stages and industry utilization must increase and documentation must mature for it to be appealing to a less adventurous audience.
Get notified of new experiments and articles on cloud native technologies, join my mailing list!.