Skip to content

This repository contains a simple web app with a SQLite databse used to test cloud deployment functionalities.

Notifications You must be signed in to change notification settings

mxagar/simple_web_app_test

Repository files navigation

Simple Web App

This repository contains a simple web app with a SQLite databse used to test cloud deployment functionalities.

⚠️ The app is for test puposes only. You should not deploy an app in a container with the database inside, due to many reasons:

  • The database is not scalable.
  • The changes in the container are ephemeral, i.e., they disappear when the container is removed.
  • etc.

However, I use this simple example for deployment tests; i.e., CICD functionalities are run on the repository to deploy it to different cloud providers using different approaches (e.g., as service, container image, etc.).

The app has two functionalities; when run locally, we can access them as follows:

  • Web UI: http://127.0.0.1:5000/: we can enter text strings to a local database; additionally, the last 5 inserted text strings are shown with their insertion id.
  • REST endpoint: 127.0.0.1:5000/text/<id>: we can get the inserted text string with the passed id.

Note that neither exception handling nor logging are implemented — on purpose.

Simple Web App: Insert entries to a DB

Simple Web App: REAST API call

Table of Contents

App Structure

The application is implemented in app.py and models.py; this is the overview of all files:

├───assets/                # Pics
├───templates/
│   └───index.html         # Flask web UI
├───Dockerfile
├───app.py                 # Flask app
├───models.py              # SQLAlchemy table
├───requirements.txt       # Dependencies: dev, prod
├───Procfile               # Command to launch the web app
├───README.md
└───test_app.py            # Tests for app.py using pytest

Setup and Local Running

I prepared this simple app to test different cloud deployment and monitoring methods. However, you can/should run it locally, too.

Option 1: Local run with flask

# Environment
python -m venv venv
source venv/bin/activate # venv\Scripts\activate
pip install -r requirements.txt

# Run
flask run

# App
http://127.0.0.1:5000/
# Fill in table with texts

# REST API
127.0.0.1:5000/text/<id> # e.g., 3
# Check that the correct text is returned

Option 2: Docker packaging and running:

# Simple build: no arguments passed
docker build -t flask-text-app .
# If we have a proxy; note that the --build_arg is optional
docker build --build-arg HTTPS_PROXY=$env:HTTPS_PROXY -t flask-text-app .

# Run
docker run -p 5000:5000 flask-text-app
# If we want to override the value of the HTTP_PROXY, first set the environment variable, then:
docker run -e HTTPS_PROXY=$env:HTTPS_PROXY -p 5000:5000 flask-text-app
# If we want to create a volume instance locally mounted in the contained; that's where the DB is saved by default
docker run -v instance:/app/instance -p 5000:5000 flask-text-app

# Stop
docker ps
docker stop <id_or_name>

Note that the Dockerfile I am using not multi-stage, so all the environment variables used in the build process are visible in the final image; maybe, that's not desired.

Cloud Deployments

Azure Deployment: Web App Service with Github Integration

This method is equivalent to a PaaS Heroku deployment.

Deploying a web application to Azure App Service directly from a GitHub repository is a convenient method to automate deployments. This approach is ideal for scenarios where your application doesn't require a containerized environment.

Note that Azure App Service's file system is ephemeral. Changes to the SQLite database will be lost whenever the app is restarted or redeployed.

Step 0: Create an Azure Account

Account creation: Build in the cloud with an Azure free account ; maybe we need to add credit card in order to be able to deploy stuff, even though we have no costs.

Also, install the Azure CLI tool, even though it is not necessary for the deployment: How to install the Azure CLI.

Step 1: Prepare Your Application

We need a Prepare the Procfile. Procfiles are native to Heroku, however, they can be used by other platforms.

Any Procfile needs to be set with the correct app:app parameter, being app:app == module_name:flask_app_instance, e.g.:

web: gunicorn -w 4 -k gevent -b 0.0.0.0:8000 app:app

