Wednesday, April 17, 2019

Developing containerized dotnet core app on Mac using docker hub


Introduction

The motivation for this blog post came from the fact that I have a mac book pro that I never used for dotnetcore development, but I knew that you can use dotnetcore on mac without any issues. This blog post is a description of how I went about creating a containerized app on Mac book then deploy the docker image to a container registry (in our case docker hub) and then creating a container by pulling the docker image from the docker hub repository.

This blog post is presented in form of a tutorial which is divided into following four parts.
1.     Creating simple API app on Mac called mac-api
2.     Creating the docker image of the API app
3.     Pushing the docker image to a container registry
4.     Creating a container by pulling docker image from container registry


Prerequisites



Before we start, we need to verify that we have correct installations for .NET Core SDK and docker.

Verifying .NET Core SDK

Open terminal
Run command
dotnet –version
2.1.505


Verifying docker

Open terminal
Run command
docker –version
Docker version 18.09.2, build 6247962

Part 1: Creating simple API app on Mac

Creating application folder

Open terminal
Run following commands
“mkdir mac-api”
“cd mac-api”

Create API application

On the same terminal where application folder was created, run the following two commands:

1.  dotnet new webapi”: This will create a new dotnet core app on your application folder (mac-api). Here is the output of running this command:

The template "ASP.NET Core Web API" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on /Users/XXXX/aspnetcore/mac-api/mac-api.csproj...
  Restoring packages for /Users/XXXX/aspnetcore/mac-api/mac-api.csproj...
  Generating MSBuild file /Users/muhammadnabeel/aspnetcore/mac-api/obj/mac-api.csproj.nuget.g.props.
  Generating MSBuild file /Users/XXXXXX/aspnetcore/mac-api/obj/mac-api.csproj.nuget.g.targets.
  Restore completed in 2.22 sec for /Users/XXXX/aspnetcore/mac-api/mac-api.csproj.

Restore succeeded.

2.  dotnet restore”: This command will restore the dependencies and tools of a project. Here is the output of this command:

Restore completed in 58.86 ms for /Users/muhammadnabeel/aspnetcore/mac-api/mac-api.csproj.


Editing the application



Once application has been created, switch to Visual Studio Code to edit the project.
1.  Open Visual Studio Code.
2.  Open the application folder “mac-api”
3.  Modify Startup.cs to remove code related to HTTPS redirecting. After the modifaction your Startup.cs code should look like the following:

4.  using Microsoft.AspNetCore.Builder;
5.  using Microsoft.AspNetCore.Hosting;
6.  using Microsoft.AspNetCore.Mvc;
7.  using Microsoft.Extensions.Configuration;
8.  using Microsoft.Extensions.DependencyInjection;
9.   
10. namespace mac_api
11. {
12.     public class Startup
13.     {
14.         public Startup(IConfiguration configuration)
15.         {
16.             Configuration = configuration;
17.         }
18.  
19.         public IConfiguration Configuration { get; }
20.  
21.         // This method gets called by the runtime. Use this method to add services to the container.
22.         public void ConfigureServices(IServiceCollection services)
23.         {
24.             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
25.         }
26.  
27.         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
28.         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
29.         {
30.             if (env.IsDevelopment())
31.             {
32.                 app.UseDeveloperExceptionPage();
33.             }
34.             app.UseMvc();
35.         }
36.     }
37. }





You can run the current app to verify that it runs and changes to Startup.cs were correct. Before running the app, we need to make sure that port that we are going to use is not used by any other application. For that we will take a look at file called “launchSettings.json” which is present in the “Properties” folder as shown below:



I already had port 5001 and 5000 being used by another application so I changed the ports on line 24 to be ports 7001 and 7000 as shown above.

Run the following command from your terminal:
dotnet run
Here is the output of the above command:

Using launch settings from /Users/XXXX/aspnetcore/mac-api/Properties/launchSettings.json...
info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
      User profile is available. Using '/Users/muhammadnabeel/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
Hosting environment: Development
Content root path: /Users/XXXX/aspnetcore/mac-api
Now listening on: https://localhost:7001
Now listening on: http://localhost:7000
Application started. Press Ctrl+C to shut down.

Now we can open the browser and browse the http://localhost:7000/api/values and see the following output:



We have created our application using dotnet core sdk on a Mac. We can now move on to the next step of dockerizing the app pushing it to docker hub.


Part 2: Dockerize the App

Adding Dockerfile

Add Dockerfile to the application as shown below:

Note on line 17, the “ENTRYPOINT” command references “mac-api.dll”. This is the application dll that is being created by dotnetcore. If you are using a different application name other than mac-api, make sure you are updating the line 17 to reflect that.


Now we are ready to build using the Dockerfile

Build docker image



In order to build docker image for our “mac-api” application, run the following command
“docker build -t mac-api .”
Again, make sure mac-api is replaced with the application name that is being used.

Running the above command will take some time as it will download all the dependencies. Once done, following output is received:

Sending build context to Docker daemon  1.243MB
Step 1/10 : FROM microsoft/dotnet:2.1-sdk as build
 ---> c3bf75a7bf55
