减少用于aws-lambda部署的scipy和numpy的大小



我正试图在aws lambda上部署一个python应用程序。它有几个大型python依赖项,最大的是scipy和numpy。结果是,我的应用程序明显大于允许的250MB。

当我试图找到一种缩小尺寸的方法时,我发现了这里详细介绍的方法:

https://github.com/szelenka/shrink-linalg

本质上,当使用pip安装时,在scipy&numpy-cython编译时,可以将标志传递给c编译器,该编译器将省略已编译的c二进制文件中的调试信息。结果是scipy和numpy被减少到原始大小的大约50%。我能够在本地运行它(ubuntu 16.04(,并创建了二进制文件而没有问题。使用的命令是:

CFLAGS="-g0 -I/usr/include:/usr/local/include -L/usr/lib:/usr/local/lib" pip install numpy scipy --compile --no-cache-dir --global-option=build_ext --global-option="-j 4"

问题是,为了在aws-lambda上运行,二进制文件必须在与lambda运行环境类似的环境中编译

https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html

在ec2实例上加载映像后,在安装了一些依赖项后,我尝试运行相同的pip安装

sudo yum install python36 python3-devel blas-devel atlas atlas-devel lapack-devel atlas-sse3-devel gcc gcc-64 gcc-gfortran gcc64-gfortran libgfortran, gcc-c++ openblas-devel python36-virtualenv

numpy编译得很好,但scipy不是。cython没有引起任何问题,但fortran编译是。我得到了以下错误:

error: Command "/usr/bin/gfortran -Wall -g -Wall -g -shared build/temp.linux-x86_64-3.6/build/src.linux-x86_64-3.6/scipy/integrate/_test_odeint_bandedmodule.o build/temp.linux-x86_64-3.6/build/src.linux-x86_64-3.6/build/src.linux-x86_64-3.6/scipy/integrate/fortranobject.o build/temp.linux-x86_64-3.6/scipy/integrate/tests/banded5x5.o build/temp.linux-x86_64-3.6/build/src.linux-x86_64-3.6/scipy/integrate/_test_odeint_banded-f2pywrappers.o -L/usr/lib64/atlas -L/usr/lib/gcc/x86_64-amazon-linux/6.4.1 -L/usr/lib/gcc/x86_64-amazon-linux/6.4.1 -L/usr/lib64 -Lbuild/temp.linux-x86_64-3.6 -llsoda -lmach -llapack -lptf77blas -lptcblas -latlas -lptf77blas -lptcblas -lpython3.6m -lgfortran -o build/lib.linux-x86_64-3.6/scipy/integrate/_test_odeint_banded.cpython-36m-x86_64-linux-gnu.so -Wl,--version-script=build/temp.linux-x86_64-3.6/link-version-scipy.integrate._test_odeint_banded.map" failed with exit status 1

我试过重新安装gfortran以及整个gcc系列,但没有任何运气。不幸的是,我对fortran编译器的经验非常有限。如果有人有任何想法,或者有c二进制文件的编译版本,我将不胜感激。

在Serverless上使用serverless-python-requirements包帮助我简化了整个过程,并减少了包的大小。我绝对建议你去看看。

这是我遵循的指南

无服务器python需求插件

确保将CCD_ 2标志留给CCD_;ELF加载命令地址/偏移未正确对齐";,

这就是我最后的serverless.yml,它给了我想要将sklearn+cv2打包为一层的结果:

custom:
pythonRequirements:
dockerizePip: true
useDownloadCache: true
useStaticCache: false
slim: true
strip: false
layer:
name: ${self:provider.stage}-cv2-sklearn
description: Python requirements lambda layer
compatibleRuntimes:
- python3.8
allowedAccounts:
- '*'

好的,所以,我解决了这个问题,尽管方法很巧妙。

我传递给pip的标志是为了减少c依赖项的大小,而不是fortran依赖项。因此,使用通常通过pip下载的预编译fortran依赖项确实没有问题。

因此,首先我在sp:文件夹中创建了一个未更改的scipy包的参考版本

pip install scipy -t sp

然后,我创建了一个go程序,作为gfortran编译器的包装器(或者从技术上讲,围绕/usr/bin中的gfortran编译程序链接(

package main
import "os"
import "strings"
import "io/ioutil"
import "log"
import "os/exec"
import "fmt"

func checkErr(err error) {
if err != nil {
log.Fatal(err)
}
}

func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil { return true, nil }
if os.IsNotExist(err) { return false, nil }
return true, err
}

func copyr(src string, dst string) {
// Read all content of src to data
data, err := ioutil.ReadFile(src)
checkErr(err)
// Write data to dst
err = ioutil.WriteFile(dst, data, 0644)
checkErr(err)
}

func main() {
search_folder := "/home/ec2-user/sp/scipy"
wrapped_compiler := "/usr/bin/inner_gfortran"
argsWithProg := os.Args
noProg := os.Args[1:]
primed := 0
check := "-o"
var (
cmdOut []byte
err    error
)
for _, el := range argsWithProg {
if primed == 1{
primed = 0
s := strings.Split(el, "scipy")
if len(s) != 2{
continue
}
src := search_folder + s[1]
src_exi, _ := exists(src)
if src_exi == false {
continue
}
primed = 2
dir_parts := strings.Split(el, "/")
dir_parts = dir_parts[:len(dir_parts)-1]
dir := strings.Join(dir_parts,"/")
exi, _ := exists(dir)
if exi == false {
os.MkdirAll(dir, os.ModePerm)
}
os.Create(el)
copyr(src, el)
}
if el == check{
primed = 1
}
}
if primed == 0 {
if cmdOut, err = exec.Command(wrapped_compiler, noProg...).Output(); err != nil {
fmt.Fprintln(os.Stderr, "There was an error running fortran compiler: ", err)
os.Exit(1)
}
os.Stdout.Write(cmdOut)
}
}

将实际编译器移动到inner_fortran

sudo mv /usr/bin/gfortran /usr/bin/inner_gfortran

把包装纸放在它的位置

包装器会将大多数指令传递给编译器,但如果指令是将fortran程序编译到scipy,并且编译的二进制文件已经存在于我的scipy参考版本中,则包装器只需将参考版本复制到正在编译的新版本中。

的确如此。scipy和numpy的缩小版本现在可以在python 3.6的aws lambda上运行。

我知道你问这个问题已经有一段时间了,但也许这会帮助其他人。

您可以使用类似lambda的docker容器来编译资源,然后将库复制回您的开发环境。使用这些编译后的文件作为lambda资源

这篇文章对我帮助很大:https://medium.com/@mohd.lutfalla/how-to-compile-resources-for-aws-lambda-f46fadc03290

我知道这个问题很老了,但现在AWS Lambda允许从存储在ECR中的Docker映像运行函数。Docker镜像的大小最高可达10GB。

https://aws.amazon.com/fr/blogs/aws/new-for-aws-lambda-container-image-support/

最新更新