In a previous post, I showed how you can
scaffold an ASP.NET Core 1.0 app that uses SQLite and deploy it into a Linux Docker container on Windows 10. In this post I will show you how to do the same thing, but instead in a Windows container rather than a Linux container.
If you are new to Docker on Windows 10, you can refer to a
previous post that will help you install and configure it.
The pre-requisites needed to proceed are:
- Windows 10 Anniversary edition or later
- Hyper-V enabled on the host Windows 10 Anniversary operating system
- Yeoman command line interface
- .NET Core 1.0 is installed on your computer
You can get more information on installing the Yeoman command line interface at
http://yeoman.io/codelab/setup.html.
Scaffolding an ASP.NET Core SQLite application with Yeoman
1) Open a command-line window in Windows 10.
2) In a suitable working directory on your hard-drive, create a folder named
dev with the following command:
mkdir dev
3) Go into the newly created directory with:
cd dev
4) In the
dev folder, create a file named
global.json with the following content:
{
"sdk": {
"version": "1.0.0-preview2-003131"
}
}
Note that for this example, we will be using ASP.NET Core version 1.0, as specified in the
global.json file.
5) You will now use the
Yeoman scaffolding tool to create an ASP.NET Core 1.0 application that uses SQLite as its database. Type the following inside the
dev folder:
yo aspnet
The following menu will display:
6) Hit the down-arrow on your keyboard until you get to “
Web Application” then hit
ENTER.
7) Hit
ENTER again to select
Bootstrap.
8) When asked for an application name, type-in
YoAspnetWin then hit
ENTER. The application will get created for you. This is what it should look like when it is done:
9) Note these suggested instructions:
cd "YoAspnetWin"
dotnet restore
dotnet build
dotnet ef database update
dotnet run
Execute the above five instructions in sequence. Eventually, your command-line windows will be running the Kestrel web server listening on port 5000:
10) View the web application by pointing your browser to
http://localhost:5000. This is what the web application, running on your host computer, looks like:
Stop the web application by hitting
CTRL + C on your keyboard.
We are now ready to deploy this SQLite database driven application into a Windows Docker container.
Writing code to create the database
We will be publishing a release version of our application and then deploy it into a Windows docker container. One of the instructions that we executed in the command line (dotnet ef database update) is intended to apply migrations and create the database. This cannot be done in production once our app is published. Instead, we will have to execute code in our application that creates the database according to our connection string.
The connection string is specified in
appsettings.json file and its content is:
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=YoAspnetWin.db" },
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
Open the
Startup.cs file in a text editor and find the Configure() method. We will use dependency injection to get an instance of the database
ApplicationDbContext context class. To do this, add an additional argument to the
Configure() method so that its signature looks like this:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
ApplicationDbContext context)
At the bottom end of the Configure() method, add this line of code:
context.Database.EnsureCreated();
EnsureCreated() is new EF core method which ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created and also it ensures it is compatible with the model for the context.
Eventually, the Configure() method will look like this:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
ApplicationDbContext context)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
} else {
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
context.Database.EnsureCreated();
}
Save your file then execute this statement from the command-line to ensure that your app compiles OK.
dotnet build
Deploying web app into Windows Docker container
We will be deploying our web app into a Windows container. Therefore, right-click on the Docker (whale) icon in your system tray and make sure it displays:
If it does not show the above message then you should switch to the Windows container.
We will next publish our application into a directory named
out. Therefore, run the following instruction inside of a command-line window in the
YoAspnetWin folder:
dotnet publish -c Release -o out
Have a peek at the files in the
out folder. You will notice that it contains all the compiled files that make up the release version of our application:
Create a text file in the
YoAspnetWin folder named
Dockerfile.nano and add to it the following content:
FROM microsoft/dotnet:1.0-sdk-projectjson-nanoserver
ENTRYPOINT ["dotnet", "YoAspnetWin.dll"]
WORKDIR /app
ENV ASPNETCORE_URLS http://+:82
EXPOSE 82
COPY out .
I will explain what each command in
Dockerfile.nano does.
FROM microsoft/dotnet:1.0-sdk-projectjson-nanoserver | We will be using the official Microsoft docker container named microsoft/dotnet:1.0-sdk-projectjson-nanoserver as our starting image. This is a suitable container because it already has the small Windows Nano server. It also has the .NET Core version 1.0 runtime, which is compatible to our web app. Note that it does not contain the .NET Core SDK, so we cannot run commands like dotnet restore, dotnet build, etc… in the container. |
ENTRYPOINT ["dotnet", "YoAspnetWin.dll"] | The web application will be started by executing the “dotnet YoAspnetWin.dll”. The name of the DLL file is dictated by the name of the containing folder during execution of the “dotnet publish” step, which we did previously. |
WORKDIR /app | A directory named /app is created in the Windows container to host our published app. |
ENV ASPNETCORE_URLS http://+:82 | The environment variable ASPNETCORE_URLS is set so that the listening Kestrel web server port in the container is 82. |
EXPOSE 82 | Port 82 is the port number that will be exposed by the container. |
COPY out . | The contents of the out folder is copied into the /app working directory in the container. |
Inside a command-prompt in the YoAspnetWin folder, execute the following to build an image named dotnetweb/nano that contains our web app:
docker build -t dotnetweb/nano -f Dockerfile.nano .
This will download the
microsoft/dotnet:1.0-sdk-projectjson-nanoserver container from
http://hub.docker.com and subsequently build our new image. When it is done, you can type the following command to see all the images you have:
docker images
You will find at least these two images.
- dotnetweb/nano is the docker image we just built
- microsoft/dotnet:1.0-sdk-projectjson-nanoserver is the base image we used
To make a container from the image and run it, type the following command:
docker run -d -p 88:82 dotnetweb/nano
Run the following command to determine which containers are running:
docker ps
It should show the following running images:
You would think that we can access the web site using
http://localhost:88. If you try that your browser will report back that the site can’t be reached. This is because there is a bug with Windows containers running under Windows 10 that does not allow that. The alternative, is to access the web server directly inside the container, which is being exposed on port 82. But, what is the IP address of the container? This is where the “docker inspect” command comes to the rescue. Grab the first two or three characters of the container-id. Looking as the previous screen-capture, the container-id is c1d. Therefore, then run the following command with your container-id:
docker inspect c1d
Grab the container’s IP address and try
http://{container-ip-address}:82. In my case, this is
http://172.29.131.95:82.
Yep. It works.
We must ensure that our database is indeed working. In order to do that, click on
Register in the top-right corner of the web app and enter a user email and password:
After clicking on the
Register button, you should see that the new user is successfully created:
Cleanup
To stop the container you can simply use the first two or three character of the container-id as follows:
docker stop c1d
To see which containers are still available, type:
docker ps -a
To delete the container completely, identify the first two to three character of the container-id then use the “
docker rm” command similar to the following:
docker rm c1d
I you run “
docker ps –a” again you will determine that the container is gone.
Find out what images you have by typing the following command:
docker images
You can delete the image by id or by name, as follows:
docker rmi ce3 OR
docker rmi dotnetweb/nano
After you remove the image you can again type “
docker images” to prove that the image is indeed deleted.