{"id":3363,"date":"2017-05-31T18:58:28","date_gmt":"2017-05-31T18:58:28","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/?p=3363"},"modified":"2020-08-25T06:26:53","modified_gmt":"2020-08-25T13:26:53","slug":"production-deis-workflow-deployment-azure","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/production-deis-workflow-deployment-azure\/","title":{"rendered":"A Production Deis Workflow Deployment on Azure"},"content":{"rendered":"<h2>Background<\/h2>\n<p><a href=\"https:\/\/deis.com\/workflow\/\">Deis Workflow<\/a> is the second version of the Deis application platform that allows for the easy deployment and scalability of <a href=\"https:\/\/12factor.net\/\">Twelve-Factor applications<\/a>. Previously, <a href=\"http:\/\/www.microsoft.com\/developerblog\/real-life-code\/2015\/07\/21\/Deis-Running-a-Heroku-Style-Platform-on-Azure.html\">we worked with Deis<\/a> to enable their V1 PaaS on Azure based on <a href=\"https:\/\/coreos.com\">CoreOS<\/a>.<\/p>\n<p>With Deis Workflow, the only requirement for installation is supporting <a href=\"http:\/\/kubernetes.io\">Kubernetes<\/a>. In November of 2016, the <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/container-service\/\">Azure Container Service<\/a> announced support for <a href=\"http:\/\/blog.kubernetes.io\/2016\/11\/bringing-kubernetes-support-to-azure.html\">Kubernetes on Azure<\/a>. With this addition, we arranged another hackfest with the team from <a href=\"https:\/\/deis.com\/\">Deis<\/a>.<\/p>\n<p>The primary goal of the hackfest was to run the Deis Workflow End-to-End tests successfully. We accomplished this goal within the first few hours of the three-day event, which allowed time to explore other topics, including one that a customer was interested in: what a &#8220;production Deis Workflow deployment on Azure&#8221; would look like and the steps necessary to get there.<!--more--><\/p>\n<p>Deis Workflow, in addition to providing a platform for Twelve-Factor applications, simplifies working with Kubernetes.\u00a0 This allows developers to more readily focus on their apps initially without having to learn how to operate Kubernetes proper.\u00a0 As needed, or as familiarity improves, operating directly with Kubernetes is possible.\u00a0 Workflow and Kubernetes can be used without interference between the two.<\/p>\n<p>The customer was interested in taking their current use of Kubernetes and Workflow for internal development and testing, and see what would be needed to move that setup into production.\u00a0 The definition of &#8220;production&#8221; in this context of Azure will rely on Deis&#8217; documentation of a <a href=\"https:\/\/deis.com\/docs\/workflow\/managing-workflow\/production-deployments\/\">production deployment<\/a>. The key points are:<\/p>\n<ul>\n<li>Internal state is moved to Azure Blob Store<\/li>\n<li>Update registration mode<\/li>\n<li>Enable TLS using a <a href=\"https:\/\/deis.com\/docs\/workflow\/managing-workflow\/platform-ssl\/\">Platform certificate<\/a><\/li>\n<\/ul>\n<p>Below we will cover the steps necessary get Deis Workflow up and running on Azure. We will also address the production deployment steps mentioned above in depth.<\/p>\n<h2>Requirements<\/h2>\n<p>To set up, configure, and install Deis Workflow, we will need to install a set of tools:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-cli\">Azure CLI<\/a>: allows for interaction with various Azure services from the CLI.<\/li>\n<li><a href=\"http:\/\/kubernetes.io\/docs\/user-guide\/prereqs\/\">kubectl<\/a>: primary tool for interacting with a Kubernetes cluster.<\/li>\n<li><a href=\"https:\/\/github.com\/deis\/workflow-cli\">Deis Workflow CLI<\/a>: tool for interacting with Deis Workflow.<\/li>\n<li><a href=\"https:\/\/github.com\/kubernetes\/helm\">Helm<\/a>: Kubernetes package manager.<\/li>\n<\/ul>\n<p>During the hackfest, the Azure Container Service had a certificate issue (since resolved) that required we use one additional tool:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Azure\/acs-engine\">acs-engine<\/a>: the open source engine for creating Azure Resource Manager (ARM) templates for deploying clusters (Docker Swarm, Kubernetes, and DC\/OS) onto Azure<\/li>\n<\/ul>\n<p>To fulfill the recommendations within the Deis <a href=\"https:\/\/deis.com\/docs\/workflow\/managing-workflow\/production-deployments\/\">Production Deployments<\/a> document, we will need to:<\/p>\n<ul>\n<li>Provision an Azure Storage account to move Workflow&#8217;s internal state into persistent storage<\/li>\n<li>Procure a wildcard certificate to enable TLS<\/li>\n<\/ul>\n<h2>Setup<\/h2>\n<p>Below, we outline the needed steps and pointers to getting the tooling set up.<\/p>\n<h3>Third Party Tools<\/h3>\n<p>Each of the following third party tools, as mentioned above, need to be installed:<\/p>\n<ul>\n<li><a href=\"https:\/\/kubernetes.io\/docs\/tasks\/kubectl\/install\/\">kubectl<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/kubernetes\/helm\/blob\/master\/docs\/install.md\">helm<\/a><\/li>\n<li><a href=\"https:\/\/deis.com\/docs\/workflow\/quickstart\/install-cli-tools\/\">Deis Workflow CLI<\/a><\/li>\n<\/ul>\n<p>To verify each is properly configured:<\/p>\n<pre class=\"lang:sh decode:true\">jims@tupperware:~$ kubectl version\r\nClient Version: version.Info{Major:\"1\", Minor:\"6\", GitVersion:\"v1.6.1\", GitCommit:\"b0b7a323cc5a4a2019b2e9520c21c7830b7f708e\", GitTreeState:\"clean\", BuildDate:\"2017-04-03T20:44:38Z\", GoVersion:\"go1.7.5\", Compiler:\"gc\", Platform:\"linux\/amd64\"}<\/pre>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ deis --version\r\nv2.9.1<\/pre>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ helm version\r\nClient: &amp;version.Version{SemVer:\"v2.1.2\", GitCommit:\"58e545f47002e36ca71ac5d1f7a987b56e1937b3\", GitTreeState:\"clean\"}\r\nError: cannot connect to Tiller<\/pre>\n<h3>Azure CLI<\/h3>\n<p>The Azure CLI will be used to setup the necessary infrastructure components within Azure.\u00a0 The different options for installing the Azure CLI are <a href=\"https:\/\/docs.microsoft.com\/en-us\/cli\/azure\/install-azure-cli\">here<\/a>.\u00a0 Once installed, we will need to log in, which is a two-step process.\u00a0 First, you issue the <strong>az login<\/strong> command and then visit the <a href=\"https:\/\/aka.ms\/devicelogin\">Device Login<\/a> page and enter the token presented\u00a0on the command line.<\/p>\n<pre class=\"lang:sh decode:true \">jims@tupperware:~$ az login\r\nTo sign in, use a web browser to open the page https:\/\/aka.ms\/devicelogin and enter the code BXSABJN4S to authenticate.\r\n[\r\n  {\r\n    \"cloudName\": \"AzureCloud\",\r\n    \"id\": \"ab12ec88-8e28-41ed-8537-5e17766001f5\",\r\n    \"isDefault\": true,\r\n    \"name\": \"Developers Research\",\r\n    \"state\": \"Enabled\",\r\n    \"tenantId\": \"cfd488bf-86f1-41af-91ab-2d7cd011db47\",\r\n    \"user\": {\r\n      \"name\": \"jaspring@microsoft.com\",\r\n      \"type\": \"user\"\r\n    }\r\n  }\r\n]<\/pre>\n<p>When you go to <a href=\"https:\/\/aka.ms\/devicelogin\">Device Login<\/a>, you will be prompted to enter the code shown in the above step. You may also need to log into your Azure account, as well.<\/p>\n<p>At this point, you are ready to use the Azure CLI.<\/p>\n<h3>acs-engine<\/h3>\n<p>As mentioned previously, during the hackfest the ACS resource provider generated a certificate that did not play well with Deis Workflow.\u00a0 The certificate contained a value in the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Subject_Alternative_Name\">Subject Alternative Name<\/a> field that the HTTP library Workflow used did not like.\u00a0 So, acs-engine was used to provision the cluster.<\/p>\n<p>The steps necessary to install acs-engine are <a href=\"https:\/\/github.com\/Azure\/acs-engine\/blob\/master\/docs\/acsengine.md\">here<\/a>.<\/p>\n<p>Deis Workflow can be installed on any Kubernetes cluster, so one can use the <a href=\"http:\/\/ms.portal.azure.com\">Azure Portal<\/a>\u00a0or the Azure CLI <a href=\"https:\/\/docs.microsoft.com\/en-us\/cli\/azure\/acs\">Azure Container Service commands<\/a>\u00a0to provision a Kubernetes cluster on Azure in place of using acs-engine.<\/p>\n<h2>Preparation<\/h2>\n<p>With the tooling in place, there is some additional preparation needed, which is detailed in the following sections. You&#8217;ll need to:<\/p>\n<ul>\n<li>Create a Resource Group<\/li>\n<li>Generate an SSH Key<\/li>\n<li>Create a Service Principal<\/li>\n<li>Determine the details of the Kubernetes cluster<\/li>\n<li>Create a storage account and retrieve its key for the preservation of Deis Workflow&#8217;s state<\/li>\n<li>Create a certificate to enable TLS on Deis Workflow.<\/li>\n<\/ul>\n<h3>Create a Resource Group<\/h3>\n<p>A Resource Group is necessary for all Azure Resource Manager resources. It requires a name and a region to which it is deployed. For this tutorial, we will use:<\/p>\n<ul>\n<li><strong>Name<\/strong>: deisprodk8s<\/li>\n<li><strong>Location<\/strong>: westus<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true\">jims@tupperware:~$ az group create --location=\"westus\" --name=\"deisprodk8s\"\r\n{\r\n  \"id\": \"\/subscriptions\/ab12ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/deisprodk8s\",\r\n  \"location\": \"westus\",\r\n  \"managedBy\": null,\r\n  \"name\": \"deisprodk8s\",\r\n  \"properties\": {\r\n    \"provisioningState\": \"Succeeded\"\r\n  },\r\n  \"tags\": null\r\n}<\/pre>\n<h3>Generate an SSH Key<\/h3>\n<p>An SSH Key is needed to access machines in the Kubernetes cluster. The generation of keys will vary from platform to platform (for example, for Ubuntu, one can look <a href=\"https:\/\/help.ubuntu.com\/community\/SSH\/OpenSSH\/Keys#Generating_RSA_Keys\">here<\/a>). For this tutorial, we will create a 2048 bit RSA key and store it in the file <strong>~\/.ssh\/id_k8s_rsa<\/strong>:<\/p>\n<pre class=\"lang:sh decode:true\">jims@tupperware:~$ ssh-keygen -b 2048 -t rsa -f ~\/.ssh\/id_k8s_rsa -C jaspring@microsoft.com\r\nGenerating public\/private rsa key pair.\r\nEnter passphrase (empty for no passphrase):\r\nEnter same passphrase again:\r\nYour identification has been saved in \/home\/jims\/.ssh\/id_k8s_rsa.\r\nYour public key has been saved in \/home\/jims\/.ssh\/id_k8s_rsa.pub.\r\nThe key fingerprint is:\r\nSHA256:HW7ptbn212QsD\/VTWcZRNVtrl9ckFjmzkYVbqSe9WG8 jaspring@microsoft.com\r\nThe key's randomart image is:\r\n+---[RSA 2048]----+\r\n|              +X@|\r\n|             .B+%|\r\n|          .   o&amp;*|\r\n|         o o o+==|\r\n|        S = . =o=|\r\n|         o . +ooE|\r\n|          . o  Bo|\r\n|            ..  +|\r\n|           ..... |\r\n+----[SHA256]-----+<\/pre>\n<p>For the Kubernetes configuration, the public key will be needed:<\/p>\n<pre class=\"lang:sh decode:true\">jims@tupperware:~$ more ~\/.ssh\/id_k8s_rsa.pub\r\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXlnm3BkYMPoXUd+r3BKmF8+k+3tVudOLviZK63FtOETAk6u4q3hk35fs\/YfIyF7BTf+f3cQ3FQwN9lOZqM\r\nF7WyfI+ncgtrdsD4CqMB9ewQRYmrnQMxO0QinfEUi2G6qRXTMZlaPRYXJo4MAklU6k9b9XPrzoodkB3eoxh\/XIah5oHUYmKlSNfTdy1xDjxhOwHoJ7BiqNKDd\r\nwf1pLwRpPW7Vg8OirqoX11CNgCPNeBclwxF3Vxzj4MNOUb18XvLXM8IGoGzOSaMhLKbK9pnUA\/iMto4WabaBOnf6c9ADmK6gndVUU3lkALnIRgWsEsTfa9rjm\r\nKqvvuL3gZaQ4QCHz7 jaspring@microsoft.com\r\n<\/pre>\n<h3>Create a Service Principal<\/h3>\n<p>A Service Principal is an entity that is granted permissions to perform operations within an Azure subscription. Kubernetes uses a Service Principal to manipulate compute resources as needed\/defined during interactions with the Kubernetes cluster.<\/p>\n<p>To create a Service Principal, use the Azure CLI:<\/p>\n<pre class=\"lang:sh decode:true\">jims@tupperware:~$ az ad sp create-for-rbac\r\n{\r\n  \"appId\": \"ad349d5e-2b0e-49ce-beaa-407c68196774\",\r\n  \"name\": \"http:\/\/azure-cli-2016-12-23-16-35-16\",\r\n  \"password\": \"b77abcf0-2f83-4e83-bc1a-c2c7dac8d9be\",\r\n  \"tenant\": \"123488bf-86f1-41af-92cb-2d7cd011db47\"\r\n}<\/pre>\n<h2>Kubernetes Cluster details<\/h2>\n<p>For the cluster, we need additional information, like:<\/p>\n<ul>\n<li>Resource group: this is the grouping under which all resources will be mapped in Azure<\/li>\n<li>Region: which Azure Data Center to create the cluster in<\/li>\n<li>DNS Prefix: a prefix used to name the public components of the Kubernetes cluster<\/li>\n<li>Agent Machine Count: how many agent machines are needed<\/li>\n<li>Agent Machine Type: what <a href=\"https:\/\/azure.microsoft.com\/en-us\/pricing\/details\/virtual-machines\/linux\/\">type of VMs<\/a> to use for the Kubernetes cluster<\/li>\n<li>Administrator Name: the name of the administrative user account on cluster nodes<\/li>\n<\/ul>\n<p>For this deployment, we will use the following values:<\/p>\n<ul>\n<li>Resource group: deisprodk8s<\/li>\n<li>Region: westus (West US)<\/li>\n<li>DNS Prefix: deisprod<\/li>\n<li>Agent Machine Count: 4<\/li>\n<li>Agent Machine Type: Standard_D2_v2 (a general compute-oriented VM with 7GB RAM and 100GB local disk)<\/li>\n<li>Administrator Name: dadmin<\/li>\n<\/ul>\n<p>acs-engine uses a configuration file to generate the templates necessary for deployment. Using the above information, including the Service Principal and the SSH Key, the configuration file <strong>~\/flow\/k8s\/config\/kubernetes.json<\/strong> resembles the following:<\/p>\n<pre class=\"lang:sh decode:true\">bash\r\n{\r\n  \"apiVersion\": \"vlabs\",\r\n  \"properties\": {\r\n    \"orchestratorProfile\": {\r\n      \"orchestratorType\": \"Kubernetes\"\r\n    },\r\n    \"masterProfile\": {\r\n      \"count\": 1,\r\n      \"dnsPrefix\": \"deisprod\",\r\n      \"vmSize\": \"Standard_D2_v2\"\r\n    },\r\n    \"agentPoolProfiles\": [\r\n      {\r\n        \"name\": \"agentpool1\",\r\n        \"count\": 4,\r\n        \"vmSize\": \"Standard_D2_v2\",\r\n        \"availabilityProfile\": \"AvailabilitySet\"\r\n      }\r\n    ],\r\n    \"linuxProfile\": {\r\n      \"adminUsername\": \"dadmin\",\r\n      \"ssh\": {\r\n        \"publicKeys\": [\r\n          {\r\n            \"keyData\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXlnm3BkYMPoXUd+r3BKmF8+k+3tVudOLviZK63FtOETAk6u4q3hk35fs\/YfIyF7BTf+f3cQ3FQwN9lOZqMF7WyfI+ncgtrdsD4CqMB9ewQRYmrnQMxO0QinfEUi2G6qRXTMZlaPRYXJo4MAklU6k9b9XPrzoodkB3eoxh\/XIah5oHUYmKlSNfTdy1xDjxhOwHoJ7BiqNKDdwf1pLwRpPW7Vg8OirqoX11CNgCPNeBclwxF3Vxzj4MNOUb18XvLXM8IGoGzOSaMhLKbK9pnUA\/iMto4WabaBOnf6c9ADmK6gndVUU3lkALnIRgWsEsTfa9rjmKqvvuL3gZaQ4QCHz7 jaspring@microsoft.com\"\r\n          }\r\n        ]\r\n      }\r\n    },\r\n    \"servicePrincipalProfile\": {\r\n      \"servicePrincipalClientID\": \"ad349d5e-2b0e-49ce-beaa-407c68196774\",\r\n      \"servicePrincipalClientSecret\": \"b77abcf0-2f83-4e83-bc1a-c2c7dac8d9be\"\r\n    }\r\n  }\r\n}<\/pre>\n<h3>Create Storage Account and Retrieve Key<\/h3>\n<p>The storage account will be created in the same Resource Group as defined above, <strong>deisprodk8s<\/strong>. The settings we will use for the storage account are:<\/p>\n<ul>\n<li>Storage Account Name: deisprodconfig<\/li>\n<li>Storage Account SKU: Standard_LRS (<strong>Standard, Locally Redundant Storage<\/strong>)<\/li>\n<li>Storage Account Kind: Storage<\/li>\n<\/ul>\n<p>Creating the storage account:<\/p>\n<pre class=\"lang:sh decode:true\">jims@tupperware:~$ az storage account create --sku=\"Standard_LRS\" --resource-group=\"deisprodk8s\" --kind=\"Storage\" --location=\"westus\" --name=\"deisprodconfig\"\r\n{\r\n  \"accessTier\": null,\r\n  \"creationTime\": \"2016-12-23T17:10:32.334380+00:00\",\r\n  \"customDomain\": null,\r\n  \"encryption\": null,\r\n  \"id\": \"\/subscriptions\/ab12ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/deisprodk8s\/providers\/Microsoft.Storage\/storageAccounts\/deisprodconfig\",\r\n  \"kind\": \"Storage\",\r\n  \"lastGeoFailoverTime\": null,\r\n  \"location\": \"westus\",\r\n  \"name\": \"deisprodconfig\",\r\n  \"primaryEndpoints\": {\r\n    \"blob\": \"https:\/\/deisprodconfig.blob.core.windows.net\/\",\r\n    \"file\": \"https:\/\/deisprodconfig.file.core.windows.net\/\",\r\n    \"queue\": \"https:\/\/deisprodconfig.queue.core.windows.net\/\",\r\n    \"table\": \"https:\/\/deisprodconfig.table.core.windows.net\/\"\r\n  },\r\n  \"primaryLocation\": \"westus\",\r\n  \"provisioningState\": \"Succeeded\",\r\n  \"resourceGroup\": \"deisprodk8s\",\r\n  \"secondaryEndpoints\": null,\r\n  \"secondaryLocation\": null,\r\n  \"sku\": {\r\n    \"name\": \"Standard_LRS\",\r\n    \"tier\": \"Standard\"\r\n  },\r\n  \"statusOfPrimary\": \"Available\",\r\n  \"statusOfSecondary\": null,\r\n  \"tags\": {},\r\n  \"type\": \"Microsoft.Storage\/storageAccounts\"\r\n}<\/pre>\n<p>Retrieving the storage account keys:<\/p>\n<pre class=\"lang:sh decode:true \">jims@tupperware:~$ az storage account keys list --name=\"deisprodconfig\" --resource-group=\"deisprodk8s\"\r\n{\r\n  \"keys\": [\r\n    {\r\n      \"keyName\": \"key1\",\r\n      \"permissions\": \"FULL\",\r\n      \"value\": \"f8nHiACBlR71UgHjPuShSukge27c+bOty2M2TmTNy61zgPXlXAZuABLxJ\/zG+3VHhvb18Ux5I9rx+1oQ3buaGQ==\"\r\n    },\r\n    {\r\n      \"keyName\": \"key2\",\r\n      \"permissions\": \"FULL\",\r\n      \"value\": \"ad9pMFJUualKDH3irkNyS3XXH0xMbs0JC56ESwiVnRaCJUnuc\/6FEQf09xzjsK4vO34LNq+4VV1N20DI9kau5g==\"\r\n    }\r\n  ]\r\n}<\/pre>\n<h3>Create a Certificate to Enable TLS<\/h3>\n<p>There are two approaches you can take to enable TLS on Deis Workflow. The first is to create an application certificate; for instance, if all of the public traffic will come through <strong>www.example.com<\/strong>, then only a certificate specific to <strong>www.example.com<\/strong> would be needed. These certificates are typically referred to as domain-specific certificates. Within Deis, these might be referred to as application-specific certificates.<\/p>\n<p>A more general approach is to use what is referred to as a wildcard certificate. The domains specified in the wildcard certificate would resemble <strong>*.example.com<\/strong>. Thus any name prefixing <strong>.example.com<\/strong> would match. This certificate can be installed in Deis Workflow as a platform certificate.<\/p>\n<p>For development and testing, you can use OpenSSL to generate a self-signed certificate. Digital Ocean has a good <a href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs\">tutorial<\/a>.\u00a0 Another option is to consider is\u00a0<a href=\"https:\/\/letsencrypt.org\">Let&#8217;s Encrypt<\/a>.<\/p>\n<p>For production, certificates typically cost money. Their retrieval can be quick or take a while, depending on the type of certificate used.<\/p>\n<p>In this tutorial, we will request a <a href=\"https:\/\/www.namecheap.com\/security\/ssl-certificates\/comodo\/positivessl-wildcard.aspx\">PositiveSSL Wildcard certificate<\/a> from <a href=\"https:\/\/namecheap.com\">Namecheap<\/a>.<\/p>\n<p>To get the certificate issued, you must create a Certificate Signing Request (CSR). Certificates have several fields that can be specified for the CSR. In this case, we will specify the following:<\/p>\n<ul>\n<li>Country (C): US<\/li>\n<li>State (ST): California<\/li>\n<li>Locality (L): Santa Cruz<\/li>\n<li>Organization (O): plusonetechnology.net<\/li>\n<li>Common Name (CN): *.plusonetechnology.net<\/li>\n<\/ul>\n<p>The <strong>Common Name<\/strong> field is what a TLS connection is verified against when a connection comes into the server.<\/p>\n<p>To create the CSR:<\/p>\n<pre class=\"lang:sh decode:true \">jims@tupperware:~$ openssl req -new -newkey rsa:2048 -nodes \r\n&gt; -out star_plusonetechnology_net.csr \r\n&gt; -keyout star_plusonetechnology_net.key \r\n&gt; -subj \"\/C=US\/ST=California\/L=Santa Cruz\/O=plusonetechnology.net\/CN=*.plusonetechnology.net\"\r\nGenerating a 2048 bit RSA private key\r\n................................+++\r\n.....+++\r\nwriting new private key to 'star_plusonetechnology_net.key'\r\n-----<\/pre>\n<p>The CSR is written as a PEM blob; in this case, it is written to the file <strong>star_plusonetechnology_net.csr<\/strong> (contents shown below):<\/p>\n<pre class=\"lang:sh decode:true \">jims@tupperware:~$ more star_plusonetechnology_net.csr\r\n-----BEGIN CERTIFICATE REQUEST-----\r\nMIICvjCCAaYCAQAweTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEx\r\nEzARBgNVBAcTClNhbnRhIENydXoxHjAcBgNVBAoTFXBsdXNvbmV0ZWNobm9sb2d5\r\nLm5ldDEgMB4GA1UEAxQXKi5wbHVzb25ldGVjaG5vbG9neS5uZXQwggEiMA0GCSqG\r\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDoudQXqW5++L5a1fUtBNNlXuLeUOJII+Ci\r\n10MdWWUosyn1hHku0AcBXHdONDLfq2W1jUMrxlXaDWKSDaM1xLUCVVKfiK9tpM\/v\r\nerhVhddUATHE8ZSXPY\/HZGkCCoJlF88FDM+aKieteQn6mN7EjemvhtNB2suy455S\r\nWKosuo\/oHRmGuI+CfqIf7hVq1TFzGd2Bd4NWmkldyJp2EvbN+2gr+5G8yXNExwiv\r\nanswPtLZD9oKs4ZmWZP17r7YizDgHdhSONZdtqOt0WhBeJOB+44hIwEzKtMzzZwZ\r\nR29MiQaBy42wSXq71AUONfOKdTBmGUf9QQLMexFiew4+UFjiwi8pAgMBAAGgADAN\r\nBgkqhkiG9w0BAQUFAAOCAQEAcyuepBBHSHNh4zlCIhHcnQnTni1COHMx\/7DIaeTq\r\nUCJm4Yt26n77EwdK\/b4l97l09PIBOdZowf9ETSgLvd2jy7KmbMFQMuRZmH1jZwSa\r\nCj8DV8PixA4qm9ARBAugQmwoEFVPmxWCC+lD7O8ti+bolP6twS\/dsjM3bPCu1l23\r\n5X8pqHfrYPI\/C28INKdr2TdUFQi0ocFIqm3RkYhfGjOrTii9m99enf3HFFX9Pr8z\r\np1QjSlewyOrkiXcQIAP1Fnck48txFCyU5tFsyTBoF6y91jR9GY5wL\/kDwvxbFiWq\r\ndBY3knjwtaKe\/iLPL7YV2NNUA\/+dJ014NJW3oeJRnnfQlQ==\r\n-----END CERTIFICATE REQUEST-----\r\n<\/pre>\n<p>For issuing the certificate, Namecheap will require that the CSR be pasted into the website. It will also require some personal information and a process to validate that the domain specified in the certificate is owned by the person submitting the request. Since this process will depend on where the certificate is purchased and the CA used, the sequence will be skipped.<\/p>\n<p>For the CSR for plusonetechnology.net, via Namecheap, DNS-based validation was used.<\/p>\n<h2>Provisioning the Kubernetes Cluster<\/h2>\n<p>Previously, we defined and updated the acs-engine configuration file as <strong>~\/flow\/k8s\/config\/kubernetes.json<\/strong>. When acs-engine is run against this file, it will generate the necessary Azure Resource Manager (ARM) templates for deploying the Kubernetes cluster:<\/p>\n<pre class=\"lang:sh decode:true \">jims@tupperware:~\/flow\/k8s\/config$ acs-engine -artifacts ~\/flow\/k8s\/output ~\/flow\/k8s\/config\/kubernetes.json\r\ncert creation took 7.712984265s\r\nwrote \/home\/jims\/flow\/k8s\/output\/apimodel.json\r\nwrote \/home\/jims\/flow\/k8s\/output\/azuredeploy.json\r\nwrote \/home\/jims\/flow\/k8s\/output\/azuredeploy.parameters.json\r\n...\r\nwrote \/home\/jims\/flow\/k8s\/output\/kubeconfig\/kubeconfig.westus.json\r\n...\r\nacsengine took 7.743813684s\r\n<\/pre>\n<p>Note: the ellipses (&#8230;) imply content that has been cut to keep the output shown more concise.<\/p>\n<p>The next step is to deploy the templates. Again, the Resource Group we are using is <strong>deisprodk8s<\/strong>.<\/p>\n<pre class=\"lang:default decode:true \">jims@tupperware:~\/flow\/k8s\/config$ az group deployment create --resource-group=\"deisprodk8s\" --template-file=\/home\/jims\/flow\/k8s\/output\/azuredeploy.json --parameters @\/home\/jims\/flow\/k8s\/output\/azuredeploy.parameters.json\r\n{\r\n  \"id\": \"\/subscriptions\/ab12ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/deisprodk8s\/providers\/Microsoft.Resources\/deployments\/azuredeploy\",\r\n  \"name\": \"azuredeploy\",\r\n  \"properties\": {\r\n    \"correlationId\": \"a58352c4-b97d-4e89-bf23-e10e0c9637da\",\r\n    \"debugSetting\": null,\r\n    \"dependencies\": [\r\n      ...\r\n    ],\r\n    \"mode\": \"Incremental\",\r\n    \"outputs\": {\r\n      \"masterFQDN\": {\r\n        \"type\": \"String\",\r\n        \"value\": \"deisprod.westus.cloudapp.azure.com\"\r\n      }\r\n    },\r\n    \"parameters\": {\r\n      ...\r\n    },\r\n    \"parametersLink\": null,\r\n    \"providers\": [\r\n      ...\r\n    ],\r\n    \"provisioningState\": \"Succeeded\",\r\n    \"template\": null,\r\n    \"templateLink\": null,\r\n    \"timestamp\": \"2016-12-23T21:54:04.178783+00:00\"\r\n  },\r\n  \"resourceGroup\": \"deisprodk8s\"\r\n}<\/pre>\n<h3>Setting Up `kubectl`<\/h3>\n<p>With the Kubernetes cluster deployed into Azure, we can now configure settings so that <strong>kubectl<\/strong> can talk to the cluster and we can interact with things.<\/p>\n<p>When acs-engine was run, a number of <strong>kubeconfig.*.json<\/strong> files were generated for each region. Since our example deployment is in <strong>westus<\/strong>, the one we will look at is <strong>kubeconfig.westus.json<\/strong>. The <strong>KUBECONFIG<\/strong> environment variable needs to be set and its status checked to utilize this file, as shown below:<\/p>\n<pre class=\"lang:sh decode:true \">jims@tupperware:~\/flow\/k8s\/output$ export KUBECONFIG=\/home\/jims\/flow\/k8s\/output\/kubeconfig\/kubeconfig.westus.json\r\njims@tupperware:~\/flow\/k8s\/output$ kubectl cluster-info\r\nKubernetes master is running at https:\/\/deisprod.westus.cloudapp.azure.com\r\nHeapster is running at https:\/\/deisprod.westus.cloudapp.azure.com\/api\/v1\/proxy\/namespaces\/kube-system\/services\/heapster\r\nKubeDNS is running at https:\/\/deisprod.westus.cloudapp.azure.com\/api\/v1\/proxy\/namespaces\/kube-system\/services\/kube-dns\r\nkubernetes-dashboard is running at https:\/\/deisprod.westus.cloudapp.azure.com\/api\/v1\/proxy\/namespaces\/kube-system\/services\/kubernetes-dashboard\r\n<\/pre>\n<p>To further debug and diagnose cluster problems, use <strong>kubectl cluster-info dump<\/strong>.<\/p>\n<p>At this point, <strong>kubectl<\/strong> is configured and we can interact with the cluster. Please note that adding the <strong>export KUBECONFIG<\/strong> to one&#8217;s startup files will be helpful for subsequent logins to the machine from which one would interact with the cluster. \u00a0You can also copy the configuration file to <strong>${HOME}\/.kube\/config<\/strong>.<\/p>\n<h3>Kubernetes Using the `az` Command line<\/h3>\n<p>A much simpler way to install the Kubernetes cluster from the command line is available. For completeness, the method to deploy the same Kubernetes cluster is documented below.\u00a0 The assumption is that the configuration is the same as what we specified in the <strong>kubernetes.json <\/strong>file used by acs-engine. That is:<\/p>\n<ul>\n<li>Resource group: deisprodk8s<\/li>\n<li>Region: westus<\/li>\n<li>DNS Prefix: deisprod<\/li>\n<li>Agent Machine Count: 4<\/li>\n<li>Agent Machine Type: Standard_D2_v2<\/li>\n<li>Administrator Name: dadmin<\/li>\n<li>\n<pre class=\"prettyprint\">SSH Key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXlnm3BkYMPoXUd+r3BKmF8+k+3tVudOLviZK63FtOETAk6u4q3hk35fs\/YfIyF7BTf+f3cQ3FQwN9lOZqMF7WyfI+ncgtrdsD4CqMB9ewQRYmrnQMxO0QinfEUi2G6qRXTMZlaPRYXJo4MAklU6k9b9XPrzoodkB3eoxh\/XIah5oHUYmKlSNfTdy1xDjxhOwHoJ7BiqNKDdwf1pLwRpPW7Vg8OirqoX11CNgCPNeBclwxF3Vxzj4MNOUb18XvLXM8IGoGzOSaMhLKbK9pnUA\/iMto4WabaBOnf6c9ADmK6gndVUU3lkALnIRgWsEsTfa9rjmKqvvuL3gZaQ4QCHz7 jaspring@microsoft.com<\/pre>\n<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ az acs create --name=\"deisprod\" \r\n&gt; --orchestrator-type=\"Kubernetes\" \r\n&gt; --resource-group=\"deisprodk8s\" \r\n&gt; --admin-username=\"dadmin\" \r\n&gt; --agent-count=4 \r\n&gt; --agent-vm-size=\"Standard_D2_v2\" \r\n&gt; --dns-prefix=\"deisprod\" \r\n&gt; --location=\"westus\" \r\n&gt; --ssh-key-value=\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXlnm3BkYMPoXUd+r3BKmF8+k+3tVudOLviZK63FtOETAk6u4q3hk35fs\/YfIyF7BTf+f3cQ3FQwN9lOZqMF7WyfI+ncgtrdsD4CqMB9ewQRYmrnQMxO0QinfEUi2G6qRXTMZlaPRYXJo4MAklU6k9b9XPrzoodkB3eoxh\/XIah5oHUYmKlSNfTdy1xDjxhOwHoJ7BiqNKDdwf1pLwRpPW7Vg8OirqoX11CNgCPNeBclwxF3Vxzj4MNOUb18XvLXM8IGoGzOSaMhLKbK9pnUA\/iMto4WabaBOnf6c9ADmK6gndVUU3lkALnIRgWsEsTfa9rjmKqvvuL3gZaQ4QCHz7 jaspring@microsoft.com\"\r\ncreating service principal.........done\r\nwaiting for AAD role to propogate.done\r\n{\r\n  \"id\": \"\/subscriptions\/ab12ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/deisprodk8s\/providers\/Microsoft.Resources\/deployments\/azurecli1482540765.0587363\",\r\n  \"name\": \"azurecli1482540765.0587363\",\r\n  \"properties\": {\r\n    \"correlationId\": \"b1a52fec-8b35-49e7-af8a-e0d7ad9d056e\",\r\n    \"debugSetting\": null,\r\n    \"dependencies\": [],\r\n    \"mode\": \"Incremental\",\r\n    \"outputs\": null,\r\n    \"parameters\": {\r\n      \"clientSecret\": {\r\n        \"type\": \"SecureString\"\r\n      }\r\n    },\r\n    \"parametersLink\": null,\r\n    \"providers\": [\r\n      {\r\n        \"id\": null,\r\n        \"namespace\": \"Microsoft.ContainerService\",\r\n        \"registrationState\": null,\r\n        \"resourceTypes\": [\r\n          {\r\n            \"aliases\": null,\r\n            \"apiVersions\": null,\r\n            \"locations\": [\r\n              \"westus\"\r\n            ],\r\n            \"properties\": null,\r\n            \"resourceType\": \"containerServices\"\r\n          }\r\n        ]\r\n      }\r\n    ],\r\n    \"provisioningState\": \"Succeeded\",\r\n    \"template\": null,\r\n    \"templateLink\": null,\r\n    \"timestamp\": \"2016-12-24T00:59:18.590702+00:00\"\r\n  },\r\n  \"resourceGroup\": \"deisprodk8s\"\r\n}<\/pre>\n<h2>Installing Deis Workflow<\/h2>\n<p>Previously, both the Deis CLI and Helm were installed. With the KUBECONFIG variable set in the prior section, the steps needed to install Deis Workflow are straightforward. The only potential issue is that we need to set up the configuration to reflect the <strong>production<\/strong>\u00a0concerns previously mentioned, which include:<\/p>\n<ul>\n<li>Internal state\/storage will use Azure Storage<\/li>\n<li>Registration for users should be <strong>admin_only<\/strong><\/li>\n<\/ul>\n<p>Installing Deis requires the following initial steps:<\/p>\n<ul>\n<li><a href=\"https:\/\/deis.com\/docs\/workflow\/quickstart\/provider\/azure-acs\/install-azure-acs\/#check-your-setup\">Check your setup and initialize Helm<\/a>\n<pre class=\"lang:default decode:true\">jims@tupperware:~\/$ helm version\r\nClient: &amp;version.Version{SemVer:\"v2.3.0\", GitCommit:\"d83c245fc324117885ed83afc90ac74afed271b4\", GitTreeState:\"clean\"}\r\nError: cannot connect to Tiller\r\njims@tupperware:~\/$ helm init\r\n$HELM_HOME has been configured at \/home\/jims\/flow\/helm.\r\n\r\nTiller (the helm server side component) has been installed into your Kubernetes Cluster.\r\nHappy Helming!\r\njims@tupperware:~\/$ helm version\r\nClient: &amp;version.Version{SemVer:\"v2.3.0\", GitCommit:\"d83c245fc324117885ed83afc90ac74afed271b4\", GitTreeState:\"clean\"}\r\nServer: &amp;version.Version{SemVer:\"v2.3.0\", GitCommit:\"d83c245fc324117885ed83afc90ac74afed271b4\", GitTreeState:\"clean\"}<\/pre>\n<\/li>\n<li><a href=\"https:\/\/deis.com\/docs\/workflow\/quickstart\/provider\/azure-acs\/install-azure-acs\/#add-the-deis-chart-repository\">Add the Deis Workflow repository<\/a>\n<pre class=\"lang:default decode:true\">jims@tupperware:~\/$ helm repo add deis https:\/\/charts.deis.com\/workflow\r\n\"deis\" has been added to your repositories<\/pre>\n<\/li>\n<\/ul>\n<p>During the install of Workflow, in order to meet the requirements laid out for a production system, some configuration information is needed.\u00a0 Specifically, the information from the storage account we setup previously.\u00a0 In addition, we need to know what config options are needed to lock down user registration to the <strong>admin_only<\/strong>.<\/p>\n<p>Note:\u00a0Since the hackfest, Deis Workflow defaults to registration being <strong>admin_only<\/strong>.<\/p>\n<p>To configure and install Workflow, we need:<\/p>\n<ul>\n<li>Storage Account Name:\u00a0deisprodconfig<\/li>\n<li>Storage Account Key: f8nHiACBlR71UgHjPuShSukge27c+bOty2M2TmTNy61zgPXlXAZuABLxJ\/zG+3VHhvb18Ux5I9rx+1oQ3buaGQ==<\/li>\n<\/ul>\n<p>And install Workflow as follows:<\/p>\n<pre class=\"lang:default decode:true\">$ export STORAGE_ACCOUNT_NAME=deisprodconfig\r\n$ export STORAGE_ACCOUNT_KEY=\"f8nHiACBlR71UgHjPuShSukge27c+bOty2M2TmTNy61zgPXlXAZuABLxJ\/zG+3VHhvb18Ux5I9rx+1oQ3buaGQ==\" \r\n$ helm install deis\/workflow --namespace=deis \r\n&gt; --set global.storage=azure,azure.accountname=$STORAGE_ACCOUNT_NAME,azure.accountkey=$STORAGE_ACCOUNT_KEY,azure.registry_container=registry,azure.database_container=database,azure.builder_container=builder\r\nNAME:   whopping-ostrich\r\nLAST DEPLOYED: Wed May 10 12:16:06 2017\r\nNAMESPACE: deis\r\nSTATUS: DEPLOYED\r\n\r\nRESOURCES:\r\n==&gt; extensions\/v1beta1\/Deployment\r\nNAME                   DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE\r\ndeis-monitor-grafana   1        1        1           0          3s\r\ndeis-logger-redis      1        1        1           0          3s\r\ndeis-database          1        1        1           0          3s\r\ndeis-controller        1        1        1           0          3s\r\ndeis-registry          1        1        1           0          3s\r\ndeis-monitor-influxdb  1        1        1           0          3s\r\ndeis-nsqd              1        1        1           0          3s\r\ndeis-logger            1        1        1           0          3s\r\ndeis-router            1        1        1           0          3s\r\ndeis-workflow-manager  1        1        1           0          3s\r\ndeis-builder           1        1        1           0          3s\r\n\r\n==&gt; v1\/Secret\r\nNAME                   TYPE    DATA  AGE\r\ndeis-router-dhparam    Opaque  1     4s\r\nminio-user             Opaque  2     4s\r\nobjectstorage-keyfile  Opaque  5     4s\r\n\r\n==&gt; v1\/ConfigMap\r\nNAME                  DATA  AGE\r\nslugrunner-config     1     4s\r\nslugbuilder-config    2     4s\r\ndockerbuilder-config  2     4s\r\n\r\n==&gt; v1\/ServiceAccount\r\nNAME                   SECRETS  AGE\r\ndeis-logger            1        4s\r\ndeis-router            1        4s\r\ndeis-builder           1        4s\r\ndeis-controller        1        4s\r\ndeis-registry          1        4s\r\ndeis-monitor-telegraf  1        4s\r\ndeis-nsqd              1        4s\r\ndeis-workflow-manager  1        4s\r\ndeis-logger-fluentd    1        4s\r\ndeis-database          1        4s\r\n\r\n==&gt; v1\/Service\r\nNAME                    CLUSTER-IP    EXTERNAL-IP  PORT(S)                                                   AGE\r\ndeis-controller         10.0.83.212          80\/TCP                                                    4s\r\ndeis-registry           10.0.93.44           80\/TCP                                                    4s\r\ndeis-nsqd               10.0.6.2             4151\/TCP,4150\/TCP                                         4s\r\ndeis-router             10.0.128.55       80:30854\/TCP,443:30567\/TCP,2222:31387\/TCP,9090:32093\/TCP  4s\r\ndeis-logger             10.0.55.43           80\/TCP                                                    4s\r\ndeis-builder            10.0.206.67          2222\/TCP                                                  4s\r\ndeis-workflow-manager   10.0.182.215         80\/TCP                                                    4s\r\ndeis-monitor-grafana    10.0.39.43           80\/TCP                                                    3s\r\ndeis-monitor-influxui   10.0.236.49          80\/TCP                                                    3s\r\ndeis-database           10.0.184.196         5432\/TCP                                                  3s\r\ndeis-monitor-influxapi  10.0.214.212         80\/TCP                                                    3s\r\ndeis-logger-redis       10.0.146.66          6379\/TCP                                                  3s\r\n\r\n==&gt; extensions\/v1beta1\/DaemonSet\r\nNAME                   DESIRED  CURRENT  READY  NODE-SELECTOR  AGE\r\ndeis-monitor-telegraf  5        5        0               3s\r\ndeis-logger-fluentd    5        5        0               3s\r\ndeis-registry-proxy    5        5        0               3s<\/pre>\n<p>To verify the Deis Workflow deployment is complete, you can use the command <strong>kubectl &#8211;namespace=deis get pods<\/strong>. When installation is complete, output should resemble:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ kubectl --namespace=deis get pods\r\nNAME                                     READY     STATUS    RESTARTS   AGE\r\ndeis-builder-587185222-rktvr             1\/1       Running   0          6m\r\ndeis-controller-1274199582-3lqgv         1\/1       Running   0          6m\r\ndeis-database-1755707874-9z55k           1\/1       Running   0          6m\r\ndeis-logger-2533678197-91h6m             1\/1       Running   0          6m\r\ndeis-logger-fluentd-69dqb                1\/1       Running   4          6m\r\ndeis-logger-fluentd-bw2v7                1\/1       Running   0          6m\r\ndeis-logger-fluentd-j5zt9                1\/1       Running   0          6m\r\ndeis-logger-fluentd-mlddn                1\/1       Running   0          6m\r\ndeis-logger-fluentd-sh8kw                1\/1       Running   0          6m\r\ndeis-logger-redis-1307646428-w32k3       1\/1       Running   0          6m\r\ndeis-monitor-grafana-59098797-2v6p2      1\/1       Running   0          6m\r\ndeis-monitor-influxdb-168332144-5l7bx    1\/1       Running   0          6m\r\ndeis-monitor-telegraf-624n4              1\/1       Running   0          6m\r\ndeis-monitor-telegraf-6bmd6              1\/1       Running   0          6m\r\ndeis-monitor-telegraf-bk833              1\/1       Running   1          6m\r\ndeis-monitor-telegraf-x98sw              1\/1       Running   0          6m\r\ndeis-monitor-telegraf-zzjjl              1\/1       Running   0          6m\r\ndeis-nsqd-1042535208-b6th3               1\/1       Running   0          6m\r\ndeis-registry-3958274866-qjv9h           1\/1       Running   0          6m\r\ndeis-registry-proxy-58tv0                1\/1       Running   0          6m\r\ndeis-registry-proxy-86rlw                1\/1       Running   0          6m\r\ndeis-registry-proxy-9h3cz                1\/1       Running   0          6m\r\ndeis-registry-proxy-djrts                1\/1       Running   0          6m\r\ndeis-registry-proxy-mtc7d                1\/1       Running   0          6m\r\ndeis-router-3258454730-5l05p             1\/1       Running   0          6m\r\ndeis-workflow-manager-3582051402-1tzl6   1\/1       Running   0          6m<\/pre>\n<h2>Finalizing Deis Workflow Readiness<\/h2>\n<p>At this point, Deis Workflow is up and running on Kubernetes on Azure. However, we still have a few steps left to finalize the readiness of the deployment, including:<\/p>\n<ul>\n<li>Setting up DNS for Deis Workflow<\/li>\n<li>Registering the Deis Workflow Administrator<\/li>\n<li>Installing the Deis Platform Certificate and Enable TLS<\/li>\n<\/ul>\n<h3>Setting up DNS for Deis Workflow<\/h3>\n<p>When applications are deployed to Workflow, a name can be specified, or one will be generated. That name is prefixed to the domain set up for Deis Workflow. Previously, a wildcard certificate was registered for the domain plusonetechnology.net. Azure DNS needs to be configured to point to Deis Workflow. We must do the following steps, detailed in the following sections:<\/p>\n<ul>\n<li>Configure Azure DNS Zone for the domain name<\/li>\n<li>Update DNS Entries for the domain name at the domain registrar<\/li>\n<li>Test to verify DNS has updated<\/li>\n<\/ul>\n<h4>Configure Azure DNS<\/h4>\n<p>To configure Azure DNS you must:<\/p>\n<ul>\n<li>Create the Azure DNS Zone<\/li>\n<li>Create the Wildcard A Record-set<\/li>\n<li>Create the Wildcard A Record<\/li>\n<\/ul>\n<p>To create the zone:<\/p>\n<pre class=\"lang:default decode:true \">jims@tupperware:~$ az network dns zone create --name=\"plusonetechnology.net\" --resource-group=\"deisprodk8s\"\r\n{\r\n  \"etag\": \"00000002-0000-0000-3639-3f9f965dd201\",\r\n  \"id\": \"\/subscriptions\/ab12ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/deisprodk8s\/providers\/Microsoft.Network\/dnszones\/plusonetechnology.net\",\r\n  \"location\": \"global\",\r\n  \"maxNumberOfRecordSets\": 5000,\r\n  \"name\": \"plusonetechnology.net\",\r\n  \"nameServers\": [\r\n    \"ns1-07.azure-dns.com.\",\r\n    \"ns2-07.azure-dns.net.\",\r\n    \"ns3-07.azure-dns.org.\",\r\n    \"ns4-07.azure-dns.info.\"\r\n  ],\r\n  \"numberOfRecordSets\": 2,\r\n  \"resourceGroup\": \"deisprodk8s\",\r\n  \"tags\": {},\r\n  \"type\": \"Microsoft.Network\/dnszones\"\r\n}<\/pre>\n<p>In the information returned, note the list of name servers. This list will be needed when updating the information for the domain at the domain registrar. This process will be covered in the next section.<\/p>\n<p>Creating the record set:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ az network dns record-set a create \r\n&gt; --name=\"*\" \r\n&gt; --zone-name=\"plusonetechnology.net\" \r\n&gt; --resource-group=\"deisprodk8s\"\r\n{\r\n  \"etag\": \"16fb3cb9-2a63-4ca2-babb-74e7a08fc9a6\",\r\n  \"id\": \"\/subscriptions\/04f7ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/jmsdeis\/providers\/Microsoft.Network\/dnszones\/plusonetechnology.net\/A\/*\",\r\n  \"metadata\": null,\r\n  \"name\": \"*\",\r\n  \"resourceGroup\": \"deisprodk8s\",\r\n  \"ttl\": 3600,\r\n  \"type\": \"Microsoft.Network\/dnszones\/A\"\r\n}<\/pre>\n<p>As part of the Deis Workflow setup, Deis provisions a load balancer for inbound traffic, referred to as the LoadBalancer Ingress, which is a public IP address. To set up the A-Record for the domain, we need to retrieve this address. Querying the Kubernetes cluster as shown below returns this information:<\/p>\n<pre class=\"lang:default decode:true\">```bash\r\njims@tupperware:~$ kubectl --namespace=deis describe svc deis-router\r\nName:           deis-router\r\nNamespace:      deis\r\nLabels:         heritage=deis\r\nAnnotations:        service.beta.kubernetes.io\/aws-load-balancer-connection-idle-timeout=1200\r\nSelector:       app=deis-router\r\nType:           LoadBalancer\r\nIP:         10.0.128.55\r\nLoadBalancer Ingress:   52.173.130.253\r\nPort:           http    80\/TCP\r\nNodePort:       http    30854\/TCP\r\nEndpoints:      10.244.0.7:8080\r\nPort:           https   443\/TCP\r\nNodePort:       https   30567\/TCP\r\nEndpoints:      10.244.0.7:6443\r\nPort:           builder 2222\/TCP\r\nNodePort:       builder 31387\/TCP\r\nEndpoints:      10.244.0.7:2222\r\nPort:           healthz 9090\/TCP\r\nNodePort:       healthz 32093\/TCP\r\nEndpoints:      10.244.0.7:9090\r\nSession Affinity:   None\r\nEvents:\r\n  FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason          Message\r\n  --------- --------    -----   ----            -------------   --------    ------          -------\r\n  15m       15m     1   service-controller          Normal      CreatingLoadBalancer    Creating load balancer\r\n  12m       12m     1   service-controller          Normal      CreatedLoadBalancer Created load balancer<\/pre>\n<p>As you can see above, the LoadBalancer Ingress IP address is <strong>52.173.130.253<\/strong>. This value is used to set up the A-Record for Azure DNS:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ az network dns record-set a add-record --record-set-name=\"*\" --resource-group=\"deisprodk8s\" --zone-name=\"plusonetechnology.net\" --ipv4-address=52.173.130.253\r\n{\r\n  \"arecords\": [\r\n    {\r\n      \"ipv4Address\": \"52.173.130.253\"\r\n    }\r\n  ],\r\n  \"etag\": \"99420e4d-8bfc-4b68-8509-1bd73f02c2ac\",\r\n  \"id\": \"\/subscriptions\/04f7ec88-8e28-41ed-8537-5e17766001f5\/resourceGroups\/jmsdeis\/providers\/Microsoft.Network\/dnszones\/plusonetechnology.net\/A\/*\",\r\n  \"metadata\": null,\r\n  \"name\": \"*\",\r\n  \"resourceGroup\": \"jmsdeis\",\r\n  \"ttl\": 3600,\r\n  \"type\": \"Microsoft.Network\/dnszones\/A\"\r\n}<\/pre>\n<p>At this point, DNS is configured on the Azure side. The next step is to update the DNS information with the domain registrar.<\/p>\n<h4>Update DNS Entries for the Domain Name at the Domain Registrar<\/h4>\n<p>The method for updating DNS information will vary from registrar to registrar. The domain plusonetechnology.net is registered with <a href=\"http:\/\/joker.com\">Joker<\/a>. Updating the DNS records for plusonetechnology.net at Joker will look like:<\/p>\n<p><!-- TODO - missing image\n\n<img decoding=\"async\" class=\"alignnone wp-image-3380 size-medium\" src=\"\/developerblog\/wp-content\/uploads\/joker_dns.png\" alt=\"Joker DNS settings\" width=\"261\" height=\"300\" \/>\n\n--><\/p>\n<p>Note that the values in the image are the same nameserver values returned when we created the DNS Zone with the Azure CLI above.<\/p>\n<h4>Test to Verify DNS Has Updated<\/h4>\n<p>After changing that information on the domain registrar, we need to verify that the DNS has been updated. This process can take just a few minutes or it can be longer.<\/p>\n<p>Since a Wildcard record was set up for the domain plusonetechnology.net, any name placed in from of the domain will now resolve to the same address.<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ nslookup muppets.plusonetechnology.net\r\nServer:        10.211.55.1\r\nAddress:    10.211.55.1#53\r\n\r\nNon-authoritative answer:\r\nName:    muppets.plusonetechnology.net\r\nAddress: 52.173.130.25<\/pre>\n<p>At this point, DNS is properly configured, and the information has propagated.<\/p>\n<h3>Register the Deis Workflow Administrator<\/h3>\n<p>With a new Deis Workflow install, the first user registered is the administrator. To register:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ deis register http:\/\/deis.plusonetechnology.net\r\nusername: jims\r\npassword:\r\npassword (confirm):\r\nemail: jaspring@microsoft.com\r\nRegistered jims\r\nLogged in as jims\r\nConfiguration file written to \/home\/jims\/.deis\/client.json\r\njims@tupperware:~$ deis users\r\n=== Users (*=admin)\r\n*jims<\/pre>\n<h3>Install the Deis Platform Certificate and Enable TLS<\/h3>\n<p>Previously, a Wildcard Certificate was ordered from Namecheap. After going through the validation process, the certificates were delivered:<\/p>\n<pre class=\"lang:default decode:true \">jims@tupperware:__plusonetechnology_net$ ls -ltra\r\ntotal 40\r\ndrwx------@  5 jims  staff   170 Dec 23 13:24 .\r\ndrwx------+ 15 jims  staff   510 Dec 23 15:31 ..\r\n-rwxr-xr-x@  1 jims  staff  7342 Dec 23 16:24 __plusonetechnology_net.p7b\r\n-rwxr-xr-x@  1 jims  staff  1944 Dec 23 16:24 __plusonetechnology_net.crt\r\n-rwxr-xr-x@  1 jims  staff  5630 Dec 23 16:24 __plusonetechnology_net.ca-bundle<\/pre>\n<p>This step is in addition to the private key and certificate signing request generated earlier.<\/p>\n<p>Deis Workflow specifies how to enable <a href=\"https:\/\/deis.com\/docs\/workflow\/managing-workflow\/platform-ssl\/\">Platform SSL<\/a>. In short, the steps are:<\/p>\n<ul>\n<li>Create a file <em>tls.crt<\/em> that has the TLS certificate, first, and chain certificates following<\/li>\n<li>Create a file <em>tls.key<\/em>\u00a0which is the private key file copied\/renamed<\/li>\n<li>Create a YAML file to update Deis Router settings\n<ul>\n<li>Secret name is deis-router-platform-cert<\/li>\n<li>Requires both tls.crt and tls.key be base64 encoded<\/li>\n<\/ul>\n<\/li>\n<li>Use kubectl\u00a0to add the new configuration<\/li>\n<\/ul>\n<p>For <code>tls.crt<\/code>:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~\/flow\/deis\/certs$ cat __plusonetechnology_net.crt __plusonetechnology_net.ca-bundle &gt; tls.crt\r\njims@tupperware:~\/flow\/deis\/certs$ more tls.crt\r\n-----BEGIN CERTIFICATE-----\r\nMIIFbTCCBFWgAwIBAgIRAJJIdJMChgmODD2ZZ\/HKQQwwDQYJKoZIhvcNAQELBQAw\r\ngZAxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO\r\nBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTYwNAYD\r\nVQQDEy1DT01PRE8gUlNBIERvbWFpbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIg\r\nQ0EwHhcNMTYxMjIzMDAwMDAwWhcNMTcxMjIzMjM1OTU5WjBkMSEwHwYDVQQLExhE\r\nb21haW4gQ29udHJvbCBWYWxpZGF0ZWQxHTAbBgNVBAsTFFBvc2l0aXZlU1NMIFdp\r\nbGRjYXJkMSAwHgYDVQQDDBcqLnBsdXNvbmV0ZWNobm9sb2d5Lm5ldDCCASIwDQYJ\r\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOi51Bepbn74vlrV9S0E02Ve4t5Q4kgj\r\n4KLXQx1ZZSizKfWEeS7QBwFcd040Mt+rZbWNQyvGVdoNYpINozXEtQJVUp+Ir22k\r\nz+96uFWF11QBMcTxlJc9j8dkaQIKgmUXzwUMz5oqJ615CfqY3sSN6a+G00Hay7Lj\r\nnlJYqiy6j+gdGYa4j4J+oh\/uFWrVMXMZ3YF3g1aaSV3ImnYS9s37aCv7kbzJc0TH\r\nCK9qezA+0tkP2gqzhmZZk\/XuvtiLMOAd2FI41l22o63RaEF4k4H7jiEjATMq0zPN\r\nnBlHb0yJBoHLjbBJervUBQ4184p1MGYZR\/1BAsx7EWJ7Dj5QWOLCLykCAwEAAaOC\r\nAeswggHnMB8GA1UdIwQYMBaAFJCvajqUWgvYkOoSVnPfQ7Q6KNrnMB0GA1UdDgQW\r\nBBQ1KAcDJ0ddMekt+zdFLFtKv8sLmzAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH\/\r\nBAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTwYDVR0gBEgwRjA6\r\nBgsrBgEEAbIxAQICBzArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21v\r\nZG8uY29tL0NQUzAIBgZngQwBAgEwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL2Ny\r\nbC5jb21vZG9jYS5jb20vQ09NT0RPUlNBRG9tYWluVmFsaWRhdGlvblNlY3VyZVNl\r\ncnZlckNBLmNybDCBhQYIKwYBBQUHAQEEeTB3ME8GCCsGAQUFBzAChkNodHRwOi8v\r\nY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9SU0FEb21haW5WYWxpZGF0aW9uU2VjdXJl\r\nU2VydmVyQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5j\r\nb20wOQYDVR0RBDIwMIIXKi5wbHVzb25ldGVjaG5vbG9neS5uZXSCFXBsdXNvbmV0\r\nZWNobm9sb2d5Lm5ldDANBgkqhkiG9w0BAQsFAAOCAQEAOdjjTf0NzrbmQnmClML8\r\nkPggIJhetlJzUaRMKV49oOWeRHyxF78DvTwelK+hCz1Of0nHddKTfUgcE1rfyGwB\r\nhh0XtVyU32Kw8HNwb8KkJMCxFZ8A0mFlNXO5H1gHiJaZaUjHTJOR7RBJWfgxNrBb\r\n3Wg\/lOp55tl9fqDL29IvRqCldJl5arcJGz71Y6GUcIokUfp00JItvbj6AbLD+Wya\r\nnXN6kATxGtigneZYCsiYlFVH5Qina3wjEyXR+L6wiPYBIYbQ4APQIR4R2gsooDw4\r\nyja5CvKDuHk9\/5jOQHiWD8CpBS5YPvuufuROzBgmDeoQx0TrwNClHynYIpD1RqE0\r\nkA==\r\n-----END CERTIFICATE-----\r\n...<\/pre>\n<p>For tls.key:<\/p>\n<pre class=\"lang:default decode:true \">jims@tupperware:~\/flow\/deis\/certs$ cp star_plusonetechnology_net.key tls.crt<\/pre>\n<p>To generate the <strong>deis-router-platform-cert.yaml<\/strong> file, first the base64 encodings of <strong>tls.crt<\/strong> and <strong>tls.key<\/strong> are needed:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~\/flow\/deis\/certs$ cat tls.crt | base64 &gt; tls.crt.b64\r\njims@tupperware:~\/flow\/deis\/certs$ cat tls.key | base64 &gt; tls.key.b64\r\njims@tupperware:~\/flow\/deis\/certs$ ls -l *b64\r\n-rw-rw-r-- 1 jims jims 10233 Dec 25 17:06 tls.crt.b64\r\n-rw-rw-r-- 1 jims jims  2266 Dec 25 17:06 tls.key.b64<\/pre>\n<p>Next, you need to create the <strong>deis-router-platform-cert.yaml<\/strong> in your editor of choice:<\/p>\n<pre class=\"lang:default decode:true\">$ cat deis-router-platform-cert.yaml\r\napiVersion: v1\r\nkind: Secret\r\nmetadata:\r\n  name: deis-router-platform-cert\r\n  namespace: deis\r\ntype: Opaque\r\ndata:\r\n  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1...S0KDQo=\r\n  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0...LS0tCg==<\/pre>\n<p>Save the file and use kubectl to add the new secret for the Deis Router:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~\/flow\/deis\/certs$ kubectl create -f deis-router-platform-cert.yaml\r\nsecret \"deis-router-platform-cert\" created<\/pre>\n<p>You can see the change picked up by using kubectl to look at the logs for the Deis Router:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ kubectl --namespace=deis logs deis-router-1741606082-4mkzh\r\n2016\/12\/26 01:13:58 INFO: Router configuration has changed in k8s.\r\n2016\/12\/26 01:13:58 INFO: Reloading nginx...\r\n2016\/12\/26 01:13:58 INFO: nginx reloaded.<\/pre>\n<p>To verify TLS is active for Deis Workflow, it is best to install a sample application and verify that it is reachable by both HTTP and HTTPS. The <a href=\"https:\/\/deis.com\/docs\/workflow\/quickstart\/deploy-an-app\/\">Deis Workflow Quickstart<\/a> shows an example of a sample app being deployed, which will be used and then curl will be used to test both HTTP and HTTPS connections:<\/p>\n<pre class=\"lang:default decode:true\">jims@tupperware:~$ deis create --no-remote\r\nCreating Application... done, created united-greenery\r\nIf you want to add a git remote for this app later, use `deis git:remote -a united-greenery`\r\njims@tupperware:~\/flow\/deis\/certs$ deis pull deis\/example-go -a united-greenery\r\nCreating build... done\r\njims@tupperware:~\/flow\/deis\/certs$ curl http:\/\/united-greenery.plusonetechnology.net\r\nPowered by Deis\r\njims@tupperware:~\/flow\/deis\/certs$ curl https:\/\/united-greenery.plusonetechnology.net\r\nPowered by Deis<\/pre>\n<h2>Conclusion<\/h2>\n<p>Deis Workflow enables application developers to easily deploy applications based on the Twelve Factor model on Kubernetes clusters. \u00a0With Kubernetes available on the Azure Container Service, developers can leverage the resources of Azure in developing and deploying their applications. \u00a0With just a few additional steps, one can move deployed services from a test\/dev environment to a production ready one.<\/p>\n<p>In April of 2017, <a href=\"https:\/\/deis.com\/blog\/2017\/deis-to-join-microsoft\/\">Microsoft acquired Deis<\/a>. \u00a0This brings even more Kubernetes expertise to Microsoft and will make the overall experience of Deis (and Kubernetes) on Azure even better.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We will cover the steps necessary to get Deis Workflow up and running on Azure, and address the production deployment steps in depth. <\/p>\n","protected":false},"author":21363,"featured_media":10968,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[15,16],"tags":[60,68,91,149,229,296],"class_list":["post-3363","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-containers","category-devops","tag-azure","tag-azure-cloud-computing","tag-azure-resource-manager-arm","tag-deis","tag-kubernetes","tag-production"],"acf":[],"blog_post_summary":"<p>We will cover the steps necessary to get Deis Workflow up and running on Azure, and address the production deployment steps in depth. <\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/3363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21363"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=3363"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/3363\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/10968"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=3363"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=3363"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=3363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}