Step 2: Create an Azure App Service

  1. Log into the Azure Portal: Visit Azure Portal.

  2. Create a Web App:

    • Go to "Create a resource" > "Web" > "Web App".
    • Fill in the details:
      • Subscription: Choose your Azure subscription; e.g., Azure subscription 1
      • Resource Group: Select an existing resource group or create a new one, e.g., rg-simple-web-app.
      • Name: Enter a unique name for your web app; e.g., simple-web-app-db.
      • Publish: Select "Code".
      • Runtime stack: Choose the appropriate runtime for your Flask app, e.g., Python 3.9.
      • Region: Choose a region near you or your users.
      • Linux Plan (App Service plan): Select an existing plan or create a new one.
      • Pricing plan: select one; e.g. Free F1: 60 minutes/day
    • Click "Review and create" > "Create".

Step 3: Configure Deployment from GitHub

  1. Open your Web App resource in the Azure Portal.
  2. Go to Deployment Center:
    • Navigate to the "Deployment Center" in the sidebar.
    • Choose "GitHub" as the source.
  3. Authorize Azure to Access GitHub:
    • You'll be prompted to authenticate with GitHub and authorize Azure to access your repositories.
  4. Configure the Build Provider and Repository:
    • For the build provider, select "App Service build service".
    • Choose your GitHub organization (or username), repository, and branch you wish to deploy.
  5. Finish the Setup:
    • Complete the configuration and click "Save".
    • Azure will start the deployment process, pulling the latest commit from the specified branch.
    • The first deployment might fail because the app service is not created yet, but the next ones should work.

Step 4: Verify Deployment

  • Once the deployment process is complete, you can navigate to your web app's URL (found in the "Overview" section of your Web App resource in the Azure Portal) to see your running Flask application.
  • Continuous Deployment: Future pushes to your selected GitHub branch will trigger automatic deployments to your Azure Web App!

Now, we can open the app from anywhere and use it!

The Continuous Deployment is achieved by Github Actions; a workflow is automatically generated in .github/workflows/main_simple-web-app-db.yml. That workflow has 2 jobs:

  • build: it sets a python environment and installs the dependencies; then, all the files are packaged into a ZIP artifact.
  • deploy: it uncompresses the ZIP artifact and uploads it to Azure after loging in. The login in happens via 3 secrets: Client ID, Tenant ID and Subscription ID. Those secret values are automatically found and set in Github by Azure.

In the workflow YAML, the secrets are referenced as follows

client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_XXX }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_XXX }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_XXX }}

The endings XXX are automatically set by Azure and they are not related to the values.

Note that the secrets can be accessed via Repository > Settings > Secrets and variables > Actions. However, they are not visible once set, we can only update them!.

To check the values of the Client ID, Tenant ID and Subscription ID, we can search for them in the Azure Portal:

  • Subscriptions > ...
  • Tenant ID: Microsoft Entra ID > ...
  • Client ID: Microsoft Entra ID > Users > ...

Step 5: Tune the Deployment: Adding Tests

We can use the file test_app.py to test our app; those tests should be run (with pytest) either during the build job or after it and before the deploy job.

