Customized Container Images for Azure AppService

Heyko Oelrichs
6 min readNov 30, 2020

--

Azure App Service is Microsoft Azure’s Platform-as-a-Service (PaaS) offering to quickly build, deploy and scale web apps and APIs. It supports .NET, .NET Core, Node.js, PHP, Java and many more in containers running on Windows or Linux.

Even though Azure App Service does already support a variety of languages out-of-the-box, there still might be cases that require a specific configuration, optimizations or other scenarios that go beyond the default configuration.

As Azure App Service uses containers under the covers, it also offers the capability to bring your own container images. In this blog post we would like to walk you through the process of building your own, customized container images for Azure App Service.

Let us start. When creating a new Azure Web App, the deployment wizard lets you choose between the default setup called “Code” and “Docker Container”:

Create Web App wizard

Selecting Publish “Docker Container” will unlock an additional page in the deployment wizard:

The “Docker” page allows us to choose between a “Single Container” or “Docker Compose”:

Select between “Single Container” and “Docker Compose”

As well as a selection of different Image sources from where we would like to download our image from:

Azure AppService can download container images from various sources

We are going to use Docker Hub for our example, to make our new container image publicly available. But you can of course also proceed with a private Azure Container Registry or any other private container image registry.

Before we proceed, let us take a deeper look into the image creation itself.

Create a customized Container Image

A good starting point for our custom image is using an existing image as the foundation. Microsoft provides a variety of different container images on Docker Hub and GitHub. These images contain already any AppSvc specific settings and configurations.

The list of available images includes for example a variety of PHP versions:

Currently available PHP container images on GitHub

Let us assume that we need an up-to-date version of PHP 7.4 with some additional PHP extensions enabled and configured.

How can we achieve that?

First of all, we are going to build a custom Dockerfile, based on the original PHP container image:

# Use Microsoft's PHP 7.4 image as a foundation
FROM mcr.microsoft.com/appsvc/php:7.4-apache_20200707.6

Next, we are setting some environment variables and update the operating system and all installed packages as part of the image build process:

# ACCEPT_EULA is required to silently install some packages
ENV ACCEPT_EULA=Y
# Update the Ubuntu operating system and all its installed packages
RUN apt update && apt upgrade -y

As soon as this is done we can install additional PHP extensions like for example mysqlnd_azure used for improved connection routing for Azure MySQL PaaS database servers:

# Install PHP extensions via PECL
RUN pecl install mysqlnd_azure

As this module requires some additional configuration, we can inject this configuration as part of our Dockerfile, too.

To keep the number of layers in our container image low, we are combining both linines into a single RUN step:

# Enable Azure Redirect
RUN echo "extension=mysqlnd_azure" >> /usr/local/etc/php/conf.d/mysqlnd_azure.ini && \
echo "mysqlnd_azure.enableRedirect = on" >> /usr/local/etc/php/conf.d/mysqlnd_azure.ini

That is it. Here is the complete Dockerfile again for better readability:

# Use Microsoft's PHP 7.4 image as a foundation
FROM mcr.microsoft.com/appsvc/php:7.4-apache_20200707.6
# ACCEPT_EULA is required to silently install some packages
ENV ACCEPT_EULA=Y
# Update the Ubuntu operating system and all it's installed packages
RUN apt update && \
apt upgrade -y
# Install PHP extensions via PECL
RUN pecl install mysqlnd_azure
# Enable Azure Redirect
RUN echo "extension=mysqlnd_azure" >> /usr/local/etc/php/conf.d/mysqlnd_azure.ini && \
echo "mysqlnd_azure.enableRedirect = on" >> /usr/local/etc/php/conf.d/mysqlnd_azure.ini

This can now be build using docker build:

docker build -t appsvc-php74-mysqlnd:latest .Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM mcr.microsoft.com/appsvc/php:7.4-apache_20200707.6
---> f8813a5c797d
Step 2/5 : ENV ACCEPT_EULA=Y
---> [..]
---> f67be3e8d490
Step 3/5 : RUN apt update && apt upgrade -y
---> Running in de14d391739d
Get:1 http://deb.debian.org/debian buster InRelease [121 kB]
Get:2 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
[..]
Step 4/5 : RUN pecl install mysqlnd_azure
---> Running in f433f3f78255
downloading mysqlnd_azure-1.1.1.tgz ...
Starting to download mysqlnd_azure-1.1.1.tgz (27,444 bytes)
.........done: 27,444 bytes
[..]
Step 5/5 : RUN echo "extension=mysqlnd_azure" >> /usr/local/etc/php/conf.d/mysqlnd_azure.ini && echo "mysqlnd_azure.enableRedirect = on" >> /usr/local/etc/php/conf.d/mysqlnd_azure.ini
---> Running in 752e515d375dSuccessfully built d2c1b847947e
Successfully tagged appsvc-php74-mysqlnd:latest

Now that our image was successfully build, we are pushing it to our public repository on Docker Hub:

docker push heoelri/appsvc-php74-mysqlnd:latestThe push refers to repository [docker.io/heoelri/appsvc-php74-mysqlnd]
f8699c0b9ff7: Preparing
6a3bc25c5427: Preparing
[..]
9ed763c36c1d: Layer already exists
0b1953527add: Layer already exists
[..]

Our container image is now uploaded to and available on Docker Hub:

Our custom container image hosted on Docker Hub

We can now go back to our AppSvc and deploy it using our custom image.

Deploy App Service using a custom container image

Now that we have a custom container image build and pushed to Docker Hub, let us go back to the Azure App Service, Create Web App wizard and insert the repository and image tag there:

Azure AppService Docker container image source

The “Review + create” summary page should look like this:

Azure AppService deployment wizard summary

The result will be an Azure App Service Web App using our previously customized container image, pulled from our Docker Hub repository. To validate that, you can go to “Container settings” in your App Service in the Azure Portal:

If you would like to have a bit more automation, Azure App Service can be triggered via a Webhook directly from Docker Hub to use a newer container image as soon as it is pushed to Docker Hub.

To leverage that, just check “Continuous Deployment” and save the Web Hook URL in your Docker Hub repository settings.

That will now trigger App Service whenever a new image version is available:

2020-11-25T14:34:05.299Z INFO  - Docker Hook Operation 9b28ec38-d4ed-4528-b2d1-a249a0276ea1 successful, Time taken: 0 Minutes and 1 Seconds
2020-11-25T14:34:05.329Z INFO - Starting container for site
2020-11-25T14:34:05.332Z INFO - docker run -d -p 4568:80 --name customized_3_4cc68609 -e WEBSITES_ENABLE_APP_SERVICE_STORAGE=false -e WEBSITE_SITE_NAME=customized -e WEBSITE_AUTH_ENABLED=False -e PORT=80 -e WEBSITE_ROLE_INSTANCE_ID=0 -e WEBSITE_HOSTNAME=customized.azurewebsites.net -e WEBSITE_INSTANCE_ID=a087bea19439345db21a76f88594d305d37fb4f2015dfe09924ae07097ffb97b heoelri/appsvc-php74-mysqlnd:latest

And that is it. This is a simple way to use customized Container Images in Azure App Service. You can of course extend this setup using other Registries like ACR or in combination with a CI/CD pipeline, but the core remains the same.

Before we close here, let us quickly validate that our module was successfully enabled and configured. One option is to use phpinfo() for that:

As you can see above, the module is loaded and configured.

Another option is to use the built-in Azure App Service (web) SSH client:

To SSH into our container and check the configuration via the php cli:

And that is it. I hope that gives you a good idea how to leverage custom container images for your Azure App Service deployments.

--

--