[Python] Can't get my Flask app to run as a bot in Azure Web App Services

Discussão em 'Python' iniciado por Stack, Março 8, 2025.

  1. Stack

    Stack Membro Participativo

    I'm working on a chat bot and I want to deploy it to Azure Web App Services. The bot receives HTTP requests from the messaging platform, and it also runs in a while loop checking something it's designed to notify about.

    The HTTP part is handled by Flask and it works fine. But I can't get the loop to work alongside Flask. My (limited) understanding is I have to get two separate event loops to co-exist. The main one is occupied by Flask and I need to have a separate one for my notification checker.

    I tried all kinds of combinations of multiprocessing, asyncio and threading. Here's the latest version I ended up with:

    import threading
    from flask import Flask, jsonify
    import logging
    import sys
    import asyncio
    import datetime
    import os
    import telegram
    import time


    logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler(sys.stdout)])


    class Bot:
    def __init__(self):
    self.bot_token = os.getenv('TOKEN')
    self.chat_id = os.getenv('CHAT_ID')
    self.telegram_bot = telegram.Bot(token=self.bot_token)
    self.sleep = os.getenv('SLEEP') # set to 60

    def run(self):
    while True:
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    message = f"It's {current_time} o'clock!\n\nSent from " + __file__
    self.send_notification(message)
    time.sleep(int(self.sleep))

    def send_notification(self, message):

    async def send_message():
    await self.telegram_bot.send_message(chat_id=self.chat_id, text=message)

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(send_message())


    bot = Bot()
    app = Flask(__name__)


    # Simple endpoint that responds to Azure ping
    @app.route('/')
    def home():
    return jsonify(
    hello='world',
    now=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    ), 200


    if __name__ == '__main__':
    bot_thread = threading.Thread(target=bot.run)
    bot_thread.daemon = True # ChatGPT says this ensures the bot thread will stop when the main thread exits
    bot_thread.start()
    app.run(host=os.getenv("HTTP_HOST", "0.0.0.0"), port=int(os.getenv("HTTP_PORT")))


    I tried the above code in the following environments:

    • Local machine in PyCharm: works fine, it both 1. serves HTTP requests and 2. sends out a message every 60 seconds until I shut it down;
    • Render.com, deployed as an experiment to a Web Service on their free tier: works fine, also both receives requests and sends out messages, but shuts down after 10-20 minutes because of the free tier limitation;
    • Asure, deployed to a Web App Service on their free tier: runs and serves HTTP requests, but the bot does not work. No errors in logs. Checked all logs under https://.scm.azurewebsites.net/api/logs/docker.

    What I have on Azure is a Linux Web App on their free tier.

    {
    "type": "Microsoft.Web/sites",
    "kind": "app,linux",
    "properties": {
    "state": "Running",
    "usageState": "Normal",
    "siteScopedCertificatesEnabled": false,
    "afdEnabled": false,
    "siteProperties": {
    "properties": [
    {
    "name": "LinuxFxVersion",
    "value": "PYTHON|3.13"
    },
    {
    "name": "WindowsFxVersion",
    "value": null
    }
    ],
    "appSettings": null
    },
    "availabilityState": "Normal",
    "csrs": [],
    "cers": null,
    "siteMode": null,
    "computeMode": null,
    "serverFarm": null,
    "reserved": true,
    "isXenon": false,
    "hyperV": false,
    "storageRecoveryDefaultState": "Running",
    "contentAvailabilityState": "Normal",
    "runtimeAvailabilityState": "Normal",
    "dnsConfiguration": {},
    "vnetRouteAllEnabled": false,
    "containerAllocationSubnet": null,
    "useContainerLocalhostBindings": null,
    "vnetImagePullEnabled": false,
    "vnetContentShareEnabled": false,
    "outboundVnetRouting": null,
    "siteConfig": {
    "numberOfWorkers": 1,
    "defaultDocuments": null,
    "netFrameworkVersion": null,
    "phpVersion": null,
    "pythonVersion": null,
    "nodeVersion": null,
    "powerShellVersion": null,
    "linuxFxVersion": "PYTHON|3.13",
    "windowsFxVersion": null,
    "windowsConfiguredStacks": null,
    "requestTracingEnabled": null,
    "remoteDebuggingEnabled": null,
    "remoteDebuggingVersion": null,
    "httpLoggingEnabled": null,
    "azureMonitorLogCategories": null,
    "acrUseManagedIdentityCreds": false,
    "acrUserManagedIdentityID": null,
    "logsDirectorySizeLimit": null,
    "detailedErrorLoggingEnabled": null,
    "publishingUsername": null,
    "publishingPassword": null,
    "appSettings": null,
    "metadata": null,
    "connectionStrings": null,
    "machineKey": null,
    "handlerMappings": null,
    "documentRoot": null,
    "scmType": null,
    "use32BitWorkerProcess": null,
    "webSocketsEnabled": null,
    "alwaysOn": false,
    "javaVersion": null,
    "javaContainer": null,
    "javaContainerVersion": null,
    "appCommandLine": null,
    "managedPipelineMode": null,
    "virtualApplications": null,
    "winAuthAdminState": null,
    "winAuthTenantState": null,
    "customAppPoolIdentityAdminState": null,
    "customAppPoolIdentityTenantState": null,
    "runtimeADUser": null,
    "runtimeADUserPassword": null,
    "loadBalancing": null,
    "routingRules": null,
    "experiments": null,
    "limits": null,
    "autoHealEnabled": null,
    "autoHealRules": null,
    "tracingOptions": null,
    "vnetName": null,
    "vnetRouteAllEnabled": null,
    "vnetPrivatePortsCount": null,
    "publicNetworkAccess": null,
    "cors": null,
    "push": null,
    "apiDefinition": null,
    "apiManagementConfig": null,
    "autoSwapSlotName": null,
    "localMySqlEnabled": null,
    "managedServiceIdentityId": null,
    "xManagedServiceIdentityId": null,
    "keyVaultReferenceIdentity": null,
    "ipSecurityRestrictions": null,
    "ipSecurityRestrictionsDefaultAction": null,
    "scmIpSecurityRestrictions": null,
    "scmIpSecurityRestrictionsDefaultAction": null,
    "scmIpSecurityRestrictionsUseMain": null,
    "http20Enabled": true,
    "minTlsVersion": null,
    "minTlsCipherSuite": null,
    "scmMinTlsCipherSuite": null,
    "supportedTlsCipherSuites": null,
    "scmSupportedTlsCipherSuites": null,
    "scmMinTlsVersion": null,
    "ftpsState": null,
    "preWarmedInstanceCount": null,
    "functionAppScaleLimit": 0,
    "elasticWebAppScaleLimit": null,
    "healthCheckPath": null,
    "fileChangeAuditEnabled": null,
    "functionsRuntimeScaleMonitoringEnabled": null,
    "minimumElasticInstanceCount": 1,
    "azureStorageAccounts": null,
    "http20ProxyFlag": null,
    "sitePort": null,
    "antivirusScanEnabled": null,
    "storageType": null,
    "sitePrivateLinkHostEnabled": null,
    "clusteringEnabled": false
    },
    "functionAppConfig": null,
    "daprConfig": null,
    "deploymentId": "<app>",
    "slotName": null,
    "trafficManagerHostNames": null,
    "sku": "Free",
    "scmSiteAlsoStopped": false,
    "targetSwapSlot": null,
    "hostingEnvironment": null,
    "hostingEnvironmentProfile": null,
    "clientAffinityEnabled": false,
    "clientAffinityProxyEnabled": false,
    "blockPathTraversal": false,
    "clientCertEnabled": false,
    "clientCertMode": "Required",
    "clientCertExclusionPaths": null,
    "hostNamesDisabled": false,
    "ipMode": "IPv4",
    "vnetBackupRestoreEnabled": false,
    "domainVerificationIdentifiers": null,
    "kind": "app,linux",
    "managedEnvironmentId": null,
    "workloadProfileName": null,
    "resourceConfig": null,
    "containerSize": 0,
    "dailyMemoryTimeQuota": 0,
    "suspendedTill": null,
    "siteDisabledReason": 0,
    "functionExecutionUnitsCache": null,
    "maxNumberOfWorkers": null,
    "cloningInfo": null,
    "hostingEnvironmentId": null,
    "slotSwapStatus": null,
    "httpsOnly": false,
    "endToEndEncryptionEnabled": false,
    "functionsRuntimeAdminIsolationEnabled": false,
    "redundancyMode": "None",
    "inProgressOperationId": null,
    "geoDistributions": null,
    "privateEndpointConnections": [],
    "publicNetworkAccess": "Enabled",
    "buildVersion": null,
    "targetBuildVersion": null,
    "migrationState": null,
    "eligibleLogCategories": "AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs,AppServiceAuthenticationLogs",
    "inFlightFeatures": [
    "SiteContainers"
    ],
    "storageAccountRequired": false,
    "virtualNetworkSubnetId": null,
    "keyVaultReferenceIdentity": "SystemAssigned",
    "autoGeneratedDomainNameLabelScope": null,
    "defaultHostNameScope": "Global",
    "privateLinkIdentifiers": null,
    "sshEnabled": true
    },
    "apiVersion": "2022-03-01"
    }


    Is there a way to get it to work on Azure Web App Services within Free Tier? Is there a specific way I should utilise to get a separate thread/process to get it to work on Azure? Or should I just do it differently? I do not necessarily have to have it run a While True loop, I just need the app to check something every minute while listening to incoming requests.

    One workaround I can think of is just have a separate endpoint that does the check and then just trigger it every minute via some external cron, but that seems way too cumbersome for the task, when the code itself should just be able to run a loop internally. (On the other hand, this can have a positive effect of keeping the Azure app alive, as their free tier will shut it down after 20 minutes similarly to Render.com. If I got the loop to work, my plan was to have the bot send a request to itself e.g. on every 10th check.)

    Continue reading...

Compartilhe esta Página