Step 2/10 : WORKDIR /app
 ---> Running in 158d21abfa1a
Removing intermediate container 158d21abfa1a
 ---> f8ae02271c20
Step 3/10 : COPY *.csproj ./
 ---> 1d978b943c2d
Step 4/10 : RUN dotnet restore
 ---> Running in 895f959a3ca7
  Restore completed in 6.48 sec for /app/mac-api.csproj.
Removing intermediate container 895f959a3ca7
 ---> 7ea0e9c30a33
Step 5/10 : COPY . ./
 ---> 8424b37d9b63
Step 6/10 : RUN dotnet publish -c Release -o out
 ---> Running in 6b826313f74d
Microsoft (R) Build Engine version 16.0.450+ga8dc7f1d34 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 689.46 ms for /app/mac-api.csproj.
  mac-api -> /app/bin/Release/netcoreapp2.1/mac-api.dll
  mac-api -> /app/out/
Removing intermediate container 6b826313f74d
 ---> 435441f76ec1
Step 7/10 : FROM microsoft/dotnet:2.1-aspnetcore-runtime as runtime
 ---> a5f99632e68d
Step 8/10 : WORKDIR /app
 ---> Running in 77cc35c90c27
Removing intermediate container 77cc35c90c27
 ---> 03938acba548
Step 9/10 : COPY --from=build /app/out .
 ---> 21698da46ef1
Step 10/10 : ENTRYPOINT ["dotnet", "mac-api.dll"]
 ---> Running in e216b0eedf7e
Removing intermediate container e216b0eedf7e
 ---> c065a2a5dbae
Successfully built c065a2a5dbae
Successfully tagged mac-api:latest

To verify that you have created a docker image for your app, run the following command:
docker images

Running the above command yields the following output:
   


This means that we have successfully created a docker image for “mac-api”.

The next step will be to push this image to docker hub.


Part 3: Pushing mac-api docker image to docker hub

After the docker image creation, the logical next step is to create docker containers that will run the docker image. Although we can use the docker image created in the previous steps to create containers, it is always good idea to push the docker image to a repository of some sort. This way we can version and share docker images with whom we like to share with. So now the question comes what options we have to use a repository to host docker images. To answer this question, we have to first clarify few docker concepts for docker repository and docker registry.
A docker repository is a collection of related docker images and docker registry is a service that hosts the docker repository.
So, to know which repository to use to host docker images, we need to first find out which docker registry we are going to use. Good news is there a bunch. Following are few of the examples:
  • Docker hub
  • Azure Container Registry
  • Google Container Registry
  • AWS Container Registry


As you can see from the examples given above, it depends on your cloud strategy. Selecting the registry is not that big of a deal for hosting a docker image because most of them work the same way. The difference is that if you chose a particular cloud provider, you are also taking advantage of complementing cloud services that particular cloud provider is providing you.

For our mac-api app, we are going to use docker hub. One reason why docker hub is chosen is because it is kind of neutral and not related to big three cloud provider.

Now let us get to the business of pushing our docker image to docker hub.
It is assumed that you have a docker hub account and start by navigating to:
https://cloud.docker.com/ and get a screen similar to the following:



The above screenshot shows the docker hub account with listed of various docker repositories. What we are going to do is to add our docker image to the above repository and then create a docker container that will pull the image from this repository. This is a three-step process
1.     Login
Run “docker login” command on your terminal
2.     Create image tag
Run “docker tag mac-api [[YourDockerHubUserId]]/mac-api
3.     Push image to docker hub
Run “docker push [[YourDockerHubUserId]]/mac-api               

The following screenshot shows the three commands and their outputs:



When we go back to our docker hub repository, we find the newly pushed docker image for mac-api listed as shown below:


                       
We have successfully pushed mac-api docker image to docker hub repository. Next step and the final step are to create docker container by pulling docker image from docker hub repository.


Part 4: Create docker container using image from docker hub

On terminal, type the following command:
docker run --name mac-api-remote --env ASPNETCORE_ENVIRONMENT=Development -p 80:80 [[YourDockerHubId]]/mac-api:latest

What is happening in this command? We are using the run command that is going to create a docker container. For the name, we have given mac-api-remote just to indicate that the image for this container is being pulled from docker hub, but any name can be used here. -p is telling us what port we are going to bind the container with.  The last is the address to our docker image.


When we run the above command, we get the following output instructing us to browse the site using http://localhost/api/values



Now we browse to http://localhost/api/values, we see the terminal window is printing some log information. This log information is indicating that a request has been received and processed.



That was the final step.


Conclusion

In this blog post we have seen how we can use a Mac Book to create an app using dotnet core and then dockerized it and push it to docker hub. As the final step we ran the docker container using the docker image that we pushed to docker hub. As mentioned above, we have used docker hub for docker registry and hosting the docker image, but we could have used any of the docker registry offered by the public cloud providers. In our next blog post, we will look into how we can use Azure’s container registry to push our docker image to and then create container by pulling docker image from a repository present in Azure Container Registry.



4 comments: