无法使用 Linux Alpine docker 为 AWS lambda 构建 zip 文件

Can't build zip file for AWS lambda using Linux Alpine docker

我有基于 Alpine linux 的 Dockerfile,它为 AWS Lambda 构建 lambda.zip 文件。 这是 Dockerfile:

FROM alpine:3.12

# -- Install OS packages:
RUN apk add gcc
RUN apk add --update --no-cache \
    bash \
    build-base \
    cargo \
    curl \
    docker \
    # gcc \
    git \
    g++ \
    lftp \
    libc-dev \
    libffi-dev \
    libsodium-dev \
    libxslt-dev\
    libzmq \ 
    zeromq-dev \
    make \
    musl-dev \
    ncftp \
    nodejs \
    npm \
    openssh-client \
    openssl \
    openssl-dev \
    rsync \
    su-exec \
    tar \
    wget \
    zip 
# geos \ 
# geos-dev \
# libc-dev 

WORKDIR /tmp/

RUN echo "http://mirror.leaseweb.com/alpine/edge/community" >> /etc/apk/repositories
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
RUN apk add --virtual .build-deps \
    --repository http://dl-cdn.alpinelinux.org/alpine/edge/community \
    --repository http://dl-cdn.alpinelinux.org/alpine/edge/main \
    libc-dev geos-dev geos && \
    runDeps="$(scanelf --needed --nobanner --recursive /usr/local \
    | awk '{ gsub(/,/, "\nso:", ); print "so:"  }' \
    | xargs -r apk info --installed \
    | sort -u)" && \
    apk add --virtual .rundeps $runDeps

RUN geos-config --cflags

# -- Install python:
RUN apk add --update --no-cache python3-dev python3 \
    && python3 -m ensurepip --upgrade \
    && pip3 install --upgrade pip pipenv setuptools docker-compose awscli shapely wheel \
    && rm -r /usr/lib/python*/ensurepip  \
    && if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi \
    && if [[ ! -e /usr/bin/python ]]; then ln

这是在 docker Alpine 图像上运行并构建 lambda.zip 的脚本,其中包含 python 文件和所有需要的依赖项,如 boto3、pip、six 等

 
#!/bin/bash

set -x
set -e

pipenv install
rm -rf lambda.zip

VENV=$(pipenv --venv)
TEMPDIR=$(mktemp -d)

rsync -r --progress ./* ${TEMPDIR}
rsync -r --progress ${VENV}/lib/python3.8/site-packages/* ${TEMPDIR}

pushd ${TEMPDIR}
zip -r lambda.zip ./*
popd

cp ${TEMPDIR}/lambda.zip ./

cleanup() {
    rm -rf ${TEMPDIR}
}

trap cleanup EXIT

然后我将这个 lambda.zip 复制到我的本地机器,解压缩并在我的本地机器上启用 pipenv 以仅使用 lambda.zip 中的那些包,我尝试测试 Shapely 包,例如 test.py:

from shapely.geometry import Polygon
print(Polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]).minimum_clearance)

在 AWS lambda 和我的本地机器上,我在 geos_c 之后遇到相同的错误:

Traceback (most recent call last):
  File "test.py", line 1, in <module>
    from shapely.geometry import Polygon
  File "/home/stark/code/job/lamba_python_312_stack/shapely/geometry/__init__.py", line 4, in <module>
    from .base import CAP_STYLE, JOIN_STYLE
  File "/home/stark/code/job/lamba_python_312_stack/shapely/geometry/base.py", line 19, in <module>
    from shapely.coords import CoordinateSequence
  File "/home/stark/code/job/lamba_python_312_stack/shapely/coords.py", line 8, in <module>
    from shapely.geos import lgeos
  File "/home/stark/code/job/lamba_python_312_stack/shapely/geos.py", line 87, in <module>
    _lgeos = load_dll('geos_c', fallbacks=alt_paths)
  File "/home/stark/code/job/lamba_python_312_stack/shapely/geos.py", line 60, in load_dll
    raise OSError(
OSError: Could not find lib geos_c or load any of its variants ['libgeos_c.so.1', 'libgeos_c.so'].

如果我解压缩文件,我会得到如下内容:

@user# ls

iohttp
aiohttp-3.7.4.post0.dist-info
attr
attrs-21.2.0.dist-info
boto3
boto3-1.18.38.dist-info
botocore
cron_runner.py
setuptools-58.0.4.virtualenv
shapely
Shapely-1.7.1.dist-info
...
xmltodict.py
yarl
yarl-1.6.3.dist-info

我在 Internet 上尝试了很多不同的解决方案,因此您可以在我的 Dockerfile 中看到一团糟,但它们都没有用。

您正在 Docker 环境中安装一些静态编译的依赖项,例如 libc-devgeos-devgeos。您还必须在 Lambda 部署 zip 文件中包含这些静态依赖项。此外,要包含在 AWS Lambda 中使用的静态编译依赖项,您必须使用 Lambda 使用的相同操作系统,即 Amazon Linux,而不是 Alpine Linux.


幸运的是,现在有两种选择可以让这一切变得更容易:

Lambda Layers

Lambda 层是 Lambda 依赖项,可以以可重用的方法打包,也可以与其他开发人员共享。在这种情况下 someone has already created a shapely Lambda Layer (and someone else here),您可以简单地将其包含在您的 Lambda 函数中,而不必尝试自行打包。

如果您仍想自己构建它,您可以查看该项目的源代码以了解他们是如何构建该层的。

Lambda Containers

您可以创建 Docker 映像并将其部署到 Lambda,而不是创建 zip 部署。如果你走这条路,你必须在你的 Lambda 容器中实现一个特定的接口,最简单的方法是从 official AWS Lambda base images.

之一开始