Option 1: We add an additional command to the build job:

      # ...
      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run tests
        run: |
          source venv/bin/activate
          pytest
        # Add this step to run your tests. Make sure 'pytest' is listed in your 'requirements.txt'

      - name: Zip artifact for deployment
        run: zip release.zip ./* -r
        # This step will only be reached if the tests pass
      # ...

Option 2: We add an additional job after the build job and before the deploy job:

   # ...
   build:
   # ...
   test:
    needs: build
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python version
        uses: actions/setup-python@v1
        with:
          python-version: '3.9'

      - name: Install dependencies
        run: |
          python -m venv venv
          source venv/bin/activate
          pip install -r requirements.txt

      - name: Run pytest
        run: |
          source venv/bin/activate
          pytest
   # ...
   deploy:
   #...

Note: in orther to push changes in a workflow, the Github Personal Access Token (PAT) needs to have this permission: Account Settings > Developer Settings > Personal access tokens > Select token > check Workflow.

Step 6: Handle Deployment

We can Start/Stop/Restart the app in the Azure portal: rg-simple-web-app > simple-web-app-db: Stop/Restart.

We can also use the Azure CLI, after installing it.

A summary of useful commands:

# Log in
az logout
az login
# Browser is prompted for logging in
# Id info is output: tenantId, etc.

# Get list or resource groups
az group list --output table

# Get list of resources in the resource group rg-simple-web-app
az resource list --resource-group rg-simple-web-app --output table

# Stop the resource simple-web-app-db from resource group rg-simple-web-app
az webapp stop --name simple-web-app-db --resource-group rg-simple-web-app

# Start again the resource simple-web-app-db from resource group rg-simple-web-app
az webapp start --name simple-web-app-db --resource-group rg-simple-web-app

# Set environment variables in the service while it's running
az webapp config appsettings set --resource-group <YourResourceGroupName> --name <YourAppServiceName> --settings KEY=VALUE

Azure Deployment: Web App Service with Containers

⚠️ I have not tested this approach entirely.

Step 0: Create an Azure Account

See Step 0: Create an Azure Account from the previous section.

This section assumes that we have already a resource group; in fact, we should be able to use the one in the previous section Azure Deployment: Web App Service with Github Integration.

Step 1: Create a Docker Image and Push It to a Registry

Create a docker image, as explained in the Option 2 of the section Setup and Local Running.

# Simple build: no arguments passed
docker build -t flask-text-app .
# If we have a proxy; note that the --build_arg is optional
docker build --build-arg HTTPS_PROXY=$env:HTTPS_PROXY -t flask-text-app .

We can use the Docker Hub registry, if we have an account, or create an Azure Container Registry instance (ACR) in our subscription. However, the ACR is not free, apparently. Also note, that all variables in the image are exposed — that's maybe relevant if the registry is public.

Docker Registry:

# Tag image
docker tag flask-text-app <your-dockerhub-username>/flask-text-app:v1.0.0

# Push the tagged image to Docker Hub
docker push <your-dockerhub-username>/flask-text-app:v1.0.0

Azure Container Registry:

az logout
az login

# Create an ACR instance if you haven't already
# Note that the name must be available and must have 5-50 alphanumeric characters
az acr create --resource-group <YourResourceGroupName> --name <RegistryName> --sku Basic

# Log in to ACR
az acr login --name <RegistryName>

# Tag your image with the ACR login server name
docker tag flask-text-app <RegistryName>.azurecr.io/flask-text-app

# Push the image to ACR
docker push <RegistryName>.azurecr.io/flask-text-app

Step 3: Deploy the Container to Azure App Service

# If we don't have one, we create a service plan
az appservice plan create --name <YourAppServicePlan> --resource-group <YourResourceGroupName> --sku S1 --is-linux

# Create a Web App with container support
az webapp create --resource-group <YourResourceGroupName> --plan <YourAppServicePlan> --name <YourAppServiceName> --deployment-container-image-name <your-dockerhub-username>/flask-text-app:latest
# Replace <your-dockerhub-username>/flask-text-app:latest
# with your ACR image path if using ACR: <RegistryName>.azurecr.io/flask-text-app:latest

# If using ACR, configure the Web App to use ACR credentials
az webapp config container set --name <YourAppServiceName> --resource-group <YourResourceGroupName> --docker-custom-image-name <RegistryName>.azurecr.io/flask-text-app:latest --docker-registry-server-url https://<RegistryName>.azurecr.io --docker-registry-server-user <ACRUsername> --docker-registry-server-password <ACRPassword>
# The username and the password can be obtained using access Keys:
# - Navigate to the Azure Container Registry resource in the Azure portal.
# - Select "Access keys" under "Settings".
# - Check "admin user"
# - Here, we can find the "Username" and two valid access keys ("passwords").

# Configure the port your app runs on (if different from the default 80)
az webapp config appsettings set --resource-group <YourResourceGroupName> --name <YourAppServiceName> --settings WEBSITES_PORT=5000

Now the Flask app, containerized and stored in a Docker registry, should be deployed and running on Azure App Service. We can access it using the URL provided by Azure for your Web App, typically https://<YourAppServiceName>.azurewebsites.net.

About

This repository contains a simple web app with a SQLite databse used to test cloud deployment functionalities.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published