Conf42 Cloud Native 2023 - Online

Multi-Cloud Deployments with GitHub Actions

Video size:

Abstract

We recently completed a project - migrating and rebuilding several sites to all run from a single repository and deployment process. All the sites are headless, though built with a variety of technologies – however all leverage the same Headless CMS backend to allow for content and asset sharing.

Summary

  • Rob URL is presenting at Conf 42 Cloud native. He will talk about how you can run multicloud deployments using GitHub actions. If you have any questions or if you just want to say hello, feel free to reach out after this.
  • Sitecore has unify all of its sites into a single headless CMS. The project involved migrating from two separate monolith solutions. A new SaaS version of Sitecore's CMS was released. This allowed the team to complete the migration.
  • How do we configure the CI CD pipelines to deploy to all these different cloud architectures? Well, we ended up choosing GitHub actions to power this. We created our own custom set of reusable workflows which are designed to make this more easy.
  • In here we're just deploying net six code so we can use the net CLI. Once that's completed, we'll then go to production. Here we only have two deployment phase, which is for preview and production. In Azure we have two different web apps which is what we saw before.
  • GitHub actions itself as a platform is super flexible. The ability to use shared templates within these YAML definitions hugely reduced the actual amount of code that we wrote to deploy each of these sites. Finally, the ability to create shared templates makes it a much more nicer way of building out your CI CD.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
