Build & release a Container Image from Azure DevOps to Azure Web App for Containers

I recently published a blog post on 4sysops.com about Web App for Containers on Azure here: https://4sysops.com/archives/web-app-for-containers-on-azure. That blog post is about the often-overlooked service in Azure that can be used to host a container/s on a web app in Azure App service.

This is a great service if you just need to run a single container or even a couple of containers that you have in Docker Compose. This service is PaaS and abstracts away an orchestration system like Kubernetes. If you need insight into the Azure App Service Web App for Containers service check out the blog post on 4sysops.

In this long blog post I am going to take things a step further and walk-through the build & release of a Container from Azure DevOps to Azure Web App for Containers. The overall goal of this post is to help someone else out if they want to setup a build and release pipeline for building and deploying a container to Azure App Service. We will use a very simple PHP web app I built that will run in the container.

Here are the components that are involved in this scenario:

  • Azure Container Registry (ACR): We will use this to store our container image. We will be pushing up the container image and pull it back down from the registry as a part of the build and release process.
  • Azure DevOps (ADO): This is the DevOps tooling we will use to build our container, push it up to ACR, pull it down into our release pipeline and then deploy to our Azure App Service.
  • App Service Web App for Containers: This is the web server service on Azure that will be used to host our container. Under the hood this will be a container that is running Linux and Apache to host the PHP web app.

Here is the data Flow for our containerized web app:

  1. Deploy the Azure App Service Web App for Containers instance
  2. Deploy the Azure Container Registry
  3. Deploy the Azure DevOps organization and project, create repository to host the code, clone repository in VS Code (Not shown in this blog post. Assume you know how to this up.)
  4. Update the application code (PHP code and Docker image) in Visual Studio code
  5. Commit application code from Visual Studio code to the Azure DevOps repo (Not shown in this blog post. Assume you know how to this up.)
  6. Setup build and then run container build and push the container image to ACR
  7.  Setup release pipeline and then kick off the release pipeline pulling down the container image from ACR and deploys the container to the App Service Web App for Containers instance.

Here is a diagram detailing out the build and release process we will be using:

Click to enlarge

Note that all code used in this blog post is hosted on my GitHub here: https://github.com/Buchatech/EOTD-PHP-APP-DOCKER-CONTAINER

Ok. Let’s get into the setup of core components of the solution and the various parts of the build and release pipeline.

For starters this solution will need a project in Azure DevOps with a repo. Create a project in Azure DevOps and a repo based on Git. Name the repo exerciseoftheday. Next up let’s create the core framework we need in Azure.

Deploy Azure App Service Web App for Containers

Let’s create the Azure App Service Web App for Containers that will be needed. We will need a resource group, an app service plan and then we can setup the app service. The PHP app we will be running is named Exercise Of The Day (EOTD) for short so our resources will use EOTD. Use the following steps to set all of this up.

We will do everything via Azure Cloud Shell. Go to https://shell.azure.com/ or launch Cloud Shell within VS Code.

Run the following Syntax:

# Create a resource group

az group create –name EOTDWebAppRG –location centralus

# Create an Azure App Service plan

az appservice plan create –name EOTDAppServicePlan –resource-group EOTDWebAppRG –sku S1 –is-linux

# Create an Azure App Service Web App for Containers

az webapp create –resource-group EOTDWebAppRG –plan EOTDAppServicePlan –name EOTD –deployment-container-image-name alpine

# Create a deployment slot for the Azure App Service Web App for Containers

az webapp deployment slot create –name EOTD –resource-group EOTDWebAppRG –slot dev –configuration-source EOTD

Deploy Azure Container Registry

Now let’s create the Azure Container Registry. Again, this is where we will store the container image. Run the following Syntax:

# Create Azure Container Registry

az acr create –resource-group EOTDWebAppRG –name eotdacr –sku Basic –admin-enabled false –location centralus

Note the loginServer from the output. This is the FQDN of the registry. Normally we would need this, admin enabled, and the password to log into the registry. In this scenario we won’t need admin enabled or the password because we will be adding a connection to Azure DevOps and the pipelines will handle pushing to and pulling the image from the registry.

When it’s all done you should see the following resources in the new resource group:

Next, we will need to build an application and a container image.

PHP Web App

We are going to build a simple PHP web application that will be easy to run in a single Docker container. It is a PHP web application that displays a picture of an exercise for the current day based on the day of week. No database is used in this application. The code is referencing an images folder for the images of the exercises. Here is the PHP web app code:

<html>
<head>
<title>
Exercise of the day (EOTD)
</title>
</head>
<center>
<h1 style="background-color:MediumSeaGreen;">
Exercise of The Day
</h1>
<p style="background-color:LightGray;">
This is a simple PHP web app that displays the exercise of the day based on the day of week!
</p>
<?php
$today=date(l);
// Find what The exercise of ? using date function
if($today==Monday){
    echo "The exercise of <b> <font color=green> Monday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/punchingbag.png'>";
}
elseif($today==Tuesday){
    echo "The exercise of <b> <font color=green> Tuesday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/sideplank.png'>";
}
elseif($today==Wednesday){
    echo "The exercise of <b> <font color=green> Wednesday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/jogging.png'>";
}
elseif($today==Thursday){
    echo "The exercise of <b> <font color=green> Thursday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/plank.png'>";
}
elseif($today==Friday){
    echo "The exercise of <b> <font color=green> Friday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/weights.png'>";
}
elseif($today==Saturday){
    echo "The exercise of <b> <font color=green> Saturday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/shoulderpress.png'>";
}
elseif($today==Sunday){
    echo "The exercise of <b> <font color=green> Sunday </font> </b> is";
    echo "<BR>";
    echo "<img src='images/bicycle.png'>";
}
?>
<p style="border:2px solid Orange;">
Visit daily for a new exercise.
</p>
</center>
</body>
</html>

