Monday, May 11, 2020

CI/CD pipelines with ASP.NET MVC 3.1, GitHub Actions and Docker Hub

Overview

In this post I will show you how easy it is to implement a CI/CD DevOps pipeline using GitHub Actions. The sample web application I will use is an ASP.NET Core 3.1 MVC application and I will deploy it to Docker Hub. 
Before proceeding, It is assumed that you have the following pre-requisites: 
  • You already have a Docker Hub account
  • You already have a GitHub account
  • You have .NET Core 3.1 (or later) installed on your computer

Creating a simple ASP.NET Core 3.1 application

In a working directory, execute the following command from within a terminal window:

dotnet new mvc -o GithubActions2DockerHub

Change directory to the newly created app:

cd GithubActions2DockerHub

Add a .gitignore file to the application:

dotnet new gitignore

Let us make the application our own by, perhaps, changing the title of the app and the background color.

1) Add the following CSS to wwwroot/css/site.css:
body {
   background-color: tomato;
}

2) In Views/Home/Index.cshtml, change the main heading from:

<h1 class="display-4">Welcome</h1>

TO

<h1 class="display-4">Welcome to our MVC Core 3.1 App</h1>

Let us now run the application and see what it looks like. In the terminal window run the following command:

dotnet run

Point your browser to https://localhost:5001. You will see a strangely colored web page that looks like this:



Stop the server by hitting CTRL + C in the terminal window.

Dockerfile

In the root folder of your project, add a file named Dockerfile (no extension) with the following content:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
WORKDIR /app

# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore

# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "GithubActions2DockerHub.dll"]

Push your code to GitHub.

We are now ready to push this application to GitGub. Go to GitHub and create a repository. I created a repository named GithubActions2DockerHub. Copy the commands that are shown on the GitHub page, under “…or push an existing repository from the command line”, that looks like this:


Back in your application’s terminal window, type the following commands to initialize a git repo, add files to the repo and commit your changes:

git init
git add .
git commit -m "1st commit"

Next, we will push the code to GitHub. Paste the git commands that you copied from GitHub. This is what it looked like for me:

git remote add origin https://github.com/medhatelmasry/GithubActions2DockerHub.gitgit push -u origin master

If you refresh the GitHub page, you will see that your code is in GitHub. It looks like this:

Click on Secrets on the left-side:

Build & Deploy using GitHub Actions

 Back it GitHub, click on the Settings tab of your application’s repo:
 
Click on Secrets on the left-side:
Click on “Add a new secret”:
 You will enter your Docker Hub username and password as GitHub secrets. Enter secrets DOCKER_USERNAME & DOCKER_PASSWORD together with their respectivev values:
    
Click on Actions on the top navigation bar in GitHub:
You will see a multitude of templates for a multitude of technologies and programming languages. Scroll down to the “Continuous integration workflows” and select “Docker image”.
This will create a .github/Workflows folder in your application. Name the file deploy_to_docker_hub.yml. 
Your deploy_to_docker_hub.yml looks like this:
name: Docker Image CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

  jobs:

    build:

      runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Build the Docker image
        run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)

Unfortunately, the above instructions only build the docker file but do not deploy to docker hub. We will replace the instructions (under steps:) with instructions that checkout, build and deploy.

Therefore delete the following lines at the bottom of the deploy_to_docker_hub.yml file:

- name: Build the Docker image
run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)

Replace the above code with the following:

- uses: docker/build-push-action@v1
  with:
    username: ${{ secrets.DOCKER_USERNAME }}
    password: ${{ secrets.DOCKER_PASSWORD }}
    repository: melmasry/aspnetmvc
    tags: v1

The image will be named melmasry/aspnetmvc and the tag (version) will be v1.
Make sure tab the instructions in deploy_to_docker_hub.yml so that it is indented as shown below:

name: Docker Image CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - uses: docker/build-push-action@v1
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}
        repository: melmasry/aspnetmvc
        tags: latest

NOTE: replace melmasry with your Docker Hub username.

Click on the green “Start commit” button:
Enter a comment and description for the commit then click on the “Commit new file” button:
 Click on “Actions” >> “All Workflows” >> your workflow.  
Then click on build:
The build and deploy should be succcessfully completed. The real test comes with pulling the image from DockerHub and running it locally on your computer. Run the following command from a terminal window on your computer:

docker run -d -p 8888:80 melmasry/aspnetmvc:latest

NOTE: replace melmasry with your Docker Hub username.
Point your browser to http://localhost:8888. You will see the same ugly tomato colored web page:
 
Happy coding!!