Hello everyone, thanks for joining my session today. I'm really excited to be here presenting at Conf 42 Cloud native, and today I'm going to be talking about how you can run multicloud deployments using GitHub actions. My name is Rob URL and I lead the developer advocacy team here at Sitecore and I've been writing software for a very long time. I'm originally from Manchester, back in the UK, but for about the past ten or eleven years or so I've been based in Melbourne, Australia. You can see a whole bunch of different social channels on the screen there, so if you've got any questions or if you just want to say hello, feel free to reach out after this. So what am I going to be talking about today? Well, I'm going to be going through a real world project that we've been running in the developer advocacy team here at Sitecore. And this was a migration project and this project covers a lot of the sites that we're responsible for as a department. If we look at how they were hosted previously, we actually had two separate monolith solutions we were maintaining and we were in the process of migrating from the first one into the second one. Now having our sites set up like this, it led to quite a few problems and I'm sure some of these are going to sound pretty familiar. We had two separate code bases to maintain, so that meant if we were working on one of the sites, we had to be sure we were in the correct code base when working on it. On top of that, the bottom one was open source and available for our community to learn from while the top one was closed source. So that different governance model also added a layer of complexity to the process. One of the biggest problems we had though was there was a whole heap of infrastructure powering these two completely separate instances. And I'm sure my manager wasn't all that happy with the reassure bill we were rebuilding each month. So we were kind of partway through this migration that we never actually ended up completing. But then what happened internally was sitecore released a new SaaS version of our CMS and that seemed to solve a lot of our problems for us. It allowed us to create a new target state architecture that we were working towards and this would allow us to unify all of these sites we were responsible for into a single location, both from a code perspective and from an architecture perspective as well. And that gave us some great benefits. First of all, it unified that developer experience, so no longer would we be dealing with two separate code repositories. We were going to work from a single Monorepo repository hosting all of the sites that we're working from. And we chose to operate in the open source model we'd used on the second repository before because we'd got a lot of great community interactions around that and we wanted to maintain that going forward. One of the big wins as well was also the drastically smaller application footprint here because we were now running off a SaaS CMS backend. We'd offloaded a lot of the infrastructure into that application instead. So all we're responsible for now is hosting these heads. It's a headless CMS. So each of the sites we're building is going to have its own head. We were going to deploy ourselves, but it gave us a lot of freedom. Then we could actually choose the best technology to host those heads. Completely different clouds depending on the head we're deploying and the technology it's built upon. And this finally would let us completed that migration, unifying everything into one single approach. So how did we set this up? Well, as we said, we wanted a Monorepo containing all of our code, but we still wanted to have the flexibility of where that code got deployed to. So this was the kind of high level architecture that we ended up going with. We'd have the four sites we talked about before we had our mvp site, which is an ASP. Net six application which we wanted to push out to an Azure web app being written in. Net. Azure is a fantastic host for that, but we had our other three sites which are now built with NextJs. So the Sukon Anz, the Subcon EU and the Subcon events site. These were built in next JS and Vercel is a fantastic host for those. So we wanted to be able to use the best technology for the job. The top three sites were powered by the new SAS CMS on the back end, and the bottom ones was the static site. There's no actual content management functionality in there, it's still in the same repo, but it's just static content in there. So our users would come along and then they'd be able to interact with these four different sites regardless of which cloud they're hosted in. And you may ask why we chose these different clouds. It mostly came back to the technology choice that we'd made in designing them and building them. The bottom three sites you see, they're really static content sites. There isn't much interaction on those. So using a static generator somewhere like NextJs was a really great fit for that. The MVP site on the other hand had a lot of interactivity in there, so we power application process through that. So building that in. Net was a great fit and it also allowed us just to migrate the existing code base over, drastically sharing our development time. So how did we configure the CI CD pipelines to deploy to all these different cloud architectures? Well, we ended up choosing GitHub actions to power this and there's a few reasons for this, but mainly it seems to be aware Microsoft's are pushing a lot of their activities nowadays and it gave us an opportunity to show our community how you could build out CI CD pipelines using that technology should they choose to do it as well. And we went through a few iterations for know much like with most things in software you kind of never get it right first time. Our first iteration was to have one big pipeline. So we had one big YaMl document which basically went and pushed our configuration and our content out to the SAS CMS XM cloud. It would then go and deploy the three different Versailles out with the different targets out of the mono repo and then finally it would also deploy our MVP site out our. Net application that was going out to our Azure web app. And it worked. It did, but it was really heavy. If I'm making a single line change to one of my JavaScript sites over here, why am I deploying my. Net site out over here? That they're not really related, they come from the same Monorepo repository. But we needed a more targeted deployments approach that allow us to iterate much more quickly when developers are committing changes into the repository. So what we ended up doing was splitting out the pipelines by the target system. So we have a different pipeline to deploy our changes to our SAS CMS. We have a different pipeline to deploy changes to the MVP application. We have a different pipeline to deploy changes to the different subcont applications and to power that. We created our own custom set of reusable workflows which are designed to make this more easy. So let's take a look over in vs code now we'll see how all this is created. Okay, so I've loaded up the repository here and what you'll see straight away is you get this GitHub folder, GitHub and this is where your GitHub actions are going to exist. And they all exist inside this workflows folder. And straight away you'll see we have a naming content here. You can see the files at the top with a build underscore prefix are there define how to build a certain type of application. So we have a shared workflow showing how to build a. Net app and we have a shared workflow describing how to build a nextjs application. If we hop down to the bottom next you'll see we have ones prefixed with a deploy underscore name. These ones are all designed to deploy to a specific cloud platform. So we have a reusable workflow that will deploy an asset to an Azure web app. We have a reusable workflow that will deploy an asset out of Versailles. And finally we have a reusable workflow that will deploy an asset out of our XM cloud, our SaaS CMS. Where all the action really happens though is that middle section, the CI CD prefixed pipelines. These are the ones that actually handle taking a commit from a repository, building an asset using the build underscore prefix workflows, and then deployments it using a deploy underscore prefix workflow. So let's start to take a look at how these work. I'm going to start off with the MVP one first. Remember the sites here is hosted in Azure. And if you think back what I said before, remember we had that one big pipeline that was triggering all the time for every commit deploying all these sites, all these assets, complete overkill. We needed to make this far more targeted. And you can do that in the top section here of your pipeline definition. So here's where we define when this specific pipeline is going to run. And what we do here is we basically set a set of paths within the repository that we want to monitor. And we're saying anytime anything changes in a commit in these paths, we want to execute this pipeline. And this is all in the on section up here. So you can see we have our push section and we have a pull request section. So whenever anything changes, either for these definitions by itself. So when anyone changes my build definition we're looking at here. This leverages the build net YaMl. So whenever anyone changes the build net YAml, or when anyone changes the deploy Azure web app Yaml, we also need to rerun this. We need to test those changes are correct inside the source folder we also have a series of subfolders all within rendering folders internally. That's where all the source code split up for this mvp application. So we're going to monitor all of those. And if any changes happen in any of those folders, again we're going to trigger this workflow. You might notice we also have a workflow dispatch trigger on here. All that means is that I can log into GitHub into the GitHub actions section. I'm going to show you shortly and I'll get a button that allows me to manually trigger a deployment. If you don't have that included, the only way to trigger Deployment will be through a push or a pull request in this case. So once we've defined when this is going to run, then we start to define what jobs we're actually going to execute. And this is very similar to most standard YAML based deployment pipelines you'll have seen in a lot of other systems. So we have our jobs download and then within there we define each of the jobs we're going to run. So we have our first job to build our net application. Then after that we're going to deploy to our staging site. And then finally after that we're going to deploy out to our production site. So let's start with our build net definition. You can see it pulls in the build net YAMl from the same location here and we're passing in the build config. This is a parameter we pass in. We want to build this in release mode. If we load up the build net YAml, you can see this is fairly straightforward. This is all just built out using MS build. We do have some old. Net framework code in there, which is why we've gone that way instead of the CLI. But you can see here you just end up with a step based action. So we start out by checking out the repository. We're going to basically install CMS build into our build runner. We install Nuget. We run a Nuget restore on our solution and then finally we're going to built the solution itself. Remember, we passed in the build configuration before allowing us to define that. This is going to be executed in release mode. So it's fairly straightforward. And what you get at the end of that is an asset which is basically the build output of your site. So once that's completed we can then move to the deployment stage. And as I said, we're going to deploy to staging site first, and then after that we're going to deploy out to our production site. So here's where we start to do that. We're again using this referenced reusable workflow, this time the deploy Azure web app workflow. And what we're saying here is this needs net. So here's how we're defining the ordering of these jobs. So this will only execute after the build net has executed. Then we're going to basically set some parameters again. So we're going to deploy in debug mode here. And that's because it's the staging sites. So we want different sites of logging to appear here. We want people to be able to debug easier in sharing than they do in production. We're going to choose the project itself we're deploying and then we're going to create the asset name and the web app name we're deploying to. Finally, we also pass in the secrets. We have some secrets here stored in GitHub and this is the publishing profile information, basically showing how to physically publish into that Azure web app. If we take a look at this deploy Azure web app shared pipeline here, you can see we first of all define all those parameters. So the build config, the project location, the asset name and the web app name. This one's a little simpler. In here we're just deploying net six code so we can use the net CLI. It becomes a lot easier. You can see we basically set this to run on a Windows machine. We start again by checking out the source code. After this we set the version of Net we're going to run so version six and we can then run a build and publish. So we CD into where the project location is. We run a. Net restore, we run a. Net build for the build configuration passed in, and then we run a. Net publish. And this is going to publish to a specific location inside of that built runner. So inside that Windows Runner and you can see the output param here and the location we've passed in with the asset name. Finally after that's completed, we could do a deployment. So we could take this deployment here and we can use the secrets that are passed in the publishing profile and publish that out to an actual web app. If we go and take a look back at our main CI CD pipeline, once that's completed, we'll then go to production. And you'll notice we have an if clause here and this basically only gets executed if it's running against main. So what that means is that when someone opens a PR against this repository, it'll build the net code first. So that steps there just to make sure everything's correct. Any source code changes are syntactically right and the solution still builds. After that we'll deploy to staging. Every commit always goes out to sharing. That's always where we need to test our code. But if we're just in a PR state, the GitHub reference won't be for Maino be for that branch. So we just stop there. Basically it'll just go to staging and allow people to test. Once that PR is merged into main, then this will execute again and it'll go all the way out to prod, running the exact same steps as before. But this time we're going to run in release mode because we need this to go out to production. Okay, so let's take a look at another example of these. Let's take a look at one of the Versaillesites. Okay, so I'm going to load up the Sukon AnZ definition here. This is a site that's built with next js and is going to be deployments out to Versaille. And again at the start you can see we have this same definition showing when we want to execute this. So we start off with a workflow dispatch which gives us the button in GitHub actions, allowing us to manually trigger a build. But then again we're monitoring pushes and pull requests. We monitor the three workflow definitions that this deployments is based on. So that's the config file we're looking at the CI CD for subcons. And this also leverages the builddexjs and deploy Versailles pipelines as well. On top of that, we need to monitor the source code for this site, and that's all located within this path, within the repository here. So if any of those assets change, that's when this pipeline is going to execute. And what does it do? Well, we start off a game by building the sites. We build the source code for the site to test that any code changes are correct and that we can move on to the deployment stage, if that's right. So if we take a look in the build next JS yaml in here, we can see we basically pass in the working directory because we have multiple next JS sites. So we need to pass in which sites it is we're building. And then we can run this on some really fast Ubuntu runners, really lightweight. We set some variable, we set some environment variables we need, and then we pass in things like the working directory as the parameter. Then we can start to execute. So we do a checkout of the code base, we choose which version of node we're working with. Then we run an NPM install to set up all the dependencies. We run an NPM build to check that the source code builds, and then we run an NPM lint to check that it's sync tactically correct and matches our development guidelines. If we hop back. Once that's completed successfully, we then go on to the deployment phase. And you'll notice here we only have one deployment phase, which is for preview and production. And that's because Vercel works a little differently than Azure. In Azure we have two different web apps. We have a staging web app and a production web app, which is what we saw before for our MVP site, infracell. How it handles it is the CLI which we'll look at in a sec, basically has a prod flag which is passed in, which is how you define whether you're doing a production or a preview build, basically. So all of that's handled inside this deploy Versel yaml. So what we do is we basically reference that one of our shared workflows we created, and then we pass in a bunch of secrets. So the versel token, our organization id and the project id for this site itself. Specifically, if we look in the deploy Versailles package here, here you can see again we're using these Ubuntu runners. We do a checkout, we set up node with the version that we're running on, and then afterwards we're going to use this aimon net versaille action. And this is one of the really nice things about GitHub actions. We haven't created this package. This is created by a member of the GitHub Actions community, and that allows us to automate deployments to Versailles. And there's thousands upon thousands of these packages out there, regardless of which language you're building, with, which cloud provider you're deploying for, there's a whole wealth of these libraries that you can build your scripts on top of. And if there isn't one, you can actually build your own and deploy for other people to use as well. So it's a really nice way of sharing code and deployment strategies with the community. We then basically pass in all the secrets we set. So our token, our id, our project id, and then we have our arguments. So here you can see we're going to pass the prod flag whenever we're merging into main, basically. So this is a merge into main, then we want to pass that flag in, and that is how we automate deployments out to a versel site. I'm not going to go through all of the different Versailles. The advantage of having these implemented via shared templates means that their definition is pretty much the same across anyway, just with different secrets and different values. So let's hop over to the browser now, and I'm going to show you how this actually looks inside of GitHub itself. If we go over to the actions tab here, we can see all of the different deployments that have actually been executed. The nice thing here is on the left you get to see all the different pipelines we defined before. So it even shows you the shared pipelines here. Now they're never actually going to be executed themselves, but the ones inside are. So you can now go and click on one of these, for example, the MVP site, and it'll then just show deployments to the MVP sites. Here's where you can see it says this workflow has a workflow dispatch trigger. So if we wanted to manually trigger this workflow, we could go and do that ourselves. Now if we go back to the all workflows view though, here you can see this is a deployment of the CI CD MVP site. Here's a deployment of the CI CD subcon EU site. So you can see it's really easy to see which assets have been deployed. On top of that we can see which branches have gone out. So here is a deployment all the way out to main for the subcontinent use site. Here's a deployment for XM cloud. This is deploying changes to our cloud cms and that's from a branch. So this will only be going out to our staging instance. It won't be going all the way to production ready for people to review those changes. If we take a look at one of these, we can see how it looks inside. So here's an example of an MVP change that went all the way out to production. So we can go and we can see where we've built the net solution. And these are the same steps that we looked at before. We can see how we set up the MS build. We installed Nuget. We ran a Nuget restore for our packages. We ran a build itself. And you can go and expand these and view detailed log files for all of them, showing exactly how that step executed and whether it was successful or not. After that, we went and did our deployments to our sharing site. So here we set up net, we ran our build and publish. So again, you're getting the full output from the CLI here. And then we took that asset and we published it out to our specific Azure web app. The same then happens for production as well. Here you can see we set up net the version that we needed to install into the runner. We then ran a build and publish and then we deployed it out to our Azure web app. So you can see how we have a single repository here we have four different sites, source code, it's all deploying to different cloud providers, different instances, all from a single CI CD instance. So this gives you a really nice single source of truth for all of your deployments. Okay, so I just want to finish up with a few conclusions here. GitHub actions itself as a platform is super flexible. You get a lot of pipelines out of the box, allowing you to do a lot of common tasks with common frameworks. But if you're working with a framework or a hosting provider where it isn't that common, there isn't something out of the box. You can see what community pipelines are available as well, like for example with the Versailles CLI wrapper that we used. On top of that, if you find something that hasn't already been automated by someone, you can create a pipeline yourself and you can share that for other people to benefit from. It's a really nice community driven CI CD platform. On top of that code reuse in your templates matters. The ability to use shared templates within these YAML definitions hugely reduced the actual amount of code that we wrote to deploy each of these sites. If we couldn't have done that, our three different Versailles sites when they got deployed would each have had to have duplicated each of that build code, each of that deploy code. And if we change that down the track, then we'd have to go and change it in each of those definitions, which is not that nicer way of working. The ability to create these shared templates makes it a much more nicer way of building out reuse into your CI CD pipelines. Finally, if you want to see all this in action, as I mentioned at the start, this repository is open source. We're still a work in progress. There's still jobs for us to do on this. You will have noticed, I think throughout this that there's actually multiple builds happening throughout. Next on my list is to share the build asset when we first do our build in our first step with the later steps to try and get our build time down. But yeah, you can go and check out a repository today and see how we're getting along. So thanks a lot for listening to my talk today. Again, you can see all my different social handles there on the screen. So if you've got any questions or just want to reach out and say hello, please get in touch. And apart from that, thanks for listening.
...

Rob Earlam

Senior Developer Advocate @ Sitecore

Rob Earlam's LinkedIn account Rob Earlam's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways