You will learn:
- Use your own container image and image registry to deploy an application to the public cloud.
- Deploy the application using Azure Container Instances (ACI).
Prerequisites
You need:
- Docker installed and running (
docker version) - Azure CLI installed (
az version) and logged in (az login) - An active Azure subscription with remaining credits
Set (and keep using) a few variables so you don’t have to edit commands later:
# Adjust these values
RG="mrg"
LOCATION="eastus"
ACR="registryflower12345" # must be globally unique (letters+numbers only)
DNS_LABEL="flower-demo-12345" # must be unique within the Azure region
IMAGE_NAME="greetings"
IMAGE_TAG="dev"
(Optional) Verify your current subscription:
az account show --query "{name:name, id:id}" -o table
# az account set --subscription "<SUBSCRIPTION_ID>"
Building the container image
Let’s take a simple application from the previous exercise (save it as app.py):
from datetime import date
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "<h1 style='color:blue'>Hello There!</h1><a href='/date'>Date and Time</a>"
@app.route("/date")
def today():
current_date = date.today()
return "<p>{}</p>".format(current_date.strftime("%d.%m.%Y"))
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
For an application with more complex dependencies, it is advantageous to create a container image.
In the Dockerfile file, we describe how to install and configure everything needed to run the app.
This makes it easier to run the same app locally and in a public/private cloud.
Create a requirements.txt file (pinning helps keep the lab reproducible):
Flask==3.0.2
Example Dockerfile:
FROM python:3.12-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app.py /app
ENV FLASK_APP=app.py
EXPOSE 80
ENTRYPOINT ["flask"]
CMD ["run", "--host", "0.0.0.0", "--port", "80"]
Note: flask run starts the development server. For production, prefer a WSGI server (e.g. gunicorn).
Build the container image and give it a name:
docker build . -t ${IMAGE_NAME}:${IMAGE_TAG}
(Optional) Test locally:
docker run --rm -p 8080:80 ${IMAGE_NAME}:${IMAGE_TAG}
# Open http://localhost:8080
Container image registry
If you want to use your own container image in the public cloud, you must first place it in an image registry. A container registry is a place from which an image can be retrieved on demand from any worker node in the cloud.
It will look something like this:
Public Cloud
+---------------+ +-----------+ +-----------+
| local machine | docker | registry | docker | container |
| docker build | push --->| images | pull --->| instance |
+---------------+ +-----------+ +-----------+
The registry must be secured against unauthorized access. Container images may contain sensitive data or source code that we do not want to share. We have to set the registry so that we have the right to write from the local machine and the cloud runtime has the right to read.
Creating an image registry (ACR)
In order to be able to use our own image in Azure, we will store it in Azure Container Registry (ACR).
First, create a resource group and then the registry:
az group create --name "$RG" --location "$LOCATION"
# Check name availability (ACR name must be globally unique)
az acr check-name --name "$ACR" --query nameAvailable -o tsv
az acr create --resource-group "$RG" --name "$ACR" --sku Basic
LOGIN_SERVER="$(az acr show --name "$ACR" --query loginServer -o tsv)"
echo "$LOGIN_SERVER"
Log in and push the image
First, log in your local Docker client to ACR:
# Does it work?
az acr login --name "$ACR"
If this login method doesn't work, you can use a username/password login.
Warning: Enabling the ACR “Admin user” is discouraged in real projects; use it only for this lab and disable it afterwards.
# Lab-only fallback (admin user)
# az acr update --name "$ACR" --admin-enabled true
# USERNAME="$(az acr credential show --name "$ACR" --query username -o tsv)"
# PASSWORD="$(az acr credential show --name "$ACR" --query passwords[0].value -o tsv)"
# docker login "$LOGIN_SERVER" -u "$USERNAME" -p "$PASSWORD"
Tag the local image with the registry DNS name (before the / slash) and push it:
# If you opened a new terminal, re-export the variable:
LOGIN_SERVER="$(az acr show --name "$ACR" --query loginServer -o tsv)"
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${LOGIN_SERVER}/${IMAGE_NAME}:${IMAGE_TAG}
docker push ${LOGIN_SERVER}/${IMAGE_NAME}:${IMAGE_TAG}
Verify the image is in the registry:
az acr repository list --name "$ACR" -o table
az acr repository show-tags --name "$ACR" --repository "$IMAGE_NAME" -o table
Creating a container using ACI
We will use the "Azure Container Instances" service to create the container.
The command to create a container is similar to docker run.
ACR admin user credentials are required for ACI to pull from a private registry. Enable the admin user now (if you have not done so yet):
az acr update --name "$ACR" --admin-enabled true
Warning: Disable the admin user after the lab (az acr update --name "$ACR" --admin-enabled false).
# ACI pulling from a private registry needs credentials (lab: admin user)
USERNAME="$(az acr credential show --name "$ACR" --query username -o tsv)"
PASSWORD="$(az acr credential show --name "$ACR" --query passwords[0].value -o tsv)"
az container create \
--resource-group "$RG" \
--name "mycontainer" \
--image "${LOGIN_SERVER}/${IMAGE_NAME}:${IMAGE_TAG}" \
--dns-name-label "$DNS_LABEL" \
--ports 80 \
--cpu 0.5 \
--memory 0.5 \
--registry-login-server "$LOGIN_SERVER" \
--registry-username "$USERNAME" \
--registry-password "$PASSWORD"
ACI does not support port mapping. The port you specify must be the same as the port the container listens on.
Detailed documentation: az container create.
Find the DNS name under which the container is accessible:
az container show \
--resource-group "$RG" \
--name "mycontainer" \
--query "{FQDN:ipAddress.fqdn,ProvisioningState:provisioningState}" \
--out table
FQDN="$(az container show --resource-group "$RG" --name "mycontainer" --query ipAddress.fqdn -o tsv)"
echo "http://$FQDN"
See the application (it will run on the port you assigned to it):
curl -fsS "http://$FQDN/" | head
See application logs:
az container logs --resource-group "$RG" --name "mycontainer"
Troubleshooting
- ACR name not available: choose a different
$ACRvalue (useaz acr check-name). docker pushdenied: ensure you used the correct$LOGIN_SERVERand that you are logged in (az acr loginor the lab-only admin-user fallback).- DNS label already in use: choose a different
$DNS_LABEL(DNS label must be unique per region). - Container stuck in image pull errors: inspect events:
az container show --resource-group "$RG" --name "mycontainer" --query "instanceView.events" -o table
- Wrong port: update the container to listen on the same port you publish (ACI does not map ports).
If you do not delete the resources, you will run out of credit.
Clean up the environment after yourself
Delete the entire resource group. This also deletes the registry and all its images:
az group delete --name "$RG" --yes --no-wait
Bibliography
- Azure Container Instances overview: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-overview
- Quickstart – deploy a container with Azure CLI: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-quickstart
- Tutorial – prepare a container registry: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-tutorial-prepare-acr
- Tutorial – deploy a container app: https://learn.microsoft.com/en-us/azure/container-instances/container-instances-tutorial-deploy-app
az container createreference: https://learn.microsoft.com/en-us/cli/azure/container?view=azure-cli-latest#az-container-create