MFI Engineering MFI Home

Get In Touch

Prefer using email? Say hi at

Simplifying Docker Deployment with Github Actions and Packages

What we started with

When we first started developing the MFI Core application, we followed most of the Microsoft documentation on deploying a Docker image to an Azure App Service. It worked and was fine, but we have a small IT team and are always seeking to reduce complexity whenever possible.

Shout out to the Nonprofit Team1 at GitHub for the free organization membership.

Here is what our process looked like when we first had everything wired up:

Step Platform Trigger
Push Code Github Developer
Publish Build Azure DevOps Github integration
Store Image Azure Container Registry Push from build pipeline
Trigger Release Azure DevOps Successful build
Pull Image from ACR Azure App Service Integration with Azure DevOps Release

This worked, but we had to bounce between Github, Azure DevOps, and Azure portal to check the progress of the image and the final deployment.

Cutting out Azure DevOps

Our first step for reducing complexity was to remove the Azure DevOps release pipeline and call an App Service webhook2 from the Azure Container Registry that would send a request to the App Service to let it know a new build was available with the appropriate tag. This was wonderful because our release pipeline really wasn’t doing much. However, it now seemed a little silly to have to log into the DevOps portal just to maintain a build pipeline. So we started to investigate GitHub actions for building and pushing the images to ACR.

Having little experience with using GitHub actions, I was impressed by how easy it was. We just included a main.yaml file in /.github/workflows/ of our project and GitHub handled the rest. To start out we were using the example from the Azure team found here: https://github.com/Azure/docker-login.

1
2
3
4
5
6
7
8
9
10
### snippet for building and pushing
- uses: azure/docker-login@v1
  with:
    login-server: contoso.azurecr.io
    username: $
    password: $

- run: |
    docker build . -t contoso.azurecr.io/k8sdemo:$
    docker push contoso.azurecr.io/k8sdemo:$

To get this to work properly, you need to get your username and password from ACR and store them as secrets in your GitHub repository under REGISTRY_USERNAME and REGISTRY_PASSWORD.

With this change, we has now cut out the need for Azure DevOps.

Step Platform Trigger
Push Code Github Developer
Publish Build Github Github Action
Store Image Azure Container Registry Push from GitHub Action
Pull Image from ACR Azure App Service Webhook call from ACR

Cutting out Azure Container Registry

While investigating GitHub actions, we came across this template: github.com/actions/starter-workflows/blob/master/ci/docker-push.yml. This template was created to build and push Docker images to GitHub’s Package service. Similar to GitHub Actions, we had no exposure to GitHub Packages. We had seen the product announcement from GitHub but had no reason to dig any deeper. Luckily, modifying our GitHub action was a simple as switching out some YAML, so we gave this starter template a shot.

One thing we instantly liked was being able to remove the ACR secrets from our repository. The template uses a token generated at runtime by GitHub called GITHUB_TOKEN to authenticate against the package repository. You’ll see shortly that this is just a temporary win, we will eventually need the App Service credentials in our webhook call to deploy the package.

NOTE: We did run into issues with the GITHUB_TOKEN after a few days. It seemed like the auto-generated token lost permission scope or something, so we had to create a personal access token with the correct permissions until GitHub fixed it. Issue: denied: Resource not accessible by integration 3

Our final main.yml ended up looking like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
name: Docker

on:
  push:
    branches:
      - master

    tags:
      - v*

env:
  IMAGE_NAME: mficore

jobs:
  push:
    runs-on: ubuntu-latest
    if: github.event_name == 'push'

    steps:
      - uses: actions/checkout@v2

      - name: Build image
        run: docker build . --file Dockerfile --tag image

      - name: Log into registry
        run: echo "$" | docker login docker.pkg.github.com -u $ --password-stdin

      - name: Push image
        run: |
          IMAGE_ID=docker.pkg.github.com/$/$IMAGE_NAME
          # Strip git ref prefix from version
          VERSION=$(echo "$" | sed -e 's,.*/\(.*\),\1,')
          # Strip "v" prefix from tag name
          [[ "$" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
          # Use Docker `latest` tag convention
          [ "$VERSION" == "master" ] && VERSION=latest
          echo IMAGE_ID=$IMAGE_ID
          echo VERSION=$VERSION
          docker tag image $IMAGE_ID:$VERSION
          docker push $IMAGE_ID:$VERSION

Once the action completed, we were able to see the package in our repo and also pull it down and run it locally.

docker image in github

Everything seemed easy breezy, but you could tell that GitHub Packages and Actions still have some kinks to be ironed out. As I mentioned above, we ran into an issue with the authentication token for our package repo. It also seems like we have no way of deleting old versions or packages, which we are still investigting. Despite these hiccups, we were happy enough with the way everything worked to set up a webhook call from GitHub and cut out ACR. Being a small nonprofit, it is always nice to save the money on our Azure for Nonprofits4 grant for running applications.

To configure your Azure App Service webhook endpoint, you need to go to the Container settings section of your App Service and select the Private Registry tab.

Azure App Service Private Registry

  • Server URL sould be https://docker.pkg.github.com
  • Login should be your GitHub account
  • Password would be a GitHub Personal Access Token5
  • creating-a-personal-access-token-for-the-command-line) with read:packages in scope
  • Image and optional tag should come from the pull instructions on your package repo
  • Continuous Deployment should be On

Next, you will want to copy the WebHooks URL so that you can paste it into the GitHub webhook configuration.

GitHub Webhook Configuration

  • The Payload URL should be the URL you copied from Azure App Service
  • Content Type should be application/json
  • No secret needed, credentials are in the Payload URL
  • Select Let me select individual events and check the Packages option

Once everything is set up, you should be able to make a push or merge into the master branch and see your package update and the App Service pull the image shortly after.

Azure Docker Logs

End Result

Now that we have all of the pieces in place, our steps look like this:

Step Platform Trigger
Push Code Github Developer
Publish Build Github Github Actions
Store Image Github Push from Github Action to Packages
Pull Image from ACR Azure App Service Github Webhook

We no longer have to monitor Azure DevOps or pay for Azure Container Registry. We can make our GitHUb Actions as complicated as we like and add in tests for the builds.