AEM Champion Session Multi Tenancy
The session will focus on how multiple tenants can exists on the same AEM as a Cloud Service instance. The focus is on the use of Adobe Cloud Manager to deploy multiple code repositories on the same AEM instance. This includes the setup of different git repositories as well as the use of git submodules.
Transcript
Hello everybody and thank you for having me and thank you for the introduction. My name is Rami Elgamal and today I’ll be taking you through multi-tenancy on AEM as a cloud service. So I’m going to start with telling you a little bit about myself. So I’m currently a senior solution architect with a lot of focus on the Adobe Stack. I work with a lot of projects that migrate content into AEM from other content management systems as well as personalization of content analytics as well as integration with commerce, whether AEM being the glass or running headless and having commerce and AEM in the background. I’ve also included my LinkedIn account so please feel free to add me. And last but not least, myself and my family are all Disney fans. We used to go to Disney once or twice a year before COVID and I’m hoping that continues later on in the future. And yeah, that’s it. So the first thing we’re going to start with is the agenda, what everybody’s been waiting for. So we will go through multiple parts of the agenda. The first one, we’re going to talk about the challenge. So why are we here today? What’s the problem? We’re also going to talk about the moving pieces, which really caused the challenge. And then we’ll talk about the solution, which is get sub modules. We’ll talk about multi-tenancy. We’ll go through an actual flow of how we can use get sub modules in a multi-tenancy world. We’ll talk about the reasoning, why we chose get sub modules, what other options you have out there, what are the pros and cons as well. And then we’ll go through a quick summary and then it’s time for Q&A. So don’t forget to ask questions. Moving on to the challenge. So as we advance, as the enterprise level implementation become more complex or more advanced in general, AEM is no different. AEM allows multiple brands, multiple business units, multiples of everything essentially, even multiple different implementations between dam and sites to exist in the same AEM instance. And AEM as a cloud service is no different. The only difference is the way the architecture works. So with AEM as a cloud service, due to the nature of the fact that it’s containerized, every time you do a deploy, really what you’re doing is you’re destroying the existing container and you’re creating a new one, binding it to the new code base that you’re pushing. What that really means is if you have code base one and you deploy it, that’s what’s on your instance. But then if you deploy code base two, you lose the first deployment that you’ve done. Due to that, the only way you can deploy all of your code on AEM as a cloud service is to unify that code base into a singular entity before deployment. Nowadays, because we have multiple business units that have rules on policies and separate implementations, that means we typically will run into multiple code repositories. And that’s really the problem that we’re trying to solve today. How can I have multiple code repositories deployed to the same AEM instance without the loss or the inability of deploying other code bases? So what are the moving pieces? Like I said earlier, it’s all about multiple code repositories. So we’ll call them tenants. So you’ll have multiple tenants. Again, each tenant could be a representation of a business unit. It could even be a representation of a specific team that’s taking care of one part of your code base. So I’ve seen it with clients where you would have a team that’s very UI front end. So they take care of your spot implementation or your headless implementation. Another team would be considered your OSGI configuration team or your dispatcher team. So even within a singular team or a single implementation, you can have multiple code repositories. So we’ll call each one of those a tenant. Now going from there, we also need a common repository and that carries two main aspects. One is your dispatcher rules. So all of your dispatcher rules have to be unified into a single entity because it gets deployed as one zip file, but also common OSGI configurations. Any OSGI configuration that’s not a factory, so essentially a single OSGI configuration, for example, we have link checker or SMTP. Both of these would require you to actually have a single OSGI configuration. And that also goes into your common repository. All this is being presented on the client side. So typically you would, if you’re using cloud manager, you would have all the Git repositories owned by the client yourself, but also another set, a parallel set of the same repositories, but they’re on cloud manager to be used for the deployment process. So this is really how it would look like from a high level. You would have multiple tenants on the left side, and these are your own repositories, but also you’d have parallel repositories on the cloud manager. But then comes the problem. What is next? Every time I deploy, if I deploy tenant one, my instance will only have tenant one code repository. What happens to the rest of them? And that’s where the magical solution comes into play. So this is really where that extra repository comes into play. That parent repository here at the bottom right corner. What the parent repository will do is it will unify all these cloud manager repositories into a singular entity. And then your CIOCD, your pipeline to your dev environment or to your production and stage environments would be ran from that single repository. Now if you think about it, the single repository would contain every single tenant. So from in this case from tenant one to tenant N, but it will also include the common OSGI configurations and your dispatcher configurations. So how does that work? This is where Git submodules come into play. So really what you’re doing is you have multiple independent repositories, which could be deployed independently. So on your local instance or for your developers, they can deploy their own repository. They don’t have to deploy everybody’s code. Your development is still localized to your own Git repository. But once it’s being pushed to the cloud, you then run the addition of the submodules. And really what it’s adding is the latest commit on that branch. And we’ll go through the flow as well and in the next couple of slides of how that works in action. But really what you see here is you have a parent repository. Within that parent repository, you’re saying here are the submodules, which are actually other repositories as well. You get to define what branch you want to run with. Keep in mind that this would be replicated based on your cycle, right? So you have, for example, let’s say you’re using Git flow. So you’ll have your develop branch, but you also will have a release branch. So this will be duplicated for both. Your Git branch for your developing Git branch would go into your dev environment and then your release branch is going to go to your stage environment and eventually to your production environment. Now, again, keep in mind that the common repository is very essential because elements like your OSGI configurations that are non factory or your dispatcher configurations need to be unified in a single entity or a single zip file. The third point here is the parent repository’s POM. So the POM will pull these children in as modules, right? So not to confuse it with the Git submodules. So the Git submodules pull in based on the commit into the parent repository, but also in the POM of that repository, you need to mention every single one of those modules in order for them to be pulled in and deployed through a typical move and clean install. So again, it’s no different from a deployment perspective. You’d have the same profiles, et cetera. So now let me take you through a typical day of the development cycle, right? You just use in all the moving parts that we’ve discussed so far. So as we mentioned, you have multiple repositories. Each repository is a representation of a tenant or a business unit. So let’s zoom in into each one of those as well. Each tenant goes through their own sprint planning. They go through their own development cycle and they go through their own tickets. So it’s a separate entity. In this case, you have tenant one. So tenant one will go through their deployment. They’re ready to go. Again, each one of those repositories could be deployed separately locally. So there’s no interconnection between these separate repositories. However, once you go through your development cycle and now you’re ready to do a development cut, whether it’s on your develop branch or release branch, again, keep in mind that everything that we’ve gone through so far would be duplicated based on how many branches you have. In this case, it’s typically two, one that goes to your dev environment and one that goes to your stage and eventually production environment. So let’s say tenant one is ready to cut where tenant two and three are not ready to cut yet. So they can go ahead, push everything into their own development branch. Once that happens, you are going to push that code into AM as a cloud service, cloud manager, and then your parent repository, again, that magical solution will only pull the commit that you’ve done for that dev. So so far, each one of those repositories now is really at a standstill in a different time. So tenant one is ready to push, but tenant two has already pushed a week ago. So their code is a week ago, well, aged a week ago and same for the rest of the repositories. So again, they’re not interconnected. And then you can deploy the entirety of the parent repository into your cloud manager. So that means it’s going to go to your dev environment. Once it goes into your dev environment, it’s going to run through all the checks and balances that cloud manager provides. So that’s your sonar cube checks and balances, the different code quality tests that they have. And once that’s done, you’re deployed to dev. Now when it’s time to cut the release, it’s the same idea. It’s just on the release branch. So now you’re ready. Your dev branch is good to go. You can merge that into your release branch. Again, you’re only doing it on your code repository. You’re not doing it for the rest of the tenants. And then the parent repository will point the commit of the release branch and it goes through the same cycle again. So it’s going to go through the deployment process. A lot of the questions that I get when it comes to that approach, for example, is what happens with the hotfix. It’s no different than what you would do today on a hotfix for a single repository. So you’re going to create your hotfix branch from your main branch or your release branch you’re going to do your work. And again, you’re duplicating that process because it’s only based on commits. So in theory now on your parent, all other tenants are still on their older commits, the ones that they were already pushed to production, but then you have your hotfix commit and then you can do your deployment process through your cloud manager. Using that approach, it allows you to do two things. One, you’re deploying everything at the same time, but two, you’re also in control of which commit is being deployed. So you’re not forcing other tenants to push in code that’s not ready to be pushed, et cetera. So why get some modules and what else is out there? So let me tell you a couple of different examples from even experiences that I’ve had to go through. I’ve had clients that their solution was, well, let’s just consolidate all of them into a single repository. Pros and cons, right? So from a pro’s perspective, that means everything from a deployment perspective is the same, right? So you have everything in a single repository, you build your CI CD through your cloud manager, and then you move on. The problem with that is, or sort of the challenges that come from that approach is you have a lot of code in there. Each one of those tenants will have their own namespace. So you have tenant one namespace, you’ll have tenant two namespace, et cetera, which makes the code base very bloated. Number two, every time you’re doing a deployment, you’re essentially deploying everything that’s on that specific branch at that specific point of time, which means you have to be a lot more careful on what code you’re pushing, let’s say to your dev environment. Another approach that I’ve seen happen is pulling in dependencies. So each one of those modules or tenants come in as dependencies. Even though that works actually pretty well in a lot of cases, it takes away from the CI CD or the pipeline or what Adobe cloud manager actually provides. Just keep in mind that every time you do a deploy on the cloud manager, you run your unit tests, you run your IT tests, as well as there is a sonar cube that runs a whole bunch of code quality tests to ensure that the code is in perfect condition based on best practices. There is no majors or critical issues with the code. If you’re pulling that code in as dependencies, they’re just dependencies, then you lose the whole sonar cube idea. You’re literally just jamming dependencies into a single artifact and you’re deploying it. So why does Git sub modules differ? The first one is obvious. It allows you for multi-tenancy. That’s really why we’re here today to see how we can do multi-tenancy. But it does it in a way where it decouples that, it decouples the teams from each other when it comes to the deployment cycle. So as I mentioned earlier, the ability of each team to deploy what they want whenever they want because they have separate branching strategies. So there is no dependency. We don’t all have to play the same game. The second part, if you notice me going through the flow, I never defined a specific development cycle. The reason for that is if you’re using Git flow, sure, continue on using Git flow. That does not change. So you’re not doing as much change to the team dynamics. It’s whatever you’re doing right now because at the end of the day, everything is going to go into your dev branch, which will get pushed into your parent repository and eventually deployed. And then the last point is what I said earlier, which is the utilization of the Adobe pipeline. So you’re still able to do the deployment through the Adobe pipeline, but you’re also able to use what Adobe has provided you to ensure best practices and the security of the code. And it’s a very nice report that tells you what’s critical, what’s minor, what’s major, et cetera. So let’s recap. First of all, now we’re officially a multi-tenant. And the way we did that is you are able to continue developing in a separate code repository. So each tenant is able to develop in their own code repository. And the secret to that is each one of those code repositories is a separate entity that then collectively gets pulled in using sub modules. Again, I cannot stress this point enough. There should be a common repository that has any non-factory OSGI configurations. It should have your dispatcher configurations for all the tenants because you cannot deploy multiple dispatchers. It has to be a single entity. The sub modules will be used to aggregate all the tenants into a single repository, which will be deployed through your typical CI CDL cloud manager. And then just simply cloud manager pipelines will be used to deploy the parent repository to different environments. Fantastic. So at this point, the presentation is done. I really hope it added some value and you’ve learned something that you didn’t know before or it solved a problem that you face on a daily basis. The fact is AEM is an enterprise level application. And with it, a lot of business units get involved. A lot of teams get involved in the same AEM instance. And multi-tenancy is a fact that we have to deal with, with AEM or any other applications. Really the focus today was to solve that one problem for AEM as a cloud service. At this point, I’m super excited to answer your questions. So keep them coming. Amazing. Thank you so much, Rami, for that session. And I’m so happy to have you live with us for some Q&A. Thank you for having me. This has been amazing. Thank you. Absolutely. So we’re going to dive in to our first question for you. It is, how would you get this flow applied when having multiple branches to deploy from? For example, develop branch to development and release to stage. That’s actually a fantastic question. I think in order to answer it, we have to go back to what your flow of code is, first of all. So let’s use Gitflow as an example. So typically with Gitflow, you would have your develop branch. You would then create a branch, whether it’s a bug or a feature from your develop branch, do your work, eventually create a PR which you merge back into your develop branch. From there, once you have critical mass of code or if it’s time to do a release, you would cut your release branch, and that release would eventually go to your stage and eventually to your production. Now, if you take that entire sort of flow and you apply it to what we’ve talked about earlier, really you’re replicating that process based on the branching. So again, think of that flow. So you would create your branch from develop, you’d merge into develop after the PR. Once you do that, you’re moving the code from your repository, from your client repository into the Cloud Manager repository. There are options of automation, of course. I think we heard earlier about a Jenkins plugin which we can use, which would allow us to move the code from your code repository into the Cloud Manager repository. And once that happens, what you’re doing is you’re running essentially a secondary job that takes that commit again into the repository that rules them all. So you’re all tenant repository. Now, when it’s time to do the release and you’ve cut the release branch, you’re replicating that process again, where you would run your, let’s say you use Jenkins or a manual process where it moves that code from your current repository to the Cloud Manager, and then you would push to your repository that rules them all again, and then you would deploy using your production pipeline. Excellent. Thank you. And our next question is from Naveen. And the question is, can we do scheduling to deploy multiple applications like minutes gap? You can. Now, keep in mind again, at the end of the day, when it comes to AM as a cloud service, because it’s containerized, if you schedule, if you do schedule, you cannot schedule unique repositories. Because if you look at it, so you have going back to the example, going, let’s say you have tenant A, B, and C. If you schedule tenant A to go first from the tenant repository and then tenant B and tenant C, at the end of the day, what’s left on your AM as a cloud service is tenant C, because every single time you do a deploy, essentially you’re destroying that container and you’re creating a new one, bind it to that code base. So going back to the code or the repository that rules them all, so your all tenant repository, you can have tenant A at any given point push to the parent repository, then maybe five minutes later tenant B will go and then tenant C. But at the end of the day, when you’re deploying, you’re deploying your all tenant repository. So rule of thumb, there is one repository that you’re deploying. What goes in it is what’s changing, right? It’s your different tenant repositories that’s going in and that’s what’s changing as you go through. Amazing. Thank you. And just a reminder, I know you must have some questions. I’ve seen a few come through here, but don’t be afraid to ask in the chat box to the right of your video. And our next question for you, Rami, is how can common dependencies be maintained in the POM files? So here is my analogy. Even though you have separate code bases, you essentially are all moving in the same house, right? So you have three different tenants, but at the end of the day, it’s the same house you’re deploying into the same house. Due to that, you can run into challenges exactly how the question says, where let’s say ACS Commons, for example, where somebody is using 5.2.0 and I think 5.2.3 came out or 5.3.0 came out. At the end of the day, because you are deploying to the same AM instance, you have to maintain those dependencies. They have to be in sync. There are multiple ways to do it. The simplest way is to do it manually where tenant A, B, and C would talk to each other. Their parent POMs have to match from a dependency perspective. The second way to do it is to have a parent POM. So the parent POM would eventually be inherited in all the tenants. I’ve actually done that for clients in the past where now you’re governed by a single parent POM. Your project, your tenant project should not have any versioning at that point. So you should not tell it, I want to use ACS 5.3.2 or whatever at that point. What you’re saying is inherit that dependency from the parent POM. The parent POM itself would be controlled by a gatekeeper of some sort just to maintain the dependencies, et cetera. Excellent. Thank you. And I love that you kind of draw on your own experiences to answer that. So thank you for that. Our next question, if you’re ready for it, have you had to deal with different OSGI configurations to the same bundle? And if so, how did you handle that? The short answer is yes. The long answer is it depends, right? So OSGI configurations come in two different flavors, right? So you have a factory OSGI configuration and a non-factory OSGI configuration. By definition, a factory means you can have multiple instances of that configuration. So think of something like, um, what would be a good example? Actually, let’s start with the non-factory. The non-factory let’s think SMTP configuration, which is typically an AEM is a singular instance or link checker, et cetera. Um, for these, because of the nature of AEM. And again, going back to the analogy of all being in the same house, um, something like an SMTP configuration has to be common among all tenants. Now, in some cases it’s not, the configurations are different. Um, there are tools out there in, again, in some cases it’s, it’s almost like an edge case and you have to deal with it one case at a time. So SMTP, again, ACS commons has a little tool that allows you to have multiple configurations, but something like link checker. Um, it’s there’s nothing that I’m aware of out there that would allow you to, to integrate a factory like, um, configuration. So that’s where the challenge is. And typically we either have all tenants in sync, so everybody’s going to activate the link checker or everybody won’t. The second option is to start customizing, uh, and playing around with filters, uh, almost like implementing the link checker again, to allow it to behave in a factory fashion based on maybe domain or content tree within AEM. Now the factory one is the easy one because he simply just tag different, different configurations based on the tenants. So maybe tenant A will have one behavior tenant B will have a different behavior as well. Um, so it’s, it’s typically for that non factory OSJ configuration. They have to think out of the box a little bit. Excellent. Yeah. Thank you for that answer is very thorough and I’m sure the audience appreciated it. So we’re just going to keep on moving because we had a few more excellent questions come through. Now our next one is from Timothy, uh, and it is how do we work around when MVNs repository cache gets confused during the container build, uh, where we need to add U flag and or call the MVN repository refresh. There is within the cloud manager itself. Um, typically your best solution would be to restart the build. Um, now if you, if you look at sort of the progression when we started, um, AEMs a cloud service, there was, there was no caching, right? So if you’re, and just to get it correctly, my understanding is you’re looking at the dependencies being cached within the build process. Um, so when that started, there was absolutely no caching. And that was a challenge because the build takes a while, right? So if you look at the phases of the build, you start your build, you do your artifact build. So it’s almost like a Maven clean install. Um, and then after that, you have to go through all of your checks and balances, your sonar cube checks. Um, and then after that you have to bind into the container and then you have to run into indexing the, the different containers, which was very time consuming. Um, so as the progression happened, they started caching those dependencies in order to speed up the process, but you’re a hundred percent correct. In some cases, it’s no different than page caching. Um, my best solution, like I said, it would be rerun the build. Um, I haven’t really seen it as often. And if you do see it quite a bit, I would probably create an Adobe ticket right away because I’ve maybe seen it once or twice and I do a lot of builds. So if you do see it consistently, it could be something that’s that requires an Adobe ticket as well. Awesome. Thank you. Our next question is from a new, uh, and it is. Would there be a scenario where we get a merge conflicts on a common file between multiple tenants? And if yes, how, how should we handle that? Awesome question. Um, so keep in mind that when we were going through the structure of these repositories, there’s two types, right? Well, three, and let me take you through them quickly. One is the tenant repository. That’s your own repository. So in theory, you should not have external, external conflicts with other tenants because nobody should be pushing code to your own tenant repository and vice versa. Um, now there is a second one, which is what we call common configuration. So that could be your dispatcher configuration, and it could be OSGI configurations. Now dispatcher it, you should still not have any. Conflicts. If you do dispatcher correctly by that, I mean each domain or each set of domains, each tenant. Should have their own V host each time. I should have their own rewrite rules. Um, Adobe actually does a pretty great job at breaking down what the, what the directories are. So make sure you have your own V host, your own farm, um, your own filters, your own rewrites. You’re almost creating your own tunnel or your own site within that dispatcher. So I don’t see conflicts in there where the conflicts might happen is going back to the OSGI configurations, where you have a non factory nature of the, of the configuration. In which case you would go back to sort of earlier to the answer that I had, where we either come to an agreement that we’re all going to have the same configuration. That’s one option, or we would think out of the box and try to find a different solution that would allow us to have our own sort of little siloed configuration as well. Um, again, you should not have this, like any, any conflicts within your own repository because of another tenant. Right. The whole point of the idea is to isolate. Um, the interaction between the different repositories as much as possible. Excellent. Thank you. And two members of the audience right now, we have a few minutes left. So if you have any last minute burning questions, make sure to put them in the chat. I think we have time for one or two more. And the next one I will ask you, Rami is, you know, you, you talked a lot about, um, multi-tenancy here and you described, you know, a lot of things that that can go into, you know, making efficient deployments and doing it correctly. But I want to know, were you able to automate any of the steps that you described earlier? And in theory, um, they could all be automated, right? Um, so again, going back to, and I’ll use, I’ll use from my own personal experience. So we’ve, with one of our clients, we had to use Jenkins, for example, um, which means you have to play around with the ammo files, et cetera. Um, the good news is specifically for Jenkins. There is a cloud manager plugin that helps. Um, so our flow or our currently our current flow for that client is you would push to your development branch. Again, each tenant would push to their own development branch. What that would do is it would fire a Jenkins build automatically to push that code from the development branch on the client repository to the cloud manager. Right. It would then run the build the, again, the one that rolls them all build that would push the commit to the higher repository. And then we would use the cloud managers because the cloud manager itself has its own automation as well. So on get change, fire up the build. So you would create your branch from dev. You’ll go through the PR process. You would merge into dev that all happens on the client side, the client repository side that would run the Jenkins build automatically pushing the code to cloud manager. And then the next step to that is to run another build. Again, this is all automated at this point, he’s in yaml files to run the build that pushes the commit, the sub module commit of the get code into the parent repository, which would then get cloud manager to realize, oh, there was a Delta. There’s a change in that get repository, fire the build to the cloud. So you can definitely automate quite a bit of the process. We did the same thing with the release branch. The only difference in this is personal preference or maybe client preference where that last step of deployment to stage, at least in my opinion, should be a manual process. And the reason for that is you don’t run stage builds twice a day, right? So the development branch tends to be a heavy or heavy traffic branch because you have multiple developers, they’re pushing code. You want to test your code on the development environment. However, when it comes to stage and production, of course, production, it’s, it’s, it’s a lot more pulled back, right? You need to be slower. You need to make sure that the code going up there is, is on up to park is it’s UAT. Um, so maybe deploy once I’ve seen people to play once a day or once every three days, once a week, or even once a sprint, which I think is a bit of a stretch. Excellent. Oh, thank you for that. And, uh, not, not another question, but a statement from an audience member of Milan. Uh, wow. Awesome content. Thank you so much for me. So Rama, you can, you can leave the skill exchange stage today, knowing that, um, your content was, was really well received and, you know, you’re helping the broader AM community. So I just wanted to take a second to thank you so much for being here with us today. It is always a pleasure chatting with you. Awesome. Thank you so much for having me. This was fantastic. Thank you.
recommendation-more-help
82e72ee8-53a1-4874-a0e7-005980e8bdf1