DEV Community

loading...

Deploy Containerized Web App to Microsoft Azure | Upload/Download files of Azure Storage Account

shivappa profile image Shivappa ・18 min read

If you are bored to read my store you can skip quoted text and go to technical content below.

Few months back I got a chance to work in new project in my company (with being active participation in the current project as well) where I have no idea what the project will be, just got hint it will be a cloud project.
When the project discussions started, everything was new and all has to go from scratch, many dilemma what to choose and how to start.

After some research we decided to go with Microsoft Azure and Python.

Since this is completely new to me and had lot of responsibility.
Then I started searching over the internet about Microsoft azure and the services it provides. Really there are plenty of services in the Azure which helps to build projects to the great extent.
Then I started googling about the services what they do and how can be used. Every time I google I was going through blogs, some are valid and easy to understand but some are not.
One day I got a nice tutorial in Microsoft website and found it has rich in documentation about the Azure. After I studied that topic, myself started exploring in the Microsoft documentation and found that it has Microsoft Learn page where it supports all the documentation on all the services it supported and it has all the levels of content from beginner to expert level.

You can learn and achieve your expertise in various topics from Microsoft Learn.
Each module provides exercises and knowledge check questions based on the topic you learnt.
And another great thing MSLearn provides for hands on is Azure sandbox subscription(You can activate during each module learning and exercises ).
This is really great for quick hands on the things we learn. This is strictly for the study purpose on the learning modules and will be deleted after the timer(You should not use for other purposes).

Later I found some online events are being conducted by Microsoft professionals and explored them. You can find very helpful and priceless content from these events. Please visit here for full list of Microsoft Virtual events.
As I was reading about Azure certification courses, I have got excited and attended AZ-900 Azure fundamentals(basic level) virtual training and it was really nice. This event sparked me to attend many more events and learn Azure.

As I was studying for the various services on the Azure in MSLearn, I got to know there is an online event Cloud Community Days in KonfHub is going to happen. Without a bit delay I registered for the 2 Days conference and I attended the same. You can join your nearest Azure Developer community.
It was really thoughtful conference in which many things I have learnt and could clarify many doubts which I had.
The content throughout was good and there were quiz sessions in between. On first day I could not attend in quiz but attended second day's first two quizzes and in the first attempt I ranked 12th and in the next I ranked 3rd. This pushed me more to learn and master the learnings from the Microsoft and attend many more such events.
Later I took "Azure Dev Challenge" and completed effective study and as a next step Azure Developer Stories contest I planned to write a blog of my steps towards Microsoft Azure.

This is my first blog ever. Thank you Microsoft and KonfHub for this motivation.

I think it is enough to stop my story and share some technical details which I built after learning Azure Dev Challenge modules.

Project Statement

Deploy a Containerized Flask Web Application which translates the given text to another language and dump the contents in a file. This file will be uploaded to Azure Storage Account in a secure way. Once it is uploaded we can download the file if we have valid SAS token for the file.

We will divide above project statement into below steps,

  1. Create Azure Container Registry(ACR).
  2. Write Flask Application to
    • translate text using Azure Cognitive Services
    • upload/download files to/from Azure Storage Account.
  3. Deploy containerized flask application to Azure Container Registry.
  4. Check logs of our container application.

Prerequisites

  1. Visual Studio Code IDE.
  2. Microsoft Azure Subscription. Get a free account if you do not have one.
  3. Latest Python version.

Step 1: Create Azure Container Registry(ACR)

Before creating Azure Container Registry, we will understand some terms.
Container Image: It is a bundled software package, which contains system libraries, tools and other platform settings with which a program can run without any changes to software package at run time.
Container Registry: It is a repository to store container images to run container based applications.

Now let's create Azure Container Registry.

Login to your Azure Portal
Click on add resource to create new Resource Group.
Alt Text
Search for "resource group" in the Azure Marketplace and click on it and create.
resourcegroup
resourcegroup-click

Add new resource group with required details.

  • Subscription: Select your valid Azure subscription where you want to create resource group.
  • Resource group: Name of the resource group to be created.
  • Region: The location where you want to create resource group.(select your nearest region) RG

Once the resource group is created. Go to the Resource Group created in above step.
Now create a container registry resource in this Resource Group.
Click on "Add"
resourcegroup-add

Search for "container registry" in the Azure Marketplace and click on it and create.
container
container-2

Fill all the required details.

  • Subscription: Select your valid Azure subscription where you want to create container registry.
  • Resource group: Select the resource group which you created in previous step.
  • Registry name: Name of your Azure Container registry to be created.
  • Location: The location where you want to create ACR (Keep same as the resource group Region).
  • SKU: Select the registry tier Basic. (Available are Basic, Standard, Premium) container-3

