Deploying a containerized Flask app to Azure Container Apps
source link: http://blog.pamelafox.org/2022/09/deploying-containerized-flask-app-to.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Friday, September 30, 2022
Deploying a containerized Flask app to Azure Container Apps
I've used Docker for many years as a local development environment (first for Khan Academy and then Berkeley CS61A), but until this week, I'd never personally made a Docker image or deployed one to production.
One of our offerings at Microsoft is Azure Container Apps, a way to run Docker containers in the cloud, which gives me a great excuse to get my feet wet in the world of containerization. 🐳
To share what I've learnt, I'll walkthrough the process of containerizing a Flask app and running it on Azure.
First, some Docker jargon:
- A Docker image is a multi-layered environment that is exactly the environment your app thrives in, such as a Linux OS with Python 3.9 and Flask installed. You can also think of an image as a snapshot or a template.
- A Docker container is an instance of an image, which could run locally on your machine or in the cloud.
- A registry is a place to host images. There are cloud hosted registries like DockerHub and Azure Container Registry. You can pull images down from those registries, or push images up to them.
These are the high-level steps:
- Build an image of the Flask application locally and confirm it works when containerized.
- Push the image to the Azure Container Registry.
- Run an Azure Container App for that image.
Build image of Flask app
I started from a very simple app, the Flask app used by other Azure tutorials:
https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart
Flask's development server is not suitable for production, so I brought in the Gunicorn server as a requirement in requirements.txt
:
Flask==2.0.2
gunicorn
I put Gunicorn settings in gunicorn_config.py
:
bind = "0.0.0.0:5000"
workers = 4
threads = 4
timeout = 120
Then I added this Dockerfile
file in the root:
# syntax=docker/dockerfile:1
# syntax=docker/dockerfile:1
FROM python:3.9.13
WORKDIR /code
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY . .
EXPOSE 5000
ENTRYPOINT ["gunicorn", "-c", "gunicorn_config.py", "app:app"]
That file tells Docker to start from a base image which has python 3.9.13 installed,
create a /code
directory, install the package requirements, copy the code into the directory,
and then finally use Gunicorn run the Flask application..
I also added a .dockerignore
file to make sure Docker doesn't copy over unneeded files:
.git*
**/*.pyc
.venv/
You can download the full code from this repository.
I built a Docker image using the "Build image" option from the VS Code Docker extension. However, it can also be built from the command line:
docker build --tag flask-demo .
Now that the image is built, I can run a container using it:
docker run -d -p 5000:5000 flask-demo
The Flask default port is 5000, so the run
command must specify that for the connections to work.
Push image to registry
I followed this tutorial to push an image to the registry, with some customizations.
I created a resource group:
az group create --name flask-container-apps --location eastus
Then I created a registry using a unique name and logged in to that registry:
az acr create --resource-group flask-container-apps \
--name pamelascontainerregistry --sku Basic
az acr login --name pamelascontainerregistry
Now comes the tricky part: pushing an image to that repository. I am working on a Mac with an M1 (ARM 64) chip, but Azure Container Apps (and other cloud-hosted container runners) expect images to be built for an Intel (AMD 64) chip. That means I can't just push the image that I built in the earlier step, I actually have to build specifically for AMD 64 and push that image.
One way to do that is with the docker buildx
command, specifying the target architecture and target registry location:
docker buildx build --push --platform linux/amd64 \
-t pamelascontainerregistry.azurecr.io/flask-demo:latest .
However, a much faster way to do it is with the az acr build
command, which uploads the code to cloud and builds it there:
az acr build --platform linux/amd64 \
-t pamelascontainerregistry.azurecr.io/flask-demo:latest \
-r pamelascontainerregistry .
⏱ The `docker buildx` command took ~ 10 minutes, whereas the `az acr build` command took only a minute. Nice!
Run Azure Container App
Now that I have an image uploaded to a registry, I can create a container app for that image. I followed this tutorial.
I upgraded the extension and registered the necessary providers:
az extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
⏱ That takes a few minutes.
Then I created the resource group and container app environment:
az group create --name flask-container-apps -location canadacentral
az containerapp env create --name flask-container-environment \
--resource-group flask-container-apps --location canadacentral
Next, I generated credentials so that the command line tool could log in to my registry:
az acr credential show --name pamelascontainerregistry
Finally, I created the container app:
az containerapp create --name my-container-app \
--resource-group flask-container-apps \
--image pamelascontainerregistry.azurecr.io/flask-demo:latest \
--environment flask-container-environment \
--registry-server pamelascontainerregistry.azurecr.io \
--register-username pamelascontainerregistry \
--registry-password $REGISTRY_PASSWORD \
--ingress external \
--target-port 5000
Once that deployed, I followed the URL from the Azure portal to view the website in the browser and verified it was all working. 🎉 Woot!
If I make any Flask code updates, all I need to do is re-build the image and tell the container app to update:
az acr build --platform linux/amd64 \
-t pamelascontainerregistry.azurecr.io/flask-demo:latest \
-r pamelascontainerregistry .
az containerapp update --name my-container-app \
--resource-group my-container-apps \
--image pamelascontainerregistry.azurecr.io/flask-demo:latest
⏱ Those commands are fairly fast, about 30 seconds each.
🐳 Now I'm off to containerize more apps!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK