J'aimerais savoir s'il existe un moyen plus intelligent.
J'ai importé mysqlclient pour me connecter à RDS (mysql / aurora) depuis AWS Lambda. Cela fonctionnait localement, je l'ai donc déployé avec sans serveur. Lorsque je teste l'exécuter à partir de la console de gestion AWS, cela ne fonctionne pas.
Apparemment, mysqlclient contient du code natif, ce qui provoque une erreur lors de la tentative d'exécuter une image construite sur un Mac sous Linux.
C'est une histoire que j'ai essayé diverses choses pour résoudre ce domaine.
En passant, le paquet externe de python est géré par serverless-python-requirements.
--Référence: Gestion des modules externes à l'aide du plug-in Serverless Framework
Mon service comme ça
ore-service
├── handler.py
├── requirements.py
├── requirements.txt
└── serverless.yml
Supposons que le contenu soit comme ça.
requirements.txt
mysqlclient
handler.py
#!/use/bin/env python
# -*- coding: utf-8 -*-
import requirements
import MySQLdb
def lambda_handler(event, context):
con = MySQLdb.connect(host='〜', db='〜', user='〜', passwd='〜', charset='utf8')
cur = con.cursor()
cur.execute("SELECT *DE D'une manière ou d'une autre ~")
....(Abréviation)
Erreur lors de la tentative de test d'exécution d'AWS Lambda à partir de la console AWS avec sls deploy. La sortie du journal a la sortie suivante.
Unable to import module 'handler': /var/task/_mysql.so: invalid ELF header
Oh je vois. J'essaye d'exécuter _mysql.so construit sur un Mac sous Linux.
serverless-python-requirements a une option appelée dockerizePip, et si elle est définie sur true, elle effectuera une compilation croisée à l'aide de l'image docker-lambda. Je vais l'essayer immédiatement.
serverless.yml ressemble à ce qui suit.
serverless.yml
...(Abréviation)
plugins:
- serverless-python-requirements
custom:
pythonRequirements:
dockerizePip: true
...(Abréviation)
Donc, malheureusement, j'ai eu une erreur lors du déploiement.
$ sls deploy
Serverless: Installing required Python packages...
Error --------------------------------------------------
Command "python setup.py egg_info" failed with error
code 1 in /tmp/pip-build-4MzA_g/mysqlclient/
Cela semble être dû au fait que python-devel et mysql-devel nécessaires pour construire mysqlclient ne sont pas dans l'image docker.
[Source] des exigences de Python sans serveur (https://github.com/UnitedIncome/serverless-python-requirements/blob/master/index.js) avec la baisse de tension, "Dois-je construire avec EC2?" Quand je l'ai regardé, je pouvais comprendre ce que je faisais. C'est comme démarrer le conteneur et faire l'installation de pip.
Ensuite, vous pouvez démarrer le conteneur vous-même, entrer avec bash et installer les bibliothèques nécessaires. Cela ressemble à ce qui suit.
$ docker run -it --rm -v "$PWD":/var/task "lambci/lambda:build-python2.7" bash
bash-4.2# cd /var/task
bash-4.2# yum -y install python-devel mysql-devel
bash-4.2# pip install mysqlclient -t .
bash-4.2# cp /usr/lib64/mysql/libmysqlclient.so.18 .
Montez / var / task du conteneur sur le courant local. Cette zone est un pakuri complet du traitement autour de installRequirements () de la source que j'ai vu plus tôt.
Ensuite, installez les bibliothèques et pip. J'ai également besoin de libmysqlclient.so.18, alors copiez-le.
serverless télécharge les fichiers et les dossiers à l'emplacement de serverless.yml sous forme de zip, afin que vous puissiez déployer sls tel quel.
(Supprimez le dockerizePip: true qui a été ajouté à serverless.yml plus tôt)
Maintenant, le mysqlclient construit pour linux est chargé depuis lambda et vous pouvez accéder en toute sécurité au RDS.
Un problème est survenu ici. Je ne peux pas exécuter localement car j'ai l'image linux mysqlclient localement.
$ python handler.py
Traceback (most recent call last):
File "handler.py", line 18, in <module>
import MySQLdb
File "./MySQLdb/__init__.py", line 19, in <module>
import _mysql
ImportError: dlopen(./_mysql.so, 2): no suitable image found. Did find:
./_mysql.so: unknown file type, first eight bytes: 0x7F 0x45 0x4C 0x46 0x02 0x01 0x01 0x00
Par conséquent, il est possible de changer le binaire de chargement mysqlclient. La stratégie consiste à placer linux ELF sous ./third-party/ et à le charger en premier si l'environnement d'exécution est autre que mac. La racine du projet ne sera pas sale avec les dossiers de packages externes.
La structure des répertoires est la suivante.
ore-service
├── third-party
│ ├── MySQLdb
│ ├── _mysql.so
│ ├── libmysqlclient.so.18
│ └── Autres fichiers divers pour linux
├── handler.py
├── requirements.py
├── requirements.txt
└── serverless.yml
Compilez comme suit. (Je viens de définir le point de montage sur tiers /)
$ mkdir third-party
$ cd $_
$ docker run -it --rm -v "$PWD"/third-party/:/var/task "lambci/lambda:build-python2.7" bash
(comme ci-dessus)
Ensuite, python détermine le système d'exploitation et change la destination de chargement. Insérez un tiers au début de sys.path, et python ne semble pas avoir de paramètres de type LD_LIBRARY_PATH, donc je chargerai directement libmysqlclient.so.18.
handler.py
import platform
if platform.system() != "Darwin":
import sys
import ctypes
sys.path.insert(0, './third-party/')
ctypes.CDLL("./third-party/libmysqlclient.so.18")
C'est boueux, mais j'ai réussi à atteindre mon objectif.
Après cela, si vous créez le répertoire tiers .gitignore et organisez le processus de compilation croisée dans un Dockerfile, cela conviendra pour la gestion de la configuration.
N'est-ce pas comme ça?