{"id":3619,"date":"2020-11-29T19:00:07","date_gmt":"2020-11-29T19:00:07","guid":{"rendered":"https:\/\/www.robhabraken.nl\/?p=3619"},"modified":"2021-07-01T09:09:00","modified_gmt":"2021-07-01T09:09:00","slug":"paas-to-aks-arm-for-eds","status":"publish","type":"post","link":"https:\/\/www.robhabraken.nl\/index.php\/3619\/paas-to-aks-arm-for-eds\/","title":{"rendered":"PaaS to AKS: ARM for EDS"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>This blog post is part of a series about building a production ready Continuous Integration setup for Sitecore using K8s containers on AKS. For more information and other articles on this subject <a rel=\"noreferrer noopener\" href=\"https:\/\/www.robhabraken.nl\/index.php\/3582\/from-sitecore-paas-to-aks-a-series\/\" data-type=\"URL\" data-id=\"https:\/\/www.robhabraken.nl\/index.php\/3582\/from-sitecore-paas-to-aks-a-series\/\" target=\"_blank\">check out the series index<\/a>.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">External Data Services<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A K8s deployment to AKS requires a number of so called external data services (EDS) that do not run, or should not run, in a container:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>SQL Server or an Azure Elastic Database pool<\/li><li>Solr Cloud 8.4.0<\/li><li>Redis 5.0 or higher<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To reduce the time required for development and testing, Sitecore provided those services in a container, but those are meant for non-production use only. You can create those services manually in the Azure portal, or run a VM to install all of them, but that wouldn&#8217;t meet our Infrastructure-as-Code requirement or satisfy our preference for PaaS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also, we&#8217;d like to throw in some extra services that could be seen as EDS and support our container deployment, to increase security and authentication for our production environment:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Azure Container Registry (ACR) to host our images privately<\/li><li><a href=\"https:\/\/www.robhabraken.nl\/index.php\/3603\/paas-to-aks-scripts-secrets\/\" target=\"_blank\" rel=\"noreferrer noopener\">Azure Key Vault for secure key management<\/a><\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">ARM Templates for EDS boilerplate<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If those containers aren&#8217;t suitable for a production setup, you might wonder how to host those External Data Services outside the Kubernetes cluster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>I think it would be great if we had a default template or boilerplate that provides the required services to support your K8s deployment, much like the Sitecore Azure Quickstart Templates for a full PaaS Web Apps deployment. So that is exactly what we have created! (*)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We chose an elastic database pool running on SQL PaaS instances, a PaaS Redis service, as well as the native Azure ACR and AKV services. The Solr instance is the only exception to the PaaS principle: due to cost considerations we are running a Solr Cloud instance using ZooKeeper on a VM (but fully scripted of course). Additionally, we deploy a VNet to aid in setting up the traffic between our containers and our EDS. You can find the templates and accompanying scripts in my GitHub repository:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/robhabraken\/paas-to-aks\/tree\/main\/azure\/templates\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/robhabraken\/paas-to-aks\/tree\/main\/azure\/templates<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Which can be easily deployed using a number of tasks in Azure DevOps:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"524\" src=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/eds-deployment-1024x524.png\" alt=\"\" class=\"wp-image-3777\" srcset=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/eds-deployment-1024x524.png 1024w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/eds-deployment-300x154.png 300w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/eds-deployment-768x393.png 768w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/eds-deployment-1536x786.png 1536w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/eds-deployment-2048x1048.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This by itself is one of the three stages of our Initial Provisioning pipeline, followed by deploying the secrets as described in my previous post, and the initialization jobs provided by Sitecore to initialize the EDS:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"308\" src=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/init-stages-1024x308.png\" alt=\"\" class=\"wp-image-3778\" srcset=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/init-stages-1024x308.png 1024w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/init-stages-300x90.png 300w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/init-stages-768x231.png 768w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/init-stages.png 1052w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">When fully developed I will also add a YAML based pipeline for Azure DevOps to the aforementioned GitHub repository.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"fixing-redis-connection\">Fixing the hard-coded Redis connection<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s one issue though, if you are running the EDS outside of the AKS cluster. When you go into the <strong>cd.yaml<\/strong> file of the Kubernetes specification files as provided by Sitecore, you will see that the connection string to Redis is hard-coded:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Sitecore_ConnectionStrings_Redis.Sessions\n  value: redis:6379,ssl=False,abortConnect=False<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can of course change it in here, but I think it is more elegant to remove the value from the specification files and place it inside a secrets file, as is being done with the Solr connection string for example. Just add this little code snippet into the YAML file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Sitecore_Redis_Connection_String\n  valueFrom:\n    secretKeyRef:\n      name: sitecore-redis\n      key: sitecore-redis-connection-string.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Change the hard-coded connection string value to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Sitecore_ConnectionStrings_Redis.Sessions\n  value: $(Sitecore_Redis_Connection_String)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add a file named &#8220;<em>sitecore-redis-connection-string.txt<\/em>&#8221; into the .\/secrets folder and add a reference to it in the <strong>kustomization.yaml<\/strong> file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: sitecore-redis\n  files:\n  - sitecore-redis-connection-string.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Subsequently, you can choose to retrieve this value either from an Azure DevOps Variable group, or by using a secret from your AKV instance.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"allow-aks-to-eds\">Allow AKS to access the EDS<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Inbound security rule for Solr NSG<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To allow for the K8s cluster to access our Solr Cloud instance, we need to add the IP addresses of the Frontend IP  configuration of the &#8220;kubernetes&#8221; load balancer resource to the Inbound security rules of the NSG that is part of our VNet as created by our ARM templates. You can find the load balancer in the additional resource group created by the AKS deployment, which in our case is named &#8220;MC_paas-to-aks_paas-to-aks-aks-cluster_westeurope&#8221;, containing the underlying infrastructure for our Kubernetes service.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Network configuration for SQL Server<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The VNet of this supporting resource group also contains a subnet called &#8220;aks-subnet&#8221; that encloses all of the resource of the K8s cluster. To allow communication between the cluster and SQL, you need to do some extra configuration. On the AKS VNet you need to Add a <em>Service endpoint<\/em> for the Microsoft.Sql service bound to the AKS subnet:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"541\" src=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/service-endpoints-1024x541.png\" alt=\"\" class=\"wp-image-3797\" srcset=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/service-endpoints-1024x541.png 1024w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/service-endpoints-300x159.png 300w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/service-endpoints-768x406.png 768w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/service-endpoints.png 1233w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And within your SQL Server resource, you need to go to <em>Firewalls and virtual networks<\/em> and add a VNet rule to add the AKS VNet and its subnet to enable the Service endpoint connection going into your SQL Server instance:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/vnet-rule.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"560\" src=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/vnet-rule-1024x560.png\" alt=\"\" class=\"wp-image-3799\" srcset=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/vnet-rule-1024x560.png 1024w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/vnet-rule-300x164.png 300w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/vnet-rule-768x420.png 768w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/vnet-rule.png 1447w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">At the time of writing of this blog post, we have configured the network manually, since we are still testing our setup. This will suffice for the time being, but upon finalizing our setup, this configuration will of course be added to the templates &amp; scripts of our EDS boilerplate.<\/p>\n\n\n\n<p class=\"has-cyan-bluish-gray-color has-text-color wp-block-paragraph\">(*) Please note that this template is still considered being <em>experimental<\/em>. Although it works as is, it is still subject to change while we&#8217;re testing it in various scenarios. It will probably be updated shortly together with new blog posts within this series.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post is part of a series about building a production ready Continuous Integration setup for Sitecore using K8s containers on AKS. For more information and other articles on this subject check out the series index. External Data Services A K8s deployment to AKS requires a number of so called external data services (EDS) [&hellip;]<\/p>\n","protected":false},"author":9,"featured_media":3637,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[73,52,39],"tags":[77,45,78,56,24,58,42,74,76,20,57],"class_list":["post-3619","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-kubernetes-services","category-continuous-integration","category-how-to","tag-aks","tag-azure","tag-azure-keyvault","tag-cloud","tag-deployments","tag-docker","tag-how-to","tag-kubernetes","tag-paas","tag-sitecore","tag-web-apps"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2020\/11\/plugging-in-external-services-scaled.jpg","_links":{"self":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts\/3619","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/comments?post=3619"}],"version-history":[{"count":79,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts\/3619\/revisions"}],"predecessor-version":[{"id":4024,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts\/3619\/revisions\/4024"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/media\/3637"}],"wp:attachment":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/media?parent=3619"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/categories?post=3619"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/tags?post=3619"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}