Gloo Edge on Azure Kubernetes Service (AKS) with Microsoft Windows node pools

If you have Microsoft Windows-based applications that you would like to migrate to the cloud, you might want to avoid the cumbersome task of rewriting these legacy applications at the same time. Microsoft recommends all new applications should be written in .Net (docs here) or .NetCore, but this could take months or years depending on the application size, complexity, and dependencies. You might want to “lift and shift” now, and do a full application redesign to take advantage of additional cloud advantages later.

To migrate your applications to the cloud faster, you could use Gloo Edge as an API gateway and ingress controller (building on the open source Envoy Proxy) to connect and secure inbound application traffic at the edge. This helps you direct user and application traffic  — the data plane — to the new operating environment of your migrated applications, and doesn’t require any other rewrites beyond replatforming to the cloud.

In this blog, we show you how to setup an Azure Kubernetes Service (AKS) cluster with both Linux and Windows node pools, how Gloo Edge’s Upstream Discovery Service (UDS) can discover both Linux and Windows Upstreams, and how you can leverage Gloo Edge as an API gateway to route traffic to both Linux and Windows Workloads.

For this walkthrough we will assume you have an active Azure subscription with the appropriate permissions to create resources. If not, here’s how you can get started with a free trial.

Let’s install the tools we will need to get everything up and running. To install the Azure CLI find the install instructions for your particular operating system. For our example, we are using MacOS so we will install with brew:

brew update && brew install azure-cli

 

Next, let’s install Glooctl, which we will use to install and interact with Gloo Edge:

curl -sL https://run.solo.io/gloo/install | sh
export PATH=$HOME/.gloo/bin:$PATH

 

Now let’s log in to Azure and create our AKS cluster with a single Linux node:

az login 

az group create --name Lab --location eastus

echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME

az aks create \
   --resource-group Lab \
   --name lab-cluster-1 \
   --node-count 1 \
   --generate-ssh-keys \
   --windows-admin-username $WINDOWS_USERNAME \
   --vm-set-type VirtualMachineScaleSets \
   --network-plugin azure

#You will be prompted for a windows password. If you get a password validation error, verify the password you set meets the Windows Server password requirements.

 

Cluster creation can take anywhere between 5-20 minutes depending on the region and other factors. Next let’s connect to the cluster and install Gloo Edge:

#Connect to AKS Cluster
az aks install-cli

az aks get-credentials --resource-group Lab --name lab-cluster-1

#Install Gloo Edge Gateway
glooctl install gateway

Creating namespace gloo-system... Done.
Starting Gloo Edge installation...
Gloo Edge was successfully installed!

#Verify Gloo Edge status
glooctl check

Checking deployments... OK
Checking pods... OK
Checking upstreams... OK
Checking upstream groups... OK
Checking auth configs... OK
Checking rate limit configs... OK
Checking secrets... OK
Checking virtual services... OK
Checking gateways... OK
Checking proxies... OK
No problems detected.
Skipping Gloo Instance check -- Gloo Federation not detected

 

Now that we have an AKS cluster with Gloo Edge running, let’s install a Linux-based microservice:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: azure-vote-back
spec:
  replicas: 1
  selector:
    matchLabels:
      app: azure-vote-back
  template:
    metadata:
      labels:
        app: azure-vote-back
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": linux
      containers:
      - name: azure-vote-back
        image: mcr.microsoft.com/oss/bitnami/redis:6.0.8
        env:
        - name: ALLOW_EMPTY_PASSWORD
          value: "yes"
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 250m
            memory: 256Mi
        ports:
        - containerPort: 6379
          name: redis
---
apiVersion: v1
kind: Service
metadata:
  name: azure-vote-back
spec:
  ports:
  - port: 6379
  selector:
    app: azure-vote-back
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: azure-vote-front
spec:
  replicas: 1
  selector:
    matchLabels:
      app: azure-vote-front
  template:
    metadata:
      labels:
        app: azure-vote-front
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": linux
      containers:
      - name: azure-vote-front
        image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 250m
            memory: 256Mi
        ports:
        - containerPort: 80
        env:
        - name: REDIS
          value: "azure-vote-back"
---
apiVersion: v1
kind: Service
metadata:
  name: azure-vote-front
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    app: azure-vote-front

EOF

 

So far we have a 1-node AKS cluster with a Linux workload installed. Let’s route traffic to the frontend service on Linux. Gloo Edge will have automatically discovered this upstream resource:

glooctl get upstream | grep azure
                                                                                                    
| default-azure-vote-back-6379   | Kubernetes | Accepted | svc name:  azure-vote-back  |
| default-azure-vote-front-80     | Kubernetes | Accepted | svc name:  azure-vote-front |

 

Let’s setup a “/linux” path and route traffic to this upstream resource:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: my-aks-vs
  namespace: gloo-system
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matchers:
      - prefix: /linux
      routeAction:
        single:
          upstream:
            name: default-azure-vote-front-80
            namespace: gloo-system
      options:
        prefixRewrite: '/'
EOF

 

Let’s test the route is operating as expected:

curl $(glooctl proxy url)/linux  
                                                                                              
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <link rel="stylesheet" type="text/css" href="/static/default.css">
 <title>Azure Voting App</title>
 <script language="JavaScript">
     function send(form){
     }
 </script>
</head>
<body>
 <div id="container">
     <form id="form" name="form" action="/"" method="post"><center>
     <div id="logo">Azure Voting App</div>
     <div id="space"></div>
     <div id="form">
     <button name="vote" value="Cats" onclick="send()" class="button button1">Cats</button>
     <button name="vote" value="Dogs" onclick="send()" class="button button2">Dogs</button>
     <button name="vote" value="reset" onclick="send()" class="button button3">Reset</button>
     <div id="space"></div>
     <div id="space"></div>
     <div id="results"> Cats - 0 | Dogs - 0 </div>
     </form>    
     </div>
 </div> 
