|
| 1 | +# Building a modern SQLite3 library for Django 4 for AWS lambda + Python 3.10 |
| 2 | + |
| 3 | +Chris B Chamberlain, August 31, 2023. |
| 4 | + |
| 5 | +## Why we're even trying this |
| 6 | + |
| 7 | +We want a light weight, read-only sql database for django, that we can run in an AWS lambda function. It has been done, and the zappa project even make a read-write version, which gives some valuable tips: |
| 8 | + |
| 9 | +- https://github.com/FlipperPA/django-s3-sqlite |
| 10 | +- https://github.com/FlipperPA/django-s3-sqlite/tree/main/shared-objects/python-3-8 |
| 11 | + |
| 12 | +## Issue 1) AWS lambda sqlite3 libary is outdated. |
| 13 | + |
| 14 | +Modern django expects `sqlite3>=3.83`. But currently the AWS python10 lambda environment offers us `sqlite3 version: 3.7.17` |
| 15 | + |
| 16 | +So, following this guide https://charlesleifer.com/blog/compiling-sqlite-for-use-with-python-applications/ |
| 17 | + |
| 18 | +we're able to build and test a modern sqlite (currenlty 3.44), but unfortunately this still doesn't work on lambda... |
| 19 | + |
| 20 | +## Issue 2) the custom sqlite3 libary must be linked against the `libc` version available on AWS lambda. |
| 21 | + |
| 22 | +So, while our locally compiled sqlite3 library works locally, in lambda we get: |
| 23 | + |
| 24 | +```ImportError: /lib64/libm.so.6: version `GLIBC_2.29' not found (required by /var/task/_sqlite3.cpython-310-x86_64-linux-gnu.so)```. |
| 25 | + |
| 26 | +NB. the version above may vary from system to system, since it's based on the local build environment. |
| 27 | + |
| 28 | +Currently, AWS lambda provides us with `GLIBC_2.26`. |
| 29 | + |
| 30 | +The recipe below resolved this problem for us. |
| 31 | + |
| 32 | +### Background reading, tips & techniques |
| 33 | + |
| 34 | +I found these articles very helpful with figuring out what was actually going on here. |
| 35 | + |
| 36 | + - https://www.cyberithub.com/solved-glibc-2-29-not-found-error-on-ubuntu-linux/ good, but not a workable fix for lambda |
| 37 | + - https://stackoverflow.com/questions/4032373/linking-against-an-old-version-of-libc-to-provide-greater-application-coverage |
| 38 | + - https://stackoverflow.com/questions/69692703/compiling-python-3-10-at-amazon-linux-2 |
| 39 | + - https://stackoverflow.com/a/69879426 |
| 40 | + - https://stackoverflow.com/a/851229 very useful big-picture |
| 41 | + - https://stackoverflow.com/a/49355708 same issue, it's not easy |
| 42 | + -https://unix.stackexchange.com/questions/487303/how-to-find-the-name-of-the-so-file-which-contains-the-code-for-a-specific-funct |
| 43 | + |
| 44 | +## A Recipe for running sqlite3 on AWS lambda |
| 45 | + |
| 46 | +To get a build that works on AWS lambda we can use a suitable EC2 Linux2 instance and the following steps: |
| 47 | + |
| 48 | +### 1) Launch an AWS EC2 instance |
| 49 | + |
| 50 | +This is a simple way to get a lambda-compatible build enivironment: |
| 51 | + |
| 52 | +I used a T2.medium instance, it's a bit faster than the smaller instances. Use the [AWS Linux 2 image](https://ap-southeast-2.console.aws.amazon.com/ec2/home?region=ap-southeast-2#Images:visibility=public-images;imageId=ami-0dab9ecf8f21f9ff3) or whatever is currently equivalent to the lambda OS. |
| 53 | + |
| 54 | +The thing that really matters is the version of libc that's installed. We want to see `ldd (GNU libc) 2.26` which is exactly what we see on lambda: |
| 55 | + |
| 56 | +``` |
| 57 | +$> ldd --version |
| 58 | +ldd (GNU libc) 2.26 |
| 59 | +... |
| 60 | +``` |
| 61 | + |
| 62 | +NB We've added a check for the current version in the projects' lambda startup function (wsgi.py) which is enabled when the environment varaible `DEBUG` is True. |
| 63 | + |
| 64 | +### 2) install the build tools |
| 65 | + |
| 66 | +with a console, e.g. `ssh`` or the AWS web ui. |
| 67 | + |
| 68 | +``` |
| 69 | +sudo yum groupinstall "Development Tools" |
| 70 | +sudo yum -y install bzip2-devel libffi-devel openssl11 openssl11-devel |
| 71 | +sudo yum install tcl |
| 72 | +``` |
| 73 | + |
| 74 | +### 3) install python 3.10.x |
| 75 | + |
| 76 | +``` |
| 77 | +wget https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz |
| 78 | +tar xzf Python-3.10.13.tgz |
| 79 | +cd Python-3.10.13/ |
| 80 | +./configure --enable-optimizations |
| 81 | +sudo make altinstall |
| 82 | +``` |
| 83 | + |
| 84 | +And now check that you have a working **python3.10** and **pip3**: |
| 85 | + |
| 86 | +``` |
| 87 | +which python3.10 |
| 88 | +python3.10 --version |
| 89 | +pip3.10 --version |
| 90 | +``` |
| 91 | + |
| 92 | +### 4) install virtualenv |
| 93 | + |
| 94 | +``` |
| 95 | +pip3.10 install virtualenv |
| 96 | +virtualenv --help |
| 97 | +``` |
| 98 | + |
| 99 | +### 5) install & configure sqlite |
| 100 | + |
| 101 | +At this time the latest version is 3.44. Recall that we need >= 3.8.3 for django compatablity, and to avoid the original `deterministic=True requires SQLite 3.8.3 or higher` error. |
| 102 | + |
| 103 | +``` |
| 104 | +wget https://www.sqlite.org/src/tarball/sqlite.tar.gz |
| 105 | +tar xzf sqlite.tar.gz |
| 106 | +cd sqlite |
| 107 | +./configure |
| 108 | +make sqlite3.c |
| 109 | +``` |
| 110 | + |
| 111 | +### 6) build the custom sqlite library (via pysqlite build script) |
| 112 | + |
| 113 | +The basis fo this recipe is [laid out here](https://charlesleifer.com/blog/compiling-sqlite-for-use-with-python-applications/) by the maintainer of pysqlite, Charles Liefer. |
| 114 | + |
| 115 | +Note that it's not stricly necessary to use the virtualenv option , but I chose that path. |
| 116 | + |
| 117 | +**6.1)** Create and activate a virtual environment |
| 118 | +``` |
| 119 | +virtualenv -p /usr/local/bin/python3.10 sqlite344 |
| 120 | +cd sqlite344 |
| 121 | +source bin/activate |
| 122 | +``` |
| 123 | + |
| 124 | + |
| 125 | +**6.2)** Clone the repo to a different name (avoids import issues) |
| 126 | + |
| 127 | +``` |
| 128 | +git clone https://github.com/coleifer/pysqlite3 pysqlite3-build |
| 129 | +cd pysqlite3-build |
| 130 | +``` |
| 131 | + |
| 132 | +**6.3)** build library using the src and header files from the sqlite config in step 5. |
| 133 | +``` |
| 134 | +cp ../../sqlite/sqlite3.[ch] . |
| 135 | +python setup.py build_static |
| 136 | +``` |
| 137 | + |
| 138 | +**6.4** check the libary locally |
| 139 | +``` |
| 140 | +python setyp.py install |
| 141 | +$ python |
| 142 | +>>> from pysqlite3 import dbapi2 as sqlite3 |
| 143 | +>>> sqlite3.sqlite_version |
| 144 | +'3.44.0' |
| 145 | +``` |
| 146 | + |
| 147 | +**6.5** Indentify and copy the library binary |
| 148 | +``` |
| 149 | +ls ~/sqlite344/pysqlite3-build/build/lib.linux-x86_64-cpython-310/pysqlite3/_sqlite3.cpython-310-x86_64-linux-gnu.so |
| 150 | +``` |
| 151 | + |
| 152 | +This file you'll want to transfer from ec2 to your dev system, e.g. |
| 153 | +``` |
| 154 | +scp -i "~/.aws/aws_ec2_keypair.pem" [email protected]:/home/ec2-user/sqlite344/pysqlite3/build/lib.linux-x86_64-cpython-310/pysqlite3/_sqlite3.cpython-310-x86_64-linux-gnu.so . |
| 155 | +``` |
| 156 | + |
| 157 | +the `_sqllite3.*.so` file needs to be in the root of our django lamdba project. See [serverless.yml](serverless.yml) which defines the contents of our lambda package. |
0 commit comments