 Okay. Here's the part where I tell you, I am definitely going to post the slides on my site tonight, but there are no slides. This is only live demos. So this is the one and only slide, this notepad. You can grab me on Twitter at Rob underscore rich, and you can grab the code that we're about to build today on GitHub. It is on GitHub right now at Docker in Windows containers. But you can get to it more easily, click on Rob rich.org, click on presentations, and here's this presentation. The code is right there. While we're here, let's click on about me and see some of the things that I've done recently. I'm a Cyrold developer advocate, a Microsoft MVP, a Docker captain, a friend of red gate. And AZ give camp is really fun. AZ give camp brings volunteer developers together with charities to build free software. We start Friday after work, Sunday afternoon we deliver that completed software to the charities. Sleep is optional caffeine provided. If you're in Phoenix, come join us for the next AZ give camp. Or if you'd like a give camp here in Boston or wherever you're connecting from, give me up on email or on Twitter. Let's get a give camp in your neighborhood too. I do a lot with Docker consulting and training. And one of the things I'm particularly proud of, I replied to a dot net rocks podcast episode. They read my comments on the air. They sent me a mug. So there's my claim to fame, my coveted dot net rocks mug. So let's build up a solution. Now here's our folder with all of our content in it. Let's build a new project. I'm gonna build a new folder net app one. And inside visual studio, let's build up a project as if containers didn't exist. So let's create a new project. I'm gonna come down here and pick an ASP.net core MVC app. Where should we put it in that folder that we just created it? I will call the solution net app one and web application one sounds great. So this will create our website and a typical ASP.net solution that we might have probably has a website and probably a windows service together with it. So dot net five, we will uncheck enable for Docker. We'll do that separately. And let's build it. And so we typically have a website, but then we also have a backend service that might do some additional work. Maybe it's resizing images or running reports. So after this finishes building, let's go build one of those, a dot net service. The demo gods being with us. Hopefully this gets us, gets this project created. This is gonna be fun. If this doesn't work, we can definitely craft this from the command line, but it looks like we've got our solution starting to jump into place. That's perfect. Here's our web application and we can see our controllers and our views. We're just now waiting for visual studio to finish. There we go. Now that we've got our website created. Oh, I thought visual studio was crashing. Now that we've got our website created, let's go build that backend Windows service. Now I'm going to choose to add a new project here. And inside of ASP.net, we are inside of a dot net project. We often build a Windows service. But here inside of dot net core, we don't have a Windows service project, but what we do have is a worker service project. And the beauty of worker service is it is actually cross platform. So we can use this as a system D service inside of Linux or a Windows service inside of Windows, worker service one, that sounds great. Dot net five, we'll leave enable Docker unchecked again. And the cool part about this is that all of the base classes associated with this are kind of gone. We don't need to spin up a service. We don't need to talk about starting and stopping the service here in program.cs. We just go grab this worker and the worker will do what we need to do. Now worker does descend from a base class, this background service base class, but that worker just says while we're not stopping, let's go do some work. Perfect. So now how would we containerize this application? Well, our first stop is to create a dot Docker ignore file. If you've ever used git and you have a dot git ignore, this will be really similar. So let's add a new item. Let's add a text file and let's create this text file as dot Docker ignore. Now much like a git ignore file, this Docker ignore file specifies all the things that we don't want in our project, bin, obj, star.user, star.suo, the dot vs folder. Those are all the things that we don't wanna end up in our container. We also don't want dev dependencies like this launch settings.json, launch settings.json and another dev dependency, app settings, app settings.development.json. We don't need development settings in our container. Okay, so now we've got this Docker ignore, that's all the things that we don't ever want in our container. Next, let's build up a Docker file. I'm gonna add a new item and I will specify this is a text file and I will call it Docker file. Now it's important that this be Docker file, not Docker file dot txt and it looks like Visual Studio helps me with that. So let me rename this and remove the dot txt part. Okay, so now we've got this Docker file. Now in our Docker file, we wanna specify the steps, the configuration as code to get from our source code into a running container. So from a command line, we would do dot net restore, dot net build, we'll build in release mode. We probably want to specify the, nope, then we'll say dot net test and dot net publish. We'll publish in release mode to the best folder. And actually in a project in a worker service, we probably are just building into the disk folder rather than publishing, publishing is a web thing. Okay, so now we've got the steps that we need to do. Now how do we wrap this in a Docker file? Well, Docker has a few steps. We'll have a from, we'll have some copy commands, we'll have some run commands and we'll have some cmd, what we use to start up the container. So our first stop, well, what's really cool is with these four commands, we can probably get the hang of most Docker files. So let's go build one up here on Docker hub. Let's take a look at dot net. And when we get here, this is a meta repo, we can look at all of the different repos we're after the SDK, the one that has the build tools. So let's pop this open, we are in dot net five. And so we can grab this one, mcr.microsoft.com slash dot net SDK five. Back in Visual Studio, from that. Okay, now let's copy all of the content from the directory where we'll run our build command, that's this directory, into the current directory in our Docker file. Where is that? I'm gonna say work dir slash src and it will automatically create and change into that directory. Okay, now we need to run some stuff. Well, we have all of our commands here. So let's, oh, I just used the, there we go. We'll run this, we'll run this, we'll run this. And the last step then is cmd. It's in the dist folder. So let's say dot net work, worker service dot DLL, worker service one dot DLL. There we go. And it is in the dist folder. Well, we've got a Docker file. There's a few things we can tidy up. Right now we're, we have the build tools associated with our production runtime image. Let's instead separate this in half and we'll use the runtime image that doesn't include the build tools. Runtime, I got this here from Docker hub when I was looking at the runtime repository. And now instead of dist, we'll say app, but we do need to copy content. Let's copy the content from the dist folder into this current folder. But right now it would copy it from our local machine right here. Let's copy it instead dash dash from equals build and we'll name this one as build. Now we have two steps. Here is our build air quote server image and here is our production runtime server image. Great. Now we have a lightweight image that is a lot smaller. It doesn't include the build tools. It only includes the content that we built, not even our source code. The other thing that we can do is right now we're copying in the content no matter what. When content changes, we will invalidate this layer and then we will re-restore our NuGet packages. I really want to restore my NuGet packages first and copy my content second. Now I do need to copy in the manifests. Let's copy worker service one.csproj into the current folder. And now we will copy in our manifest, then we'll restore NuGet packages, then we'll copy in everything else and do all the steps. We now have a Docker file that's ready to go. So dircd to netapp1, cd netapp1 again, cd to worker service one and let's Docker build and what did I tag this one? We'll tag it as netapp1svc, 0.1. Okay, so this will do all of those steps. The configuration is code. Restore our NuGet packages, run our .NET build and ultimately publish that content into the new image that will include only our built assets. So here's that .NET restore. It's restoring all of our NuGet packages. This is the slowest part of this whole demo as we download various things from NuGet. Once we've got our packages downloaded, then we can quickly build, run all of our tests. Here's our build. Did we typo anything in our content? Let's see. Nope, looks like our build is gonna pass. Let's see, let's see, it's almost gonna pass. Okay, our build passed, let's run all of our tests. We don't have any failing tests because we don't have any tests, so that's succeeded. Let's copy the content into the new image and we're ready to go. Docker run, let's run this app and we've got our Windows service into a container. Now the cool part is it will spin up and then every second it will tell us it's doing the work. That's great. Now let's do the same thing for our website. We already have this docker file and dot docker ignore. Let's copy those, set them in place here in our web application and now we can start in customizing this for our web application. Let me show all files. I'll include these, there we go. Our docker ignore, anything we need to tweak here? Nope, that looks pretty good. In our docker file, we're no longer building a runtime, we're building ASP net and so now we want this to be web application one. This one will also be web application one and we do have a publish step now. So let's publish this content. This is not that unlike right click publish right here where we'll go copy all of our content, both our DLLs and our CSS files and JavaScript files but we're doing it inside of a automated build here. So we've got that content into the disk folder and we'll flip over into our ASP net image and we'll do that. There is one more piece that we need to do. We need to add some environment variables that tells ASP net to run in production mode and to run on port 80. There we go. So let's our workers still doing the thing. Let's get into the web application one folder and we will docker build net app one web and we will build from the current folder. Okay, so now we're doing the same thing that we did with the worker service. We're running through that configuration as code. Our first step, let's do a .net restore and grab all of those new get packages. That's perfect. Once we have our new get packages in place we'll do a regular .net build, a .net test validate all of our unit tests work as expected and finally a .net publish into the other image. Now what's really cool about this is that if ever we have some failing unit tests or if our build or publish step fails then the docker file will stop there and we won't end up building this new image. We can't get to a new image if we have failing tests. That's perfect. So back in our command line, let's take a look at this. We're building our solution. Let's see, is our source code gonna work? Did we make any typos in our CS prods or in our C sharp controllers or in our razor views? Let's see, this is so exciting. It's gonna go, it's gonna build. I love that it shows me how long it's been running this step by the way. Ooh, no errors, no warnings. Great, let's run all of our tests. There aren't any failing tests because, well, there aren't any tests. So now we're in the publish stage. Let's collect all of those DLLs and JavaScript in CSS files and publish those into that other container and it looks like let's do that publish. It's almost there. It's almost there. Oh, this is so much fun. So now that we've got our web application published, we'll let's copy that into the new image, the really small image, and now we've got our website ready to go. So we can say Docker run, I'm gonna say let's map port 5000 for my local machine to port 80 inside the container and let's do this net app one dash web dot colon V0.1. So now we're running this website on our inside of Docker and we'll spin up our website. Let's go to localhost 5000 on our machine, localhost 5000 and we're able to pull up this website. That was cool. Now that's if we build the Docker files ourselves, let's see what tooling ASP.net has to make this process a little bit easier. Let me hit control C. Oh, nope. Docker image, Docker container list. Let's go kill off those two containers. Docker container RM minus FBE0A and Docker container RM minus F8B85. Okay, so now we've stopped those two. Our website is no longer running. Our worker service is, nope. Oh, oops if I spell it right, container RM. Now our worker service will stop in a second. There we go. And we've just shut down those servers. Perfect. So we just took this for lack of better term, Brownfield application and we were able to build it into Docker. Let's create a new project. And in this case, we will start with our ASP.net website. And I will build this in the net app two folder here in the side of our source code and web application one is fine. Let's click next. And now we get to pick the options associated with building this container. Now, this time I'm going to choose, yes, I do want to build in Docker. I'm asked to pick the operating system and we didn't have time for this demo, but these containers work equally well on Windows and on Linux because we didn't specify anything that was operating system specific. We'll choose Linux in this case because that's where our containers are running. But if we wanted to, we could flip over to Windows containers and run these in Windows as well. Okay, now that we've got our website created, let's also go create our worker service, add a new project and worker service. There we go, worker service one. And in my haste, I am going to accidentally uncheck enable Docker support and let's create that project. So we have now this Docker file and the dot Docker ignore file generated for us. Now, the ones that we built were a lot simpler than this. This one is a little bit confusing, but we can kind of see what's going on. It starts with the SDK, it copies in our CS Proj and then it does a dot net restore, copies in everything else and does a dot net build, dot net publish. We don't have a dot net test command in this, but then finally we go grab all of our content, copy it from the publish one into this new app and we're ready to go. So we can see the same steps there. Now, arguably the one that we built was a lot simpler, but this one gets it done if you wanna go really fast. Okay, so now how about this other project? Well, I want to add Docker support to it. I'm gonna right click here on worker service and I'm going to say add, much like I would add a new item or anything else, I'm gonna say add Docker support. Now, when I choose Docker support, it will ask me that operating system question. We're on Linux containers right now, so let's pick that. And we get the Docker file and the Docker ignore file built for us. That's perfect. Now, let's see if we can add container orchestration support. I'm gonna right click on web application one and I'm gonna choose add. And instead of Docker support, I'm gonna choose container orchestrator support. Now I'm after a Docker compose file in this case, but we can also use Helm if we wanted to. Let's choose Docker compose and it will create this new project that is this Docker compose file. Now, it isn't actually a folder inside of our solution. If we pop open NetApp 2, we can see that we have the web service and the worker service, but we don't have a Docker compose folder. It's more of a pseudo project for Visual Studio's benefit. That's okay. Oh, I got Visual Studio really mad. Now let's add this second worker service into this Docker compose file. I'm gonna right click on the worker service and I'm gonna choose to add container orchestrator support. I will choose Docker compose as well. And now I've got this great developer time set up with this Docker compose file. Now the beauty here is that we've got both our website and our windows service running inside of Docker compose. If we pop open this Docker compose file, we can take a look at the content that it used to build this. Looks like Visual Studio is taking its time here. Let me pop it open in VS code instead. Let's take a look at that Docker compose file. We can see that this Docker compose file includes the details both for our website and it looks like it's still working on the windows service. And if we specify Docker registry, we can automatically get that support as well. So once we've got this Docker compose file, is Visual Studio really stuck? There we go. Now that we've got this Docker compose file with both pieces, website and worker service, let's start up our application. I'm going to set a break point here in my home controller. I'm also going to set a break point here inside my worker service. And now you notice here that I have my Docker compose project set. Now I could flip it over to working with a particular project if I wanted to, but using this Docker compose file, I'm going to be able to debug both pieces simultaneously. So now it's going to do the build and start processes. We'll see that these are actual Docker commands here. These aren't, you know, Visual Studio Docker. This is actual Docker. It's doing a Docker pull and it looks like all of those are in place. And now it's going to do a Docker build for both the website and for the worker service. And this does take a minute, but once we've got this in place, then we'll be able to debug both applications simultaneously. Now, one of the things that it's doing is it's actually grabbing VS debug. VS debug is a really elegant mechanism for debugging inside of containers. If you look here at the Docker compose override file, it kind of sets up mechanisms for being able to grab our user secrets, grab our certificates and set those in place as well. So in this case, it's pulling the images. It already has them, but it just wants to double check. It went to go look for another container, not finding it. It didn't need to delete it and that's totally fine. Next up, it will start to build this project. Ooh, yes, I've got Windows file sharing enabled because I've built this content on my Windows file system instead of inside WSL. It's noting that I might have a little bit less performance. That's fine. In this case, it worked out really well. Okay, preparing containers. So now it's gonna do that Docker build process. I love that Docker, it's running actual Docker commands. Here's a Docker compose F, passing in all of the Docker compose things and where's my command at the end, config. So we've got our Docker compose file in place and now we're building our project. It looks like it's outputting the Docker compose file as well. Building worker service one. So here's all of those configuration as code steps. We'll grab our Docker file, we'll restore our NuGet packages into that image, we'll do our .NET build and ultimately produce that final debug image. Now in this case, it's running in debug mode because we're debugging rather than building that production runtime image, but that's totally fine. That'll allow us to set breakpoints. It looks like my time is running short, so you may need to, this may be an exercise for the user to be able to do the final debugging inside of Visual Studio. This has been a lot of fun being able to show you building Docker containers from ASP.NET. You can grab the code that we built right now on GitHub and hit me up on Twitter as you experiment with this and let me know how your journey is going or if you get stuck, shoot me a question on Twitter. That'll be a lot of fun. For those of you who are watching this video live, I'll grab you in that spot where the conference is designated. Thanks for watching everybody. Thank you so much, Rob. This has been an awesome talk, very well prepared. I wish that we had time for questions, but we don't. If you would like to continue the conversation, you can continue in the breakout room, which I have linked here. You should probably have a full hour because the next talk doesn't start until think three. And the cool part is I'm actually speaking in that hour. So if you want to learn more about Cypress testing, hit me up there. All right. Thank you so much, Rob. Have a great day and enjoy the rest of DevCon. Thanks everyone. Bye.