</body>
</html>

 

Great! So we have now created a route for “/linux” and routed traffic to the azure-vote-front upstream which was discovered by Gloo Edge automatically with UDS. Now let’s add a Windows node pool and route traffic for “/windows” to a new Windows workload:

az aks nodepool add \
   --resource-group Lab \
   --cluster-name lab-cluster-1 \
   --os-type Windows \
   --name npwin \
   --node-count 1

 

Now let’s add a sample Windows application:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample
  labels:
    app: sample
spec:
  replicas: 1
  template:
    metadata:
      name: sample
      labels:
        app: sample
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": windows
      containers:
      - name: sample
        image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
        resources:
          limits:
            cpu: 1
            memory: 800M
          requests:
            cpu: .1
            memory: 300M
        ports:
          - containerPort: 80
  selector:
    matchLabels:
      app: sample
---
apiVersion: v1
kind: Service
metadata:
  name: sample
spec:
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
  selector:
    app: sample
EOF

 

Gloo Edge has automatically discovered this Windows upstream resource:

glooctl get upstream | grep sample

| default-sample-80 | Kubernetes | Accepted | svc name:   sample       |

 

Now let’s update our virtual service to route to our Windows application:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: my-aks-vs
  namespace: gloo-system
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matchers:
      - prefix: /linux
      routeAction:
        single:
          upstream:
            name: default-azure-vote-front-80
            namespace: gloo-system
      options:
        prefixRewrite: '/'
    - matchers:
      - prefix: /windows
      routeAction:
        single:
          upstream:
            name: default-sample-80
            namespace: gloo-system
      options:
        prefixRewrite: '/'        
EOF

 

Finally, let’s test the route is operating as expected:

curl $(glooctl proxy url)/windows                                                                                            

<!DOCTYPE html>

<html lang="en">
<head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>
    Home Page - My ASP.NET Application
</title><script src="/Scripts/modernizr-2.6.2.js"></script>
<link href="/Content/bootstrap.css" rel="stylesheet"/>
<link href="/Content/Site.css" rel="stylesheet"/>
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon" /></head>
<body>
 <form method="post" action="./" id="ctl01">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="yrtIIx/ipV/1nWGzgulo9q+YB/0p1AS8vkgfL5u9lrLv/APyqBzvb34w4hofVx86XIfC3xYYyCzUGk8AhiEeWX7bJoLTYr9ONU48JGE0Ebs=" />
<script src="/bundles/MsAjaxJs?v=c42ygB2U07n37m_Sfa8ZbLGVu4Rr2gsBo7MvUEnJeZ81" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
if (typeof(Sys) === 'undefined') throw new Error('ASP.NET Ajax client-side framework failed to load.');
//]]>
</script>
<script src="Scripts/jquery-1.10.2.js" type="text/javascript"></script>
<script src="Scripts/bootstrap.js" type="text/javascript"></script>
<script src="Scripts/respond.js" type="text/javascript"></script>
<script src="/bundles/WebFormsJs?v=AAyiAYwMfvmwjNSBfIMrBAqfU5exDukMVhrRuZ-PDU01" type="text/javascript"></script>
<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="CA0B0334" />
    
     <div class="navbar navbar-inverse navbar-fixed-top">
         <div class="container">
             <div class="navbar-header">
                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                 </button>
                 <a href="./" class="navbar-brand">Application name</a>
             </div>
             <div class="navbar-collapse collapse">
                 <ul class="nav navbar-nav">
                     <li><a href="./">Home</a></li>
                     <li><a href="About">About</a></li>
                     <li><a href="Contact">Contact</a></li>
                 </ul>
             </div>
         </div>
     </div>
     <div class="container body-content">
        
 <div class="jumbotron">
     <h1>ASP.NET</h1>
     <p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS, and JavaScript.</p>
     <p><a href="http://www.asp.net" class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
 </div>

 <div class="row">
     <div class="col-md-4">
         <h2>Getting started</h2>
         <p>
             ASP.NET Web Forms lets you build dynamic websites using a familiar drag-and-drop, event-driven model.
         A design surface and hundreds of controls and components let you rapidly build sophisticated, powerful UI-driven sites with data access.
         </p>
         <p>
             <a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301948">Learn more &raquo;</a>
         </p>
     </div>
     <div class="col-md-4">
         <h2>Get more libraries</h2>
         <p>
             NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.
         </p>
         <p>
             <a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301949">Learn more &raquo;</a>
         </p>
     </div>
     <div class="col-md-4">
         <h2>Web Hosting</h2>
         <p>
             You can easily find a web hosting company that offers the right mix of features and price for your applications.
         </p>
         <p>
             <a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301950">Learn more &raquo;</a>
         </p>
     </div>
 </div>

         <hr />
         <footer>
             <p>&copy; 2021 - My ASP.NET Application</p>
         </footer>
     </div>

 </form>
</body>
</html>

 

Once you are done, to cleanup your environment and avoid any ongoing charges, run the following command:

az group delete --name Lab --yes --no-wait

 

As you can see, Gloo Edge makes it easy to route traffic both to Linux and Windows-based applications in Kubernetes. We touched on just a simple method of routing with virtual services, but I encourage you to spend some time in our docs and give Gloo Edge a try for yourself. 

Please feel free to reach out to us on Slack anytime, our experts are here to help you be successful faster.

If AKS is interesting to you, you should also read Gloo Mesh and Istio on Azure Kubernetes Service (AKS) with Global Virtual Network Peering.