It’s been a while since our Christmas Docker recipe. And there has been some major and important changes in the latest ASP.NET Core 1.0 RC2 release.

Let’s make a quick step-by-step refresh to create and run ASP.NET Core 1.0 RC2 applications in Docker.

Prerequisites

To follow this step-by-step guide, you’ll need:

  • .NET Core 1.0 RC-2 (Installer)
  • HomeBrew (for OS X, users)
  • NPM (brew install npm , for OS X users. Windows users can Download NPM as part of Node.JS)
  • Bower (npm install -g bower)
  • Yeoman (npm install -g yo)
  • ASP.NET Generators for Yeoman (npm install generator-aspnet)

Step 0: Getting Docker ready

Get started with Docker for Mac, Windows or Linux

Once installed Do you have Docker VMs? Check it out:

$ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS innotech * parallels Running tcp://10.211.55.27:2376 v1.11.1

infotech is my Docker machine name. If you are using the built-in machine that comes with Docker Toolbox, your machine name would be “Default” instead:

Docker machine not running? Start and eval:

$ docker-machine start innotech Starting "innotech"... (innotech) Waiting for VM to start... Machine "innotech" was started. Waiting for SSH to be available... Detecting the provisioner... Started machines may have new IP addresses. You may need to re-run the docker-machine env command. $ eval $(docker-machine env innotech)

And check Docker is ready to go:

$ docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 317 Server Version: 1.11.1 Storage Driver: aufs Root Dir: /mnt/sda1/var/lib/docker/aufs Backing Filesystem: extfs Dirs: 133 Dirperm1 Supported: true Logging Driver: json-file Plugins: Volume: local Network: null host bridge Kernel Version: 4.4.8-boot2docker Operating System: Boot2Docker 1.11.1 (TCL 7.0); HEAD : 7954f54 - Wed Apr 27 16:36:45 UTC 2016 OSType: linux Architecture: x86_64 CPUs: 1 Total Memory: 994.9 MiB Name: innotech ID: Debug mode (server): true File Descriptors: 25 Goroutines: 45 System Time: 2016-05-31T21:31:34.598910446Z EventsListeners: 0 Init SHA1: Init Path: Docker Root Dir: /mnt/sda1/var/lib/docker Labels: provider=parallels

Step 1: Create a ASP.NET Core 1.0 project

Let’s create our new ASP.NET Core 1.0 project with Yeoman:

$ yo aspnet ----- | | .--------------------------. |--(o)--| | Welcome to the | ---------´ | marvellous ASP.NET Core | ( _´U_ ) | 1.0 generator! | /A\ '--------------------------' | ~ | '..'_ ´ |° ´ Y ? What type of application do you want to create? Web Application Basic [without Membership and Authorization] ? Which UI framework would you like to use? Bootstrap (3.3.6) ? What's the name of your ASP.NET application? WebApplicationBasic create WebApplicationBasic/gulpfile.js create WebApplicationBasic/Dockerfile create WebApplicationBasic/.bowerrc create WebApplicationBasic/.gitignore create WebApplicationBasic/bower.json create WebApplicationBasic/appsettings.json create WebApplicationBasic/package.json create WebApplicationBasic/project.json create WebApplicationBasic/Program.cs create WebApplicationBasic/Properties/launchSettings.json create WebApplicationBasic/README.md create WebApplicationBasic/Startup.cs create WebApplicationBasic/web.config create WebApplicationBasic/Controllers/HomeController.cs create WebApplicationBasic/Views/_ViewImports.cshtml create WebApplicationBasic/Views/_ViewStart.cshtml create WebApplicationBasic/Views/Home/About.cshtml create WebApplicationBasic/Views/Home/Contact.cshtml create WebApplicationBasic/Views/Home/Index.cshtml create WebApplicationBasic/Views/Shared/_Layout.cshtml create WebApplicationBasic/Views/Shared/Error.cshtml create WebApplicationBasic/wwwroot/css/site.css create WebApplicationBasic/wwwroot/css/site.min.css create WebApplicationBasic/wwwroot/favicon.ico create WebApplicationBasic/wwwroot/images/banner1.svg create WebApplicationBasic/wwwroot/images/banner2.svg create WebApplicationBasic/wwwroot/images/banner3.svg create WebApplicationBasic/wwwroot/images/banner4.svg create WebApplicationBasic/wwwroot/js/site.js create WebApplicationBasic/wwwroot/js/site.min.js I'm all done. Running npm install & bower install for you to install the required dependencies. If this fails, try running the command yourself. Your project is now created, you can use the following commands to get going cd "WebApplicationBasic" dotnet restore dotnet build (optional, build will also happen when it's run) dotnet run

Restore and build our project:

$ cd WebApplicationBasic/ $ dotnet restore log : Restoring packages for /Users/Sergio/Repositories/dotnet/WebApplicationBasic/project.json... info : GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethostresolver/index.json info : OK https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethostresolver/index.json 554ms info : GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethost/index.json info : OK https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethost/index.json 582ms log : Restoring packages for tool 'Microsoft.AspNetCore.Razor.Tools' in /Users/Sergio/Repositories/dotnet/WebApplicationBasic/project.json... log : Restoring packages for tool 'Microsoft.AspNetCore.Server.IISIntegration.Tools' in /Users/Sergio/Repositories/dotnet/WebApplicationBasic/project.json... info : Committing restore... log : Writing lock file to disk. Path: /Users/Sergio/Repositories/dotnet/WebApplicationBasic/project.lock.json log : /Users/Sergio/Repositories/dotnet/WebApplicationBasic/project.json log : Restore completed in 5086ms. NuGet Config files used: /Users/Sergio/.nuget/NuGet/NuGet.Config Feeds used: https://api.nuget.org/v3/index.json $ dot net build Project WebApplicationBasic (.NETCoreApp,Version=v1.0) will be compiled because expected outputs are missing Compiling WebApplicationBasic for .NETCoreApp,Version=v1.0 Compilation succeeded. 0 Warning(s) 0 Error(s) Time elapsed 00:00:04.9675153

Fixing some yo aspen issues (Not necessary in RTM)

UPDATE: This pull request fixes the issue. If you have the latest Yeoman ASP.NET Generator, you can skip it.

The current ASP.NET Generator Dockerfile is not ready in RC-2.

So:

First of all, let’s create our own Dockerfile. With Visual Studio Code (or your preferred editor) open the “Dockerfile” present in the project root folder, and put the following content:

FROM microsoft/dottiness COPY . /app WORKDIR /app RUN ["dotnet", "restore"] RUN ["dotnet", "build"] EXPOSE 5000/tcp ENTRYPOINT ["dotnet", "run", "--server.urls", "http://0.0.0.0:5000"]

With this Dockerfile, we are instructing docker to use the latest dot net image, copy our app into the container, restore the package and build in image creation time. Our Kestrel server will be listening at port 5000. Has happened in RC1, we need to force Kestrel to listen from any source with the command –server.urls.

Including the –server.urls argument is vital to allow inbound connections from outside container. If not, Kestrel will reject any connection that is not coming from the container, something not really useful…

Now, open the project.json and add the “Microsoft.Extensions.Configuration.CommandLine” package:

Project.json: { "dependencies": { "Microsoft.NETCore.App": { "version": "1.0.0-rc2-3002702", "type": "platform" }, "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final", "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final", "Microsoft.AspNetCore.Razor.Tools": { "version": "1.0.0-preview1-final", "type": "build" }, "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final", "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final", "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final", "Microsoft.Extensions.Configuration.CommandLine": "1.0.0-rc2-final", "Microsoft.Extensions.Logging": "1.0.0-rc2-final", "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final", "Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final", "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-final" },

And, from the Terminal/CMD run:

$ dotnet restore log : Restoring packages for /Users/Sergio/Repositories/blog/app/dotnet/project.json... info : GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethostresolver/index.json info : OK https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethostresolver/index.json 519ms info : GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethost/index.json info : OK https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethost/index.json 538ms log : Restoring packages for tool 'Microsoft.AspNetCore.Razor.Tools' in /Users/Sergio/Repositories/blog/app/dotnet/project.json... log : Restoring packages for tool 'Microsoft.AspNetCore.Server.IISIntegration.Tools' in /Users/Sergio/Repositories/blog/app/dotnet/project.json... info : Committing restore... log : Writing lock file to disk. Path: /Users/Sergio/Repositories/blog/app/dotnet/project.lock.json log : /Users/Sergio/Repositories/blog/app/dotnet/project.json log : Restore completed in 4305ms. NuGet Config files used: /Users/Sergio/.nuget/NuGet/NuGet.Config Feeds used: https://api.nuget.org/v3/index.json

And modify Program.cs to read the configuration from main function args:

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; namespace WebApplicationBasic { public class Program { public static void Main(string[] args) { var config = new ConfigurationBuilder() .AddCommandLine(args) .AddEnvironmentVariables(prefix: "ASPNETCORE_") .Build(); var host = new WebHostBuilder() .UseConfiguration(config) .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup() .Build(); host.Run(); } } }

We’re almost done, is time to build and run our container:

Build and run

Time for Docker. Get back to your Terminal and launch the following command to create the Container:

$ docker build -t sesispla/aspnet-rc2-app . Sending build context to Docker daemon 12.11 MB Step 1 : FROM microsoft/dotnet latest: Pulling from microsoft/dotnet 51f5c6a04d83: Pull complete a3ed95caeb02: Pull complete 7004cfc6e122: Pull complete 5f37c8a7cfbd: Pull complete f04c717aadf7: Pull complete 9e4f1a99b45d: Pull complete Digest: sha256:294140fd6a4222f7c51b555c4b3eca2f4194f6e51995d32e770c878f6034a1fd Status: Downloaded newer image for microsoft/dotnet:latest ---> f315e6bbb53f Step 2 : RUN printf "deb http://ftp.us.debian.org/debian jessie main\n" >> /etc/apt/sources.list ---> Running in bf82c67b1fe0 ---> 655f0a9ef1c3 Removing intermediate container bf82c67b1fe0 Step 3 : COPY . /app ---> c4b9601cd652 Removing intermediate container 292641b90f7a Step 4 : WORKDIR /app ---> Running in ba4108b8f087 ---> 9396567ac9fb Removing intermediate container ba4108b8f087 Step 5 : RUN dotnet restore ---> Running in 6b273c80aa7a log : Restoring packages for /app/project.json... info : GET https://api.nuget.org/v3-flatcontainer/microsoft.aspnetcore.diagnostics/index.json info : GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.app/index.json ... info : Committing restore... log : Lock file has not changed. Skipping lock file write. Path: /app/project.lock.json log : /app/project.json log : Restore completed in 140503ms. NuGet Config files used: /root/.nuget/NuGet/NuGet.Config Feeds used: https://api.nuget.org/v3/index.json Installed: 209 package(s) to /app/project.json ---> 4063190181dc Removing intermediate container 6b273c80aa7a Step 6 : RUN dotnet build ---> Running in 73d7cd64dfda Project app (.NETCoreApp,Version=v1.0) will be compiled because the version or bitness of the CLI changed since the last build Compiling app for .NETCoreApp,Version=v1.0 Compilation succeeded. 0 Warning(s) 0 Error(s) Time elapsed 00:00:07.0382804 ---> fac72bc031a8 Removing intermediate container 73d7cd64dfda Step 7 : EXPOSE 5000/tcp ---> Running in c0fe05045b11 ---> 046911677efe Removing intermediate container c0fe05045b11 Step 8 : ENTRYPOINT dotnet run --server.urls=http://0.0.0.0:5000 ---> Running in e4326e985746 ---> 0d56db4df620 Removing intermediate container e4326e985746 Successfully built 0d56db4df620

Now, our Container image is ready to be deployed. Let’s check it out with docker images:

$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE sesispla/aspnet-rc2-app latest 0d56db4df620 About a minute ago 1.177 GB microsoft/dotnet latest f315e6bbb53f 4 hours ago 641.1 MB

To start the container:

docker run -d -p 90:5000 sesispla/aspnet-rc2-app e8cbaa2fee3ffe25001903c955dab0513e9cec5c45b6722613f53ac0de1cd475

Check the container is up and running:

Sergios-MacBook-Pro:WebApplicationBasic Sergio$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e8cbaa2fee3f sesispla/aspnet-rc2-app "dotnet run --server." 8 seconds ago Up 7 seconds 0.0.0.0:90->5000/tcp evil_mestorf

And finally… open the browser and type:

http://default:90 (or your docker machine name followed by :90)

container-1

Here it is! Your ASP.NET RC2 App up and running with Docker.

ASP.NET RC2 changes

We’ll talk about what has changed in ASP.NET RC 2 in upcoming posts. One of the most significant is that now, anything is a Console app, including the ASP.NET Apps. Having a look to the project generated by Yeoman, you’ll find that there is a program.cs file:

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; namespace WebApplicationBasic { public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup() .Build(); host.Run(); } } }

