在 Python 中将 MySQL 与 AWS Lambda 结合使用时出现问题

Problems using MySQL with AWS Lambda in Python

我正在尝试起床 运行 AWS Lambda Python(Python 顺便说一句,初学者)但是在包含 MySQL 依赖项时遇到了一些问题。我正在尝试按照 Mac.

上的说明进行操作 here

对于第 3 步,我在项目的根目录执行命令时遇到了一些问题

sudo pip install MySQL-python -t /

错误:

Exception: Traceback (most recent call last): File "/Library/Python/2.7/site-packages/pip-1.5.6-py2.7.egg/pip/basecommand.py", line 122, in main status = self.run(options, args) File "/Library/Python/2.7/site-packages/pip-1.5.6-py2.7.egg/pip/commands/install.py", line 311, in run os.path.join(options.target_dir, item) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 292, in move raise Error, "Destination path '%s' already exists" % real_dst Error: Destination path '/MySQL_python-1.2.5-py2.7.egg-info/MySQL_python-1.2.5-py2.7.egg-info' already exists

我最终编写了以下 lambda 函数(在我的 Mac 上运行良好),即:

import MySQLdb

def lambda_handler(event, context):
   # Open database connection
   db = MySQLdb.connect(...)

   # prepare a cursor object using cursor() method
   cursor = db.cursor()

   sql = "SELECT * FROM Users"

   try:
      # Execute the SQL command
      cursor.execute(sql)
      # Fetch all the rows in a list of lists.
      results = cursor.fetchall()
      for row in results:
         fname = row[0]
         lname = row[1]
         age = row[2]
         sex = row[3]
         income = row[4]
         # Now print fetched result
         print ("lname=%s" %(lname))
   except:
      print "Error: unable to fecth data"

   # disconnect from server
   db.close()

我接下来要做的是转到 /Library/Python/2.7/site-packages 并复制我执行 sudo pip install 时下载的 MySQLdb folders/files MySQL-python(没有 -t /)(我确定我在这里做错了什么)到我的 lambda 项目,然后将内容与 lambda_function.py 和上传到 AWS Lambda。

然后我得到:

Unable to import module 'lambda_function': No module named MySQLdb

感谢任何帮助和建议!

编辑

能够做到 make sudo pip install MySQL-python -t /pathToProject 工作(感谢评论中的帮助)但现在我在运行 lambda 函数时得到了这个:

Unable to import module 'lambda_function': /var/task/_mysql.so: invalid ELF header

我知道如果我在 Linux 盒子上工作,那么它应该可以正常工作(正如某些人所建议的那样),但我想知道我是否可以在 OS 上工作X盒.

我认为您的问题主要是因为缺少开发包。我认为您将需要以下内容:

sudo yum -y 安装mysql-devel

问题在我的 Ubuntu 安装程序中发生类似,真正的问题是因为它在 mysql 客户端连接器驱动程序上挂起。所以解决方案是安装 Mysql client-dev 包以使 MySQL-python 快乐(使用客户端库)。

# Ubuntu only(or setup vm for ubuntu inside your mac) 
# Three dependencies for MySQL python recompilation 
sudo apt-get install python-dev  libssl-dev

#Now the mysql client-dev  
sudo apt-get install libmysqlclient-dev

# If you like mariadb client
sudo apt-get install libmariadbclient-dev

对于MAC

# try this first
fink install mysql-unified-dev

# or this if above fail. 
brew install mysql
# you must add this to your user profile startup if you use brew 
export PATH=$PATH:/usr/local/mysql/bin

你可以在这里得到类似的答案:Mac OS X - EnvironmentError: mysql_config not found

然后尝试 pip 安装。

我不建议任何人使用 "sudo pip"。你应该为你的 python 开发设置 Virtualenv 和 virtualwrapper,这样你就可以在没有 sudo 的情况下进行 pip。而且更容易隔离和测试新的部署。 (虽然它没有解决 mysqlclient-dev 库问题)

您必须使用 Amazon Linux 实例来构建您的 python 程序包,然后将它们包含在您的 Lambda 部署程序包中。查看 this excellent article 了解如何操作。文章中提到的软件包与您需要的不同,但同样它帮助我为我的 lambda 构建了 psycopg2 和 pymssql。

对于像 Lambda 这样的用例,使用像 PyMySQL 这样的纯 python 实现会更快乐。

这是对遵循 Python Database API 规范的 MySQLdb 的替代品。对于大多数事情,例如触发的 Lambda 事件,它会一样快。

我在生产中经常使用它,效果很好。

使用 lambda-docker,您可以设置和测试您的 Lambda 函数,而无需访问类似的 Linux 环境。

要设置您的 lambda,请将 lambda-docker 构建映像用于 运行 分离的 docker 容器,并在容器上使用 运行 pip install <package> 命令。然后导出容器,抓取 usr/lib 下安装的包,并将它们放入您的 AWS Lambda 包中。

然后您可以通过在 lambda-docker 图像上 运行ning 您的 lambda 来测试兼容性。如果有效,请放心上传到 AWS Lambda。

docker run -d -v "$PWD":/var/task lambci/lambda:build-python2.7 tail -f /dev/null 

docker ps

docker exec 0c55aae443e6 pip install pandas

docker exec 0c55aae443e6 pip install sqlalchemy

docker exec 0c55aae443e6 pip freeze

docker exec 0c55aae443e6 python -c "import site; print(site.getsitepackages())"

