Distributed Mind


Musings on software development

A lap around #AWS and #docker-machine

Written on 16. August 2015

This post will show you how can create a Docker Machine instance on AWS (EC2) starting from scratch. This means you’re starting with your free AWS account with nothing configured after you have signed up for your free AWS trial account.

To create a Docker machine instance in AWS, the docker-machine command requires several params for the AWS driver.

At a minimum, we need to provide these params:

  • amazonec2-access-key
  • amazonec2-secret-key
  • amazonec2-region
  • amazonec2-zone
  • amazonec2-vpc-id

To get the amazonec2-access-key and the amazonec2-secret-key we need to have an Amazon EC2 Access Key and an Amazon EC2 Secret Key.

Both of them can be obtained by creating a user in IAM.

Clicking on the “Create New Users” button will bring up this view where we create a new user named “awsdockeruser”:

Make sure to check “Generate an access key for each user”.

After creating the user, we’ll get both keys:

Make sure to create a copy at safe place as this is the last time you’ll see them in IAM.

In order to manage AWS EC2 instances we need the appropriate permissions. To assign the permission to manage EC2 instances our awsdockeruser needs to be a member of a group which has that permission.

So lets create that group in the IAM dashboard:

We’ll called it “awsdockergroup”

Next, assign the policy to manage EC2 instances (there might be lower privileges that are sufficient):

Next, lets add the “awsdockeruser” to our group:

Next, we need to know region, zone and VPC id. These can be obtained by using the AWS CLI.

On Linux and OS X, it can be installed using PIP package manager.

pip install awscli

Then AWS CLI needs to be configured:

aws configure

AWS CLI configurations asks you for your AWS Access Key ID, your AWS Secret Access Key (remember them? 😀), Default region name (codes can be found here - I’ve choosen eu-central-1), and Default output format which I set to json.

To get the vpc-id, just run:

aws ec2 describe-subnets

The output will look like this:

{
    "Subnets": [
        {
            "VpcId": "<avpcid>",
            "CidrBlock": "172.31.0.0/20",
            "MapPublicIpOnLaunch": true,
            "DefaultForAz": true,
            "State": "available",
            "AvailabilityZone": "eu-central-1a",
            "SubnetId": "<asubnetid>",
            "AvailableIpAddressCount": 4091
        },
        {
            "VpcId": "<anothervpcid>",
            "CidrBlock": "172.31.16.0/20",
            "MapPublicIpOnLaunch": true,
            "DefaultForAz": true,
            "State": "available",
            "AvailabilityZone": "eu-central-1b",
            "SubnetId": "<anothersubnetid>",
            "AvailableIpAddressCount": 4091
        }
    ]
}

The amazonec2-zone param is the last character of the AvailabilityZone of the subnet you choose to use, so a or b here.

Ok, it’s time to spin up our Docker machine instance…

docker-machine create \ 
                -d amazonec2 \
                --amazonec2-access-key <YOURACCESSKEY> \
                --amazonec2-secret-key <YOURSECRETKEY> \
                --amazonec2-zone a \
                --amazonec2-region eu-central-1 \
                --amazonec2-vpc-id <YOURVPCID> 
                awsdocker

After about 60 seconds, your console should confirm your machine is ready to rock’n roll

Launching instance...
To see how to connect Docker to this machine, run: docker-machine env awsdocker

To connect to the machine, run this command:

eval $(docker-machine env awsdocker)

To make sure everything works as expected, just run docker ps.

If you’re working with various machines, you might want to know which is your current active machine:

You can have this in your prompt if you use my bash prompt definiton from here.

Using docker-machine ip awsdocker you can get the public IP address of your machine.

If you’re deploying some containers, you might wonder, why you can`t access your containers exposed ports like http://<machine-ip>:<someport>: because firewall 😱.

So head over to the EC2 dashboard “Security Groups” section, select your “docker-machine” Security Group (which has been created when spinning up your machine) and make sure to allow some inbound traffic:

Happy shipping! 😀

Integration-testing ASP.NET 5 / MVC 6 Controllers on DNX Beta 4 #aspnet5

Written on 16. May 2015

In ASP.NET 5 bootstrapping a self host test server has changed compared to ASP.NET 4 / Web API.

This post will show you how you can run integration tests using ASP.NET 5 / MVC 6 API Controllers on Beta 4 of DNX.

First, the Controller implementation:

CustomersController.cs:

[Route("api/[controller]")]
public class CustomersController
{
    [HttpGet]
    public IActionResult GetAll()
    {
        return new ObjectResult(
            new List<Customer>()
            {
                new Customer
                {
                    CompanyName = "PDMLab",
                    AddressLine = "Ludwig-Erhard-Allee 10",
                    ZipCode = "76131",
                    City = "Karlsruhe",
                    Website = "https://pdmlab.com"
                }
            });
    }
}

Our tests will use Xunit, so we’ll add Xunit to our project.json file:

...
 "dependencies": {
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
    "xunit": "2.1.0-*",
    "xunit.runner.dnx": "2.1.0-beta2-build79",
    "xunit.runner.visualstudio": "2.1.0-beta1-build1051"
  },
...

Furthermore, our HttpClient we’re using resides in Microsoft.AspNet.WebApi.Client which we’ll add also to our project.json file:

...
 "dependencies": {
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
    "Microsoft.AspNet.WebApi.Client": "5.2.3",
    "xunit": "2.1.0-*",
    "xunit.runner.dnx": "2.1.0-beta2-build79",
    "xunit.runner.visualstudio": "2.1.0-beta1-build1051"
  },
...

Finally, we need Kestrel to run our tests on *nix based systems as well as Microsoft.AspNet.Hosting which contains the classes required for bootstrapping a self host server - :

...
  "dependencies": {
    "Kestrel": "1.0.0-beta4",
    "Microsoft.AspNet.Hosting": "1.0.0-beta4",
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
    "Microsoft.AspNet.WebApi.Client": "5.2.3",
    "xunit": "2.1.0-*",
    "xunit.runner.dnx": "2.1.0-beta2-build79",
    "xunit.runner.visualstudio": "2.1.0-beta1-build1051"
  },
...

The final part required is the test itself:

public class CustomerControllerIntegrationTest
    {
        [Fact]
        public void ShouldReturnCustomer()
        {
            var config = new Configuration();
            config.AddCommandLine(new[] { "--server.urls", "http://localhost:5001" });

            var serverFactoryLocation = string.Empty;
            if(!IsMono()) {
                serverFactoryLocation = "Microsoft.AspNet.Server.WebListener";
            } else {
                serverFactoryLocation = "Kestrel";
            }
            var context = new HostingContext()
            {
                Configuration = config,
                ServerFactoryLocation = serverFactoryLocation,
                ApplicationName = "AspNet5IntegrationTesting",
                StartupMethods = new StartupMethods(builder => builder.UseMvc(), services =>
                {
                    services.AddMvc();
                    return services.BuildServiceProvider();
                })
            };


            using (new HostingEngine().Start(context))
            {
                var client = new HttpClient();
                var customers = client.GetAsync("http://localhost:5001/api/customers").Result
                    .Content.ReadAsAsync<List<Customer>>().Result;

                Assert.Equal(customers[0].CompanyName,"PDMLab");
            }
        }

        public static bool IsMono()
        {
            return Type.GetType("Mono.Runtime") != null;
        }

    }

The implementation is pretty straight forward:

First we’re adding the host and port our test server should listen on to our application configuration.

Then we’re checking if we’re running on mono and depdending on the result, we’re bootstrapping Kestrel oder WebListener.

After that we’re bootstrapping a HostingContext instance which configures our StartupMethods (which contains the stuff that resides in your Startup.cs normally), Configuration and the host to use.

Using this context we’re firing up a new HostingEngine instance which gets disposedd after the assertions have finished.

That’s it… almost, because there’s one caveat:

The tests currently fail on Kestrel / *nix with this error:

xUnit.net DNX test runner (64-bit DNX 4.5.1)
Copyright (C) 2015 Outercurve Foundation.

Discovering: AspNet5IntegrationTesting
Discovered:  AspNet5IntegrationTesting
Starting:    AspNet5IntegrationTesting
   AspNet5IntegrationTesting.Tests.CustomerControllerIntegrationTest.ShouldReturnCustomer [FAIL]
      System.ArgumentException : GCHandle value belongs to a different domain
      Stack Trace:
           at System.Runtime.InteropServices.GCHandle.op_Explicit (IntPtr value) [0x00000] in <filename unknown>:0 
           at System.Runtime.InteropServices.GCHandle.FromIntPtr (IntPtr value) [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.Networking.UvMemory.DestroyMemory (IntPtr memory) [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.Networking.UvLoopHandle.ReleaseHandle () [0x00000] in <filename unknown>:0 
           at System.Runtime.InteropServices.SafeHandle.RunRelease () [0x00000] in <filename unknown>:0 
           at System.Runtime.InteropServices.SafeHandle.Dispose (Boolean disposing) [0x00000] in <filename unknown>:0 
           at System.Runtime.InteropServices.SafeHandle.Dispose () [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.KestrelThread.ThreadStart (System.Object parameter) [0x00000] in <filename unknown>:0 
         --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.KestrelThread.Stop (TimeSpan timeout) [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.KestrelEngine.Dispose () [0x00000] in <filename unknown>:0 
           at Kestrel.ServerFactory+<>c__DisplayClass3_0.<Start>b__1 () [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.Disposable.Dispose (Boolean disposing) [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Server.Kestrel.Disposable.Dispose () [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Hosting.HostingEngine+<>c__DisplayClass9_0.<Start>b__1 () [0x00000] in <filename unknown>:0 
           at Microsoft.AspNet.Hosting.HostingEngine+Disposable.Dispose () [0x00000] in <filename unknown>:0 
           at AspNet5IntegrationTesting.Tests.CustomerControllerIntegrationTest.ShouldReturnCustomer () [0x00000] in <filename unknown>:0 
           at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
           at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0 
Finished:    AspNet5IntegrationTesting

=== TEST EXECUTION SUMMARY ===
   AspNet5IntegrationTesting  Total: 1, Errors: 0, Failed: 1, Skipped: 0, Time: 0.697s

If somebody has an idea about this, please send me a pull request at GitHub (yes, you can grab the source over there) ;-)

Update [2015/05/17]: This is a known Kestrel issue as described here (Description starts here) and a fix is on the way.

Deploying a ASP.NET MVC 6 API as Azure API App in Azure App Services

Written on 02. May 2015

Setting some context…

Disclaimer: this post is based on Visual Studio 2015 RC, DNX beta 4 and Azure SDK 2.6 and might become obsolete (hopefully pretty fast).

At Build 2015 “The Lesser Scotts“ have been showing how to create a Azure API App based on ASP.NET Web API 2 using the Azure SDK Tools in Visual Studio 2013 which you should definitely watch before reading this post.

Well, deploying stable stuff is pretty neat.
But my first thought of course has been “What about ASP.NET MVC 6 on DNX beta 4?”.

So I asked the lesser Scotts at Twitter:

Scotts answer was pretty promising:

My next step of course was installing Azure SDK 2.6 for Visual Studio 2015 RC and trying to create a new Azure API App as shown in the video above.

But there were no templates… so back to Twitter - this time Brady Gaster was my victim:

No promising answer but at least some facts :)

Ok, lets do some reverse engineering using Visual Studio 2013 and Azure SDK 2.6 and find out what happens when you’re using the SDK tooling - I’ll skip this step here and show you, how to get stuff done in Visual Studio 2015 RC and ASP.NET MVC 6 on DNX beta 4 now.

First things first, the ASP.NET MVC 6 API

First, create a new ASP.NET Web Application in Visual Studio 2015 RC:

After this, your solution should look like this:

As the default ValuesController.cs in the Controllers folder is pretty useless (and it will cause problems as you can read later on), we’ll delete it and create a SpeakersController.cs instead.

SpeakersController.cs:

using System.Collections.Generic;
using HelloApiApps.Models;
using Microsoft.AspNet.Mvc;

namespace HelloApiApps.Controllers
{
    [Route("api/[controller]")]
    public class SpeakersController
    {
        [HttpGet]
        public IEnumerable<Speaker> GetAll()
        {
            return new List<Speaker>
            {
                new Speaker
                {
                    Id = 1,
                    FirstName = "Scott",
                    LastName = "Hanselman",
                    Twitter = "shanselman"
                },
                new Speaker
                {
                    Id = 2,
                    FirstName = "Scott",
                    LastName = "Hunter",
                    Twitter = "coolcsh"
                },
                new Speaker
                {
                    Id = 3,
                    FirstName = "Damian",
                    LastName = "Edwards",
                    Twitter = "DamianEdwards"
                }
            };
        }
    }
}

We also create a folder Models and create a Speaker.cs in it:

Speaker.cs:

namespace HelloApiApps.Models
{
    public class Speaker
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Twitter { get; set; }
    }
}

At this time, you should be able to test your API locally at http://localhost:someport/api/speakers and you should get the JSON representation of our speaker list.

Creating the Swagger API definition file

According to the Scott’s video we now would create the Swagger file, but Swashbuckle shown in the video doesn’t work with ASP.NET MVC 6 right now.

To the rescue, there’s already a work in progress project on GitHub which is porting Swashbuckle to ASP.NET MVC 6: Ahoy!

We create a local clone of Ahoy and for the sake of keeping this post simple, we add the Swashbuckle.Swagger project inside our API Solution as existing project:

Next, we have to register Swashbuckle / Swagger inside our Startup.cs

Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Swashbuckle.Application;
using Swashbuckle.Swagger;

namespace HelloApiApps
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
        }

        // This method gets called by a runtime.
        // Use this method to add services to the container
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            // Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.
            // You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.
            // services.AddWebApiConventions();

            services.AddSwagger(s =>
            {
                s.SwaggerGenerator(c =>
                {
                    c.Schemes = new[] { "http", "https" };
                    c.SingleApiVersion(new Info
                    {
                        Version = "v1",
                        Title = "Swashbuckle Sample API",
                        Description = "A sample API for testing Swashbuckle",
                        TermsOfService = "Some terms ..."
                    });
                });

                s.SchemaGenerator(opt => opt.DescribeAllEnumsAsStrings = true);
            });
        }

        // Configure is called after ConfigureServices is called.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            // Configure the HTTP request pipeline.
            app.UseStaticFiles();

            // Add MVC to the request pipeline.
            app.UseMvc();
            // Add the following route for porting Web API 2 controllers.
            // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");

            // Add MVC to the request pipeline.
            app.UseMvc(routes =>
            {
                routes.EnableSwagger("swagger/docs/{apiVersion}");
            });
        }
    }
}

The default Swagger URL for Swashbuckle has been (as shown in the video): http://somehost:someport/swagger/docs/v1.

By default, with Ahoy this has changed to http://somehost:someport/swagger/v1/swagger.json whereas in both cases v1 depends on the Version property being set during Swashbuckle registration in Startup.cs inside the ConfigureServices method in the code shown above.

Thus, we have to change the route definition back to the old format as Azure API Apps expect it in that format. You can see that inside the Configure method of the Startup.cs in the code shown above.

Next, we can open up our App in a browser again and browse to http://localhost:someport/swagger/docs/v1 and we should get the JSON representation.

Adding Azure API Apps Metadata

Now we need to add the Metadata for the Azure API Apps manually as we don’t have the tooling support right now…

First, add a JSON file named apiapp.json in the root of the API App project:

Then paste this content into it:

 {
  "$schema": "http://json-schema.org/schemas/2014-11-01/apiapp.json#",
  "id": "HelloApiApps",
  "namespace": "microsoft.com",
  "gateway": "2015-01-14",
  "version": "1.0.0",
  "title": "HelloApiApps",
  "summary": "",
  "author": "",
  "endpoints": null
}

Next, add the following folders and files to your wwwroot folder in the API App solution:

The content of the apiDefinition.swagger.json is the output from http://localhost:someport/swagger/docs/v1. Copy and paste it into the file.

Important Note: Make sure your API Controllers don’t contain ambiguous method names (e.g. “Get” twice), this will cause problems at the moment. Thanks to @Mohit for sorting that out!

The last file required, is the apiappconfig.azureresource.json and it’s content is this:

{
  "$schema": "http://schemas.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "$system": {
      "type": "Object"
    }
  },
  "resources": []
}

<Update [2015/05/02]>: Instead of providing a static snapshot of your Swagger API definition, you can also provide an endpoint where the current swagger definition can be read from. This solves two problems: the route can be the new route template Ahoy introduces and your Swagger definition is always up to date when add new API endpoints (Controllers / Methods).

Just update the apiapp.json and add an fill the endpoint property (please note that I’m use the new default Ahoy route here):

 {
  "$schema": "http://json-schema.org/schemas/2014-11-01/apiapp.json#",
  "id": "HelloApiApps",
  "namespace": "microsoft.com",
  "gateway": "2015-01-14",
  "version": "1.0.0",
  "title": "HelloApiApps",
  "summary": "",
  "author": "",
  "endpoints": {
        "apiDefinition": "/swagger/v1/swagger.json"
    }
}

Kudos go to @pkefal for the hint (By the way: if you want to create API Apps using Node.js you should definitely read his article)

</end of update [2015/05/02]>

Another step, the Azure SDK tooling does, is adding two NuGet packages and we have to do that also. Update your project.json and add Microsoft.Azure.AppService.ApiApps.Service and System.IdentityModel.Tokens.Jwtto it.

{
  "webroot": "wwwroot",
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
    "Microsoft.Azure.AppService.ApiApps.Service": "0.9.40",
    "System.IdentityModel.Tokens.Jwt": "4.0.2.202250711",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
    "Swashbuckle.Swagger": "1.0.0-*"
  },

  "commands": {
    "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": { }
  },

  "exclude": [
    "wwwroot",
    "node_modules",
    "bower_components"
  ],
  "publishExclude": [
    "node_modules",
    "bower_components",
    "**.xproj",
    "**.user",
    "**.vspscc"
  ]
}

Publishing the API App

If there were tooling support, you could create a new Azure API App using the tooling. Yet: no tooling, thus we have to use the new Azure Portal.

Login using your credentials and create a new Azure API App.

Disclaimer: Depending on your plan and the settings, this will cost you money!

After a few seconds, you’ll get the success notification inside the Azure Portal and you should have a new tile on the Portal startpage and clicking on it should get you some details:

Now make sure to set the “Access Level” inside the “Settings” / “Application Settings” to “public (anonymous)” and click the “Save” button afterwards:

Back to Visual Studio 2015 RC, we can now publish our Azure API App by right clicking the project and selecting “Publish…”:

The next dialog allows you to select where to publish:

Select “Microsoft Azure Web Apps” and click “Next”.

Now from “Existing Web Apps”, select the API App you just created inside the Azure Portal and click “OK”:

The next dialog sums up the connection details and can be confirmed with “Next”:

During the next step, you can choose your DNX version to be used for the API App - the defaults are ok, click “Next”:

The last step is to hit the “Publish” button:

The publishing process should take only a few seconds:

When it is finished, it should open up your default browser showing you this:

Back to the Azure Portal you should now be able to click on the “API Definition” tile and see the API Operations and be able to download the Swagger using the button above the list of Operations.

That’s it!

lowerCamelCase JSON with ASP.NET MVC 6

Written on 01. May 2015

With ASP.NET MVC 6 the default output for JSON is still UpperCamelCase.

To change that to lowerCamelCase JSON, simply add this to the ConfigureServices method:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.ConfigureMvc(options =>
    {
        (options.OutputFormatters.First(f => f.Instance is JsonOutputFormatter).Instance as
            JsonOutputFormatter).SerializerSettings.ContractResolver = 
            new CamelCasePropertyNamesContractResolver();
    });
}

That’s it!

Running ASP.NET 5 Beta 4 in Docker with DNX runtime #aspnet5 #docker

Written on 27. April 2015

Update [2015-05-02]

As Microsoft has updated it’s Dockerfile to support DNX (beta 4 as of this Update), the introduction of this post has become obsolete and you can jump directly over here (in this post).

</end of update> [2015-05-02]

Microsoft recently changed the naming for the ASP.NET 5 runtime from “K” to “DNX“. With that, the following K utilities have been renamed:

  • kpm -> dnu
  • kvm -> dnvm
  • k -> dnx

When you’re trying to run DNX with the current ASP.NET 5 Docker image being provided by Microsoft, you’ll fail.

Based on the Dockerfile Microsoft provides here, I created a local Docker image to be able to run ASP.NET 5 Beta 4 with the new DNX runtime.

The Dockerfile for the base ASP.NET 5 image goes here:

Update [2015-05-01]

You don’t have to do the following step, there’s also a working Microsoft Dockerfile, just jump to here (in this post), to read the instructions how to use it.

FROM mono:3.12

# Get build dependencies, download/build/install mono 4.1.0
RUN apt-get update -qq \
    && apt-get install -qqy git autoconf libtool automake build-essential mono-devel gettext unzip \
    && git clone https://github.com/mono/mono.git \
    && cd mono \
    && git reset --hard 53dc56ee39a8e3b013231957aca4671b202c6410 \
    && ./autogen.sh --prefix="/usr/local" \
    && make \
    && make install \
    && cd .. \
    && rm mono -r

# Install aspnet 1.0.0-beta4
ENV DNX_FEED https://www.myget.org/F/aspnetmaster/api/v2
ENV DNX_USER_HOME /opt/dnx

RUN curl -sSL https://raw.githubusercontent.com/aspnet/Home/7d6f78ed7a59594ce7cdb54a026f09cb0cbecb2a/dnvminstall.sh | DNX_BRANCH=master sh
RUN bash -c "source $DNX_USER_HOME/dnvm/dnvm.sh \
    && dnvm install 1.0.0-beta4 -a default \
    && dnvm alias default | xargs -i ln -s $DNX_USER_HOME/runtimes/{} $DNX_USER_HOME/runtimes/default"

# Install libuv for Kestrel from source code (binary is not in wheezy and one in jessie is still too old)
RUN LIBUV_VERSION=1.4.2 \
    && curl -sSL https://github.com/libuv/libuv/archive/v${LIBUV_VERSION}.tar.gz | tar zxfv - -C /usr/local/src \
    && cd /usr/local/src/libuv-$LIBUV_VERSION \
    && sh autogen.sh && ./configure && make && make install \
    && rm -rf /usr/local/src/libuv-$LIBUV_VERSION \
    && ldconfig

# Update NuGet feeds

RUN mkdir -p ~/.config/NuGet/
RUN curl -o ~/.config/NuGet/NuGet.Config -sSL https://gist.githubusercontent.com/AlexZeitler/a3412a4d4eeee60f8ce8/raw/45b0b5312845099cdf5da560829e75949d44d65f/NuGet.config

ENV PATH $PATH:$DNX_USER_HOME/runtimes/default/bin

Now lets build the base image:

sudo docker build -t pdmlab/aspnet:1.0.0 .

Based on that, lets create a Dockerfile for our ASP.NET 5 Beta 4 DNX web application:

FROM pdmlab/aspnet:1.0.0
ADD . /app
WORKDIR /app
RUN ["dnu", "restore"]

EXPOSE 5004
ENTRYPOINT ["dnx", "./src/HelloMvc6", "kestrel"]

Update [2015-05-01]

You can also use this Dockerfile using the Microsoft Beta 4 base Image:

FROM microsoft/aspnet:vs-1.0.0-beta4
ADD . /app
WORKDIR /app
RUN ["dnu", "restore"]

EXPOSE 5004
ENTRYPOINT ["dnx", "./src/HelloMvc6", "kestrel"]

</end of update> [2015-05-01]

Update [2015-05-02]

You can use this Dockerfile using the Microsoft DNX Beta 4 base Image:

FROM microsoft/aspnet:1.0.0-beta4
ADD . /app
WORKDIR /app
RUN ["dnu", "restore"]

EXPOSE 5004
ENTRYPOINT ["dnx", "./src/HelloMvc6", "kestrel"]

</end of update> [2015-05-02]

After that, we need to build or application image:

sudo docker build -t aspnet5beta4dnx

With that done, we can run our container:

sudo docker run -t -d -p 80:5004 aspnet5beta4dnx

If erverything went well, you should be able to browse http://localhost/api/values on your host.

The source for the HelloMvc6 application comes here:

project.json:

{
  "webroot": "wwwroot",
  "version": "1.0.0-*",

  "dependencies": {
    "Kestrel": "1.0.0-beta4",
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
    "Microsoft.AspNet.StaticFiles": "1.0.0-beta4"
  },

  "commands": {
    "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000",
    "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5004"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": { }
  },

  "exclude": [
    "wwwroot",
    "node_modules",
    "bower_components"
  ],
  "publishExclude": [
    "node_modules",
    "bower_components",
    "**.xproj",
    "**.user",
    "**.vspscc"
  ]
}

Startup.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;

namespace HelloMvc6
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
        }

        // This method gets called by a runtime.
        // Use this method to add services to the container
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            // Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.
            // You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.
            // services.AddWebApiConventions();
        }

        // Configure is called after ConfigureServices is called.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            // Configure the HTTP request pipeline.
            app.UseStaticFiles();

            // Add MVC to the request pipeline.
            app.UseMvc();
            // Add the following route for porting Web API 2 controllers.
            // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
        }
    }
}

ValuesController.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;

namespace HelloMvc6.Controllers
{
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        // GET: api/values
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        [HttpPost]
        public void Post([FromBody]string value)
        {
        }

        // PUT api/values/5
        [HttpPut("{id}")]
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}