为什么我的容器化 Selenium 应用程序仅在 AWS Lambda 中失败?

Why is my containerized Selenium application failing only in AWS Lambda?

我正在尝试在使用 Selenium 和 Firefox/geckodriver 的 AWS Lambda 中获取 运行 的函数,以便 运行。我决定采用创建容器映像的方式,然后上传并 运行 宁它,而不是使用预先配置的 运行 时间。我能够创建 Docker 正确安装 Firefox 和 Python 的文件,下载 geckodriver,并安装我的测试代码:

FROM alpine:latest

RUN apk add firefox python3 py3-pip
RUN pip install requests selenium

RUN mkdir /app
WORKDIR /app

RUN wget -qO gecko.tar.gz https://github.com/mozilla/geckodriver/releases/download/v0.28.0/geckodriver-v0.28.0-linux64.tar.gz
RUN tar xf gecko.tar.gz
RUN mv geckodriver /usr/bin

COPY *.py ./

ENTRYPOINT ["/usr/bin/python3","/app/lambda_function.py"]

Selenium 测试代码:

#!/usr/bin/env python3
import util
import os
import sys
import requests

def lambda_wrapper():
    api_base = f'http://{os.environ["AWS_LAMBDA_RUNTIME_API"]}/2018-06-01'
    response = requests.get(api_base + '/runtime/invocation/next')
    request_id = response.headers['Lambda-Runtime-Aws-Request-Id']
    try:
        result = selenium_test()
        
        # Send result back
        requests.post(api_base + f'/runtime/invocation/{request_id}/response', json={'url': result})
    except Exception as e:
        # Error reporting
        import traceback
        requests.post(api_base + f'/runtime/invocation/{request_id}/error', json={'errorMessage': str(e), 'traceback': traceback.format_exc(), 'logs': open('/tmp/gecko.log', 'r').read()})
        raise

def selenium_test():
    from selenium.webdriver import Firefox
    from selenium.webdriver.firefox.options import Options
    options = Options()
    options.add_argument('-headless')
    options.add_argument('--window-size 1920,1080')
    
    ffx = Firefox(options=options, log_path='/tmp/gecko.log')
    ffx.get("https://google.com")
    url = ffx.current_url
    ffx.close()
    print(url)

    return url
    

def main():
    # For testing purposes, currently not using the Lambda API even in AWS so that
    # the same container can run on my local machine.
    # Call lambda_wrapper() instead to get geckodriver logs as well (not informative).
    selenium_test()
    

if __name__ == '__main__':
    main()

我能够使用 docker build -t lambda-test . 在本地计算机上成功构建此容器,然后使用 docker run -m 512M lambda-test.

运行 它

但是,当我尝试将 完全相同的容器 上传到 Lambda 至 运行 时,它因错误而崩溃。我将内存限制设置为 1024M,超时设置为 30 秒。回溯表明 Firefox 被信号意外杀死:

START RequestId: 52adeab9-8ee7-4a10-a728-82087ec9de30 Version: $LATEST
/app/lambda_function.py:29: DeprecationWarning: use service_log_path instead of log_path
  ffx = Firefox(options=options, log_path='/tmp/gecko.log')
Traceback (most recent call last):
  File "/app/lambda_function.py", line 45, in <module>
    main()
  File "/app/lambda_function.py", line 41, in main
    lambda_wrapper()
  File "/app/lambda_function.py", line 12, in lambda_wrapper
    result = selenium_test()
  File "/app/lambda_function.py", line 29, in selenium_test
    ffx = Firefox(options=options, log_path='/tmp/gecko.log')
  File "/usr/lib/python3.8/site-packages/selenium/webdriver/firefox/webdriver.py", line 170, in __init__
    RemoteWebDriver.__init__(
  File "/usr/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 157, in __init__
    self.start_session(capabilities, browser_profile)
  File "/usr/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 252, in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
  File "/usr/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/usr/lib/python3.8/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Process unexpectedly closed with status signal

END RequestId: 52adeab9-8ee7-4a10-a728-82087ec9de30
REPORT RequestId: 52adeab9-8ee7-4a10-a728-82087ec9de30  Duration: 20507.74 ms   Billed Duration: 21350 ms   Memory Size: 1024 MB    Max Memory Used: 131 MB Init Duration: 842.11 ms    
Unknown application error occurred

我也让它上传了 geckodriver 日志,但是里面没有太多有用的信息:

1608506540595   geckodriver INFO    Listening on 127.0.0.1:41597
1608506541569   mozrunner::runner   INFO    Running command: "/usr/bin/firefox" "--marionette" "-headless" "--window-size 1920,1080" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofileQCapHy"
*** You are running in headless mode.

我怎么才能开始调试呢?事实上,完全相同的容器根据其 运行 的位置表现不同这一事实对我来说似乎很可疑,但我对 Selenium、Docker 或 Lambda 的了解还不够,无法准确指出问题所在。

我的 docker run 命令是否没有准确地在 Lambda 中重新创建环境?如果是这样,那么我 运行 什么命令可以更好地模拟 Lambda 环境?我不太确定从这里还能去哪里,因为我实际上无法在本地重现错误以进行测试。

如果有人想查看完整代码并尝试自己构建它,存储库是 here - lambda 代码在 lambda_function.py.

至于之前的研究,this question a) is about ChromeDriver and b) has no answers from over a year ago. The only has information about how to run a container in Lambda, which I'm already doing. This answer 几乎 我的问题,但我知道没有版本不匹配,因为容器适用于我的笔记本就好了。

我有完全相同的问题和可能的解释。 我觉得你想要的暂时做不到

根据 AWS DevOps Blog Firefox 依赖于 fallocate 系统调用和 /dev/shm。 但是 AWS Lambda 不会挂载 /dev/shm 因此 Firefox 在尝试分配内存时会崩溃。不幸的是,无法为 Firefox 禁用此处理。

但是,如果您可以使用 Chromium,chr​​omedriver --disable-dev-shm-usage 有一个选项可以禁用 /dev/shm 的使用,而是将共享内存文件写入 /tmp。 chromedriver 在 AWS Lambda 上对我来说效果很好,如果这是你的选择的话。 根据 AWS DevOps Blog,您还可以使用 AWS Fargate 运行 Firefox/geckodriver.

2015 年的 AWS forum 中有一个条目要求在 Lambdas 中安装 /dev/shm,但此后没有任何反应。