docker container export -o lambda_ready_container 0c55aae443e6

只需上传两个包来更新您的 lambda 层: - sqlalchemy - PyMySQL(代替 mysqlclient 使用的驱动程序)

现在将您的驱动程序 url 更新为 "mysql+pymysql://..."。

这使您可以为现有环境使用与 Lambda 环境兼容的 pymysql 驱动程序。

不要忘记为 RDS 设置 VPC 端点。这样可以控制性能和安全性。

AWS 最近针对 Lambda 中的数据库驱动程序和数据库访问问题提出了一个很好的解决方案:Aurora Data API。数据 API 使用 AWS 标准身份验证通过 HTTP 隧道 SQL。这绕过了在 Lambda 中编译本机代码和使用传统数据库连接模型的问题。

我最终为它编写了一个 DB-API 兼容驱动程序:aurora-data-api (and a SQLAlchemy dialect 使用它):

import aurora_data_api

cluster_arn = "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-serverless-cluster"
secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:MY_DB_CREDENTIALS"
with aurora_data_api.connect(aurora_cluster_arn=cluster_arn, secret_arn=secret_arn, database="my_db") as conn:
    with conn.cursor() as cursor:
        cursor.execute("select * from pg_catalog.pg_tables")
        print(cursor.fetchall())

Lambda -> Layers(添加新层)

https://pypi.org/project/PyMySQL/#files

下载 pymysql 的 zip

当你下载时,解压缩然后将父文件夹重命名为 "python" 然后重新压缩(应该是 python/{pysql 文件所在的位置}

向 Lambda 添加一个名为 'pymysql' 的层并上传该 zip

然后在 Lambda 函数中导入 pymysql

TLDR:是的,您可以在 AWS Lambda Python 函数中使用 mysqlclient

这是一种方法 - 通过为 mysqlclient(即 MySQLdb)创建您自己的 AWS Lambda 层。

Then I get Unable to import module 'lambda_function': No module named MySQLdb

I know that if I work on a Linux box, then it should work fine (as suggested by some people), but I am wondering if I can make it work from an OS X box.

我在我的 AWS Lambda Python 函数中尝试 import MySQLdb 时也遇到了完全相同的错误。

经过大量搜索解决方案并且不满意使用 pymysql 作为替代品(出于性能和兼容性原因),我最终构建了自己的 AWS Lambda Layer for mysqlclient. I could not find a "ready-made" layer for mysqlclient - not even at the awesome KLayers project。我很高兴分享一个 GitHub 存储库,其中包含一个“现成”层示例和一个简单的解决方案,可以根据您的要求使用 AWS 推荐的程序构建您自己的自定义层。

mysqlclient (MySQLdb) is a Python wrapper around a high-performance C implementation of the MySQL API. This makes it typically much faster than pure-python implementations such as pymysql in most cases (see this list 一些例子),但它也带来了一些问题,例如你所面临的问题。

因为它是针对 mysql-devel 包编译的(例如 MySQL 提供的 .rpm.deb 文件),mysqlclient 链接到特定于平台的二进制文件,例如 libmysqlclient.so 才能工作。换句话说,来自 Mac OS 笔记本电脑(作为示例)的 libmysqlclient.so 将无法在使用某种形式的 Amazon Linux 2 的 AWS Lambda 环境中工作写作。您需要 libmysqlclient.so 在 AWS Lambda 环境中(或尽可能接近它)编译,才能在您的 AWS Lambda 函数中运行。

Docker images from lambci.

的形式提供了一个紧密模拟的 AWS-Lambda 环境

因此,要打包兼容 AWS-Lambda 的 mysqlclient,您可以:

  • 拉一个合适的docker容器比如lambci/lambda:build-python3.8
  • 导入 MySQL repo GPG key
  • 安装 MySQL repo setup RPM 以便 yum 可以找到并下载其他 MySQL 回购包
  • yum install 必要的依赖项,例如适合您的用例的 mysql-devel rpm
  • 运行 pip install mysqlclient 容器中
  • 压缩必要的 libmysqlclient.so 文件和 mysqlclient 的 python lib 目录

这或多或少是 AWS 官方推荐的程序:请参阅 如何使用带有 Docker 的模拟 Lambda 环境创建 Lambda 层? .

由此创建的 zip 可用于为 mysqlclient 创建一个新的 AWS Lambda 层。您可以使用此层轻松使用 mysqlclient,而不会在您的 Lambda 函数中出现任何错误。

经过大量的努力,我终于得到了完整的工作过程,并将其自动化到 this GitHub project 中的单个脚本 (build.sh) 中。该代码构建了一个 layer.zip 文件,您可以将其作为新的 AWS Lambda 层直接上传。该项目当前为 Python3.8 和 MySQL 服务器 8.0.x 构建,但可以轻松适应不同的 Python 版本和目标 MySQL 版本,使用提供的说明和工具。回购协议中还有一个随时可用的 layer.zip - 如果您想对 MySQL v8.0.x 和 Python 3.8 使用 mysqlclient (均经过测试)在您的 AWS Lambda 函数中。我们的生产环境使用 SqlAlchemy,它使用这个 MySqlClient Lambda 层,它对我们来说效果很好。

将 Lambda 函数配置为使用按描述构建的层(例如,使用上述存储库中的工具)后,您可以像往常一样在 Lambda 函数中 import MySQLdb 并继续编写您真正的代码:

import MySQLdb

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': 'MySQLdb was successfully imported'
    }

希望对您有所帮助。