Back

Automated deployment of a Vue Flask app using Azure Pipelines

June 15, 2021 5 minute read
Pipeline on a rail track
Source: Unsplash

In this article we will look at how to automate the deployment of a Vue Flask app to Azure App Service with Azure Pipelines. In a previous article I covered building a Vue Flask app to query a SQL database and return data to the browser to view or download. We will start with the same template, prepare it for deployment and configure the app service and pipeline in Azure. The result will be a deployed Flask app which serves a static Vue.js frontend. If you have your own application, you can adapt these steps. Before starting, you will need an Azure subscription alongside Python, Node.js and Yarn installed.

Download the template

First go to this public repository and download the project template as a zip file. Extract the folder contents and open the folder in a code editor like Visual Studio Code.

Configure the template for deployment

One thing needs adding before we deploy. Create a new file startup.py at the top of the folder - same directory as run.py. This will be the file Azure App Service uses to start the application.

/startup.py
""" 
The startup file for Azure App Service that just imports the app object.
"""

from app import app

Setting up the automated pipeline

Here are the step by step actions the video below will go through to create the automated pipeline:

  • Create a new Azure App Service in the Azure portal
  • Set the environment variable SCM_DO_BUILD_DURING_DEPLOYMENT to true in the App Service
  • Create a new project in Azure DevOps
  • Create an Azure Repo in the project
  • Push the application code to the Azure Repo
  • Set up an Azure Pipeline in the project
  • Build and deploy the app to Azure App Service
  • Check the site is deployed (had to hard refresh with Ctrl + F5) 😄
How to change video quality?
How to change video playback speed?

Setting the SCM_DO_BUILD_DURING_DEPLOYMENT environment variable to true took me a while to figure out. It's in this section of the docs and states:

If your app fails because of a missing dependency, then your requirements.txt file was not processed during deployment. This behavior happens if you created the web app directly on the portal rather than using the az webapp up command as shown in this article. The az webapp up command specifically sets the build action SCM_DO_BUILD_DURING_DEPLOYMENT to true. If you provisioned the app service through the portal, however, this action is not automatically set.

The YAML I used for the build and deploy steps looked like this:

pipeline.yml
# Python to Linux Web App on Azure
# Build your Python project and deploy it to Azure as a Linux Web App.
# Change python version to one thats appropriate for your application.
# https://docs.microsoft.com/azure/devops/pipelines/languages/python

trigger:
- master

variables:
  # Azure Resource Manager connection created during pipeline creation
  azureServiceConnectionId: 'f59ed866-b638-412b-bdce-02504965ee64'

  # Web app name
  webAppName: 'vue-flask-app'

  # Agent VM image name
  vmImageName: 'ubuntu-latest'

  # Environment name
  environmentName: 'vue-flask-app'

  # Project root folder. Point to the folder containing manage.py file.
  projectRoot: $(System.DefaultWorkingDirectory)

  # Python version: 3.6
  pythonVersion: '3.6'

stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: BuildJob
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: UsePythonVersion@0
      inputs:
        versionSpec: '$(pythonVersion)'
      displayName: 'Use Python $(pythonVersion)'
    
    - task: NodeTool@0
      inputs:
        versionSpec: '10.x'
      displayName: 'Install Node.js'
      
    - script: pip install --upgrade pip
      displayName: 'Upgrade pip'
      workingDirectory: $(projectRoot)

    - script: pip install pipenv
      displayName: 'Install pipenv'

    - script: python -m pipenv install --dev
      displayName: 'Install Python dependencies'

    - script: python -m pipenv run pip freeze > requirements.txt
      displayName: 'Generate requirements.txt'

    - script: |
        curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.9.4
        export PATH="$HOME/.yarn/bin:$PATH"
        yarn install
        yarn upgrade
      displayName: 'Install Node dependencies'

    - script: yarn build
      displayName: 'Build Vue app'

    - script: |
        pip install codecov
        pip install pytest
        pip install pytest-sugar
        pip install pytest-cov
        pip install pytest-azurepipelines
        python -m pipenv run pytest --junitxml=$(System.DefaultWorkingDirectory)/testResults.xml  --cov=app --cov-report=xml --cov-report=html
      displayName: 'Run tests with pytest'

    - task: PublishTestResults@2
      displayName: "Publish test results"
      inputs:
        testResultsFiles: '$(System.DefaultWorkingDirectory)/testResults.xml'
        testRunTitle: '$(Agent.OS) - $(Build.BuildNumber)[$(Agent.JobName)] - Python $(python.version)'
        failTaskOnFailedTests: true
      condition: succeededOrFailed()
      
    - task: PublishCodeCoverageResults@1
      displayName: "Publish code coverage"
      inputs:
        codeCoverageTool: Cobertura
        summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
        reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov'

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

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

- stage: Deploy
  displayName: 'Deploy Web App'
  dependsOn: Build
  condition: succeeded()
  jobs:
  - deployment: DeploymentJob
    pool:
      vmImage: $(vmImageName)
    environment: $(environmentName)
    strategy:
      runOnce:
        deploy:
          steps:
          
          - task: UsePythonVersion@0
            inputs:
              versionSpec: '$(pythonVersion)'
            displayName: 'Use Python version'

          - task: AzureWebApp@1
            displayName: 'Deploy Azure Web App :  vue-flask-app'
            inputs:
              azureSubscription: $(azureServiceConnectionId)
              appName: $(webAppName)
              package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip

              startUpCommand: 'gunicorn --bind=0.0.0.0 --workers=4 --timeout 600 startup:app'

Your azureServiceConnectionId will be different so be sure to change that.

Deployment was successful!

You now have a deployed Vue Flask app with a continuous integration pipeline configured. You can deploy new features with a simple push to the master branch which will trigger the pipeline. You could completely change the application we have deployed and take it in your own direction. Not only that, you might have noticed that this setup also publishes pytest code test coverage to the pipeline! Let me know in the comments if this helped you and if you have any questions. I know this was quite Azure specific, I think you could set up a similar pipeline using AWS or Google Cloud Platform.

I really like the Vue Flask combination for the ease of creating an interactive experience with Vue, alongside the many packages for data science that Python offers. You could separate this setup and have Vue served from a CDN and Python running as the API layer, but for a quick starter single-deploy setup this is perfect. It might need a little tailoring to your own needs, the template we used in this tutorial used Python 3.6 and pipenv, your setup might not, so adjust the Pipeline and App Service accordingly.

If you enjoyed this article be sure to check out other articles on the site 👍 you may be interested in: