{"id":18339929,"url":"https://github.com/mxagar/simple_web_app_test","last_synced_at":"2026-05-02T04:31:27.927Z","repository":{"id":222447640,"uuid":"757295806","full_name":"mxagar/simple_web_app_test","owner":"mxagar","description":"This repository contains a simple web app with a SQLite databse used to test cloud deployment functionalities.","archived":false,"fork":false,"pushed_at":"2024-02-15T16:06:30.000Z","size":69,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-15T12:49:53.343Z","etag":null,"topics":["azure","deployment","static-web","test"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mxagar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-14T07:45:03.000Z","updated_at":"2024-02-26T14:11:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"974fd6dc-9379-4dfc-842b-a0a7dcd5edbc","html_url":"https://github.com/mxagar/simple_web_app_test","commit_stats":null,"previous_names":["mxagar/simple_web_app_test"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxagar%2Fsimple_web_app_test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxagar%2Fsimple_web_app_test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxagar%2Fsimple_web_app_test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxagar%2Fsimple_web_app_test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mxagar","download_url":"https://codeload.github.com/mxagar/simple_web_app_test/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248109291,"owners_count":21049283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","deployment","static-web","test"],"created_at":"2024-11-05T20:19:52.506Z","updated_at":"2026-05-02T04:31:22.905Z","avatar_url":"https://github.com/mxagar.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simple Web App\n\nThis repository contains a simple web app with a SQLite databse used to test cloud deployment functionalities.\n\n:warning: The app is **for test puposes only**. You should not deploy an app in a container with the database inside, due to many reasons:\n\n- The database is not scalable.\n- The changes in the container are ephemeral, i.e., they disappear when the container is removed.\n- etc.\n\nHowever, 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.).\n\nThe app has two functionalities; when run locally, we can access them as follows:\n\n- 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.\n- REST endpoint: `127.0.0.1:5000/text/\u003cid\u003e`: we can get the inserted text string with the passed id.\n\nNote that neither exception handling nor logging are implemented \u0026mdash; on purpose.\n\n![Simple Web App: Insert entries to a DB](./assets/simple_web_app_db.jpg)\n\n![Simple Web App: REAST API call](./assets/simple_web_app_rest.jpg)\n\n## Table of Contents\n\n- [Simple Web App](#simple-web-app)\n  - [Table of Contents](#table-of-contents)\n  - [App Structure](#app-structure)\n  - [Setup and Local Running](#setup-and-local-running)\n  - [Cloud Deployments](#cloud-deployments)\n    - [Azure Deployment: Web App Service with Github Integration](#azure-deployment-web-app-service-with-github-integration)\n      - [Step 0: Create an Azure Account](#step-0-create-an-azure-account)\n      - [Step 1: Prepare Your Application](#step-1-prepare-your-application)\n      - [Step 2: Create an Azure App Service](#step-2-create-an-azure-app-service)\n      - [Step 3: Configure Deployment from GitHub](#step-3-configure-deployment-from-github)\n      - [Step 4: Verify Deployment](#step-4-verify-deployment)\n      - [Step 5: Tune the Deployment: Adding Tests](#step-5-tune-the-deployment-adding-tests)\n      - [Step 6: Handle Deployment](#step-6-handle-deployment)\n    - [Azure Deployment: Web App Service with Containers](#azure-deployment-web-app-service-with-containers)\n      - [Step 0: Create an Azure Account](#step-0-create-an-azure-account-1)\n      - [Step 1: Create a Docker Image and Push It to a Registry](#step-1-create-a-docker-image-and-push-it-to-a-registry)\n    - [Step 3: Deploy the Container to Azure App Service](#step-3-deploy-the-container-to-azure-app-service)\n\n\n## App Structure\n\nThe application is implemented in [`app.py`](./app.py) and [`models.py`](./models.py); this is the overview of all files:\n\n```\n├───assets/                # Pics\n├───templates/\n│   └───index.html         # Flask web UI\n├───Dockerfile\n├───app.py                 # Flask app\n├───models.py              # SQLAlchemy table\n├───requirements.txt       # Dependencies: dev, prod\n├───Procfile               # Command to launch the web app\n├───README.md\n└───test_app.py            # Tests for app.py using pytest\n```\n\n## Setup and Local Running\n\nI prepared this simple app to test different cloud deployment and monitoring methods. However, you can/should run it locally, too.\n\n**Option 1: Local run with flask**\n\n```bash\n# Environment\npython -m venv venv\nsource venv/bin/activate # venv\\Scripts\\activate\npip install -r requirements.txt\n\n# Run\nflask run\n\n# App\nhttp://127.0.0.1:5000/\n# Fill in table with texts\n\n# REST API\n127.0.0.1:5000/text/\u003cid\u003e # e.g., 3\n# Check that the correct text is returned\n```\n\n**Option 2: Docker packaging and running:**\n\n```bash\n# Simple build: no arguments passed\ndocker build -t flask-text-app .\n# If we have a proxy; note that the --build_arg is optional\ndocker build --build-arg HTTPS_PROXY=$env:HTTPS_PROXY -t flask-text-app .\n\n# Run\ndocker run -p 5000:5000 flask-text-app\n# If we want to override the value of the HTTP_PROXY, first set the environment variable, then:\ndocker run -e HTTPS_PROXY=$env:HTTPS_PROXY -p 5000:5000 flask-text-app\n# If we want to create a volume instance locally mounted in the contained; that's where the DB is saved by default\ndocker run -v instance:/app/instance -p 5000:5000 flask-text-app\n\n# Stop\ndocker ps\ndocker stop \u003cid_or_name\u003e\n```\n\nNote that the [`Dockerfile`](./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.\n\n## Cloud Deployments\n\n### Azure Deployment: Web App Service with Github Integration\n\nThis method is equivalent to a PaaS Heroku deployment.\n\nDeploying 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.\n\nNote that Azure App Service's file system is ephemeral. Changes to the SQLite database will be lost whenever the app is restarted or redeployed.\n\n#### Step 0: Create an Azure Account\n\nAccount creation: [Build in the cloud with an Azure free account ](https://azure.microsoft.com/en-us/free/search/); maybe we need to add credit card in order to be able to deploy stuff, even though we have no costs.\n\nAlso, install the Azure CLI tool, even though it is not necessary for the deployment: [How to install the Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli).\n\n#### Step 1: Prepare Your Application\n\nWe need a Prepare the [`Procfile`](./Procfile). Procfiles are native to [Heroku](https://www.heroku.com/), however, they can be used by other platforms.\n\nAny [`Procfile`](./Procfile) needs to be set with the correct `app:app` parameter, being `app:app == module_name:flask_app_instance`, e.g.:\n\n```bash\nweb: gunicorn -w 4 -k gevent -b 0.0.0.0:8000 app:app\n```\n\n#### Step 2: Create an Azure App Service\n\n1. **Log into the Azure Portal**: Visit [Azure Portal](https://portal.azure.com/).\n2. **Create a Web App**:\n\n   - Go to \"Create a resource\" \u003e \"Web\" \u003e \"Web App\".\n   - Fill in the details:\n     - **Subscription**: Choose your Azure subscription; e.g., `Azure subscription 1`\n     - **Resource Group**: Select an existing resource group or create a new one, e.g., `rg-simple-web-app`.\n     - **Name**: Enter a unique name for your web app; e.g., `simple-web-app-db`.\n     - **Publish**: Select \"Code\".\n     - **Runtime stack**: Choose the appropriate runtime for your Flask app, e.g., Python 3.9.\n     - **Region**: Choose a region near you or your users.\n     - **Linux Plan (App Service plan)**: Select an existing plan or create a new one.\n     - **Pricing plan**: select one; e.g. `Free F1: 60 minutes/day`\n   - Click \"Review and create\" \u003e \"Create\".\n\n#### Step 3: Configure Deployment from GitHub\n\n1. **Open your Web App resource** in the Azure Portal.\n2. **Go to Deployment Center**:\n   - Navigate to the \"Deployment Center\" in the sidebar.\n   - Choose \"GitHub\" as the source.\n3. **Authorize Azure to Access GitHub**:\n   - You'll be prompted to authenticate with GitHub and authorize Azure to access your repositories.\n4. **Configure the Build Provider and Repository**:\n   - For the build provider, select \"App Service build service\".\n   - Choose your GitHub organization (or username), repository, and branch you wish to deploy.\n5. **Finish the Setup**:\n   - Complete the configuration and click \"Save\".\n   - Azure will start the deployment process, pulling the latest commit from the specified branch.\n   - The first deployment might fail because the app service is not created yet, but the next ones should work.\n\n#### Step 4: Verify Deployment\n\n- 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.\n  - Example: [https://simple-web-app-db.azurewebsites.net/](https://simple-web-app-db.azurewebsites.net/).\n- **Continuous Deployment: Future pushes to your selected GitHub branch will trigger automatic deployments to your Azure Web App!**\n\nNow, we can open the app from anywhere and use it!\n\nThe **Continuous Deployment** is achieved by *Github Actions*; a workflow is automatically generated in [`.github/workflows/main_simple-web-app-db.yml`](.github/workflows/main_simple-web-app-db.yml). That workflow has 2 jobs:\n\n- `build`: it sets a python environment and installs the dependencies; then, all the files are packaged into a ZIP artifact.\n- `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.**\n\nIn the workflow YAML, the secrets are referenced as follows\n\n```yaml\nclient-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_XXX }}\ntenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_XXX }}\nsubscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_XXX }}\n```\n\nThe endings `XXX` are automatically set by Azure and they are not related to the values.\n\nNote that the secrets can be accessed via Repository \u003e Settings \u003e Secrets and variables \u003e Actions. **However, they are not visible once set, we can only update them!**.\n\nTo check the values of the **Client ID, Tenant ID and Subscription ID**, we can search for them in the Azure Portal:\n\n- Subscriptions \u003e ...\n- Tenant ID: Microsoft Entra ID \u003e ...\n- Client ID: Microsoft Entra ID \u003e Users \u003e ...\n\n#### Step 5: Tune the Deployment: Adding Tests\n\nWe can use the file [`test_app.py`](./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.\n\n**Option 1**: We add an additional command to the `build` job:\n\n```yaml\n      # ...\n      - name: Install dependencies\n        run: pip install -r requirements.txt\n\n      - name: Run tests\n        run: |\n          source venv/bin/activate\n          pytest\n        # Add this step to run your tests. Make sure 'pytest' is listed in your 'requirements.txt'\n\n      - name: Zip artifact for deployment\n        run: zip release.zip ./* -r\n        # This step will only be reached if the tests pass\n      # ...\n```\n\n**Option 2**: We add an additional job after the `build` job and before the `deploy` job:\n\n```yaml\n   # ...\n   build:\n   # ...\n   test:\n    needs: build\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python version\n        uses: actions/setup-python@v1\n        with:\n          python-version: '3.9'\n\n      - name: Install dependencies\n        run: |\n          python -m venv venv\n          source venv/bin/activate\n          pip install -r requirements.txt\n\n      - name: Run pytest\n        run: |\n          source venv/bin/activate\n          pytest\n   # ...\n   deploy:\n   #...\n```\n\nNote: in orther to push changes in a workflow, the Github Personal Access Token (PAT) needs to have this permission: Account Settings \u003e Developer Settings \u003e Personal access tokens \u003e Select token \u003e check Workflow.\n\n#### Step 6: Handle Deployment\n\nWe can Start/Stop/Restart the app in the Azure portal: `rg-simple-web-app` \u003e `simple-web-app-db`: Stop/Restart.\n\nWe can also use the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/), after [installing it](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli).\n\nA summary of useful commands:\n\n```bash\n# Log in\naz logout\naz login\n# Browser is prompted for logging in\n# Id info is output: tenantId, etc.\n\n# Get list or resource groups\naz group list --output table\n\n# Get list of resources in the resource group rg-simple-web-app\naz resource list --resource-group rg-simple-web-app --output table\n\n# Stop the resource simple-web-app-db from resource group rg-simple-web-app\naz webapp stop --name simple-web-app-db --resource-group rg-simple-web-app\n\n# Start again the resource simple-web-app-db from resource group rg-simple-web-app\naz webapp start --name simple-web-app-db --resource-group rg-simple-web-app\n\n# Set environment variables in the service while it's running\naz webapp config appsettings set --resource-group \u003cYourResourceGroupName\u003e --name \u003cYourAppServiceName\u003e --settings KEY=VALUE\n```\n\n### Azure Deployment: Web App Service with Containers\n\n:warning: I have not tested this approach entirely.\n\n#### Step 0: Create an Azure Account\n\nSee [Step 0: Create an Azure Account](#step-0-create-an-azure-account) from the previous section.\n\nThis 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](#azure-deployment-web-app-service-with-github-integration).\n\n#### Step 1: Create a Docker Image and Push It to a Registry\n\nCreate a docker image, as explained in the Option 2 of the section [Setup and Local Running](#setup-and-local-running).\n\n```bash\n# Simple build: no arguments passed\ndocker build -t flask-text-app .\n# If we have a proxy; note that the --build_arg is optional\ndocker build --build-arg HTTPS_PROXY=$env:HTTPS_PROXY -t flask-text-app .\n```\n\nWe 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 \u0026mdash; that's maybe relevant if the registry is public.\n\n**Docker Registry**:\n\n```bash\n# Tag image\ndocker tag flask-text-app \u003cyour-dockerhub-username\u003e/flask-text-app:v1.0.0\n\n# Push the tagged image to Docker Hub\ndocker push \u003cyour-dockerhub-username\u003e/flask-text-app:v1.0.0\n```\n\n**Azure Container Registry**:\n\n```bash\naz logout\naz login\n\n# Create an ACR instance if you haven't already\n# Note that the name must be available and must have 5-50 alphanumeric characters\naz acr create --resource-group \u003cYourResourceGroupName\u003e --name \u003cRegistryName\u003e --sku Basic\n\n# Log in to ACR\naz acr login --name \u003cRegistryName\u003e\n\n# Tag your image with the ACR login server name\ndocker tag flask-text-app \u003cRegistryName\u003e.azurecr.io/flask-text-app\n\n# Push the image to ACR\ndocker push \u003cRegistryName\u003e.azurecr.io/flask-text-app\n```\n\n### Step 3: Deploy the Container to Azure App Service\n\n```bash\n# If we don't have one, we create a service plan\naz appservice plan create --name \u003cYourAppServicePlan\u003e --resource-group \u003cYourResourceGroupName\u003e --sku S1 --is-linux\n\n# Create a Web App with container support\naz webapp create --resource-group \u003cYourResourceGroupName\u003e --plan \u003cYourAppServicePlan\u003e --name \u003cYourAppServiceName\u003e --deployment-container-image-name \u003cyour-dockerhub-username\u003e/flask-text-app:latest\n# Replace \u003cyour-dockerhub-username\u003e/flask-text-app:latest\n# with your ACR image path if using ACR: \u003cRegistryName\u003e.azurecr.io/flask-text-app:latest\n\n# If using ACR, configure the Web App to use ACR credentials\naz webapp config container set --name \u003cYourAppServiceName\u003e --resource-group \u003cYourResourceGroupName\u003e --docker-custom-image-name \u003cRegistryName\u003e.azurecr.io/flask-text-app:latest --docker-registry-server-url https://\u003cRegistryName\u003e.azurecr.io --docker-registry-server-user \u003cACRUsername\u003e --docker-registry-server-password \u003cACRPassword\u003e\n# The username and the password can be obtained using access Keys:\n# - Navigate to the Azure Container Registry resource in the Azure portal.\n# - Select \"Access keys\" under \"Settings\".\n# - Check \"admin user\"\n# - Here, we can find the \"Username\" and two valid access keys (\"passwords\").\n\n# Configure the port your app runs on (if different from the default 80)\naz webapp config appsettings set --resource-group \u003cYourResourceGroupName\u003e --name \u003cYourAppServiceName\u003e --settings WEBSITES_PORT=5000\n```\n\nNow 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://\u003cYourAppServiceName\u003e.azurewebsites.net`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmxagar%2Fsimple_web_app_test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmxagar%2Fsimple_web_app_test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmxagar%2Fsimple_web_app_test/lists"}