I was encouraged by a colleague to checkout Microsoft’s “Bridge to Kubernetes”, a Visual Studio/Visual Studio Code extension and pattern for live debugging kubernetes services. The idea is that you can run a containerized microservice and then redirect traffic seamlessly back to a development environment’s debug shell by stepping in and directing the kubernetes service to your visual studio code environment.
Let’s start with their guide and see what we can do.
Setup
Let's create a cluster in the standard fashion, less the ACR
$ az ad sp create-for-rbac -n ijk8suser --skip-assignment --output json > my_sp.json && export SP_PASS=`cat my_sp.json | jq -r .password` && export SP_ID=`cat my_sp.json | jq -r .appId`
$ az group create -n ijk8senv --location centralus
$ az aks create -n ijk8s --resource-group ijk8senv --location centralus --node-count 3 --enable-cluster-autoscaler --min-count 2 --max-count 4 --generate-ssh-keys --network-plugin azure --network-policy azure --service-principal $SP_ID --client-secret $SP_PASS
Then verify we can access it
$ (rm ~/.kube/config || true) && az aks get-credentials -n ijk8s -g ijk8senv --admin
rm: /Users/johnsi10/.kube/config: No such file or directory
Merged "ijk8s-admin" as current context in /Users/johnsi10/.kube/config
JOHNSI10-M1:~ johnsi10$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-nodepool1-91930899-vmss000000 Ready agent 46h v1.17.11
aks-nodepool1-91930899-vmss000001 Ready agent 46h v1.17.11
Setting up the Sample App
Download the sample app to a local workspace
JOHNSI10-M1:~ johnsi10$ cd Workspaces/
JOHNSI10-M1:Workspaces johnsi10$ git clone https://github.com/Microsoft/mindaro
Cloning into 'mindaro'...
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 8176 (delta 10), reused 9 (delta 4), pack-reused 8144
Receiving objects: 100% (8176/8176), 29.91 MiB | 26.50 MiB/s, done.
Resolving deltas: 100% (1909/1909), done.
JOHNSI10-M1:Workspaces johnsi10$ cd mindaro/
JOHNSI10-M1:mindaro johnsi10$ chmod +x ./bridge-quickstart.sh
Next we can use their installer. This does a few things, but most importantly sets up helm, launches the chart and gets a dynamic DNS for us to use:
$ ./bridge-quickstart.sh -g ijk8senv -n ijk8s
Bridge to Kubernetes
Bike Sample App - Quickstart script
-----------------------------------
Using kubernetes namespace: bikeapp
Defaulting Git repository root to current directory: /Users/johnsi10/Workspaces/mindaro
Checking directory /Users/johnsi10/Workspaces/mindaro for GIT repo Microsoft/Mindaro
Setting the Kube context to ijk8s in ijk8senv
Merged "ijk8s" as current context in /Users/johnsi10/.kube/config
helm repo add && helm repo update
"stable" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "harbor" chart repository
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "appdynamics-charts" chart repository
...Successfully got an update from the "banzaicloud-stable" chart repository
...Successfully got an update from the "mlifedev" chart repository
...Successfully got an update from the "bitnami" chart repository
fetch from s3: fetch object from s3: AccessDenied: Access Denied
status code: 403, request id: 80BE0CD47D276BD8, host id: 3m2BBUW6h+jXqAlz/UL29hD6fOGu7mBciBb6pNceeTkZTJb2nURaVFPNKDmVJo/ASo9QCPXAAIc=
...Unable to get an update from the "vnc" chart repository (s3://idjhelmtest/helm):
plugin "bin/helms3" exited with error
fetch from s3: fetch object from s3: AccessDenied: Access Denied
status code: 403, request id: 8E76635B3A05323E, host id: lohIjEseIueQU/BvIGjM75/o/g2h9NNl7bDrDujjIkpvTkAiI9IPCHBGBR3jm8Tb1wATt0GlRBQ=
...Unable to get an update from the "fbs3" chart repository (s3://idjhelmtest/helm):
plugin "bin/helms3" exited with error
fetch from s3: fetch object from s3: AccessDenied: Access Denied
status code: 403, request id: FT8XBM3Z6GDJBJ1J, host id: D711jp36icvta5Sb+/fCAZGVeSFYA2B6riYwvf4WaVJetgg4pNsbDFrFRXam8etgpcG2Yuvpa5Y=
...Unable to get an update from the "fbs3b" chart repository (s3://idjhelmtest/helm/):
plugin "bin/helms3" exited with error
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈
helm install traefik ingress controller in bikeapp
NAME: bikesharing-traefik-bikeapp
LAST DEPLOYED: Thu Oct 22 13:44:25 2020
NAMESPACE: bikeapp
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get Traefik's load balancer IP/hostname:
NOTE: It may take a few minutes for this to become available.
You can watch the status by running:
$ kubectl get svc bikesharing-traefik --namespace bikeapp -w
Once 'EXTERNAL-IP' is no longer '<pending>':
$ kubectl describe svc bikesharing-traefik --namespace bikeapp | grep Ingress | awk '{print $3}'
2. Configure DNS records corresponding to Kubernetes ingress resources to point to the load balancer IP/hostname found in step 1
Waiting for BikeSharing ingress Public IP to be assigned...
BikeSharing ingress Public IP: 20.37.130.17
The Nip.IO FQDN would be 20.37.130.17.nip.io
---
Chart directory: /Users/johnsi10/Workspaces/mindaro/samples/BikeSharingApp/charts/
helm install bikesharingapp (average time to install = 4 minutes)
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "harbor" chart repository
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "appdynamics-charts" chart repository
...Successfully got an update from the "nginx-stable" chart repository
fetch from s3: fetch object from s3: AccessDenied: Access Denied
status code: 403, request id: 630C8468E7A91C65, host id: tUYAXQL5f27U/07NqvlLeOy98aSyAXYZsngHcpALEDBGl2t0hGFuYJ6WNqeRjMv48qq41gR5qdY=
...Unable to get an update from the "vnc" chart repository (s3://idjhelmtest/helm):
plugin "bin/helms3" exited with error
fetch from s3: fetch object from s3: AccessDenied: Access Denied
status code: 403, request id: 48FB03424BE1E0AA, host id: OqttkiZf4psf0ILt65wv4X+fat6xZRYMQbcOCtlRUI0O6LEcN/4FksuoDGmXpGNVah70rJNneMY=
...Unable to get an update from the "fbs3b" chart repository (s3://idjhelmtest/helm/):
plugin "bin/helms3" exited with error
...Successfully got an update from the "mlifedev" chart repository
fetch from s3: fetch object from s3: AccessDenied: Access Denied
status code: 403, request id: 764437D2D9C8B333, host id: L0yN/9wBiv5iZ/mmtPNQdwsUpy9C0FxoGZXF3Dw9MuVzNvNO3j8U+Pc/mu4+ulw92amfQGuyug4=
...Unable to get an update from the "fbs3" chart repository (s3://idjhelmtest/helm):
plugin "bin/helms3" exited with error
...Successfully got an update from the "banzaicloud-stable" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 9 charts
Deleting outdated charts
NAME: bikesharingapp
LAST DEPLOYED: Thu Oct 22 13:44:37 2020
NAMESPACE: bikeapp
STATUS: deployed
REVISION: 1
TEST SUITE: None
To try out the app, open the url:
bikeapp.bikesharingweb.20.37.130.17.nip.io
At this point we have "bikeapp.bikesharingweb.20.37.130.17.nip.io" for our sample app
Debugging
Open in VS Code:
$ cd samples/BikeSharingApp/Bikes
$ ls
Dockerfile TESTING.md charts package-lock.json package.json server.js
$ code .
First, install the “Bridge to Kubernetes” extension:
We pick the Kubernetes Icon, then at the top, under clusters, choose our cluster.
There we should see the bikapp namespace. Right click and choose “Use Namespace”
We will now see it move from ‘default’ to “bikeapp”:
Use “View/Terminal” if not already showing and do “npm install” to install node dependencies
Next, choose the Debug icon and Launch via NPM with Kubernetes:
Once we grant the install privilege, we can see it start to work
Let’s open server.js and change line 110 - the mongodb call for “availableBikes” to limit from 10 to 2.
var cursor = mongoDB.collection(mongoDBCollection).find(query).sort({ hourlyCost: 1 }).limit(2);
Then click the green refresh to see it update our local instance:
Refreshing the get bikes page now drops from 10 bikes to 2:
Changing it to 20 and saving:
var cursor = mongoDB.collection(mongoDBCollection).find(query).sort({ hourlyCost: 1 }).limit(20);
Then clicking the refresh icon
Now gives us the full list again:
If we follow the guide, we can remove lines 234/235:
Save and reload, now bikes show the image in the bike details page:
If we stop debugging (the red square), then it should restore to the current helm deployed service:
Refreshing the page indeed shows just the service was redirected - the existing app takes back over:
I tried to replicate with a fresh expressjs app. It ran on port 3000 but no go.
That's when I realized that I was redirecting the backend service to a frontend express app. We would need to make some changes...
Trying WSL (Windows Subsystem for Linux)
Here I fired up a quick express app
Then copied over a basic Dockerfile
$ cat Dockerfile
FROM node:lts-alpine
ENV PORT 80
EXPOSE 80
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
As well as the two vscode files:
$ cat .vscode/launch.json
{
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch via NPM with Kubernetes (Preview)",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"start"
],
"port": 9229,
"preLaunchTask": "bridge-to-kubernetes.service"
},
{
"type": "node",
"request": "launch",
"name": "Launch via NPM",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"start"
],
"port": 9229
}
]
}
$ cat .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "bridge-to-kubernetes.service",
"type": "bridge-to-kubernetes.service",
"service": "bikesharingweb",
"ports": [
3000
]
}
]
}
The key differences to note are the script uses “start” (instead of “debug”) and the service is not “bikes” (which was the API backend), rather “bikesharingweb”, the web front end service.
I did need to relaunch vscode to get it to notices those files and re-install the bridge extension:
Don’t forget, if you are in a new system, to set Kubernetes to the right namespace
Note, if you get an error likeWaiting for EndpointManager to come up ...
Error getting authority: Error initializing authority: Could not connect: No such file or directory
This is because Windows requires the EndpointManager to be installed.
While WSL cannot seem to trigger the install, following these exact same steps in Windows (on the same host), did trigger the installer:
Windows
Afterwhich, the Debug console worked just fine and we see we properly re-routed the app locally:
However, even after installing the first time with Windows, WSL could not trigger a pass-through:
Which leads me to believe that they have some OS dependencies on re-routing traffic that don’t yet jive with WSL.
Summary
Using their writeup as a start point, I dug into the sample app on Mac, WSL and Windows and then went on to redirect the frontend service to a wholly different Express app.
While WSL seems broken for the time being, Mac and Windows worked just fine. This can be really useful for debugging just part of a large containerized service, such as the bike app, wherein we did not need to replicate the MongoDB locally or create a whole local cluster just to debug one service.