Intro
.NET is a relative newcomer in the open-source world, and its popularity is nowhere near mainstream platforms like Node.js. So you can imagine there're few tutorials that deal with .NET and frameworks such as ASP.NET on Heroku. And those that do, probably won't use containers.
Do you see C#/.NET here? Yes, me neither.
Getting started
This tutorial will assume you have Docker, .NET Core and Heroku tools installed. I will use Linux (Ubuntu), but AFAIK those tools are cross-platform so the steps will be the same for any supported OS.
Let's take the easiest case — simple MVC app. If you don't have one, just create it by running
dotnet new mvc --name mymvc
I'll also assume you have a dockerfile ready, maybe something like proposed in this tutorial:
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS builder
WORKDIR /sources
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish --output /app/ --configuration Release
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
WORKDIR /app
COPY --from=builder /app .
CMD ["dotnet", "MyMvc.dll"]
Note how ENTRYPOINT was substituted with CMD — more on that later.
So, cd to your app's folder and let's begin.
-
Login to Heroku container registry.
heroku container:login
-
If you don't have existing git repo,
git init
a new one -
Run
heroku create
to create a new app, note the git repo address provided, e.g.Creating salty-fortress-4191... done, stack is heroku-16 https://salty-fortress-4191.herokuapp.com/ | https://git.heroku.com/salty-fortress-4191.git
-
(Optional) Check that you have heroku remote by running
git remote -v
-
Tell Heroku to use containers:
heroku stack:set container
-
Create heroku.yml file. Minimalistic version is something like:
build: docker: web: Dockerfile
-
By default ASP.NET core runs on port 5000 and 5001 (https). Heroku won't allow that. If you try running it as-is, Kestrel won't start, throwing an exception:
System.Net.Sockets.SocketException (13): Permission denied
Heroku seems to allow you app to listen on port specified in
$PORT
environment variable. So you need to ensure your app listens on that, rather than default one. In case you're using the default app, just substituteCreateWebHostBuilder
with the following one inProgram.cs
:public static IWebHostBuilder CreateWebHostBuilder(string[] args) { var port = Environment.GetEnvironmentVariable("PORT"); return WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseUrls("http://*:"+port); }
-
Commit everything:
git add . && git commit -m 'Meaningful commit message'
-
Now push the code to get container built and released (fingers crossed):
git push heroku master
Now remember when ENTRYPOINT was substituted with CMD in dockerfile? We don't pass any arguments to container, so
ENTRYPOINT ["dotnet", "MyMvc.dll"]
andCMD ["dotnet", "MyMvc.dll"]
should behave similarly. But if you leave ENTRYPOINT, you'll get an error:
What a great error — "Unexpected fomation update response status"! Really tells you the root of the problem.
The real problem is that when using minimalistheroku.yml
that I showed above, Heroku will expect CMD instruction in your dockerfile. When you add it, everything should work just fine.
Conclusion
Now you should have some idea how to deploy simple ASP.NET Core apps to Heroku. Is it intuitive? Absolutely not. Is Heroku the best platform to host your .NET apps? Probably not. But as it's easy to sign up there and the most basic plan is free — maybe you might want to host something there, just for fun.
References
- https://devcenter.heroku.com/articles/container-registry-and-runtime
- https://devcenter.heroku.com/articles/build-docker-images-heroku-yml
- https://docs.docker.com/engine/examples/dotnetcore/ (Dockerfile)
Автор: S0ren