Use a custom Docker image for Web App for Containers

Web App for Containers provides built-in Docker images on Linux with support for specific versions, such as PHP 7.0 and Node.js 4.5. Web App for Containers uses the Docker container technology to host both built-in images and custom images as a platform as a service. In this tutorial, you learn how to build a custom Docker image and deploy it to Web App for Containers. This pattern is useful when the built-in images don't include your language of choice, or when your application requires a specific configuration that isn't provided within the built-in images.

Prerequisites

To complete this tutorial, you need:

If you don't have an Azure subscription, create a free account before you begin.

Download the sample

In a terminal window, run the following command to clone the sample app repository to your local machine, then change to the directory that contains the sample code.

git clone https://github.com/Azure-Samples/docker-django-webapp-linux.git --config core.autocrlf=input
cd docker-django-webapp-linux

Build the image from the Docker file

In the Git repository, take a look at Dockerfile. This file describes the Python environment that is required to run your application. Additionally, the image sets up an SSH server for secure communication between the container and the host.

FROM python:3.4

RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/

# ssh
ENV SSH_PASSWD "root:Docker!"
RUN apt-get update \
        && apt-get install -y --no-install-recommends dialog \
        && apt-get update \
    && apt-get install -y --no-install-recommends openssh-server \
    && echo "$SSH_PASSWD" | chpasswd 

COPY sshd_config /etc/ssh/
COPY init.sh /usr/local/bin/

RUN chmod u+x /usr/local/bin/init.sh
EXPOSE 8000 2222
#CMD ["python", "/code/manage.py", "runserver", "0.0.0.0:8000"]
ENTRYPOINT ["init.sh"]

To build the Docker image, run the docker build command, and provide a name, mydockerimage, and tag, v1.0.0. Replace <docker-id> with your Docker Hub account ID.

docker build --tag <docker-id>/mydockerimage:v1.0.0 .

The command produces output similar to the following:

# The output from the commands in this article has been shortened for brevity.

Sending build context to Docker daemon  5.558MB
Step 1/13 : FROM python:3.4
 ---> 9ff45ddb54e9
Step 2/13 : RUN mkdir /code
 ---> Using cache
 ---> f3f3ac01db0a
Step 3/13 : WORKDIR /code
 ---> Using cache
 ---> 38b32f15b442
.
.
.
Step 13/13 : ENTRYPOINT init.sh
 ---> Running in 5904e4c70043
 ---> e7cf08275692
Removing intermediate container 5904e4c70043
Successfully built e7cf08275692
Successfully tagged cephalin/mydockerimage:v1.0.0

Test that the build works by running the Docker container. Issue the docker run command and pass the name and tag of the image to it. Be sure to specify the port using the -p argument.

docker run -p 2222:8000 <docker-ID>/mydockerimage:v1.0.0

Verify the web app and container are functioning correctly by browsing to http://localhost:2222.

Test web app locally

Push the Docker image to Docker Hub

A registry is an application that hosts images and provides services image and container services. In order to share your image, you must push it to a registry.

Note

Pushing to a Private Docker Registry? See the optional instructions to Push a Docker image to Private Registry.

Docker Hub is a registry for Docker images that allows you to host your own repositories, either public or private. To push a custom Docker image to the public Docker Hub, use the docker push command and provide a full image name and tag. A full image name and tag looks like the following sample:

<docker-id>/image-name:tag

If you haven't logged in to Docker Hub, do so using the docker login command before attempting to push an image.

docker login --username <docker-id> --password <docker-hub-password>

A "login succeeded" message confirms that you are logged in. Once logged in, you can push the image to Docker Hub using the docker push command.

docker push <docker-id>/mydockerimage:v1.0.0

Verify that the push succeeded by examining the command's output.

The push refers to a repository [docker.io/<docker-id>/mydockerimage:v1.0.0]
c33197c3f6d4: Pushed
ccd2c850ee43: Pushed
02dff2853466: Pushed
6ce78153632a: Pushed
efef3f03cc58: Pushed
3439624d77fb: Pushed
3a07adfb35c5: Pushed
2fcec228e1b7: Mounted from library/python
97d2d3bae505: Mounted from library/python
95aadeabf504: Mounted from library/python
b456afdc9996: Mounted from library/python
d752a0310ee4: Mounted from library/python
db64edce4b5b: Mounted from library/python
d5d60fc34309: Mounted from library/python
c01c63c6823d: Mounted from library/python
v1.0.0: digest: sha256:21f2798b20555f4143f2ca0591a43b4f6c8138406041f2d32ec908974feced66 size: 3676

