 Hi, my name is Ken Murray. I'm a CICD engineer at IBM, working within the Cloud Pack for Watson AIOps team. My area of expertise is in the adoption of GitOps in the development and operation of deployment pipelines. Today, I'm joined with my colleague Ying Mo to present on the topic of GitOps and cross-plane and its application across both traditional non-Cloud native IT environments and Cloud native applications. So an overview of the agenda. First stage here, I'm going to talk about the adoption of cross-plane and Argo CD in CI CD pipelines. I'm going to give a generic view of deployment pipeline as it exists today and how we use our deployment pipelines in the infrastructure provisioning and lifecycle management, but also in application deployments. Going to talk about cross-plane as an abstraction platform for infrastructure provisioning. So basically using GitOps to both manage our infrastructure and also application deployments. Ying Mo will then discuss ongoing development work in the area of Ansible cross-plane provider for more traditional IT systems. He'll discuss the provider in terms of how it works and how people can use it and how it can be adopted and then finally a deep dive across the Ansible provider runtime lifecycle. Some best practices in the use of the provider and then finally comparing the Ansible provider with the Ansible operator. So if we just look at a typical deployment pipeline, very generic view, you typically would have a build here coming in on the left-hand side of the deployment pipeline. The first stage would typically be the acquisition of infrastructure onto which you would run your deployments. The second stage would typically be some kind of configuration you might want to do on that infrastructure. So in terms of a Kubernetes cluster, for example, you might want to set up some storage or some prerequisites before you do your application deployment. Application deployment then you would deploy your software components onto that infrastructure. You may want to do some close deploy checks to ensure that your applications are deployed to a sufficient level before you would trigger some downstream test pipelines. You may want to export some deployment logs, trigger them downstream test pipelines, export those test pipeline results to some location that could be downloaded by individuals at a later point. And then finally, you may want to do some notification out to the wider teams that your deployments have completed. The stages in such a pipeline that would be typically, as I call it, GitOps ready, would be those that would be more typical to application deployment and the application of Kubernetes manifests onto a cluster. So in terms of this generic pipeline view here, we could look at perhaps configuration, application deployment more typical to the typical view of GitOps and the application of GitOps. Infrastructure management in particular would not be as biblical as it stands today to GitOps. Infrastructure management typically involves the use of custom infrastructure APIs. So provisioning of infrastructure, you would typically point to that infrastructure providers, APIs to create those environments which typically don't lend themselves quite well to the adoption of GitOps. So infrastructure provisioning and lifecycle management, then if we just focus a little bit in on the infrastructure section of our pipeline, the current infrastructure. Today, what we do in our pipelines is that we maintain a pool of Kubernetes clusters. So this pool is available for deployment at any time. It speeds up our complete deployment lifecycle in a sense that we have clusters prepared before they're actually needed for deployment. So we maintain a set of pre-provision clusters in a pool. When we request a cluster from our deployment pipeline, we go to our pool manager and it basically picks a cluster available for the deployment pipeline. Our pool manager then also tops up that pool. So it communicates via custom APIs to IBM's infrastructure provider to create clusters. So in this case, we're using Red Hat OpenShift clusters. So we communicate with our infrastructure provider over their APIs to create a cluster at a particular config level. And it's placed into our pool ready for deployment at a later stage. So as I said, the pool manager is set at a provisioned level. So we have a set level of X number of clusters we need for deployment pipeline. It supports variation in cluster specifications. So different specifications of our worker nodes, for example, CPU memory and storage. And the key point here, I guess, is that the infrastructure, the interface to the infrastructure provider is a custom interface. It's non-cloud native. So quite interesting if we could think of infrastructure management like we do application management, could we adopt and leverage the benefits of GitOps and span that across both infrastructure and application deployment. So with the use of crossplane, we can achieve just that. Crossplane provides an abstraction platform for infrastructure provisioning. So we have rolled out a provider for our infrastructure management crossplane provider. It defines a cluster as a very simple resource type. So the complexities of managing our clusters for our end users is really masked to our users. They don't need to be aware of the API specification. They just need to be aware of the kind of type of cluster and the configuration of the cluster within the OCP resource, the OpenShift platform resource definition here on the left-hand side. So you can see here, we specify things like the cluster name, the diversion of the OpenShift we want to provision, things like the site, what data center we want that cluster to be deployed into and then some very high-level configuration for the workers within the cluster. So the user base, it just needs to populate out these parameters here and the provider communicates across to our cloud provider to provision the cluster. The provider also provides the cluster endpoint and credentials then written to secrets on our control plane cluster. So the control plane is essentially our cluster running our gross CD. So it is our master cluster for that basically performs the provisioning of our infrastructure and running our deployment pipeline as well. As with any type of Kubernetes resource type, we get status again via the provider back onto the OCP resources. So on our control plane cluster, we can list all the clusters we've got deployed in our pool and get the status of those as we would wish any other Kubernetes resource type. So in the definition of our resources then for cross-plane, we're using Helm. Helm provides a very good mechanism by which we can override and set configurations for our clusters. So the template basically allows for the passing of customized attributes for our cluster deployment. So here on the left-hand side, for example, you can see we're taking the fall values from a values file. But we're then also taking like actually it's like a cluster name, we're overriding those to create the overall OCP definition. And we'll see that on the next slide. So here we can see the default values for our clusters. So this will be in one particular cluster pool type. This pool type is basically holding clusters of OCP 4.10.3 in site XYZ. And this is the default configuration for the workers. The cluster pool YAML then is basically overriding the cluster name each time it creates a new cluster instance in our pool. So when we want to create another cluster, we essentially just add another line here into our cluster pool YAML. And that then merges with the names template to produce the overall cluster instance here on the far right-hand side. So how can we bring this together then? To manage infrastructure and applications using GitOps. So this is kind of just a high level view of the flows then. If we start with GitHub on the left-hand side, on the blue kind of trend here, we're looking at the default values coming in for the clusters. Our cluster pool YAML, which defines the instances of the clusters that we want to exist in our pool. As I said in the previous slide, it has merged with the names template and it basically routes through Argo CD, which syncs off of the Git repository holding those artifacts true to provider and opposed to requests across to our infrastructure provider to generate the clusters in the pool. So if we want to, as I said in the previous slide, if we want to create clusters, we essentially just add a new cluster entry into the cluster pool YAML. And similarly, if we want to delete a cluster, it just gets removed from that cluster pool YAML and Argo CD detects the update or the commit representing that removal or addition of a cluster and reconciles that via the cross-plane provider onto our cluster pool. Application deployment then, again, cloud native, right? So it's using the traditional cloud native path here. We have our values.YAML, again, helm driven, our application templates, which contains all of the templates for our application deployment. And I thought of the green line, which essentially is again, syncing off of GitHub, our GitHub repository for application deployment true Argo CD and this time straight on to the cluster that's chosen for deployment. So as I said at the start of the presentation, our deployment pipeline will choose a cluster from the pool for a particular deployment run and that becomes the active cluster then for the deployment. So we deploy straight onto that cluster, again, using Argo CD. So some of the benefits of doing it this way, right? So managing both infrastructure and applications using GitOps, we have a common approach. So we leverage the same Argo CD approach for both managing our infrastructure. So you can think of infrastructure as code. It's managed and maintained within GitHub. Some of the benefits there obviously, right? But with anything else, suppose in GitHub is that we've got it versioned and audited. We've got an audit trail of when clusters get created and removed from our pool. Single sources truth, of course, for deployment infrastructure, it avoids configuration drift. Okay, so when we look at our cluster pools, we know definitively that those clusters map back to our definition within GitHub. There can be no configuration drifts in the sense of individuals creating clusters and placing them in the pool that may contravene our configuration templates for those clusters in that given pool. Cluster state is continuously reconciled and this is an important aspect. If clusters get inadvertently deleted from our pool, Argo CD will see that and it will detect the change and it will drive the cluster pool back to its defined state as per GitHub. So again, that's a very important characteristic for us. Not only in terms of application management and to ensure that your applications are mapping back to the single sources truth, but obviously also for our infrastructure. So I'm going to hand over to Ying Mo now and Ying Mo is going to discuss some ongoing work in the development of an Ansible Provider. Again, more focused on mapping GitOps across to traditional IT systems. So again, continuing on with applying the concepts of Argo CD and crossplane to the non-cloud-based world. So over to you Ying Mo. Thanks, Ken. So my name is Mo Ying and I'm from IBM as a software engineer. Next, I'm going to share with you how Argo CD can be extended to reach traditional IT system by leveraging crossplane. So a little bit background, as we all know, both Argo CD and crossplane are designed for cloud native and it can only be run in Kubernetes. However, this may not be true for many real world organizations where they may have IT systems that are traditional based such as application run on VM or bare metal. And they may have already heavily invested on management automation for those kind of systems. For management automation, Ansible has a popular automation technology as a large user base and mature ecosystem. It is widely adopted by many of such organizations to automate management for different varieties of IT systems ranging from cloud native to non-cloud native. So the question here is how we can drive GitOps for those systems consistently, no matter it is cloud native or non-cloud native. Here, I'd like to introduce an open source project that is currently working in progress, the crossplane provider for Ansible. It is targeted to extend the crossplane scope by enabling its integration with Ansible to build a bridge between the cloud native and non-cloud native world. So that can open a door to drive and reuse existing automation assets to manage hybrid technologies using the same way consistently and help organizations transition to cloud native while keep their existing investments. Let's take a quick look at how it looks. So this is diagram that illustrates how Ansible provider works at a high level. Basically Ansible provider is driven at its core by a Kubernetes controller called Ansible run controller where it watches the Ansible run Kubernetes customer resource along with a reference to the provider config that includes all the information to run Ansible contents. The controller usually retrieves the Ansible contents remotely from many places such as Ansible BXC, Automation Hub, or Git repo. And then it will store the content into a working directory so that can be launched by a embedded Ansible runner command line. So here are some examples to demonstrate how to use it. For example, we can define an Ansible run to call an Ansible row or an Ansible paybook. And both of them are usually retrieved from a remote place which can be defined as a requirements in the provider config. So as it is shown here, this one is retrieved from Ansible Galaxy and this one is retrieved from a Git repo. And if it is a private Git repo that requires credential to access, it can also define the credentials information here. We can also pass variables to customize the Ansible run like this. And for some simple cases, we can even define an inline paybook directly within the Ansible run resource which does not require any remote retrieval. Next, let's take a little bit deep dive into the Ansible provider design so that you can better understand what happens inside the provider. First, let's take a quick look at how Cross-Pen provider manages its resource. So to manage resource, or usually we call it external resource on targeted system, it requires us to define managed resource that includes the desired state and is watched by the Cross-Pen managed resource we consider on local system. To manage the external resource, we will have to operate on the managed resource where the changes will be detected by the managed resource reconcealer. Then the reconcealer will operate on the external resource on targeted system to reflect the changes that we made on the managed resource. To be more accurate, this is a typical reconciliation loop that consists of five phases. It starts from the connect phase that is usually used to establish the connection to the targeted system. Then observe, delete, create, and update that map to the resource crowd operations. During the observe phase, the provider will usually detect if the resource on target system exists or not. If it does not exist, it will advance to the create phase to create the resource. And if it does exist but not up to date, it will then go to the update phase to update the resource so that the desired state and the actual state are always in sync. Once it's finished, it will recue to wait for the next loop. And if user deletes the managed resource, that means we will never need external resource on targeted system anymore. Then this will trigger the delete phase where the provider will help us delete the resource on targeted system. So this is what we call as cross band managed resource lifecycle. Now it turns to the S-baran lifecycle. You may see that it is actually a subset of the cross plan managed resource lifecycle. Generally, in Ansible provider, there are two types of lifecycle for the S-baran that are supported. In the first type, the provider uses observe to handle the case when the managed resource S-baran is present and use delete to handle the case when the managed resource S-baran is absent. And both observe and delete will call the same set of Ansible contents. In the second type, the provider will run the Ansible contents in observe first, but using the check mode, which is essentially a dry run. And if any change is detected after the dry run, the provider will then trigger a create or update to kick off the actual run of the same set of Ansible contents. Also, both two types will use connect to prepare the runtime environment for the Ansible run. For example, to retrieve the Ansible contents from a remote place. Okay, now you learned how Ansible run will be driven by following the lifecycle. Ansible provider implements the lifecycle to decide when to run the Ansible. But in order to do the actual work to manage the resource on target system, it relies on the Ansible contents. Now let's see what are the best practices for Ansible users who creates the Ansible contents so that can align with lifecycle much better. So here are a couple of rules. The first rule is to keep your Ansible rules or playbooks adaptant. It is always a best practice to write Ansible rules or playbooks in an adaptant way. For Ansible provider, this is required because the same Ansible contents will be run many times in their reconciliation loop. You should guarantee that each Ansible run should produce the same results if the decided state is not changed. The second rule is to ensure your Ansible rules or playbooks to support state field. It is also a common practice in many Ansible modules that support state field and behave differently according to the state value. And for Ansible provider, this is required because the same Ansible contents will be used to handle both the case when the Ansible run resource is present and absent. Next, let's take a quick look at the difference between Ansible provider and Ansible operator, which is another similar technology created by Red Hat that can bring Ansible into Kubernetes world. So as listed here, there are a few major differences. First, Ansible operator is mainly designed for managing Kubernetes resources, while Ansible provider can be used to manage both Kubernetes and non-Kubernetes resources. And second, Ansible operator requests you to bundle Ansible contents that manage resource into a container image at build time before you deploy it. And Ansible provider on the other side works as a driver without having to build Ansible contents into it, but instead to prepare and call those Ansible contents at runtime. And third, Ansible operator is usually implemented as a co-located Kubernetes application to operate on in-class resources. While Ansible provider usually runs on one Kubernetes cluster, then operates on the resources on another remote system, which can be either Kubernetes or non-Kubernetes. Lastly, all the discussions around the Ansible provider was extracted from the design doc, which I put the link here just for your reference. You can read it to get a more detailed version. And since it is an open source project that is evolving rapidly, I would encourage you to join us and feel free to open Git issue or submit PR if you have any good idea on how to improve it. Okay, that's all for this session and thanks for watching.