On my previous post I described how to automate a deployment to Heroku using CircleCI and Docker. I will switch gears now and write about how to set up a cross-platform development environment for the project. You see, turns out a friend of mine that I collaborated for this project could not even run the web application on his MacBook. For all that fanfare about .NET Core being cross-platform and whatnot, this project just would not run on his Mac (weird binding problems that he spent hours debugging).
Luckily, we figured out how to use Docker to normalize on a development environment. Getting this to work required enough steps that it will take multiple blog posts to go over them. Here are the high level goals:
- Have a docker container with .net core installed run the application, but have the source point to the code in the host machine (we will use Docker volumes for that).
- Setup a file watcher so that every time the code is modified on the host machine, the process is restarted in the docker container (for javascript as well as for C# code).
For now, let's start by looking at a new Docker.dev file that we use for development and go over the many hurdles to get this to work.
FROM microsoft/aspnetcore-build:2.0.3 # Required inside Docker, otherwise file-change events may not trigger ENV DOTNET_USE_POLLING_FILE_WATCHER 1 ENV ASPNETCORE_ENVIRONMENT Development ENV DOCKER_ENVIRONMENT Development # Set a working dir at least 2 deep. The output and intermediate output folders will be /code/obj and /code/bin WORKDIR /code/app # By copying these into the image when building it, we don't have to re-run restore everytime we launch a new container COPY *.csproj . COPY NuGet.config . COPY Directory.Build.props . RUN dotnet restore # This will build and launch the server in a loop, restarting whenever a *.cs file changes ENTRYPOINT dotnet watch run --no-restore
Setup the Image Binaries
The first hurdle is that ASP.NET Core gets plenty confused when the bin and obj folders are shared between the docker container and the host machine. Instead of dealing with that, one solution is to keep them separate by using a Directory.Builds.props file. This file configures dotnet to output the bin and obj folders next to the project directory instead of inside it. So if your local project live in C:\dev\daycare, the binaries will get produced in C:\dev\daycare_bin, not ideal, but hey, the show must go on.
Notice in line #14 that the Directory.Builds.props file is copied into the image before the solution is built. BUT, the real trick is line #9, where the working directory within the image is set to '/code/app'. This needs to be 2 folders deep so that the binaries are produced in /code/daycare_bin. Now the binaries inside and outside the docker container are separate and we can move on.
Setup the Dotnet File Watcher
There is an official Microsoft.DotNet.Watcher.Tools package that we can use to setup the watcher for .net code. Well it turns out the version that supports Docker is only available in preview. Starting from .NET Core 2.1 it has been rolled into the DotNetCLI, but for projects running on previous versions of .NET Core an extra file is needed to add the nuget feed that has the version 2.1.0-preview of this tool. You can see the contents of the Nuget.config file here and it needs to be copied into the image as you can see in line #13.
Then, even when you get this version, it doesn't work out-of-the-box from within a Docker container that uses volumes, you need to configure it to use file polling instead of relying on the OS file notifications. This is done by setting the DOTNET_USE_POLLING_FILE_WATCHER environment variable to 1 as you can see in line #4.
Finally, you can see in line #18 that the docker entry point now uses 'dotnet watch run' to kick off the file watcher.
Run it!
After you build the image with this new Docker file you can run it and map the port and the volume like this:
docker run -p 5000:80 -v .:/code/app
Notice that the current directory is mapped to the folder /code/app inside the container. When the container runs it will use the binary from within the container but your sources for the file watcher and building. You can now navigate to localhost:5000 to load the application that is running in docker and if you modify any C# code in your machine you will notice that dotnet in docker will automatically restart.
But wait, there is more! But it will have to wait until the next blog post :P. In the mean time you can see the website hosted in Heroku here or browse the sources at the project page.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.