Launch Azure Cloud Shell

The Azure Cloud Shell is a free interactive shell that you can use to run the steps in this article. It has common Azure tools preinstalled and configured to use with your account. Just click the Copy to copy the code, paste it into the Cloud Shell, and then press enter to run it. There are two ways to launch the Cloud Shell:

Click Try It in the upper right corner of a code block. Cloud Shell in this article
Click the Cloud Shell button on the menu in the upper right of the Azure portal. Cloud Shell in the portal

Deploy app to Azure

You can host native Linux applications in the cloud by using Azure Web Apps. To create a Web App for Containers, you must run Azure CLI commands that create a group, then a service plan, and finally the web app itself.

Create a resource group

In the Cloud Shell, create a resource group with the az group create command.

A resource group is a logical container into which Azure resources like web apps, databases, and storage accounts are deployed and managed.

The following example creates a resource group named myResourceGroup in the West Europe location. To see all supported locations for App Service, run the az appservice list-locations command.

az group create --name myResourceGroup --location "West Europe"

You generally create your resource group and the resources in a region near you.

Create a Linux App Service plan

In the Cloud Shell, create an App Service plan in the resource group with the az appservice plan create command.

The following example creates an App Service plan named myAppServicePlan in the Standard pricing tier (--sku S1) and in a Linux container (--is-linux).

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku S1 --is-linux

When the App Service plan has been created, the Azure CLI shows information similar to the following example:

{ 
  "adminSiteName": null,
  "appServicePlanName": "myAppServicePlan",
  "geoRegion": "West Europe",
  "hostingEnvironmentProfile": null,
  "id": "/subscriptions/0000-0000/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan",
  "kind": "linux",
  "location": "West Europe",
  "maximumNumberOfWorkers": 1,
  "name": "myAppServicePlan",
  < JSON data removed for brevity. >
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
} 

Create a web app

In the Cloud Shell, create a web app in the myAppServicePlan App Service plan with the az webapp create command. Don't forget to replace <app_name> with a unique app name, and with your Docker ID.

az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app_name> --deployment-container-image-name <docker-ID>/mydockerimage:v1.0.0

When the web app has been created, the Azure CLI shows output similar to the following example:

{
  "availabilityState": "Normal",
  "clientAffinityEnabled": true,
  "clientCertEnabled": false,
  "cloningInfo": null,
  "containerSize": 0,
  "dailyMemoryTimeQuota": 0,
  "defaultHostName": "<app_name>.azurewebsites.net",
  "deploymentLocalGitUrl": "https://<username>@<app_name>.scm.azurewebsites.net/<app_name>.git",
  "enabled": true,
  < JSON data removed for brevity. >
}

Configure environment variables

Most Docker images have environment variables that need to be configured. If you are using an existing Docker image built by someone else, the image may use a port other than 80. You tell Azure about the port that your image uses by using the WEBSITES_PORT app setting. The GitHub page for the Python sample in this tutorial shows that you need to set WEBSITES_PORT to 8000.

To set app settings, use the az webapp config appsettings update command in the Cloud Shell. App settings are case-sensitive and space-separated.

az webapp config appsettings set --resource-group myResourceGroup --name <app_name> --settings WEBSITES_PORT=8000

Note

Deploying from a Private Docker Registry? See the optional instructions to Configure Web App to use Docker container from a Private Registry.

Test the web app

