我第一次将 Python Flask 应用程序部署到 Azure(免费版本)。我有一个使用 SQLAlchemy 连接到 SQL Server 数据库的 Python Flask 应用程序。与数据库和表的连接在网站加载时发生。在本地,这运行得很完美。但是,当我将应用程序部署到 Azure Web App 时,尝试连接到 SQL Server 时遇到连接超时问题。应用程序失败,并出现一个指示登录超时已过期的操作错误。以下是错误回溯:
Traceback (most recent call last):
File "/tmp/8dc4e3d6e3306fa/antenv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 145, in __init__
self._dbapi_connection = engine.raw_connection()
...
pyodbc.OperationalError: ('HYT00', '[HYT00] [Microsoft][ODBC Driver 17 for SQL Server]Login timeout expired (0) (SQLDriverConnect)')
此问题仅在应用程序部署到 Azure 时出现,而不是在本地运行时出现。以下是有关环境和配置的一些具体信息:
环境:Azure Web 应用程序 数据库:SQL Server 连接方法:使用pyodbc作为驱动程序的SQLAlchemy requirements.txt 文件中的依赖项版本:
我已确认应用程序已从本地环境成功连接到数据库,因此这似乎是 Azure 部署特有的问题。
以下是我在应用程序中设置 SQLAlchemy 引擎的方法:
user = os.environ["User_Name"]
password = os.environ["Password"]
hostName = os.environ["hostName"]
port = os.environ["port"]
db = os.environ["database"]
db_context = os.environ["context"]
# Establishing the database connection URI
database_uri = f"mssql+pyodbc://{user}:{password}@{hostName}:{port}/{db}?driver=ODBC+Driver+17+for+SQL+Server"
engine = create_engine(database_uri)
环境变量全部拼写正确,并作为应用程序设置下的环境变量正确添加到我的 Azure Web 应用程序中。
我尝试增加连接超时参数(尽管我可以尝试更大的数字)。在本地运行时,由于数据库连接和我运行的设置查询,Flask 应用程序的加载大约需要 20-35 秒。有关更多上下文/信息,这里是用于 Flask 应用程序的启动命令:
gunicorn --bind=0.0.0.0 --timeout 600 app:app
我已检查并确认该应用程序可以在本地运行,并且环境变量名称均与我在 Azure Web 应用程序仪表板上编写的内容匹配。我没有尝试在网络应用程序仪表板中添加连接字符串,因为我认为这不会有帮助,因为该应用程序在本地运行得很好。此外,对于我自己和其他人来说,SQL 服务器已经启动并顺利运行。
更新:
os.environ['var1']
在我的代码中获取它们。网站必须在加载页面时连接到 SQL 数据库/服务器(页面必须使用将来传递给数据库查询的参数加载),因此我还设置了所有环境我的环境变量中的连接字符串作为部署槽设置。如果缺少参数,应用程序将重定向到一个默认页面。此外,这些是最新的错误日志:
2024-03-29T13:31:08.492306143Z [2024-03-29 13:31:08 +0000] [78] [INFO] Starting gunicorn 21.2.0
2024-03-29T13:31:08.494285752Z [2024-03-29 13:31:08 +0000] [78] [INFO] 收听:http://0.0.0.0:8000 (78) 2024-03-29T13:31:08.495537458Z [2024-03-29 13:31:08 +0000] [78] [INFO] 使用工作人员:同步 2024-03-29T13:31:08.513749140Z [2024-03-29 13:31:08 +0000] [79] [INFO] 使用 pid 启动工作进程:79
2024-03-29T13:40:20.623Z 错误 - 站点 remi2 的容器 remi2_1_322f8881 未在预期时间限制内启动。经过的时间 = 600.4550416 秒 2024-03-29T13:40:20.631Z 错误 - 容器 remi2_1_322f8881 未响应端口 1433 上的 HTTP ping,站点启动失败。请参阅容器日志进行调试。 2024-03-29T13:40:20.675Z 信息 - 停止站点 remi2,因为它在启动过程中失败。
我是初学者,所以我不知道该怎么做。我将 Web 应用程序的启动时间从 230 秒增加到 600 秒,但它仍然超时。
此 Flask 应用程序使用
pymssql
连接到 SQL Server 数据库,以在 Persons
表上执行 CRUD 操作。
flask_sql_app/
│
├── templates/
│ ├── index.html
│ └── exception.html
│
├── app.py
└── requirements.txt
# app.py
from flask import Flask, jsonify, request, render_template
import pymssql
app = Flask(__name__)
# Function to get connection
def get_conn():
try:
conn = pymssql.connect(
server='samdbsserver.database.windows.net',
database='your database',
user='your user name',
password='Your Password',
port=1433
)
return conn, None
except pymssql.Error as e:
return None, f"Error connecting to the database: {e}"
# Check if table exists
def table_exists(conn):
try:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Persons'")
return cursor.fetchone()[0] == 1
except pymssql.Error as e:
raise Exception(f"Error checking if table exists: {e}")
# Create table if not exists
def create_table():
conn, error = get_conn()
if conn:
try:
if not table_exists(conn):
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE Persons (
ID INT NOT NULL PRIMARY KEY IDENTITY,
FirstName VARCHAR(255),
LastName VARCHAR(255)
)
""")
conn.commit()
except pymssql.Error as e:
raise Exception(f"Error creating table: {e}")
finally:
conn.close()
else:
raise Exception(error)
# Home route to display UI
@app.route("/")
def home():
return render_template("index.html")
# Route to handle exceptions
@app.route("/exception")
def handle_exception():
return render_template("exception.html")
# Route to create the table
@app.route("/create_table")
def route_create_table():
try:
create_table()
return jsonify({"message": "Table created successfully"}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
# Route to insert a person
@app.route("/insert_person", methods=["POST"])
def insert_person():
try:
data = request.json
first_name = data["first_name"]
last_name = data.get("last_name")
conn, error = get_conn()
if conn is not None:
cursor = conn.cursor()
cursor.execute("INSERT INTO Persons (FirstName, LastName) VALUES (%s, %s)", (first_name, last_name))
conn.commit()
conn.close()
return jsonify({"message": "Person inserted successfully"}), 201
else:
raise Exception(error)
except KeyError as e:
return jsonify({"error": f"Missing required field: {e}"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
# Route to get all persons
@app.route("/get_all_persons")
def get_all_persons():
try:
conn, error = get_conn()
if conn is not None:
cursor = conn.cursor()
cursor.execute("SELECT * FROM Persons")
rows = cursor.fetchall()
conn.close()
return jsonify(rows)
else:
raise Exception(error)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Route to get a single person
@app.route("/get_person/<int:person_id>")
def get_person(person_id):
try:
conn, error = get_conn()
if conn is not None:
cursor = conn.cursor()
cursor.execute("SELECT * FROM Persons WHERE ID = %s", (person_id,))
row = cursor.fetchone()
conn.close()
if row:
return jsonify(row)
else:
return jsonify({"message": "Person not found"}), 404
else:
raise Exception(error)
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == "__main__":
app.run(debug=True, port=5000)
为应用程序创建虚拟环境:
python3 -m venv .venv
source .venv/bin/activate
<!-- exception.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exception</title>
</head>
<body>
<h1>An Exception Occurred</h1>
<p>{{ error }}</p>
</body>
</html>
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask SQL App</title>
</head>
<body>
<h1>Welcome to Flask SQL App</h1>
<form action="/insert_person" method="post">
<label for="first_name">First Name:</label>
<input type="text" id="first_name" name="first_name" required><br>
<label for="last_name">Last Name:</label>
<input type="text" id="last_name" name="last_name"><br>
<button type="submit">Insert Person</button>
</form>
<button onclick="getAllPersons()">Get All Persons</button>
<script>
function getAllPersons() {
fetch('/get_all_persons')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
}
</script>
</body>
</html>
需求.txt:
Flask
pyodbc
pymssql
python-dotenv
web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<customErrors mode="Off"/>
</system.web>
</configuration>