This guide will be useful to startups who developed an application on the .net core and started to think about the place for its deployment, so it can bring some happiness to the whole world, besides their relatives.
Based on the knowledge of our nature, surely we like to get something for free. Attention please, we have good news for you – now it is possible! There is a great solution now that’s called Heroku platform.
Heroku is a cloud-based PaaS platform that supports a variety of programming languages (C# was not officially supported at the time of this article, but that doesn’t stop us) and is based on a managed container system for deployment and launching applications. Heroku provides DNS * .herokuapp.com.
Preliminary preparation
First of all, we will need a sample of the .Net Core application that you are proud of and which you think deserves to be shown to the world.
I will use the .Net Core web API as a test case.
Next, we will use Docker as a container of the application.
To do this without any problems, you need to register at https://hub.docker.com/. There you can also find instructions for the Docker Desktop installation that we’ll need later.
Additionally, we need to check in the developed project with Docker files on GitHub.
And the last thing we need to do is to register for Heroku.
The crucial moment is the deployment
Step 1. Prepare to create a container image
When my test solution is ready for deployment, its structure generally looks like:
Step 2. Create an image
My next step is to create an image to containerize the application. For this, we need to add two files to the solution folder: Dockerfile and .dockerignore.
Content of the Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["LimeHomeTest.Web/LimeHomeTest.Web.csproj", "LimeHomeTest.Web/"]
COPY ["LimeHomeTest.Services/LimeHomeTest.Services.csproj", "LimeHomeTest.Services/"]
COPY ["LimeHomeTest.Repository/LimeHomeTest.Repository.csproj", "LimeHomeTest.Repository/"]
COPY ["LimeHomeTest.Dto/LimeHomeTest.Dto.csproj", "LimeHomeTest.Dto/"]
RUN dotnet restore "LimeHomeTest.Web/LimeHomeTest.Web.csproj"
COPY . .
WORKDIR "/src/LimeHomeTest.Web"
RUN dotnet build "LimeHomeTest.Web.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "LimeHomeTest.Web.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
CMD ASPNETCORE_URLS=http://*:$PORT dotnet LimeHomeTest.Web.dll
Let’s look at it more closely. Dockerfile is something like a batch script with a few steps. As we can see from the script, first we copied all draft solutions, builds, publishes, and packagings.
It is worth noting the last line, as it executes the command to launch the application when the image is launched in the docker container.
It’s specific to Heroku since Heroku gets the HTTP traffic through a port that is a $ PORT environment variable. Therefore, you must specify a default address for use in the container applying the $PORT variable.
The .dockerignore file contains everything that will be ignored by the docker.
After the Dockerfile and .dockerignore files have been written and placed in the solution folder, and Docker Desktop has been installed and started, we can try to create an image locally using Power Shell to ensure that it was created successfully. In the future, the image will be created using the workflow on GitHub, but let’s be consistent…
So, let’s run the Power Shell (as well as Docker Desktop) using the administrator rights, go to the solution folder (the dockerfile is also there) and then run the command:
docker build . -t <image_name> -f Dockerfile
If the image was created successfully, among the other things in the display we can see the following:
Successfully built cb992a976e1d
Successfully tagged lime-test:latest
If not, congratulations, time to start making some song and dance about it. Read the mistakes carefully and you may understand what is wrong …
Step 3. Create an application on Heroku
And we are moving on. The next step is to create an application on Heroku. On the dashboard you will find the New button:
We click on it, fill in the App name field, select the closest region, and click the Create app. The application has been created, so we successfully passed step 3.
Step 4. The last one. Set up continuous integration using the GitHub tools
We have the last leap left, so we gather all our strength and quickly pass the last stage. To do this, you need to create a project repository on GitHub. We will not dwell on this in detail.
So, your application based on the docker files is already in the GitHub repository. You will need to use the secret key provided by Heroku to set up continuous integration.
All we need to do is to copy the secret key. Then click on the user icon in the upper right corner of the Heroku dashboard and select “Account settings”. Scroll down until you see the “API Key”, then click Reveal and copy the key.
In the GitHub repository select the “Settings” menu, then “Secrets” – “New secret”.
In the Name field, we prescribe the name of the key (I’ve chosen – HEROKU_API_KEY) and insert the key copied from Heroku in the Value field.
And now it’s time to set up an Action on GitHub. We return to the repository, select the Actions menu (located in the same place as “Settings”), “set up a workflow yourself”.
That’s how we get the main.yml – workflow-file on GitHub that can help us to configure the same continuous integration. So, let’s edit this file as follows:
name: HerokuContainer
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build and deploy the Docker image
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
APP_NAME: ${{ 'lime-home-test' }}
run: |
docker login --username=_ --password=$HEROKU_API_KEY registry.heroku.com
heroku container:push web -a $APP_NAME
heroku container:release web -a $APP_NAME
In the first line, we set the name of the workflow, then apply the trigger to start the workflow on the push in the master-branch. Each workflow must have at least one job that contains a sequence of steps to complete a specific task.
In our case, we use one job that runs on the default virtual environment (ubuntu-latest). In the HEROKU_API_KEY and APP_NAME variables we write down the secret Heroku key and the name of the app that was created on Heroku, then a line of code launches a docker file to create an image of the container:
docker login --username=_ --password=$HEROKU_API_KEY registry.heroku.com
The next command builds the image and pushes it into the Heroku container (lime-home-test app). After that, we can finally create a release, putting it in the last line of code.
So, the workflow is ready, so we’ll click Start commit in the window that appears – Commit changes, that will start the execution of this workflow.
After that, if you go to Actions, you will see the created workflow with commits.
In my case, it’s the Update main.yml commit in the build stage (followed by a yellow circle).
You can see the information about the course of the build by following the commit link. On the build tab, you can find all the necessary information. If everything went smoothly, then all items will have green ticks.
Locally
After that, don’t forget to process Pull in the local project to extract the workflow that was just created in GitHub. You will find the main.yml file in the root folder of the project. Next folder – .github -> workflows-> main.yml.
As you probably already guessed, in the described way we can manually create the workflow as the main.yml-file, and it would be started in the commit stage as well.
Hurrah! We did it, the workflow worked without errors, and the application should already be deployed on Heroku.
And from now on, every change that will get into the master branch automatically through the configured workflow on GitHub will be deployed on Heroku.
Congrats!
P.s. If you need any help with hosting your application or developing it and validating its idea or features – UppLabs is here to help!