Containers, Kubernetes and DevOps for an Old as Dirt Developer (Or DevOps Nirvana with Kubernetes), Part 3/5

Table Of Content

Part 1: Intro to containers, Kubernetes and DevOps

Part 2: Getting started (setting up my dev environment)

Part 3: My first app –  .net core, vscode, WSL, containerize and deploy to local k8s AND debug it while running in k8s??? GTFOH

Part 4: Deploy to k8s in the cloud (Azure Container Service – AKS) and debug from VSCode (coming soon)

Part 5: Set up CI/CD pipeline for Kubernetes using DevOps best practices (comming soon)

My first app –  .net core, vscode, WSL, containerize and deploy to local k8s AND debug it while running in k8s??? GTFOH

This blog post starts out with really basic stuff. I have never created a dotnet core app running in WSL while debugging from VSCode. I had no idea how to do any of this stuff. I know, I know. I’m SO behind the curve on all the linux-y goodness and containers. But now that I’m in this world, It’s pretty freaking cool and I can’t wait to REALLY dive in! Anyway, feel free to jump ahead to all the container awesomeness if you are already familiar with dotnet core on WSL and debugging from VSCode.

For my first container based app, I figured I would use an asp.net core app (I am after all, super familiar with c#). The plan is to do this incrementally.

  1. Create a .net core app, run in WSL, debug from VSCode (which is running from windows)
  2. Containerize this, deploy to my local kubernetes cluster, then debug from VSCode
  3. Deploy this to k8s in the cloud (Azure Container Service – AKS), then debug from VSCode (coming soon, part 4)
  4. Set up CI/CD pipeline using DevOps best practices deploying this app to my k8s in the cloud (Azure Container Service – AKS, coming soon, part 5)

Creating the .net core app, run in WSL, debug from VSCode

Installing all the nonsense to start writing/debugging a .net core app in WSL

All right, to do all this, I’m going to need to .net core SDK installed on Windows, VSCode installed on Windows, .net core SDK in WSL, and also the debugger in WSL. I already have .net SDK core and VSCode installed on my windows machine but if you need to do that, here is a link to install VSCode (Totally awesome editor. It is rapidly become my go to editor of choice these days). To install .net core SDK on windows, click here.

To install the .net core SDK in WSL, I followed the directions here.

Next I installed the vscode debugger for .net. Following these directions will install the debugger at ~/vsdbg/vsdbg

  1. sudo apt-get install unzip
  2. curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/vsdbg

Creating and debugging my first console app running in WSL using VSCode

Like I said earlier, this section is super basic. Feel free to skip this and jump to the fun container parts.

From with WSL, you can easily reach into the windows file system (/mnt/<drive>/<whatever>). And from windows, you should never reach into the WSL file system. If you really want to now why, here you go.

So in order to run in WSL yet still edit and debug from windows and VSCode, I’m going to put my code in C:\mysource\myfirstconsoleapp or from WSL, /mnt/c/mysource/myfirstconsoleapp

 

  1. From WSL
    cd /mnt/c/mysource
  2. Create the dotnet core console app
    dotnet new console –-name myfirstconsoleapp
  3. cd to the console app
    cd myfirstconsoleapp
  4. run the app
    dotnet run
    image

Woo. Looks like I just ran my first dotnet core app on WSL. Let’s edit this. From windows, I launched VSCode and opened up c:\mysource\myfirstconsoleapp. Then modified the file Program.cs
image
From WSL, i do a dotnet run
image
Cool. Proof I’m running in Linux.

From VSCode, I press F5 and my debug window shows
image
Hmmm… windows. Makes sense, I am in VSCode on windows after all. Let me jump into the terminal tab, type in bash to change to a bash shell and from there type in dotnet run
image
Ok… cool. Makes sense. Running in linux. Press F5 and….
image
Windows… Hmmm… so how do I debug this running in WSL? A quick google search…. bam! We need to modify the launch.json under .vscode directory to launch in WSL and attach to the debugger in WSL.

I changed my launch.json to:

{
   // Use IntelliSense to find out which attributes exist for C# debugging
   // Use hover for the description of the existing attributes
   // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
   "version": "0.2.0",
   "configurations": [
        {
            "name": ".NET Core WSL Launch (console)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            // If you have changed target frameworks, make sure to update the program path.
            "program": "/mnt/c/mysource/myfirstcoreapp/bin/Debug/netcoreapp2.0/myfirstcoreapp.dll",
            "args": [],
            "cwd": "/mnt/c/mysource/myfirstcoreapp",
            // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
            "console": "internalConsole",
            "stopAtEntry": false,
            "internalConsoleOptions": "openOnSessionStart",
            "pipeTransport": {
                "pipeCwd": "${workspaceRoot}",
                "pipeProgram": "bash.exe",
                "pipeArgs": [ "-c" ],
                "debuggerPath": "~/vsdbg/vsdbg"
            }
        },
        {
            "name": ".NET Core WSL Attach",
            "type": "coreclr",
            "request": "attach",
            "processId": "${command:pickRemoteProcess}",
            "pipeTransport": {
                "pipeCwd": "${workspaceRoot}",
                "pipeProgram": "bash.exe",
                "pipeArgs": [ "-c" ],
                "debuggerPath": "~/vsdbg/vsdbg",
                "quoteArgs": true
            }
        }
    ,]
}
launch.json

Pressed F5….
image
Awesome. I am now debugging a dotnet core console app running in WSL from VSCode (launched from windows). I can now set breakpoints, step into, watch variables, and all the debugging goodness that I need.

To get more info on debugging .net core using VSCode, check out this. There will be links in that article that will take you to configuring your launch.json for C# Debugging. From there, you will find more links to attaching to a remote process.

Time to do some container dev!

Some DevOps musings

Ok, looks like I have all the pieces I need. Time to start doing some container dev. Since I’m a DevOps person (emphasis on Dev) , I’m always thinking about how the workflow is going to be.  And at first glance, it will be something like this:

  1. Create a .net core MVC app (doesn’t have to be .net core, that just happens to be my personal language of choice these days)
  2. Add features/debug locally
  3. When I feel like the app is ready, create a docker image
  4. Deploy that image to a docker registry
  5. Push that image from the docker registry to my local k8s
  6. Debug my app running in k8s (somehow)
  7. Rinse and repeat until satisfied and then check in to source control
  8. Once checked into source control, my CI/CD pipeline kicks off which builds my app, runs unit tests, creates a docker image, pushes that docker image to a docker registry
  9. Now my release can grab the image, and push it into k8s, maybe to a dev > canary > all the way to prod type of pipeline

A couple of pain points in this workflow. Steps 1-7 is my dev inner loop. That’s a lot of steps to do every time I change my code and want to debug. Also, I’m now debugging locally, then deploying to k8s locally and then debugging again, before sending it through a CI/CD pipeline which will ultimately deploy my image into k8s that will be different than my local k8s, which means potentially more debugging!

Also, if the infrastructure within k8s for my app changes, someone needs to go in and tweak things. Like, enabling SSL, or load balancing, or tweaking memory and cpu restrictions. Do they have to do that manually? That totally bypasses my pipeline and makes my skin crawl. Reverting to prior releases become difficult because those infrastructure changes aren’t versioned with my code and images.

And finally, this requires me to have deep knowledge on Docker and Kubernetes. Nothing wrong with that but personally, I love to sling code. Containers/Docker/Kubernetes is cool but I don’t want to spend too much effort learning all that. Because I WANT TO SLING CODE!!!!

And this is where Helm and Draft come in. They solve these pain points for me. Helm is a package manager for Kubernetes. Or said another way, helm can act like Infrastructure as Code for me with helm charts and then using helm, I can also deploy specific versions of my app. Both the code (the docker image) and the k8s infrastructure. And of course, rollback becomes ridiculously simple since everything is encapsulated. Sure sounds like we’re getting awfully close to DevOps Nirvana.

Draft is a development tool that makes all of this nonsense easy for me. I can take my app code and from the command line type draft create. Draft will auto detect what language I’m working in and will then create for me a Docker file (which describes how to create my image) and will generate for me a set of helm charts (which describe the k8s infrastructure for my app).

The command draft up will create my docker image, deploy that image to a docker repository, then push my app using helm and my helm charts to my k8s. My k8s can be running locally on my laptop, or I can even spin up k8s in Azure that will now be identical to production and use that in my inner dev loop!!!! So now, my DevOps workflow will look like this:

  1. Create a .net core MVC app (once again, does not have to be a .net core app) and then do a draft create
  2. Tweak my code in my editor (me being dev, I would want to work on my app code while my team mates with kubernetes/helm/ops knowledge, looking at you @jldeen, would tweak the helm charts to modify the infrastructure)
  3. Build, create my image, deploy to registry, push to k8s with draft up
  4. Debug my app running in k8s  (remember, this can be a local k8s on my laptop or an identical cluster in the cloud) with draft connect
  5. Rinse and repeat until satisfied and then check in to source control
  6. Kick off my ci/cd pipeline which builds my app, runs unit tests, creates a docker image, pushes that image to a docker registry, create a helm package that references this image
  7. Release management can now deploy using helm (image + infrastructure) to my environments (dev > canary > prod or something like that)

This simplifies things and cleans up the DevOps-y-ness tremendously! My inner dev loop has been simplified. Edit code, draft up, draft connect. This builds my app and deploys it into k8s that can now be identical to production and debugs. Rinse and repeat and then check in code. And best of all for a lazy developer like me, I need a minimum amount of docker/kubernetes knowledge.

The infrastructure within k8s is also captured and stored in source control as helm charts so it is versioned right along side my code. Checking in now deploys my image and infrastructure through my pipeline all the way to prod.

Create app, deploy and debug in a local kubernetes cluster

Like I said earlier, we’re going to do this incrementally. For this blog post, we will build, deploy and debug to a local Kubernetes cluster. In my next blog post, we will build, deploy and debug into a Kubernetes cluster in Azure Container Service – AKS that will be identical to our production pipeline

Create and containerize .net core mvc app

First let’s go and create a .net core mvc app.

  1. Open a powershell console, browse to the directory for your app. At this moment, Draft breaks if there are upper case letters in the directory name. My share code directory is c:\mysource. From here type in dotnet new mvc –name myfirstmvc
    image
  2. Change directory to the myfirstmvc directory
    cd myfirstmvc
  3. Launch code from that directory
    code .
  4. This should open VSCode in that directory. VSCode should detect this is a .net core app and ask to add the necessary assets. This should add a .vscode directory with a launch.json and a tasks.json file
  5. From within VSCode, open up the terminal and change it to bash by typing bash (you can default your terminal to bash if you want. Scott Hanselman has a great post on customizing your terminal for WSL development. Jessica Deen also has a sweet terminal that she uses described here.
    image
  6. in the bash terminal, type draft create
    image
    Notice how it created a charts directory and also a Dockerfile as well as a draft.toml file.
  7. Now in the terminal, type draft up. This will create my docker image, deploy to a registry (if it is configured), and then the image is pushed to k8s (at this moment, this is my local k8s)
    image
  8. To browse my app running in kubernetes, I don’t have to do any docker/kubernetes commands to open up that port or to route to the right place, I just type in draft connect
    image
    Notice the proxy is on localhost:51131
  9. Launch browser from windows, point to localhost:51131
    image

Woohoo. I containerized my app and created helm charts and deployed to my local kubernetes cluster, all with the commands draft create, drat up and draft connect! Now I just need to configure this for debugging.

Enable debugging the running image in a kubernetes cluster with VSCode

To enable debugging, we need VSCode to attach to a debugger running in the container. This means I need to modify my Dockerfile to install the .net core debugger in my image. I also need to modify my Dockerfile to build my app in debug mode. Finally, I need to modify the launch.json file so that pressing f5 will attach the debugger to a process running in the image.

  1. Modify the Dockerfile to install procps tool and then install .net core debugger by adding the following to docker file
    # install ps
    RUN apt-get update && apt-get install -y procps
    # Installing vsdbg debbuger into our container
    WORKDIR /vsdbg
    RUN apt-get update \
        && apt-get install -y --no-install-recommends \
        unzip \
        && rm -rf /var/lib/apt/lists/* \
        && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg
    Dockerfile

    image
    Also, we need to make sure we compile in Debug mode instead of Release mode so we have the pdb files.

  2. Modify the launch.json to use docker to connect the debugger to a process running in the image
    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": ".NET Core Remote Attach",
                "type": "coreclr",
                "request": "attach",
                "processId": "${command:pickRemoteProcess}",
                "pipeTransport": {
                    "pipeProgram": "docker",
                    "pipeArgs": [  "exec", "-i", "b04e852d5964" ],
                    "debuggerPath": "/vsdbg/vsdbg",
                    "pipeCwd": "${workspaceRoot}",
                    "quoteArgs": false
                },
                "sourceFileMap": {
                    "/app": "${workspaceFolder}"
                }
            }
        ,]
    }
    launch.json

    image
    In order for this to work, we do need to pass in the container ID under pipeArgs.

  3. Time to make a new image and deploy this image (with debugging enabled). From the terminal, draft up
    image
  4. We do need the container id for the image that we just deployed for the debugger to work so let’s do a docker ps in the terminal.
    image
    capture the container id
  5. Bring up launch.json and paste that id into the pipeArgs
    image
  6. Connect to the app with draft connect
    image
  7. Bring up the HomeController.cs and set a breakpoint in the Index() method
    image
  8. Press F5 to start the debugger, pick the process in the running container you want to attach to
    image
  9. Now let’s bring up a browser and point it to app (look at your draft connect to find the port, in my example it’s port 51457)
    image
    and….. BAM! look at that, hit my breakpoint!!!!
    image

We have now containerized our .net core app, built, created an image and deployed it into a local kubernetes cluster. And we can debug from VSCode the image that is running in our kubernetes cluster!!!!.

The process of getting the container id with docker ps and then pasting that into my launch.json seems a bit clunky and I’m sure there’s a better way. I’m pretty slammed with prepping for Build conference right now but as soon as that’s done next week, Jessica Deen and I will see if we can tweak that and make this process nicer. Maybe even with Draft. 🙂

Now all I need to do is check in all this code to source control, ping Jessica to tweak the helm charts to set up DNS, SSL, load balancing, tweak the size of the CPU and memory etc etc etc. And then we can send it on down a CI/CD pipeline.

In my next blog post, I’ll show you how to set up and do all this with a Kubernetes cluster hosted in Azure. This makes me super excited because now, as a dev, I can edit my code locally, but have my development environment and my debugging done in a Kubernetes cluster up in Azure that has access to all the app’s production dependencies. My Dev and Prod environment will be the same!

Next > Part 4: Deploy to k8s in the cloud (Azure Container Service – AKS) and debug from VSCode (coming soon)

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *