ASP.NET Core RC2, Meet Docker

So you read my last post DevOps for ASP.NET Core RC2 and thought, but Donovan what about Docker? OK then, let’s deploy to Docker.  This will be a continuation of the previous two posts using a private Windows-based build agent.  If you have not read those, go catch up because I will assume you have.

The first thing we are going to need is a Docker host.  We are going to use Docker Machine to provision our host in Azure for us. To get Docker Machine, you need to install the Docker Toolbox from here.

  1. Download Docker Toolbox from https://www.docker.com/products/docker-toolbox
  2. Double-click the exe
  3. Click Next
  4. Click Next
    For our purposes, the only required options are Docker Client for Windows and Docker Machine for Windows.
  5. Click Next
  6. Check Add docker binaries to PATH
  7. Click Next
  8. Click Install
  9. Click Finish

imageWith Docker Toolbox installed, we can use Docker Machine to create our host in Azure.  Docker Machine requires your Azure Subscription ID to create your host so we need go get that now.

  1. Log in to the Azure Portal
  2. Click Subscriptions from the menu
  3. Copy your Subscription Id

With your Azure Subscription Id, we can now create a Docker host in Azure.

  1. Open the Command Prompt
  2. Enter the following command:
    docker-machine create -d azure --azure-subscription-id {Azure Subscription ID} --azure-open-port 80 --azure-resource-group {resource group name} {vm name in all lowercase}

    If you care to see all the options, you can read the Azure driver docs here.

  3. Copy the code presented
    SNAGHTML289fca11
  4. Visit https://aka.ms/devicelogin
  5. Enter the code
  6. Press Continue
  7. Log in to your Azure Subscription

Docker Machine will now generate certificates, create a Linux Virtual Machine in Azure, and configure Docker on that machine.

SNAGHTML28af539b

Using Docker Machine to switch your environment can be a little confusing at first.  It might be easier to understand if you know what the command is doing.  To talk to your Docker host, you use the Docker command.  For the Docker command to connect to the Docker host it must know the Docker host’s address and use certificates to authenticate.  The certificates are stored in a folder on your machine.  When you switch environments, you are telling the Docker command where to find the correct certificates and the address of the host you want to connect to.  This is done by setting environment variables the Docker command knows to look for.  When you use Docker Machine to switch your environment, it just shows you the values the environment variables need to be and shows you a command to execute to set them.  For example, if I use Docker Machine to connect to the host I just created, I would issue the following command:

docker-machine env blogtestdockerhost

This would return the following output:

SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://138.91.159.75:2376
SET DOCKER_CERT_PATH=C:\Users\dbrown\.docker\machine\machines\blogtestdockerhost
SET DOCKER_MACHINE_NAME=blogtestdockerhost
REM Run this command to configure your shell:
REM     @FOR /f "tokens=*" %i IN ('docker-machine env blogtestdockerhost') DO @%i

However, I would still not be able to use the Docker command to connect to my host.  If I were to try to list the images on my Docker host issuing the following command:

docker images

I would get the following error:

An error occurred trying to connect: Get http://./pipe/docker_engine/v1.23/images/json:
open //./pipe/docker_engine: The system cannot find the file specified.

The reason for the error is the Docker Machine command just showed me what the values need to be but did not set them. To set them, I need to copy the last line of the Docker Machine output starting at the @ sign.  Running that command at the command prompt will actually set the environment variables so I can access the host.

@FOR /f "tokens=*" %i IN ('docker-machine env blogtestdockerhost') DO @%i

docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

Take note of the DOCKER_HOST and DOCKER_CERT_PATH values. We are going to need those when we create our Service Endpoints in Visual Studio Team Services.

With our Docker host in place, we can move to setting up Visual Studio Team Services.  To make things easier, we are going to install a Docker Integration extension that has tasks for Docker.

  1. Visit https://marketplace.visualstudio.com/items?itemName=ms-vscs-rm.docker
  2. Click Install
  3. Select your account
  4. Click Continue
  5. Click Confirm
  6. Click Proceed to the account

Before we get to the build, we need to make some changes to the project.

  1. Open project from the previous post in Visual Studio
  2. Right-click the web project
  3. Select Add / New Item…
  4. Select Text File
  5. Name the file dockerfile
  6. Click Add
    If the file is added with an extension, remove it. The file name is simply dockerfile with no extension.
  7. Copy and paste the text below into the file:
    FROM microsoft/dotnet:1.0.0-rc2-core
    
    ADD . /app
    
    WORKDIR /app
    
    ENTRYPOINT dotnet {projectName}.dll

    Replace {projectName}.dll with the name of your output DLL. It is very important that the casing is correct. Remember we are running ASP.NET in a Linux container and Linux is case sensitive.

  8. Open Program.cs
  9. Add the following code above UseKestrel:
    .UseUrls(Environment.GetEnvironmentVariable("ASPNETCORE_URLS") ?? String.Empty)
    image
  10. Build the project to make sure there are no errors
  11. Save and check in all the files