Then Review + create and Create.
After the Azure Container Registry is created, open and update admin user setting.
container-5
Enable under Admin user, this is required to deploy web application directly from container registry.
container-4

Now we successfully created Azure Container Registry.
It is time to write some code.

Step 2: Write Flask Application

We will be using Visual Studio Code code editor for all our coding. A best code editor with vast features.
Now on I will be referring Visual Studio Code as VS Code.

  1. Download and install VS Code based on your OS. (Can be used on Windows, MacOS, Linux).
  2. Install latest python version from python.org

Create a folder Azure-Dev-Stories in your computer and open the folder in VSCode.
Open cmd and run below commands. (I am using windows. If you are using other OS please replace corresponding commands)

md Azure-Dev-Stories
cd Azure-Dev-Stories
code .

(code . opens VSCode)
Now VSCode is opened and you can see empty folder like this.
vscode

Our environment for coding is ready.

To isolate our project we will create virtual environment.
Open VSCode terminal (Ctrl + `) and execute below command.

python -m venv .venv

vscode-venv

To make containerized image of our code we need to install below extensions from VSCode marketplace.

  • Azure App service, Azure Account extension (For Azure services)
  • Docker extension (For dockerizing the application).

Search for Azure account and docker in marketplace and install them as shown in below screens.
vscode-azure

Install Docker extension.

vscode-docker

After both the extensions are installed your VS Code looks similar to this.
vscode-extension
Go to Azure extension and login to your Microsoft Azure account.

Now create a file app.py and add below contents in it and save the file.

from flask import Flask

app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

Now we need to create our first Dockerfile.

Dockerfile: It is a text file which contains all the steps to be performed to create our application as a image.

Go to command palette (Ctrl + Shift + P) in VSCode and type build image, select Azure Container Registry: Build Image in Azure... from the search list.

vscode-buildimage

You will be asked to add a Dockerfile to the workspace on bottom screen, select Yes.
vscode-add-Docker

Select Python:Flask (select other relevant application platform if you are building for other frameworks)
Then select app.py as entry point and select port 5000 and select Yes to add the docker compose files.
After the above steps your VSCode folder looks like this.
vscode-fileslist
Newly created Dockerfile looks like this.

# For more information, please refer to https://aka.ms/vscode- docker-python
FROM python:3.8-slim-buster

EXPOSE 5000

# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1

# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install pip requirements
COPY requirements.txt .
RUN python -m pip install -r requirements.txt

WORKDIR /app
COPY . /app

# Creates a non-root user and adds permission to access the /app 
 folder
# For more info, please refer to https://aka.ms/vscode-docker- python-configure-containers
RUN useradd appuser && chown -R appuser /app
USER appuser

# During debugging, this entry point will be overridden. For more 
information, please refer to https://aka.ms/vscode-docker-python- debug
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]`
Enter fullscreen mode Exit fullscreen mode

Now go to requirements.txt file and add below packages.

flask
gunicorn
python-dotenv
python-dateutil
requests
azure-storage-blob
Enter fullscreen mode Exit fullscreen mode

We set what packages are required for our project and we need to install them in our virtual environment.
Go to VS Code terminal and run below command to install the packages.
pip install -r requirements.txt

It takes some time to install all the packages.

Now our bare flask application framework is ready and we need to deploy it to Azure as web application to make sure our application is running.

Step 3: Deploy containerized flask application to Azure Container Registry

Open command palette (Ctrl + Shift + P) and search build image and select Azure Container Registry: Build Image in Azure... from the search list.
Now give a name to image. With this name it will be pushed to Azure Container Registry.
vscode-add-docker-deploy

Replace .Run.ID with master to keep our image tag always the master.
vscode-add-docker-deploy-2

azure-dev-stories:master

Select the Azure Container Registry from the list which we have created before.
vscode-add-docker-deploy-3
Choose Linux. (You can choose other OS for which you want to make the image)
vscode-add-Docker-4
After the image is successfully created you will see output like this.
vscode-add-Docker-5
Confirm whether the image is present in Azure Container Registry(ACR).
Go to Docker extension and see the steps in below image.
vscode-docker-image-ACR

Our application image is built and is present in ACR. Now we need to deploy as Azure web application.
Right click on master and select Deploy Image to Azure App Service....
vscode-docker-image-ACR-2

  • Enter Unique global name for your application. If similar name is exists, it shows error and choose different name. This name must be unique across global azure web applications, because our deployed application is accessible over the internet.
  • Select the Resource group which we already created during ACR creation(it shows the list of resource groups).
  • Create App Service Plan for the application.
  • Give a name to Service Plan (the agent on which our application runs).
  • Select pricing tier F1 Free.
  • Select the Location for the Service Plan. After above steps you will see deploy message at the bottom screen. vscode-docker-image-deploy-appservice Once the web application is deployed you will see a message Successfully create web app ....

Open the site and check our application is up and running.
We see our bare app like this.webapp-deployed

Now go to Azure Portal and check image tag is applied properly.
To check it, go to Azure home search bar, search for app services and select the web application deployed.
webapp-search
Go to Deployment Center -> Settings and confirm Registry, Image, Tag(master) are fine.
webapp-settings

Now we successfully deployed flask web application as containerized App in Azure. And we need make valuable use from the web application.
So our next step is,

translate text using Azure Cognitive Services

To translate the user input to another language we will use Azure Cognitive Services.
Now go to Azure Portal.
Create a resource -> search for translator service and install it.
createresource
translator
Fill required fields.

  • Subscription: Valid subscription.
  • Resource group: select resource group created before.
  • Region: Select nearest region
  • Name: give a unique name to translator service
  • Pricing tier: Free F0, (You can choose higher plan also based on your requirements)

3

Then Review + create and Create.

Go to the translator service created, note down KEY1 or KEY2, Endpoint and Location, we need them to input in our flask application later.
translator-keys

We created Azure translator and we need to use it in our application to get the translated text.
Go to VSCode. Create .env file and add below details.

KEY=0b5c31e83e36459f953b097d01931143
ENDPOINT=https://api.cognitive.microsofttranslator.com/
LOCATION=eastus
Enter fullscreen mode Exit fullscreen mode
  • KEY : Translator service key1 or key2
  • ENDPOINT: Translator Endpoint
  • LOCATION: Translator service Location

Note: When we created Dockerfile, but silently .dockerignore file also created. This contains the list of files which we need ignore while building the docker image.
Look careful all contents, it has .env also. But we must need this file to get our translator service details.
So remove the line containing .env.
vscode-dockerignorefile
We need to ignore our virtual environment libraries which are present in .venv. Add **/.venv line in .dockerignore.

For the docker to refer our environment variables properly, we need to add .env file in docker-compose.yml.
Open docker-compose.yml file and add section env_file after ports like this(take care of the indentation. ports and env_file should be on same level).

version: '3.4'

services:
  azuredevstories:
    image: azuredevstories
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - 5000:5000
    env_file: 
      - .env
Enter fullscreen mode Exit fullscreen mode

Now we are done with all the configuration. Let's write some code.
Copy paste below code in app.py.

import logging
import os
import requests, uuid

from flask import Flask, render_template, request

from dotenv import load_dotenv
load_dotenv()

app = Flask(__name__)

# Setup logging to get the logs from the application
level = logging.DEBUG
format = f'%(asctime)s %(levelname)s %(name)s : %(message)s'
logging.basicConfig(filename='app.log', level=level, format=format)

@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')

@app.route('/', methods=['POST'])
def index_post():
    """translate given string and put in file and upload to Azure storage account"""
    # Read values from the form
    original_text = request.form['text']
    target_language = request.form['language']

    # Load values from .env
    key = os.environ.get('KEY', '')
    endpoint = os.environ.get('ENDPOINT', '')
    location = os.environ.get('LOCATION', '')

    # Construct the translator method
    path = '/translate?api-version=3.0'
    target_language_parameter = '&to=' + target_language
    constructed_url = endpoint + path + target_language_parameter

    # Set up header for post request
    headers = {
        'Ocp-Apim-Subscription-Key': key,
        'Ocp-Apim-Subscription-Region': location,
        'Content-type': 'application/json',
        'X-ClientTraceID': str(uuid.uuid4())
    }

    # body of request
    body = [{'text': original_text}]

    # Make call using post
    translator_request = requests.post(constructed_url, headers=headers, json=body)
    # Retrieve JSON response
    translator_response = translator_request.json()
    # Retrieve translation response
    translated_text = translator_response[0]['translations'][0]['text']

    return render_template(
        'results.html',
        original_text=original_text,
        translated_text=translated_text,
    )

Enter fullscreen mode Exit fullscreen mode

Create templates folder in current workspace directory.
Inside templates folder, create index.html with which we can receive the input and send it to Azure translation service from our flask application.
Copy paste below code in index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
        integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <title>Translator</title>
</head>
<body>
    <div class="container">
        <h1>Translation service</h1>
        <div>Enter the text you wish to translate and upload to Azure</div>
        <div>
            <form method="POST">
                <div class="form-group">
                    <textarea name="text" cols="10" rows="2" class="form-control"></textarea>
                </div>
                <div class="form-group">
                    <label for="language">Select Translation Language</label>
                    <select name="language" class="form-control">
                        <option value="en">English</option>
                        <option value="it">Italian</option>
                        <option value="ja">Japanese</option>
                        <option value="ru">Russian</option>
                        <option value="de">German</option>
                    </select>
                </div>
                <div>
                    <button type="submit" class="btn btn-success">Translate and Upload !</button>
                </div>
            </form>
        </div>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Inside templates folder, create results.html and copy paste below code.
This will display the results once the input is translated.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
        integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <title>Upload</title>
</head>
<body>
</br>
    <div class="container">
        <h2>Translation results</h2>
    </br>
        <div>
            <strong>Input text :</strong> {{ original_text }}
        </div>
        <div>
            <strong>Translated text:</strong> {{ translated_text }}
        </div>
        <div>
            <a href="{{ url_for('index') }}">Try another one!</a>
        </div>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Our final folder structure looks like this.
vscode-folderstructure

Now our application is ready to make translations. Run locally and confirm there is no issue.
flask run

But we have not yet deployed it Azure, so it is time to re-build and deploy it to Azure.
Go to command palette (Ctrl + Shift + P).
Search build image and select Azure Container Registry: Build Image in Azure...,
Simply click enter key in all steps and keep all settings without change. (make sure all are valid and recent settings).
Image build starts and once it is completed you will see the results under Output in terminal section.
Go to docker extension, refresh and confirm the image tag time is changed (a few seconds ago).
vscode-docker-imagetage-details

Wait for sometime to docker image apply on the web application. Now open already deployed web application website https://mydockerdemotest.azurewebsites.net/ and check changes are reflected.

You can also open the web application in App services -> Select your application -> Overview -> Browse.

Website looks like this.
webapp-deployed-input

Enter input text to be translated and select the language of translation. Click Translate. We have the translated text like this.
webapp-deployed-output

Wow!, now our application is able to translate the text.

Now we will write this translation text to a file and upload to Azure Storage Account and download whenever we want.
That is a great service provided by Azure to store our files in a more secure way.
Let's learn on how to upload and download the files in Azure service.

Use Azure Storage Account to securely upload and download files.

Azure Storage account: It contains all our Azure storage objects like blobs(containers), queues, tables and disks. This account can be accessed over the internet using the unique namespace which we will be doing in this section.
Let's begin...
Again go to Azure Portal and install Azure Storage Account service.
Here are the steps.
Create new resource.
createresource

search storage account in azure marketplace and create the service.
15

Fill the required fields.

  • Subscription: Valid subscription.
  • Resource group: select resource group created before.
  • Storage account name: give a unique name to storage account
  • Location: Select nearest location
  • Performance: select Standard
  • Account kind: Default StorageV2 (general purpose v2)
  • Replication: Default Read-access-geo-redundant storage (RA-GRS)

17

Click Review + create then create to create the service.
Go to the created resource and fetch the connection details of the storage account.
Select Access keys and click on Show keys. It displays the key and connection string.
18

Copy and note down the key and Connection string which is required to put in our code to upload the file to this storage account.
19

Let's create a container in which we will be storing our files.
Go to overview and select containers.
20
Create a new container.
21
Give a name to container and select private from the dropdown because nobody else should not access our storage account.
22
Our storage container is created. Now let's go to code part from which we can upload the files to this container.

Go to VSCode.
As we already copied the connection string, copy it to .env file along with our container name and storageaccount name which we already created.
Our complete .env file looks like this.

KEY=0b5c31e83e36459f953b097d01931143
ENDPOINT=https://api.cognitive.microsofttranslator.com/
LOCATION=eastus
STORAGEACCOUNT_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=storageaccounttranslator;AccountKey=oJrELlq6ts0GGrvBVCISsARadZL0ozRPLF+m2afgRjz5mTcnV9TzmpDAFOQqPcZ0u+8OW90Qp35k5842QkB95w==;EndpointSuffix=core.windows.net
CONTAINER_NAME=translations
STORAGE_ACCOUNT_NAME=storageaccounttranslator
SASTOKEN_LIFE=12
Enter fullscreen mode Exit fullscreen mode
  • STORAGEACCOUNT_CONNECTION_STRING : Connection string of the the storage account which we copied.
  • CONTAINER_NAME : Name of the container we created.
  • STORAGE_ACCOUNT_NAME : Our storage account name.
  • SASTOKEN_LIFE : How long sas token is valid for the file.(in months)

Now we need to write code to upload the translated file to storage account.
Go to appy.py file and add below methods.
upload_storageaccount and generate_sastoken()

def generate_sastoken(filename, saslife, connections):
    container_name = connections.get('container_name')
    connection_string = connections.get('storageaccount_connection')
    try:
        blob_service_client = BlobServiceClient.from_connection_string(connection_string)
        # generateToken with expiry
        sas_token = generate_blob_sas(blob_service_client.account_name, 
                container_name=container_name, blob_name=filename,
                account_key=blob_service_client.credential.account_key,
                permission=AccountSasPermissions(read=True),
                expiry=datetime.now() + relativedelta(months=saslife))

        print(f'sas token generated: {sas_token}')
        return sas_token
    except Exception as e:
        print(f'failed to generate token for file {filename} error: {e}')


def upload_storageaccount(infile, inpath, connections):
    connection_string = connections.get('storageaccount_connection')
    container_name = connections.get('container_name')
    account_name = connections.get('account_name')
    try:
        blob_service_client = BlobServiceClient.from_connection_string(connection_string)
        blob_client = blob_service_client.get_blob_client(container = container_name, blob = infile)

        # Upload the file data
        filepath = inpath + '/' + infile
        with open(filepath, 'rb') as data:
            blob_client.upload_blob(data, overwrite = True)

        url = f"https://{account_name}.blob.core.windows.net/{container_name}/{infile}"
        print(f'File {infile} uploaded to {url}')
        return url
    except Exception as e:
        print(f'failed to upload the report {infile} to {inpath}')
Enter fullscreen mode Exit fullscreen mode

and call these methods from index_post() after the text is translated.
Update results.html to display the storage path and sas token with which we can download the file.

        <div>
            <strong>Storage file path:</strong> {{ storagepath }}
        </div>
        <div>
            <strong>SAS Token:</strong> {{ sas_token }}
        </div>
Enter fullscreen mode Exit fullscreen mode

For complete code please refer my GitHub repository.

Now re-build the container image and check the results.
Go to command palette (Ctrl + Shift + P) in VSCode and type build image, select Azure Container Registry: Build Image in Azure... from the search list.
and follow remaining steps from our last re-build step.
Go to application website and check whether latest changes are reflected.
Enter input for translation text and language then click Translate and Upload!
After successful translation and upload the file we will get output like this.
storageaccount-finalresults

Now go and download the file in new browser tab using below url combination.
<Storage file path>?<SAS Token>

Sample URL
https://storageaccounttranslator.blob.core.windows.net/translations/translated_file_ja.txt?se=2022-04-02T07%3A02%3A42Z&sp=r&sv=2020-06-12&sr=b&sig=%2BK83mBfp2Oun%2Bs0O8U0YTuJaHxOnyItEtdrUsrmpTO4%3D

Try to download file without SAS Token, you will get
error like this.
webapp-deployed-container-nonsas

So it is sure that completely secured in storage account. Without SAS token file cannot be downloaded using bare url.

Also we can confirm whether the file is present in the storage account container. Go to Azure Portal -> Storage Account -> Container -> Select container. Here our uploaded files will be present.

If we encounter any issue in our application. How to check for it ? There is a solution in Azure service too. Let's look into it.

Step 4: Check logs of our container application.

In this step we will see how to check the logs of our container application.
Go to Azure Portal -> Search -> App Services -> Web application we created.

  • If we want to look at the previous logs on application crash, container crash or any other issues we can go to Diagnose and solve problems, here it displays the detailed information. webapp-logging-diagnose
  • If we want to see the live log to Log stream. Here we can see whether our container application is started or encountered any issues. webapp-logging-livelog

Note: You might be wondering, we have stored the secrets in .env how to protect them. Yes! there is a solution in Azure called Azure Key Vault. I will be writing a blog on it soon.

Now we reached the final step.

We have created many Azure services so far in this blog, what happens if we leave them ? As Azure says Pay As You Go! means if we leave our resources it will be chargeable. So we must clear the resources which we have used.

OK! let's delete them.
Go to Azure Portal -> select our created Resource Group -> Delete the resource group. This deletes the entire resource group which contains many services.
If you want to delete only specific service, you do so too.
delete-RG

That's all !!!!!

Conclusion:

Thank you for your time in reading my first blog.
We learnt how to use Microsofts's Visual Studio Code, Azure Cognitive Services, Azure Storage Account, Azure web application, Containerizing web app, Azure Container Registry, Flask framework, etc. I hope it helped you to gain some knowledge on Azure services.
Your comments/suggestions are valuable for me to learn more.

Discussion (0)

pic
Editor guide