示例:使用 Azure 库创建数据库

此示例演示了如何在 Python 脚本中使用 Azure SDK 管理库来创建 Azure Database for MySQL 灵活服务器实例和数据库。 它还提供了一个简单的脚本,用于使用 mysql-connector 库(不是 Azure SDK 的一部分)查询数据库。 可使用类似代码创建 Azure Database for PostgreSQL 灵活服务器实例和数据库。

本文稍后会介绍等效的 Azure CLI 命令。 如果更喜欢使用 Azure 门户,则请参阅创建 MySQL 服务器创建 PostgreSQL 服务器

除非另行说明,否则本文中的所有资源在 Linux/macOS bash 和 Windows 命令行界面上的工作方式相同。

1:设置本地开发环境

如果尚未设置,则请设置一个可在其中运行代码的环境。 提供以下选择:

2:安装所需的 Azure 库包

创建一个具有以下内容的名为 requirements.txt 的文件:

azure-mgmt-resource
azure-mgmt-rdbms
azure-identity
mysql-connector-python

在已激活虚拟环境的终端中,安装规定的项目:

pip install -r requirements.txt

注意

在 Windows 上,尝试将 mysql 库安装到一个 32 位的 Python 库会产生有关 mysql.h 文件的错误。 在这种情况下,请安装 64 位版本的 Python,然后重试。

3:编写代码以创建数据库

创建名为“provision_db.py”的 Python 文件,其中包含以下代码。 注释提供了详细信息。 具体而言,请为 AZURE_SUBSCRIPTION_IDPUBLIC_IP_ADDRESS指定环境变量。 后一个变量是用于运行此示例的工作站 IP 地址。 可使用 WhatsIsMyIP 查找 IP 地址。

import random, os
from azure.identity import DefaultAzureCredential
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.rdbms.mysql_flexibleservers import MySQLManagementClient
from azure.mgmt.rdbms.mysql_flexibleservers.models import Server, ServerVersion

# Acquire a credential object using CLI-based authentication.
credential = DefaultAzureCredential()

# Retrieve subscription ID from environment variable
subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"]

# Constants we need in multiple places: the resource group name and the region
# in which we provision resources. You can change these values however you want.
RESOURCE_GROUP_NAME = 'PythonAzureExample-DB-rg'
LOCATION = "southcentralus"

# Step 1: Provision the resource group.
resource_client = ResourceManagementClient(credential, subscription_id)

rg_result = resource_client.resource_groups.create_or_update(RESOURCE_GROUP_NAME,
    { "location": LOCATION })

print(f"Provisioned resource group {rg_result.name}")

# For details on the previous code, see Example: Provision a resource group
# at https://docs.microsoft.com/azure/developer/python/azure-sdk-example-resource-group


# Step 2: Provision the database server

# We use a random number to create a reasonably unique database server name.
# If you've already provisioned a database and need to re-run the script, set
# the DB_SERVER_NAME environment variable to that name instead.
#
# Also set DB_USER_NAME and DB_USER_PASSWORD variables to avoid using the defaults.

db_server_name = os.environ.get("DB_SERVER_NAME", f"python-azure-example-mysql-{random.randint(1,100000):05}")
db_admin_name = os.environ.get("DB_ADMIN_NAME", "azureuser")
db_admin_password = os.environ.get("DB_ADMIN_PASSWORD", "ChangePa$$w0rd24")

# Obtain the management client object
mysql_client = MySQLManagementClient(credential, subscription_id)

# Provision the server and wait for the result
poller = mysql_client.servers.begin_create(RESOURCE_GROUP_NAME,
    db_server_name, 
    Server(
        location=LOCATION,
        administrator_login=db_admin_name,
        administrator_login_password=db_admin_password,
        version=ServerVersion.FIVE7
    )
)

server = poller.result()

print(f"Provisioned MySQL server {server.name}")

# Step 3: Provision a firewall rule to allow the local workstation to connect

RULE_NAME = "allow_ip"
ip_address = os.environ["PUBLIC_IP_ADDRESS"]

# For the above code, create an environment variable named PUBLIC_IP_ADDRESS that
# contains your workstation's public IP address as reported by a site like
# https://whatismyipaddress.com/.

# Provision the rule and wait for completion
poller = mysql_client.firewall_rules.begin_create_or_update(RESOURCE_GROUP_NAME,
    db_server_name, RULE_NAME, 
    { "start_ip_address": ip_address, "end_ip_address": ip_address }  
)

firewall_rule = poller.result()

print(f"Provisioned firewall rule {firewall_rule.name}")


# Step 4: Provision a database on the server

db_name = os.environ.get("DB_NAME", "example-db1")
 
poller = mysql_client.databases.begin_create_or_update(RESOURCE_GROUP_NAME,
    db_server_name, db_name, {})

db_result = poller.result()

print(f"Provisioned MySQL database {db_result.name} with ID {db_result.id}")

代码中的身份验证

在本文后续阶段,你会使用 Azure CLI 登录到 Azure,以便运行示例代码。 如果帐户有权在 Azure 订阅中创建资源组和存储资源,此代码则会成功运行。

若要在生产脚本中使用此类代码,则可将环境变量设为使用基于服务主体的方法来进行身份验证。 若要了解详细信息,请参阅如何使用 Azure 服务对 Python 应用进行身份验证。 需确保服务主体有足够权限在订阅中创建资源组和存储资源,具体方法则是在 Azure 中为其分配适当的角色;例如,订阅的参与者角色。

有关 PostreSQL 数据库服务器,请参阅:

4:运行脚本

  1. 如果尚未登录,则请使用 Azure CLI 登录到 Azure:

    az login
    
  2. 设置 AZURE_SUBSCRIPTION_IDPUBLIC_IP_ADDRESS 环境变量。 可运行 az account show 命令,以从输出中的 id 属性获取订阅 ID。 可使用 WhatsIsMyIP 查找 IP 地址。

    set AZURE_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000000
    set PUBLIC_IP_ADDRESS=<Your public IP address>
    
  3. (可选)设置 DB_SERVER_NAMEDB_ADMIN_NAMEDB_ADMIN_PASSWORD 环境变量;否则,系统会使用代码默认值。

  4. 运行以下脚本:

    python provision_db.py
    

5:插入记录并查询数据库

使用以下代码创建一个名为“use_db.py”的文件。 请注意针对环境变量 DB_SERVER_NAMEDB_ADMIN_NAMEDB_ADMIN_PASSWORD 的依赖关系。 可从运行前一代码 provision_db.py 的输出中或从代码自身获取这些值。

此代码仅适用于 MySQL;你可为 PostgreSQL 使用其他库。

import os
import mysql.connector

db_server_name = os.environ["DB_SERVER_NAME"]
db_admin_name = os.getenv("DB_ADMIN_NAME", "azureuser")
db_admin_password = os.getenv("DB_ADMIN_PASSWORD", "ChangePa$$w0rd24")

db_name = os.getenv("DB_NAME", "example-db1")
db_port = os.getenv("DB_PORT", 3306)

connection = mysql.connector.connect(user=db_admin_name,
    password=db_admin_password, host=f"{db_server_name}.mysql.database.azure.com",
    port=db_port, database=db_name, ssl_ca='./BaltimoreCyberTrustRoot.crt.pem')

cursor = connection.cursor()

"""
# Alternate pyodbc connection; include pyodbc in requirements.txt
import pyodbc

driver = "{MySQL ODBC 5.3 UNICODE Driver}"

connect_string = f"DRIVER={driver};PORT=3306;SERVER={db_server_name}.mysql.database.azure.com;" \
                 f"DATABASE={DB_NAME};UID={db_admin_name};PWD={db_admin_password}"

connection = pyodbc.connect(connect_string)
"""

table_name = "ExampleTable1"

sql_create = f"CREATE TABLE {table_name} (name varchar(255), code int)"

cursor.execute(sql_create)
print(f"Successfully created table {table_name}")

sql_insert = f"INSERT INTO {table_name} (name, code) VALUES ('Azure', 1)"
insert_data = "('Azure', 1)"

cursor.execute(sql_insert)
print("Successfully inserted data into table")

sql_select_values= f"SELECT * FROM {table_name}"

cursor.execute(sql_select_values)
row = cursor.fetchone()

while row:
    print(str(row[0]) + " " + str(row[1]))
    row = cursor.fetchone()

connection.commit()

所有这些代码均使用 mysql.connector API。 唯一的特定于 Azure 的部分是 MySQL 服务器的完整主机域 (mysql.database.azure.com)。

接下来,从 https://www.digicert.com/CACerts/BaltimoreCyberTrustRoot.crt.pem 下载通过 TSL/SSL 与 Azure Database for MySQL 服务器通信所需的证书,然后将证书文件保存到 Python 文件所在的同一文件夹中。 有关详细信息,请参阅 Azure Database for MySQL 文档中的获取 SSL 证书

最后,运行以下代码:

python use_db.py

如果看到不允许使用客户端 IP 地址的错误,则请检查是否正确定义了环境变量 PUBLIC_IP_ADDRESS。 如果已用错误的 IP 地址创建 MySQL 服务器,则可在 Azure 门户中添加其他服务器。 在此门户中,选择 MySQL 服务器,然后选择连接安全性。 将工作站的 IP 地址添加到允许的 IP 地址列表中。

6:清理资源

如果无需保留在此示例中创建的资源组和存储资源,则请运行 az group delete 命令。

资源组不会在订阅中产生任何持续费用,但资源组中的资源(如存储帐户)则可能会继续产生费用。 最好清理未主动使用的所有组。 --no-wait 参数允许命令立即返回,而不是等到操作完成再返回。

az group delete -n PythonAzureExample-DB-rg  --no-wait

你还可以使用 ResourceManagementClient.resource_groups.begin_delete 方法从代码中删除资源组。 示例:创建资源组中的代码演示了相关用法。

有关参考:等效 Azure CLI 命令

以下 Azure CLI 命令完成了与 Python 脚本相同的预配步骤。 对于 PostgreSQL 数据库,请使用 az postgres flexible-server 命令。

az group create --location southcentralus --name PythonAzureExample-DB-rg

az mysql flexible-server create --location southcentralus --resource-group PythonAzureExample-DB-rg ^
    --name python-azure-example-mysql-12345 --admin-user azureuser --admin-password ChangePa$$w0rd24 ^
    --sku-name Standard_B1ms --version 5.7 --yes

# Change the IP address to the public IP address of your workstation, that is, the address shown
# by a site like https://whatismyipaddress.com/.

az mysql flexible-server firewall-rule create --resource-group PythonAzureExample-DB-rg --name python-azure-example-mysql-12345 ^
    --rule-name allow_ip --start-ip-address 10.11.12.13 --end-ip-address 10.11.12.13

az mysql flexible-server db create --resource-group PythonAzureExample-DB-rg --server-name python-azure-example-mysql-12345 ^
    --database-name example-db1

另请参阅