Aniss Chohra
Aniss Chohra

Reputation: 453

Flask deployment on Azure fails with 503 - Server Unavailable

on Azure I am trying to deploy a flask API on a web app hosted on Linux. The code of the app.py is the following:

app = Flask(__name__)
CORS(app)
basedir = os.path.abspath(os.path.dirname(__file__))
from db_helpers import init_db

app.config[
    'SQLALCHEMY_DATABASE_URI'
] = my_microsoft_azure_database_connection.database_connection_uri
app.config['DEBUG'] = False
app.config['TESTING'] = False
app.config['CSRF_ENABLED'] = True
app.config['SECRET_KEY'] = 'needs-to-be-changed'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db_engine = init_db()
db = SQLAlchemy(app, metadata=BaseDeclarative.metadata)

api = Api(app, default_mediatype='application/json')

api.add_resource(
    ListAssessmentCaseEndpoint, '/assessments', endpoint='list_assessment_cases'
)
api.add_resource(
    AssessmentCaseEndpoint, '/assessments/<uuid:uuid>', endpoint='assessment_cases'
)

api.add_resource(ListAuditLogEndpoint, '/auditing', endpoint='list_audit_logs')
api.add_resource(AuditLogEndpoint, '/auditing/<uuid:uuid>', endpoint='audit_logs')

api.add_resource(HealthCheckEndpoint, '/healthcheck', endpoint='health_check')


# Add Swagger documentation to Flask app
Swagger(app, template_file='swagger.yml')

In order to deploy the application on Azure, we first build an artifact (zip file) using the following ARM template:

trigger:
- develop

variables:

  webAppName: 'test-api'

  environmentName: 'test-api'

  projectRoot: $(System.DefaultWorkingDirectory)/WebApi

  pythonVersion: '3.7'

stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: BuildJob
    steps:
    - task: UsePythonVersion@0
      inputs:
        versionSpec: '$(pythonVersion)'
      displayName: 'Use Python $(pythonVersion)'
    
    - script: |
        python3 -m venv .venv
        source .venv/bin/activate
        pip install --upgrade pip
        pip install waitress==2.1.2
        pip install setup
        pip install -r requirements.txt
      workingDirectory: $(projectRoot)
      displayName: "Install requirements"

    - task: ArchiveFiles@2
      displayName: 'Archive files'
      inputs:
        rootFolderOrFile: '$(projectRoot)'
        includeRootFolder: false
        archiveType: zip
        archiveFile: $(Build.ArtifactStagingDirectory)/$(webAppName).zip
        replaceExistingArchive: true

    - publish: $(Build.ArtifactStagingDirectory)/$(webAppName).zip
      displayName: 'Upload package'
      artifact: drop

We then deploy the artifact on a Web App (hosted on linux using Python 3.7). To do that, we use the following template:

steps:
- task: AzureWebApp@1
  displayName: 'Azure Web App Deploy: test-api'
  inputs:
    azureSubscription: 'azurerm-sp-apps-dev'
    appType: webAppLinux
    appName: 'testt-api'
    package: '$(System.DefaultWorkingDirectory)/test-api-WebApi/drop/test-api.zip'
    runtimeStack: 'PYTHON|3.7'
    startUpCommand: 'python3.7 -m pip install -r requirements.txt && python3.7 -m pip install gunicorn && python3.7 -m gunicorn --bind=0.0.0.0:80 --workers=4 app:app'
    configurationStrings: '-StartupCommand startup.sh'
    deploymentMethod: zipDeploy

and we also use an application gateway that is configured as the following:

steps:
- task: test-AppGateway.appGatewayCreate-task.test-AppGateway-CreateUpdate@1
  displayName: 'Create or Update Application Gateway Config API'
  inputs:
    connectedServiceName: 'azurerm-sp-apps-dev'
    AppName: 'test-api'
    HealthProbeUri: /healthcheck
  timeoutInMinutes: 120
  retryCountOnTaskFailure: 10

But at the deployment, we have the following strange behaviours:

1 + In the application logs (in the availability and Performance menu), we can see that the gunicorn server has been started correctly and that it is listening on http://0.0.0.0:80, and that the 4 workers have been booted...

2 + But in the platform logs, we have the following errors:


Platform logs contain errors or warnings
Container test-api_0_a8607b6c for site test-api did not start within expected time limit.
Container test-api_0_a8607b6c didn't respond to HTTP pings on port: 8000, failing site start

We have investigated the issue and here are some solutions that we tried, but did not work:

3 + This leads for the /healthcheck endpoint to not respond (503 server unavailable leading to 502 bad gateway on the application gateway)

4 + another issue encountered is that for some reason, the virtual environment created during the build time is not recognized at the deployment phase:

this is the following docker command used internally at the deployment:

docker run -d -p 9286:8000 --expose=49494 --name test-api_0_f5ccb0ab -e WEBSITE_CORS_ALLOWED_ORIGINS=https://test.dev.app.test.com,https://test.eastus2-dev-platform-ase1.appserviceenvironment.net -e WEBSITE_CORS_SUPPORT_CREDENTIALS=True -e WEBSITES_PORT=80 -e WEBSITE_SITE_NAME=test-api -e WEBSITE_AUTH_ENABLED=False -e WEBSITE_ROLE_INSTANCE_ID=0 -e WEBSITE_HOSTNAME=test-api.eastus2-dev-platform-ase1.appserviceenvironment.net -e WEBSITE_INSTANCE_ID=MY-ID -e HTTP_LOGGING_ENABLED=1 -e WEBSITE_USE_DIAGNOSTIC_SERVER=True appsvc/python:3.7_20221128.12.tuxprod python3.7 -m pip install -r requirements.txt && python3.7 -m pip install gunicorn && python3.7 -m gunicorn --bind=0.0.0.0:80 --workers=4 app:app

Any idea how to make it work?

Upvotes: 0

Views: 330

Answers (1)

Aniss Chohra
Aniss Chohra

Reputation: 453

After spending much time trying to find the source of the problem; I finally made the rest api work correctly by changing the startup command to the following:

gunicorn -w 2 -k uvicorn.workers.UvicornWorker app:app

now all the errors are gone and the web app is fully functional. Also, you have to add the following to requirements.txt:

gunicorn~=20.1.0
uvicorn~=0.20.0

Upvotes: 0

Related Questions