将 Yesod 部署到 Heroku,无法静态构建
Posted
技术标签:
【中文标题】将 Yesod 部署到 Heroku,无法静态构建【英文标题】:Deploying Yesod to Heroku, can't build statically 【发布时间】:2012-01-29 06:48:59 【问题描述】:我是 Yesod 的新手,我在静态构建 Yesod 时遇到了问题 这样我就可以部署到 Heroku。
我更改了默认的 .cabal 文件以反映静态编译
if flag(production)
cpp-options: -DPRODUCTION
ghc-options: -Wall -threaded -O2 -static -optl-static
else
ghc-options: -Wall -threaded -O0
它不再构建。我收到一大堆警告,然后是 大量未定义的引用,如下所示:
Linking dist/build/personal-website/personal-website ...
/usr/lib/ghc-7.0.3/libHSrts_thr.a(Linker.thr_o): In function
`internal_dlopen':
Linker.c:(.text+0x407): warning: Using 'dlopen' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/ghc-7.0.3/unix-2.4.2.0/libHSunix-2.4.2.0.a(HsUnix.o): In
function `__hsunix_getpwent':
HsUnix.c:(.text+0xa1): warning: Using 'getpwent' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/ghc-7.0.3/unix-2.4.2.0/libHSunix-2.4.2.0.a(HsUnix.o): In
function `__hsunix_getpwnam_r':
HsUnix.c:(.text+0xb1): warning: Using 'getpwnam_r' in statically
linked applications requires at runtime the shared libraries from the
glibc version used for linking
/usr/lib/libpq.a(thread.o): In function `pqGetpwuid':
(.text+0x15): warning: Using 'getpwuid_r' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/libpq.a(ip.o): In function `pg_getaddrinfo_all':
(.text+0x31): warning: Using 'getaddrinfo' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/ghc-7.0.3/site-local/network-2.3.0.2/
libHSnetwork-2.3.0.2.a(BSD__63.o): In function `sD3z_info':
(.text+0xe4): warning: Using 'gethostbyname' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/ghc-7.0.3/site-local/network-2.3.0.2/
libHSnetwork-2.3.0.2.a(BSD__164.o): In function `sFKc_info':
(.text+0x12d): warning: Using 'getprotobyname' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/ghc-7.0.3/site-local/network-2.3.0.2/
libHSnetwork-2.3.0.2.a(BSD__155.o): In function `sFDs_info':
(.text+0x4c): warning: Using 'getservbyname' in statically linked
applications requires at runtime the shared libraries from the glibc
version used for linking
/usr/lib/libpq.a(fe-misc.o): In function `pqSocketCheck':
(.text+0xa2d): undefined reference to `SSL_pending'
/usr/lib/libpq.a(fe-secure.o): In function `SSLerrmessage':
(.text+0x31): undefined reference to `ERR_get_error'
/usr/lib/libpq.a(fe-secure.o): In function `SSLerrmessage':
(.text+0x41): undefined reference to `ERR_reason_error_string'
/usr/lib/libpq.a(fe-secure.o): In function `initialize_SSL':
(.text+0x2f8): undefined reference to `SSL_check_private_key'
/usr/lib/libpq.a(fe-secure.o): In function `initialize_SSL':
(.text+0x3c0): undefined reference to `SSL_CTX_load_verify_locations'
(... snip ...)
如果我只使用-static
而不使用-optl-static
进行编译
一切正常,但应用程序在尝试时崩溃
从 Heroku 开始。
2011-12-28T01:20:51+00:00 heroku[web.1]: Starting process with command
`./dist/build/personal-website/personal-website -p 41083`
2011-12-28T01:20:51+00:00 app[web.1]: ./dist/build/personal-website/
personal-website: error while loading shared libraries: libgmp.so.10:
cannot open shared object file: No such file or directory
2011-12-28T01:20:52+00:00 heroku[web.1]: State changed from starting
to crashed
我尝试按照here 中的建议将 libgmp.so.10 添加到 LD_LIBRARY_PATH 然后得到以下错误:
2011-12-28T01:31:23+00:00 app[web.1]: ./dist/build/personal-website/
personal-website: /lib/libc.so.6: version `GLIBC_2.14' not found
(required by ./dist/build/personal-website/personal-website)
2011-12-28T01:31:23+00:00 app[web.1]: ./dist/build/personal-website/
personal-website: /lib/libc.so.6: version `GLIBC_2.14' not found
(required by /app/dist/build/personal-website/libgmp.so.10)
2011-12-28T01:31:25+00:00 heroku[web.1]: State changed from starting
to crashed
2011-12-28T01:31:25+00:00 heroku[web.1]: Process exited
似乎我正在编译的 libc 版本是 不同的。我还尝试将 libc 添加到一批库中 与我对 libgmp 所做的方式相同,但这会导致分段错误 当应用程序在 Heroku 端启动时。
在我的电脑上一切正常。我正在使用 ghc 运行 64 位 archlinux 7.0.3。 The blog post on the official Yesod blog 看起来很简单 但我在这一点上难住了。谁有想法?如果有办法让这个东西在静态构建的情况下工作,我也愿意。
编辑
根据Employed Russians
的回答,我执行了以下操作来解决此问题。
先在项目目录下新建一个目录lib
,把缺少的共享库复制进去。您可以通过运行ldd path/to/executable
和heroku run ldd path/to/executable
并比较输出来获取此信息。
然后我做了heroku config:add LD_LIBRARY_PATH=./lib
,所以当应用程序启动时,动态链接器将在新的 lib 目录中查找库。
最后我创建了一个 ubuntu 11.10 虚拟机并从那里构建并部署到 Heroku,它有一个足够老的 glibc 可以在 Heroku 主机上运行。
编辑: 我已经在Yesod wiki上写了一篇教程
【问题讨论】:
【参考方案1】:我不知道 Yesod 是什么,但我完全了解您的其他每个错误的含义。
首先,您应该不尝试静态链接。你得到的警告是完全正确的:如果你静态链接,并使用你得到警告的例程之一,那么你必须安排在一个系统上运行 exactly em> 与您在构建时使用的 libc.so.6 版本相同。
与流行的看法相反,静态链接在 Linux 上产生更少而不是更多的可移植可执行文件。
您的其他(静态)链接错误是由于链接时缺少libopenssl.a
造成的。
但是让我们假设您要走“理智”的路线,并使用动态链接。
对于动态链接,Linux(和大多数其他 UNIX)支持向后兼容性:旧的二进制文件继续在新的系统上工作。但它们不支持前向兼容性(在较新系统上构建的二进制文件通常不会在旧系统上运行)。
但这就是您想要做的:您在使用 glibc-2.14(或更高版本)的系统上构建,并且在使用 glibc-2.13(或更高版本)的系统上运行。
您需要知道的另一件事是 glibc 由大约 200 多个二进制文件组成,这些二进制文件必须完全匹配。两个关键二进制文件是/lib/ld-linux.so
和/lib/libc.so.6
(但还有更多:libpthread.so.0
、libnsl.so.1
等)。如果其中一些二进制文件来自不同版本的 glibc,您通常会崩溃。当您尝试将 glibc-2.14 libc.so.6
放在 LD_LIBRARY_PATH
上时,这正是您所得到的——它不再与系统 /lib/ld-linux
匹配。
那么解决方案是什么?有几种可能性(难度越来越大):
您可以将ld-2.14.so
(/lib/ld-linux
符号链接的目标)复制到目标系统,并显式调用它:
/path/to/ld-2.14.so --library-path <whatever> /path/to/your/executable
这通常有效,但可能会混淆查看 argv[0]
的应用程序,并为重新执行自身的应用程序中断。
您可以在旧系统上构建。
您可以使用appgcc
(此选项已消失,请参阅this 了解它以前的含义)。
您可以设置与目标系统匹配的 chroot 环境,并在该 chroot 中构建。
您可以自己构建一个 Linux-to-olderLinux 交叉编译器
【讨论】:
好的,所以我已经将ld-2.14.so
与我的系统中的libc.so.6
和libgmp.so.10
一起复制到了我的heroku 构建中,并将heroku 运行命令更改为您指定的命令。应用程序现在无声地崩溃(至少在 heroku 日志中没有崩溃的输出,状态只是从开始变为崩溃)。如果我运行heroku run ./lib/ld-linux-x86-64.so.2 --list /path/to/executable
,所有库依赖项都已满,但大多数都满足系统库。我是否必须从我的系统中复制应用程序使用的所有库?
你不必静态构建;据我了解,即使没有-static
和朋友,GHC 也会将 GHC RTS 与您的可执行文件捆绑在一起(这是您需要的,因为 Heroku 可能没有 GHC 运行时可用作 lib),除非您使用-dynamic
标志。默认情况下,RTS 使用的库 是动态喜欢的,这对您的情况是明智的。另见:***.com/questions/699908/…
我在发帖后发现了这一点。看起来主要问题是我的 libc
比 Heroku 主机上的更新,正如 Employed Russian
所建议的那样。
感谢受雇的俄罗斯人,我最终创建了一个 ubunut 虚拟机来构建,现在看来可以工作了。
我认为这种说法非常不准确:“与流行的看法相反,静态链接在 Linux 上产生的可移植可执行文件更少,而不是更多。”如您所知,Linux 永远不会破坏 API 兼容性。你的意思是说 Glibc 而不是 Linux?我真的不明白这与 Linux 本身有什么关系。【参考方案2】:
你有几个问题。
您不应该在最前沿的发行版上构建生产二进制文件。生产系统上的库将无法向前兼容。
您不应该静态链接 glibc - 它总是会在运行时尝试加载其他库。例如基于 cpu 的程序集。这就是你的第一个警告。
最后一个链接器错误看起来与命令行中缺少 openssl 库有关。
但总而言之 - 降级您的发行版。
【讨论】:
鉴于 heroku 仍然在那个旧版本上——这对于提供稳定平台的意图是有意义的——人们可以称 glibcs > 2.11 几乎没有“流血边缘”。不过,您的回答在 2011 年 12 月可能是有道理的。【参考方案3】:我在启动 Heroku(使用 glibc-2.11)时遇到了类似的问题,我有一个需要 glibc-2.14 的应用程序,但我无法访问源代码并且无法重新构建它。我尝试了很多东西,但没有任何效果。
我的解决方法是在 Amazon Elastic Beanstalk 上启动该服务并仅提供一个 API 接口。
【讨论】:
以上是关于将 Yesod 部署到 Heroku,无法静态构建的主要内容,如果未能解决你的问题,请参考以下文章
部署到 Heroku 时 Django 应用程序崩溃 - Worker 无法启动