With the code in place, we can move on to creating the build. Clone the build definition we created in the last post.

  1. imageClick the Build hub in Visual Studio Team Services
  2. Hover your mouse to the left of the build definition in the Explorer
  3. Click the down arrow
  4. Select Clone…
  5. Remove the last two tasks (Trackyon Zip and Copy and Publish Build Artifacts)
  6. Click Add build step…
  7. Select the Utility category
  8. Click Add next to Copy Files
  9. Select the Build category
  10. Click Add next to Docker twice (2)
    We are going to build and run the image from our build. We could also move the run step to Release Management.
  11. Select the third Command Line task
    Field Value
    Tool dotnet
    Arguments publish -c $(BuildConfiguration) -o $(Build.StagingDirectory)/drop
    Working folder {Path to Web Project folder}
  12. Select the Copy Files task
    Field Value
    Source Folder src/{Project folder}
    Contents dockerfile
    Target Folder $(Build.StagingDirectory)/drop
  13. Select the first Docker task
  14. Click Manage next to Docker Host Connection
  15. Click New Service Endpoint
  16. Select Docker Host
    Field Value
    Connection Name {Docker Host VM Name}
    Server URL {The DOCKER_HOST value from Docker Machine i.e. tcp://[ipaddress]:2376}
    CA Certificate {Open ca.pem from the DOCKER_CERT_PATH folder and copy and paste the value here.}
    Certificate {Open cert.pem from the DOCKER_CERT_PATH folder and copy and paste the value here.}
    Key {Open key.pem from the DOCKER_CERT_PATH folder and copy and paste the value here.}
  17. Click OK
  18. Click New Service Endpoint
  19. Select Docker Registry
    Field Value
    Connection Name Docker Hub
    Docker Registry https://index.docker.io/v1/
    Docker ID {Your Docker ID from Docker Hub. If you do not have one you can create a free account at hub.docker.com}
    Password {Docker Hub password}
    Email {Email used with Docker Hub}
  20. Click OK
  21. Return to your build definition
  22. Click the Refresh icon next to Docker Host Connection
  23. Click the Refresh icon next to Docker Registry Connection
    Field Value
    Docker Host Connection {Select Docker Host connection}
    Docker Registry Connection {Select Docker Hub connection}
    Action Build an image
    Docker File $(Build.StagingDirectory)/drop/dockerfile
    Image Name {must match "[a-z0-9]+(?:[._-][a-z0-9]+)*" }
    Context $(Build.StagingDirectory)/drop
  24. Select the second Docker task
  25. Click the Refresh icon next to Docker Host Connection
  26. Click the Refresh icon next to Docker Registry Connection
    Field Value
    Docker Host Connection {Select Docker Host connection}
    Docker Registry Connection {Select Docker Hub connection}
    Action Run an image
    Image Name {must match "[a-z0-9]+(?:[._-][a-z0-9]+)*" }
    Container Name  
    Ports 80:80
    Environment Variables ASPNETCORE_URLS=http://*:80
  27. Click Save
  28. Rename build
  29. Click OK
  30. Click Queue build…
  31. Click OK

Once the build completes, you should be able to use a browser and enter your Docker Host’s IP address in the address bar. You can get your Docker Host’s IP address from the Docker Machine output.

image

If your images fail to run, use the logs command to see what errors were thrown. If you see “Did not find a suitable dotnet SDK at '/usr/share/dotnet', install dotnet SDK from https://github.com/dotnet/cli” and check the spelling and casing of your DLL in your dockerfile.

Now you can deploy your ASP.NET Core RC2 application to Azure Web Apps or Docker containers.

Comments (9) -

  • Camilo Hernández

    6/2/2016 5:05:08 PM | Reply

    It Would be great if you make a post like this one using Azure service containers.

    • Donovan

      6/4/2016 3:03:00 PM | Reply

      That is a great idea. I will add that to my blog backlog.

  • Oleksii Udovychenko

    6/9/2016 2:36:15 AM | Reply

    Great post! I hope you will continue blogging about .net + docker + *nix.

    • Donovan

      6/10/2016 7:12:26 PM | Reply

      I just installed Ubuntu 16.04 in my laptop so I would say they will keep coming. Smile

  • Eelco Los

    6/9/2016 1:52:04 PM | Reply

    Hey Donovan. I've checked up on these and the previous blog (donovanbrown.com/.../DevOps-for-ASPNET-Core-RC2) regarding the .net Core RC2 and docker publishing. However, using either build or release provides kind of the same error for me:

    The project will build, but the error for building an image returns:
    6e475a6c-49e3-4e1e-8aec-bc5206f1fefe=tcp://xx.xx.x.xx:2376/
    6e475a6c-49e3-4e1e-8aec-bc5206f1fefe exists true
    ##[debug]6e475a6c-49e3-4e1e-8aec-bc5206f1fefe exists true
    ##[debug]check path : null
    ##[debug]task result: Failed
    Not found docker: null

    I don't know what I missed. The docker-machine env command and docker images work fine.

    • Donovan

      6/10/2016 7:17:26 PM | Reply

      Can you show me your Dockerfile?  Also want to make sure you set the working directory of the task that builds your image.

      • Eelco Los

        6/10/2016 7:46:31 PM | Reply

        Hey Donovan,
        Thanks for helping out!
        I gotten a bit further down the line. Here's the dockerfile:
        FROM microsoft/dotnet:1.0.0-rc2-core

        ADD . /app

        WORKDIR /app

        ENTRYPOINT dotnet {projectName}.dll

        So after I figured that out, I gotten the {projectName}.dll changed again to PeopleTracker.Preview.dll.
        When you save it in visual studio though, the encoding will automaticly switch to UTF-8-BOM, which will result in docker requesting step 1: 2016-06-10T18:13:23.5846983Z Step 1 : FROM
        And not run at all.
        So, after changing the dll file again, converting to UTF-8 and some other small issues, I got it working now (with release management, after the build version up here was up and running).

        One final question though (of a more different nature): After I got the build version up and running and testing release management as well I got an error regarding running the image: Bind for 0.0.0.0:80 failed: port is already allocated.
        Even with a seperate build this error occurs. Any Ideas? Smile

        • Donovan

          6/12/2016 9:07:14 PM | Reply

          Glad to hear you are making progress. One make sure the casing of your dll name matches what is produced. One letter off an it will fail. You your docker host run a docker ps -a command
          If there are any other containers running on port 80 run docker stop of that container if it is running then docker rm to remove it.  Then try and deploy your image again.

  • Maruthi Donthi

    7/15/2016 4:22:02 PM | Reply

    Hi, I have created a docker host on azure, Docker project on using visual studio team services. I am trying to create a build step for docker build. But I am getting no docker on build agent and build fails. I see one blog saying that there is no docker installed on Build agent by default. Can anyone help me to install docker on host agent or private agent or an alternative solution for my problem.

    I am using nodejs docker project. Here is my Dockerfile and logs.
    FROM ubuntu:12.04

    # Install dependencies
    RUN apt-get update -y
    RUN apt-get install -y git curl apache2 php5 libapache2-mod-php5 php5-mcrypt php5-mysql

    # Install app
    RUN rm -rf /var/www/*
    ADD src /var/www

    # Configure apache
    RUN a2enmod rewrite
    RUN chown -R www-data:www-data /var/www
    ENV APACHE_RUN_USER www-data
    ENV APACHE_RUN_GROUP www-data
    ENV APACHE_LOG_DIR /var/log/apache2

    EXPOSE 80
    EXPOSE 22
    EXPOSE 443

    CMD ["/usr/sbin/apache2", "-D",  "FOREGROUND"]

    Error Logs:

    2016-07-14T20:18:41.8911510Z ##[debug]found 1 matches
    2016-07-14T20:18:41.8911510Z ##[debug]matches:
    2016-07-14T20:18:41.8921510Z ##[debug]C:/a/1/s/Dockerfile
    2016-07-14T20:18:41.8921510Z ##[debug]found 1 matches
    2016-07-14T20:18:41.8921510Z ##[debug]736a1369-e437-4edd-8e57-a49306cb16e9=tcp://13.91.252.37:2376/
    2016-07-14T20:18:41.8931509Z 736a1369-e437-4edd-8e57-a49306cb16e9 exists true
    2016-07-14T20:18:41.8931509Z ##[debug]736a1369-e437-4edd-8e57-a49306cb16e9 exists true
    2016-07-14T20:18:41.9111514Z ##[debug]check path : null
    2016-07-14T20:18:41.9131510Z ##[debug]task result: Failed
    2016-07-14T20:18:41.9131510Z Not found docker: null
    2016-07-14T20:18:41.9271512Z Finishing task: Docker
    2016-07-14T20:18:41.9491512Z ##[error]System.Exception: Task Docker failed. This caused the job to fail. Look at the logs for the task for more details.
    2016-07-14T20:18:41.9491512Z ##[error]   at Microsoft.TeamFoundation.DistributedTask.Worker.JobRunner.Run(IJobContext jobContext, IJobRequest job, IJobExtension jobExtension, CancellationTokenSource tokenSource)

    Thanks in advance.

Pingbacks and trackbacks (1)+

Add comment

Loading