My $0.02
Heroku is great. You can deploy a concept for free and the next highest tier is a few bucks away. When you’re ready to go to scale, it’s ready and prorated to the second. Neat.
Docker is great. You can build, test, and deploy in any configuration on a local stack. Everything is ephemeral awesome.
.NET Core is great. I can write great apps with the framework I prefer and not be stuck on Windows. If you’re an old, crusty Win32 developer, run dotnet new winforms && dotnet run from the .NET Core 3 CLI and tell me I’m wrong. Gnarly?
Recipe
This recipe assumes you got the tools setup already and have some experience in the tooling. ##Tooling
- .NET Core 3 SDK
- I’m using the Blazor Server option for my example.
- Heroku CLI
- Login
heroku login
then loginheroku container:login
.
- Login
- Docker
- I’m using Engine 19.x currently to take advantage of that new, sweet buildkit system. Should work on at least on 18.x as well.
Prepare Dockerfile
- Create a
Dockerfile
file at the root of your project (it isn’t required to be there but for this example I’ll put it next to the csproj).* You can use Visual Studio 2017/2019 to produce a Dockerfile for you by right-clicking the project and adding container support.) - Inside the
Dockerfile
add the following:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build
WORKDIR /src
COPY blazor-server.csproj .
RUN dotnet restore "blazor-server.csproj"
COPY . .
RUN dotnet build "blazor-server.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "blazor-server.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "blazor-server.dll"]
Now you got a buildable image script. The caveat for Heroku containers is that it has to be able to inject the port number as an environment variable. That’ll come in as an environment variable named PORT. Let’s update the Dockerfile to reflect that.
- Update
ENTRYPOINT ["dotnet", "blazor-server.dll"]
toCMD ASPNETCORE_URLS=http://*:$PORT dotnet blazor-server.dll
You should look like this now:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build
WORKDIR /src
COPY blazor-server.csproj .
RUN dotnet restore "blazor-server.csproj"
COPY . .
RUN dotnet build "blazor-server.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "blazor-server.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
CMD ASPNETCORE_URLS=http://*:$PORT dotnet blazor-server.dll
If you don’t want to comment/uncomment your ENTRYPOINT
when you need to run locally, you can run with a env flag e.g. docker run -d -e PORT=1234 -p 9876:1234 --name blazored my-blazor-server-image:neverlatest
. * Use docker-compose and simplify your dev life!
Build and push to Heroku container ?service?
- Create a new app space in Heroku and get a redonkulous name using
heroku create
. - Run
heroku container:push web -a {redonkulous-new-name-you-just-got}
.web
is the type:web
orworker
-a
is the app name reference- You can also push an already built image via
docker push
but this command handles everything for you as long as it can see the Dockerfile (It uses Docker to build the image, retags it, and pushes for you. Here’s the Heroku docs if you need them.
- Run
heroku container:release web
to publish.- Should be running momentarily!
That’s it!
At least, as far as I know. Mine works. :)
Bonus
This will work with anything as long as you can map the port env var at runtime. Going to scale is a different story but being able to enjoy the goodness of containers on an affordable platform is fantastic.
If you have any issues, sound off on Twitter @bitobrian and let me know if this helped.