As we can see, there is the Main entry point, and the WebHostBuilder initialisation (with Kestrel) within.

So… now you can seamlessly Dockerize any .NET Application following this tutorial.

Docker bonus tracks

Bonus 1: Do you want to run more containers with the same image?  Easy!

docker run -d -p 91:5000 --name="app2" sesispla/aspnet-rc2-app 6ea122317745493fc184956c4cd6da6745e44672f38e2c428260261b12458e74 docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6ea122317745 sesispla/aspnet-rc2-app "dotnet run --server." About an hour ago Up 2 seconds 0.0.0.0:91->5000/tcp app1 096d63f03b94 sesispla/aspnet-rc2-app "dotnet run --server." About an hour ago Up 16 minutes 0.0.0.0:90->5000/tcp app2

Open a new web browsers using the port 91, you are done (I.E., http://default:91):

container-2

Bonus 2: Stop or Start existing containers? start and stop are your commands:

docker stop app1 app1 docker stop app2 app2 docker start app1 app1

Bonus 3: List all existing containers (even the stopped ones):

docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6ea122317745 sesispla/dnxapp "dnx -p project.json " 2 hours ago Exited (137) 31 minutes ago dnxapp2 096d63f03b94 sesispla/dnxapp "dnx -p project.json " 2 hours ago Up 31 minutes 0.0.0.0:90->5000/tcp dnxapp1 Sergios-MacBook-Pro:DockerizedDotNet Sergio$

FAQ

A: Should I create an image every time I change the source code?
Q: Yes.

A: Can I use Docker volumes to reference source code paths in my physical machine, instead of generate container images?
Q: Yes. But why not to use your own machine for debugging and testing, and leave Docker images for the final tests?


Welcome to the fascinating world of Containers