Reputation: 13
I am having a difficult time with the Networking between my Azure Container App (ACA) and Azure Storage Account Table.
I have a python (Flask) app API that is containerized with Docker. I am utilizing Azure Container Registry (ACR) and ACA with the CICD setup on commits to my main branch in GitHub. I recently wanted to implement authentication, so I created an Azure Storage Account Table to verify API keys. This works GREAT locally. It does not work at all from the cloud. I have narrowed it down to a networking error which I will elaborate on. Below I will provide what I'm doing locally as well as in the cloud in the hopes that you can help me troubleshoot.
Building the Docker Image
docker build -t testapp_${env}:${VERSION} --platform linux/amd64 .
Running the Docker image with the table connection string
docker run -p 8000:80 -e AZURE_TABLE_CONN_STRING='hardcodingfortesting' testapp_${env}:${VERSION}
POST successfully
curl -X POST http://localhost:8000/parse-report \
-H "X-API-KEY: 123" \
-H "Content-Type: application/json" \
The logic works great I can alter the API key and it will tell me it does not match.
Now the exact same package is being pushed to ACR and ACA and I get an error when I try to post.
POST unsuccessful
curl -X POST https://removingforsecurity.io \
-H "X-API-KEY: 123" \
-H "Content-Type: application/json" \
I know with a great deal of certainty that this is a networking issue. This is because in the Networking tab of my Storage Account if I change Public Network Access to Enabled from all networks I can successfully POST.
Error log in ACA after attempting POST:
Content: {"odata.error":{"code":"AuthorizationFailure","message":{"lang":"en-US","value":"This request is not authorized to perform this operation.\nRequestId:9e37b393-b002-006b-1217-64ed0a000000\nTime:2024-02-20T16:08:39.0510443Z"}}}
I have tried the following to resolve this issue:
Please let me know what I should do.
EDIT: Adding Dockerfile
# Python runtime image base
FROM python:3.10-slim
# Set the working directory in the container
WORKDIR /app
# Ensure requirements are copied
COPY requirements.txt /app/
# Install requirements
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . /app
# Where to find application instance
ENV FLASK_APP=main_parser.py
# Expose port
EXPOSE 80
# Run command
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:80", "main_parser:app"]
Upvotes: 1
Views: 1172
Reputation: 3639
The authorization issue between your Azure Container App (ACA) and Azure Storage Account Table may be due to IP address restrictions or Managed Identity permissions read access roles.
Guide on using storage mounts in Azure Container Apps
I followed DOC to Deploy a Flask Web App as a Container in Azure Container Apps .
Connect to an Azure File Share via a Private Endpoint.
Set up a private endpoint with a private link for Azure Container Registry. If you are using a private link, make sure to run Azure storage and the Azure container on the same private endpoint.
The below Flask code with Azure Storage Table display, which is deployed to Azure Container Apps:
app .py:
import os
from flask import (Flask, redirect, render_template, request, send_from_directory, url_for)
from azure.data.tables import TableServiceClient
from azure.core.credentials import AzureNamedKeyCredential
app = Flask(__name__)
# Azure Storage Account details
account_name = "Azureaccount_name"
account_key = "Azureaccount_key"
table_name = "Azuretable_name"
table_service_endpoint = f"https://{account_name}.table.core.windows.net/"
# Create the AzureNamedKeyCredential
credential = AzureNamedKeyCredential(account_name, account_key)
# Create the TableServiceClient using the connection string
table_service_client = TableServiceClient(endpoint=table_service_endpoint, credential=credential)
# Get the table client for the specified table
table_client = table_service_client.get_table_client(table_name)
@app.route('/')
def index():
print('Request for index page received')
return render_template('index.html')
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')
@app.route('/hello', methods=['POST'])
def hello():
name = request.form.get('name')
if name:
print('Request for hello page received with name=%s' % name)
try:
entities = table_client.list_entities()
entities_list = []
for entity in entities:
entities_list.append(entity)
return render_template('hello.html', name=name, entities=entities_list)
except Exception as e:
error_message = f"An error occurred: {e}"
print(error_message)
return render_template('hello.html', name=name, error=error_message)
else:
print('Request for hello page received with no name or blank name -- redirecting')
return redirect(url_for('index'))
if __name__ == '__main__':
app.run()
hello.html:
<!doctype html>
<html>
<head>
<title>Hello Azure - Python Quickstart</title>
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
</head>
<body>
<main>
<div class="px-4 py-3 my-2 text-center">
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='images/azure-icon.svg') }}" alt="Azure Logo" width="192" height="192"/>
<h1 class="display-6 fw-bold">Hello {{name}}</h1>
<p class="fs-5">It is nice to meet you!</p>
<a href="{{ url_for('index') }}" class="btn btn-primary btn-lg px-4 gap-3">Back home</a>
</div>
{% if entities %}
<div class="container">
<h2>Entities in the Azure Table:</h2>
<ul>
{% for entity in entities %}
<li>
<strong>PartitionKey:</strong> {{ entity.PartitionKey }} <br>
<strong>RowKey:</strong> {{ entity.RowKey }} <br>
{% for key, value in entity.items() if key not in ['PartitionKey', 'RowKey'] %}
<strong>{{ key }}:</strong> {{ value }} <br>
{% endfor %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if error %}
<div class="container">
<p>An error occurred: {{ error }}</p>
</div>
{% endif %}
</main>
</body>
</html>
index.html:
<!doctype html>
<html>
<head>
<title>Hello Azure - Python Quickstart</title>
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
</head>
<body>
<main>
<div class="px-4 py-3 my-2 text-center">
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='images/azure-icon.svg') }}" alt="Azure Logo" width="192" height="192"/>
<h1 class="display-6 fw-bold text-primary">Welcome to Azure</h1>
</div>
<form method="post" action="{{url_for('hello')}}" class="col-md-6 mx-auto text-center">
<label for="name" class="form-label fw-bold fs-5">Could you please tell me your name?</label>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center align-items-center my-1">
<input type="text" class="form-control" id="name" name="name" style="max-width: 256px;">
</div>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center my-2">
<button type="submit" class="btn btn-primary btn-lg px-4 gap-3">Say Hello</button>
</div>
</form>
</main>
</body>
</html>
requirements.txt:
Flask==2.2.2
gunicorn
Werkzeug==2.2.2
azure-storage-blob
azure-data-tables
Dockerfile:
# syntax=docker/dockerfile:1
FROM python:3.11
WORKDIR /code
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY . .
EXPOSE 50505
ENTRYPOINT ["gunicorn", "app:app"]
.dockerignore
.git*
**/*.pyc
.venv/
Local:
build a Docker image from a Dockerfile.
docker build --tag flask-demo1 .
docker tag 8xxxxxe63 sampath344.azurecr.io/samapth-app/flask-demo:v2
az acr login
command, and push a Docker image to an Azure Container Registry (ACR).az acr login --name "ContainerregistryName" --password "password" --username "password"
docker push sampath344.azurecr.io/samapth-app/flask-demo:v2
Azure:
Upvotes: 0