Dockerfile

Part of the process is to create a Dockerfile that will be used to create the Docker image using the Docker build process. This creates the actual Linux server installs PHP and Apache web server on it then copies the PHP website files in.

# Dockerfile
FROM ubuntu:latest
MAINTAINER Steve Buchanan <sbuchanan@buchatech.com>
ENV DEBIAN_FRONTEND noninteractive
# Install Core Components
RUN apt-get update
RUN apt-get -y install curl
RUN apt-get install -y software-properties-common && \
add-apt-repository ppa:ondrej/php && apt-get update
RUN apt-get install -y vim
# Install PHP 5.6
RUN apt-get install -y php5.6 php5.6-mysql php5.6-mcrypt php5.6-cli php5.6-gd php5.6-curl php5.6-xml php5.6-mbstring php5.6-sqlite
# Enable apache mods
RUN a2enmod php5.6
RUN a2enmod rewrite
# Apache environment variables
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
ENV APACHE_PID_FILE /var/run/apache2.pid
# Expose ports on Apache
EXPOSE 80
EXPOSE 8080
EXPOSE 443
# Override the default apache site 000-default.conf file with our configuration
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
# Override the default apache2.conf file with our configuration
COPY apache2.conf /etc/apache2/apache2.conf
# Copy the samsoncz folder into the DocumentRoot directory var
COPY exerciseoftheday /var/www/html/exerciseoftheday
# Copy the phpinfo.php into the DocumentRoot directory
COPY phpinfo.php /var/www/html
# By default, start up apache in the foreground, override with /bin/bash for interative.
CMD /usr/sbin/apache2ctl -D FOREGROUND
# Set working directory for exerciseoftheday
WORKDIR /var/www/html/exerciseoftheday

We are not going to use Docker locally to build the code. We are going to let Azure DevOps build the container image for us and then push it up to ACR. In order to do this, we will need to commit the Dockerfile and the PHP code up to the Azure DevOps repo. All of the needed code should be in the exerciseoftheday folder downloaded from my GitHub repo. So, you don’t have to code the PHP web app or the Dockerfile. I simply wanted to show it in the blog post.

 

The next step would be to commit the code from VS Code to Azure DevOps repo then build your dockerfile. You can either upload all of the files from the exerciseoftheday folder to your Azure DevOps repo or you are welcome to go through the entire process of connecting VS Code and Azure DevOps and syncing from there.

 

Next, we are going to get into setup and configuration in Azure DevOps building out the build and release pipelines.

Add ACR in Azure DevOps

Before we setup the build we will need to add the Azure Container Registry as a service connection in Azure DevOps. This service connection will be used in the build and the release pipelines. Use the following steps to set this up:

  • Go to https://dev.azure.com navigate to ORGANIZATION NAME > PROJECTNAME > Settings > Service connections*
  • Click Create service connection.
  • Select Docker Registry.

Select Azure Container Registry. Select the proper subscription, the proper ACR, add any needed details and click save.

When it is done you should see the service connection for the ACR listed. Note the name of the service connection should be the FQDN of the ACR. We took note of this earlier when we created the ACR instance.

Build Pipeline

Now that we have the service connection for ACR we can go ahead and setup the build pipeline. I have included the YAML file for the build pipeline in the exerciseoftheday folder on my GitHub. Below is the YAML code for the build pipeline so that you can see it.

# Docker
# Build a Docker image 
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
trigger:
- master
resources:
- repo: self
variables:
  tag: '$(Build.BuildId)'
stages:
- stage: Build
  displayName: Build image
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    
    - task: Docker@2
      displayName: Build an image and push to ACR
      inputs:
          containerRegistry: eotd.azurecr.io
          repository: eotd-iv0
          command: 'buildAndPush'
          Dockerfile: '**/Dockerfile'
          tags: |
            $(Build.BuildId)

Ensure the EOTD Build and Push.yml file is uploaded to your repo. Should look similar to:

Next, we will import the build pipeline in Azure DevOps. To do this use the following steps:

Go to https://dev.azure.com navigate to ORGANIZATION NAME > PROJECTNAME > Repos > Files > exerciseoftheday

Click on Set up build

Select Existing Azure Pipeline YAML file.

Point to the EOTD Build and Push.yml in the repo.

Ensure the code shows properly, save it and run.

If the build is successful it should look similar to:

The EOTD ACR instance repository should also have the image in it now. Use the following path to check this:

Go to https://portal.azure.com Dashboard > Resource groups > EOTDWebAppRG > eotdacr – Repositories > eotd-iv0.

Release Pipeline

This will deploy the container image to the Azure App Service Web App for Containers instance. TO setup and configure the release pipeline use the following steps.

Go to https://dev.azure.com navigate to ORGANIZATION NAME > PROJECTNAME > Pipelines > Releases

Click the arrow next to New and select “Import release pipeline” to import the EOTD to Azure Web App.json file.

After it is imported you will need to update the Agent Pool and authorize your Azure subscriptions in both the dev and prod Stages.

Set the Agent Pool and the source to the repo.

Click on Create release to run the pipeline.

After it is done you should see it succeeded.

You check that the container image is running on the web app by going to https://portal.azure.com > Dashboard > EOTD – Container settings

When you visit the URL of the web app you should see the similar to the following screenshot:

Leave a Comment