Azure DevOps Deployment

In this lab we will create a tiny, single node kubernetes cluster, and deploy our code to it. In the following, we’ll use K8s (Kubernetes) or AKS (Azure Kubernetes Service) to shorten things…

Preparation

  1. Login to Azure Portal and switch to your organization.

Create a new Kubernetes cluster

  1. Click on Create a resource on the upper left corner and search for kubernetes in the search field, then select Kubernetes Service from the list.
  2. Click Create
  3. On the following pages, fill in all mandatory fields, be sure to select the cheapest possible Node size. We don’t need much computing power for our demo app so the smallest possible configuration will do. As of writing this, this is named B2s.
    Also be sure to only select 1 node (default is 3)!!
  4. Click Review + create and once it passes validation, hit the Create button and grab a coffee….

Creating the deployment YAML

###Background Unlike a VM where you can ssh into and run bash scripts and all this, kubernetes is steered via an API server (the Kubernetes master node). Almost the only tool you will ever need is kubectl to manage deployments and configuration. And - almost all of these actions are driven by sending a specifically tailored .yml (or .yaml) file with the help of kubectl.

  1. As we first want to test our deployment yaml, we will install a very basic k8s cluster (Don’t call it cluster anyways…) named minikube.
    So, click the link above and follow the instructions for your operating system.
  2. Fire it up by running minikube start and wait for it to com up.
  3. Check that everything is smooth by running minikube kubectl get ns. The output should be similar to this:
     $ minikube kubectl get ns
     💾  Downloading kubectl v1.15.2
     NAME              STATUS   AGE
     default           Active   95s
     kube-node-lease   Active   98s
     kube-public       Active   98s
     kube-system       Active   98s
    
  4. Create a subdirectory named manifests and create preliminary deployment.yaml in that directory with the following content:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demoapp
      labels:
        app: demoapp
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: demoapp
      template:
        metadata:
          labels:
            app: demoapp
        spec:
          containers:
            - name: demoapp
              imagePullPolicy: IfNotPresent
              image: nemoregistrysandbox.azurecr.io/demoapp
              ports:
                - containerPort: 8080
    

    NOTE: Minikube is running in a virtual machine (at least on MacOS and Windows) and therefore it’s isolated from the host operating system(yes, your laptop) for security reasons. Our deployment.yml file references an image named demoapp, which indicates, that it should look in a local registry. The local registry for minikube unfortunately is the one within the VM. So now we will turn things around and expose the VM’s docker registry to our laptop. To do so, run

    $ eval $(minikube docker-env)
    

    to set your environment to point to minikube’s docker registry and re-run the build:

    $ docker build -t demoapp --build-arg JAR_FILE=build/libs/demo-0.0.1-SNAPSHOT.jar .
    
  5. Minikube is running in a virtual machine, so it is isolated from the host (your laptop) and therefore you cannot access the ports directly. The easiest way to expose the port is by using the port-forward.
    $ kubectl get pods
    NAME                       READY   STATUS    RESTARTS   AGE
    demoapp-84b67c64c8-24lvj   1/1     Running   0          2d
    

    Then note the pod name and run:

    $kubectl port-forward demoapp-84b67c64c8-24lvj 8080
    

    Now you’ll be able to curl to your pod:

    $ curl -X GET http://localhost:8080/hello 
    
  6. Challenging Exercise - somewhat Try to modify the deployment.yaml and scale the service to 2 instances. You should then also try to create a kubernetes service. A service in k8s terms is a loadbalancer that distributes the load to your different pods.

##Extend the pipeline

Now that the k8s deployment is working, the k8s cluster is up’n running, we want to deploy our code to AKS. Doing so requires a few steps:

  • Creating the cluster
  • Allow AKS to connect to the ACR (Azure Container Registry)
  • Create image pull secrets, so that AKS has the credentials to connect to ACR
  • Creating a Release pipeline