Verify that the web app works by browsing to it (http://<app_name>azurewebsites.net).

Test web app port configuration

Change web app and redeploy

In your local Git repository, open app/templates/app/index.html. Locate the first HTML element and change it to.

<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
      <div class="navbar-header">         
        <a class="navbar-brand" href="#">Azure App Service - Updated Here!</a>       
      </div>            
    </div>
  </nav> 

Once you've modified the Python file and saved it, you must rebuild and push the new Docker image. Then restart the web app for the changes to take effect. Use the same commands that you have previously used in this tutorial. You can refer to Build the image from the Docker file and Push the Docker image to Docker Hub. Test the web app by following the instructions in Test the web app.

Connect to Web App for Containers using SSH

SSH enables secure communication between a container and a client. In order for a custom Docker image to support SSH, you must build it into a Dockerfile. You enable SSH in the Docker file itself. The SSH instructions have already been added to the sample dockerfile, so you can follow these instructions with your own custom image:

  • A RUN instruction that calls apt-get, then sets the password for the root account to "Docker!".

    ENV SSH_PASSWD "root:Docker!"
    RUN apt-get update \
            && apt-get install -y --no-install-recommends dialog \
            && apt-get update \
      && apt-get install -y --no-install-recommends openssh-server \
      && echo "$SSH_PASSWD" | chpasswd 
    

    Note

    This configuration does not allow external connections to the container. SSH is available only through the Kudu/SCM Site. The Kudu/SCM site is authenticated with the publishing credentials.

  • A COPY instruction that instructs the Docker engine to copy the sshd_config file to the /etc/ssh/ directory. Your configuration file should be based on this sshd_config file.

    COPY sshd_config /etc/ssh/
    

    Note

    The sshd_config file must include the following items:

    • Ciphers must include at least one item in this list: aes128-cbc,3des-cbc,aes256-cbc.
    • MACs must include at least one item in this list: hmac-sha1,hmac-sha1-96.
  • An EXPOSE instruction that exposes port 2222 in the container. Although the root password is known, port 2222 cannot be accessed from the internet. It is an internal port accessible only by containers within the bridge network of a private virtual network. After that, commands copy SSH configuration details and start the ssh service.

    EXPOSE 8000 2222
    
    RUN service ssh start
    

Open SSH connection to container

Web App for Containers does not allow external connections to the container. SSH is available only through the Kudu site, which is accessible at https://<app_name>.scm.azurewebsites.net.

To connect, browse to https://<app_name>.scm.azurewebsites.net/webssh/host and sign in with your Azure account.

You are then redirected to a page displaying an interactive console.

You may wish to verify that certain applications are running in the container. To inspect the container and verify running processes, issue the top command at the prompt.

top

The top command exposes all running processes in a container.

PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 1 root      20   0  945616  35372  15348 S  0.0  2.1   0:04.63 node
20 root      20   0   55180   2776   2516 S  0.0  0.2   0:00.00 sshd
42 root      20   0  944596  33340  15352 S  0.0  1.9   0:05.80 node /opt/s+
56 root      20   0   59812   5244   4512 S  0.0  0.3   0:00.93 sshd
58 root      20   0   20228   3128   2664 S  0.0  0.2   0:00.00 bash
62 root      20   0   21916   2272   1944 S  0.0  0.1   0:03.15 top
63 root      20   0   59812   5344   4612 S  0.0  0.3   0:00.03 sshd
65 root      20   0   20228   3140   2672 S  0.0  0.2   0:00.00 bash
71 root      20   0   59812   5380   4648 S  0.0  0.3   0:00.02 sshd
73 root      20   0   20228   3160   2696 S  0.0  0.2   0:00.00 bash
77 root      20   0   21920   2304   1972 R  0.0  0.1   0:00.00 top

Congratulations! You've configured a custom Docker image for a Web App for Containers.

Use a private image from Docker Hub (optional)

In Create a web app, you specified an image on Docker Hub in the az webapp create command. This is good enough for a public image. To use a private image, you need to configure your Docker account ID and password in your Azure web app.

In the Cloud Shell, follow the az webapp create command with az webapp config container set. Replace <app_name>, and also and with your Docker ID and password.

az webapp config container set --name <app_name> --resource-group myResourceGroup --docker-registry-server-user <docker-id> --docker-registry-server-password <password>

The command reveals output similar to the following JSON string, showing that the configuration change succeeded:

[
  {
    "name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
    "slotSetting": false,
    "value": "false"
  },
  {
    "name": "DOCKER_REGISTRY_SERVER_USERNAME",
    "slotSetting": false,
    "value": "<docker-id>"
  },
  {
    "name": "DOCKER_REGISTRY_SERVER_PASSWORD",
    "slotSetting": false,
    "value": null
  },
  {
    "name": "DOCKER_CUSTOM_IMAGE_NAME",
    "value": "DOCKER|<image-name-and-tag>"
  }
]

Use a Docker image from any private registry (optional)

In this section, you learn how to use a Docker image from a private registry in Web App for Containers, and it uses Azure Container Registry as an example. The steps for using other private registries are similar.

Azure Container Registry is a managed Docker service from Azure for hosting private images. The deployments may be any type, including Docker Swarm, Kubernetes, and Web App for Containers.

Create an Azure Container Registry

In the Cloud Shell, use the az acr create command to create an Azure Container Registry. Pass in the name, resource group, and Basic for the SKU. Available SKUs are Classic, Basic, Standard, and Premium.

az acr create --name <azure-container-registry-name> --resource-group myResourceGroup --sku Basic --admin-enabled true

Creating a container produces the following output:

 - Finished ..
Create a new service principal and assign access:
  az ad sp create-for-rbac --scopes /subscriptions/resourceGroups/myResourceGroup/providers/Microsoft.ContainerRegistry/registries/<azure-container-registry-name> --role Owner --password <password>

Use an existing service principal and assign access:
  az role assignment create --scope /subscriptions/resourceGroups/myResourceGroup/providers/Microsoft.ContainerRegistry/registries/<azure-container-registry-name> --role Owner --assignee <app-id>
{
  "adminUserEnabled": false,
  "creationDate": "2017-08-09T04:21:09.654153+00:00",
  "id": "/subscriptions/<subscriptionId>/resourceGroups/myResourceGroup/providers/Microsoft.ContainerRegistry/registries/<azure-container-registry-name>",
  "location": "westeurope",
  "loginServer": "<azure-container-registry-name>.azurecr.io",
  "name": "<azure-container-registry-name>",
  "provisioningState": "Succeeded",
  "resourceGroup": "myResourceGroup",
  "sku": {
    "name": "Basic",
    "tier": "Basic"
  },
  "storageAccount": {
    "name": "myazurecontainerre042025"
  },
  "tags": {},
  "type": "Microsoft.ContainerRegistry/registries"
}

Log in to Azure Container Registry

In order to push an image to the registry, you need to supply credentials so the registry accepts the push. You can retrieve these credentials by using the az acr show command in the Cloud Shell.

az acr credential show --name <azure-container-registry-name>

The command reveals two passwords that can be used with the user name.

<
  "passwords": [
    {
      "name": "password",
      "value": "{password}"
    },
    {
      "name": "password2",
      "value": "{password}"
    }
  ],
  "username": "<registry-username>"
}

