I have a Python script with a Windows .exe
dependency, which in return relies on a (closed-source) Windows DLL. The Python script runs just fine in Ubuntu via a call to Wine.
Is it possible (and practical) to run this on AWS Lambda?
What would be involved in preparing the code package?
Update: the lambda container image feature supports images up to 10gb. I haven't tried it but I think that would be a viable approach, and wouldn't require the hacks I did below to reduce the wine build size.
TL;DR;
Is it Possible? Yes.
Is it practical? The approach I tried is not. A better approach might be to try and put wine into different lambda layers or a custom execution environment.
Will it work for you? It depends, deployment package size and disk space are the limiting factors.
Old, somewhat hacky method to fit wine into the regular lambda environment:
I compiled a custom wine with minimal dependencies for lambda, compressed it and then put it onto S3.
Then, in the lambda at runtime, I downloaded the archive, extracted it to /tmp
and ran it with a custom empty wine prefix.
My test windows executable was 64bit curl.exe.
From https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html, I first tried amzn-ami-hvm-2018.03.0.20181129-x86_64-gp2
, but it had an older compilation environment and wouldn't configure.
With AMI amzn2-ami-hvm-2.0.20190313-x86_64-gp2
on a t3.2xlarge
ec2, I was able to configure and compile. These are the commands I used, references aws-compile and building-wine:
> sudo yum groupinstall "Development Tools"
> mkdir -p ~/wine-dirs/wine-source
> git clone git://source.winehq.org/git/wine.git ~/wine-dirs/wine-source
> cd ~/wine-dirs/wine-source
> ./configure --enable-win64 --without-x --without-freetype --prefix /opt/wine
> make -j8
> sudo mkdir -p /opt/wine
> sudo chown ec2-user.ec2-user /opt/wine/
> make install
> cd /opt/
> tar zcvf ~/wine-64.tar.gz wine/
This was only a 64-bit build. It also had almost no other optional wine dependencies.
I removed a lot of optional dependencies from the wine build at compilation time, but it was still too big. /tmp
is limited to 500MB.
I deleted files in the package subdirectories, including what looked like optional libs, until I got it down to around 300MB uncompressed.
I verified that wine would still run curl.exe
after deleting files from the build.
I created a tar.bz2
of wine and curl with default bz2 options, it ended up around 80MB. The compressed and extracted files together required about 390MB.
That way there is enough room to both download the archive and extract it to /tmp
inside the lambda.
> du -h .
290M ./wine/lib64/wine
292M ./wine/lib64
276K ./wine/share/wine
8.0K ./wine/share/applications
288K ./wine/share
5.0M ./wine/curl-7.66.0-win64-mingw/bin
5.0M ./wine/curl-7.66.0-win64-mingw
12M ./wine/bin
308M ./wine
390M .
> ls
wine wine.tar.bz2
wine.tar.bz2
to S3Create an S3 bucket and upload the wine.tar.bz2
file to it.
Create an AWS Lambda using the python 3.7 runtime. While this uses a different underlying AMI than what wine was built on above, it still worked.
In the lambda execution role, grant access to the S3 bucket.
RAM: 1024MB
. I chose this because lambda CPU power scales with the memory.
Timeout: 1 min
I needed to follow the advice from this question and answer to change the wine prefix inside the lambda. I also turned off the display as it suggested.
e.g.:
handler():
... download from S3 to /tmp, cd to /tmp
subprocess.call(["tar", "-jxvf", "./wine.tar.bz2"])
os.environ['DISPLAY'] = ''
os.environ['WINEARCH'] = 'win64'
os.environ['WINEPREFIX'] = '/tmp/wineprefix'
subprocess.call(["./wine/bin/wine64", "./wine/curl-7.66.0-win64-mingw/bin/curl.exe", "http://www.stackoverflow.com"])
Success!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With