我正在尝试在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二进制的编译版本,我将不胜感激。
[好,所以,我解决了这个问题,尽管很怪癖。
我传递给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_gfortran
sudo mv /usr/bin/gfortran /usr/bin/inner_gfortran
然后将go包装器放回原处
包装器将把大多数指令传递给编译器,但是如果指令是将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