From your local terminal window, log in to the Azure Container Registry using the docker login command. The server name is required to log in. Use the format {azure-container-registry-name>.azurecr.io.

docker login <azure-container-registry-name>.azurecr.io --username <registry-username> --password <password> 

Confirm that the login succeeded.

Push an image to Azure Container Registry

Note

If you're using your own image, tag the image as follows:

docker tag <azure-container-registry-name>.azurecr.io/mydockerimage

Push the image by using the docker push command. Tag the image with the name of the registry, followed by your image name and tag.

docker push <azure-container-registry-name>.azurecr.io/mydockerimage:v1.0.0

Verify that the push successfully added a container to the registry by listing the ACR repositories.

az acr repository list -n <azure-container-registry-name>

Listing the images in the registry confirms that mydockerimage is in the registry.

[
  "mydockerimage"
]

Configure Web App to use the image from Azure Container Registry (or any private registry)

You can configure Web App for Containers so that it runs a container stored in the Azure Container Registry. Using the Azure Container Registry is just like using any private registry, so if you need to use your own private registry, the steps to complete this task are similar.

In the Cloud Shell, run az acr credential show to display the username and password for the Azure Container Registry. Copy the username and one of the passwords so you can use it to configure the web app in the next step.

az acr credential show --name <azure-container-registry-name>
{
  "passwords": [
    {
      "name": "password",
      "value": "password"
    },
    {
      "name": "password2",
      "value": "password2"
    }
  ],
  "username": "<registry-username>"
}

In the Cloud Shell, run the az webapp config container set command to assign the custom Docker image to the web app. Replace <app_name>, <docker-registry-server-url>, <registry-username>, and <password>. For Azure Container Registry, <docker-registry-server-url> is in the format https://<azure-container-registry-name>.azurecr.io.

az webapp config container set --name <app_name> --resource-group myResourceGroup --docker-custom-image-name mydockerimage --docker-registry-server-url https://<azure-container-registry-name>.azurecr.io --docker-registry-server-user <registry-username> --docker-registry-server-password <password>

Note

https:// is required in <docker-registry-server-url>.

The command reveals output similar to the following JSON string, showing that the configuration change succeeded:

[
  {
    "name": "DOCKER_CUSTOM_IMAGE_NAME",
    "slotSetting": false,
    "value": "mydockerimage"
  },
  {
    "name": "DOCKER_REGISTRY_SERVER_URL",
    "slotSetting": false,
    "value": "<azure-container-registry-name>.azurecr.io"
  },
  {
    "name": "DOCKER_REGISTRY_SERVER_USERNAME",
    "slotSetting": false,
    "value": "<registry-username>"
  },
  {
    "name": "DOCKER_REGISTRY_SERVER_PASSWORD",
    "slotSetting": false,
    "value": null
  }
]

Clean up deployment

After the sample script has been run, the following command can be used to remove the resource group and all resources associated with it.

az group delete --name myResourceGroup

Next steps