NOTE: All these steps are in the Microsoft documentation

  1. Since we already created the cluster, we can continue with the second step which is outlined here. Create a new file (shell script) with the following content:
    #!/bin/bash
       
    AKS_RESOURCE_GROUP=myAKSResourceGroup
    AKS_CLUSTER_NAME=myAKSCluster
    ACR_RESOURCE_GROUP=myACRResourceGroup
    ACR_NAME=myACRRegistry
       
    # Get the id of the service principal configured for AKS
    CLIENT_ID=$(az aks show --resource-group $AKS_RESOURCE_GROUP --name $AKS_CLUSTER_NAME --query "servicePrincipalProfile.clientId" --output tsv)
       
    # Get the ACR registry resource id
    ACR_ID=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query "id" --output tsv)
       
    # Create role assignment
    az role assignment create --assignee $CLIENT_ID --role acrpull --scope $ACR_ID
    

    Save the file, make it executable with:

    $ chmod +x <scriptname>
    

    Afterwards, login to AKS and set the subscription:

    $ az login -u <your user name>
    $ az account set -s <your subscription>
    

    Note that you will be challenged for your password. upon login.
    Then execute your grant script from above

  2. Create a new service connection of type Azure Resource Manager in Azure DevOps and add the following content to your azure-pipelines.yml:
    variables:
        azureSubscriptionEndpoint: <your new service connection>
        azureContainerRegistry: <yourregistry>.azurecr.io
        azureResourceGroup: <your resource group>
        kubernetesCluster: <your clustername>
    steps:
    
    • task: Kubernetes@1 displayName: ‘kubectl login’ inputs: connectionType: ‘Azure Resource Manager’ azureSubscriptionEndpoint: $(azureSubscriptionEndpoint) azureResourceGroup: $(azureResourceGroup) kubernetesCluster: $(kubernetesCluster) command: login

    • task: Kubernetes@1 displayName: kubectl apply inputs: connectionType: ‘Azure Resource Manager’ azureSubscriptionEndpoint: $(azureSubscriptionEndpoint) azureResourceGroup: $(azureResourceGroup) kubernetesCluster: $(kubernetesCluster) command: apply arguments: -f manifests/deployment.yml

    </pre>

  3. You will now discover that your deployment works fine, but looking at the cluster with:
    $ kubectl get pods
    

    you will discover that your pod has a state of ImagePullBackOff or ErrImagePull. If you dig deeper with:

    $ kubectl describe pod <pod name> 
    

    the event log will tell you that it failed to pull the docker image, with an authentication problem. To fix this, you need to extend the deployment.yml from:

    :
         containers:
           - name: demoapp
             imagePullPolicy: IfNotPresent
             image: nemoregistrysandbox.azurecr.io/demoapp
             ports:
               - containerPort: 8080
    

    with the pullsecrets:

    :
         containers:
           - name: demoapp
             imagePullPolicy: IfNotPresent
             image: nemoregistrysandbox.azurecr.io/demoapp
             ports:
               - containerPort: 8080
         imagePullSecrets:
           - name: acr-auth
    

    Now, that you reference the act-auth image pull secrets, you need to define them within the cluster. This can be achieved by different methods. First, by using kubectl on the command line. However, this would be a manual step that we need to avoid (at all cost, to be honest).
    So, instead we’re creating a YAML file that we can deploy in an automated way. Therefore we need to create a new yaml file that will let us deploy the secrets.

  4. Creating this yaml file requires that you first login to that docker registry with docker login, which then creates a .docker/config.json file in your HOME directory. Run the follwing command and copy the output:
    $ cat ~/.docker/config.json | base64
    


    Create a new file named manifests/pullsecrets.yml with the following content:

    apiVersion: v1
    kind: Secret
    metadata:
      name: acr-pullsecrets
      namespace: default
    data:
      .dockerconfigjson: <your base64 encoded ~/<user>/.docker/config.json file>
    type: kubernetes.io/dockerconfigjson
      
    
  5. Check-in your changes to git and push-it.
  6. If everything went ok, let’s do some kubectl magic:
    $ kubectl get ns
    $ kubectl get pods --all-namespaces
    $ kubectl describe pod <pod name>
    $ kubectl port-forward <pod name> 8080
    
  7. Once you port-forwarded the pod, you should be able to access it via curl:
    $ curl -X GET http://localhost:8080/hello
    

References


  • Reset App Name