docker - 为什么我的 Haskell 程序不能在带有 Alpine 或 Scratch 的 Docker 中运行?
问题描述
我在尝试构建一个无限期地运行一个 Haskell 应用程序的 Docker 容器时遇到了一些问题。对于初学者,我想使用一个基本图像,它提供了我需要从我的代码中使用的程序。它基于scratch linux。但是,当我构建我的 Haskell 程序并将其复制到该容器时,我收到一个错误:
standard_init_linux.go:211: exec 用户进程导致“没有这样的文件或目录”
接下来,如果可能的话,我想保持我的构建过程和文件结构非常简单。我在 Main.hs 的 Haskell 中只有一个脚本,它对进程有一个依赖项。如果避免堆栈和 cabal 文件以及子目录和所有这些是可能且合理的,那么如果 build 指令仅在 Docker 或 Haskell 文件中,那就太好了。
但是,我对构建有一个问题,因为堆栈 ghc 行需要几分钟来下载 ghc 并处理和构建所有内容,并且每当我进行小的代码更改时,该行都会重新执行。这使得开发非常困难。
在 Docker 映像中运行简单的 Haskell 脚本的更好过程是什么?
这是我简化的 Docker 镜像:
# Pretty standard just using the latest stack-build
FROM fpco/stack-build:lts-15.4 as haskell
# Setup a build dir and copy code to it
WORKDIR /opt/build
COPY Main.hs /opt/build
# This step takes forever and reruns every time I make a code change.
RUN stack ghc --package process -- Main.hs
# Alpine failed here for file not found.
FROM ubuntu:latest
COPY --from=haskell /opt/build/Main /Main
ENTRYPOINT ["/Main"]
Haskell 程序的简化版本。
import System.Process (readProcess)
import Control.Monad (forever)
main = forever $ do
output <- readProcess "/bin/ls" [] ""
print output
解决方案
该图像旨在与Haskell Stack 的 Docker 集成一起使用。一个非常合理的路径是使用该路径在主机系统目录中构建二进制文件,然后使用该 Dockerfile 的后半部分将二进制文件打包成 Docker 映像。
如果您查看构建的内容,它是一个动态链接的二进制文件,具有非默认依赖项。如果我更改ubuntu
为alpine
(临时)并更改ENTRYPOINT
为,CMD
那么我可以运行
$ docker run --rm 101681db8d96 ldd /Main
Error loading shared library libgmp.so.10: No such file or directory (needed by /Main)
这也不会从打包在 Alpine 映像中的 musl libc 开始(原因不明显),因此您需要安装 GNU libc 兼容包以及 libgmp 包。
(因为它是一个动态链接的二进制文件,你也不能在FROM scratch
镜像中运行它,除非你愿意手动安装 GNU libc 和你需要的其他支持库。
对于构建阶段,正如镜像名称所暗示的那样,它包含LTS Haskell 15.4的完整副本,但需要在镜像中四处寻找才能找到它。
$ docker run --rm -it fpco/stack-build:lts-15.4 sh
在这个 shell 中,您可以在/home/stackage/.stack
;中找到 Stack 安装。将环境变量指向该STACK_ROOT
目录将使stack
命令找到它。这避免了在重建时再次下载 ghc 和 LTS Haskell 环境的其余部分的需要。一旦你完成了这些,你的 Dockerfile 的其余部分就像你展示的那样工作。
这给我们留下了最终的 Dockerfile:
FROM fpco/stack-build:lts-15.4 as haskell
# Tell `stack` where to find its content (not in $HOME)
ENV STACK_ROOT /home/stackage/.stack
WORKDIR /opt/build
COPY Main.hs .
RUN stack ghc --package process -- Main.hs
# Switch Ubuntu back to Alpine
FROM alpine:latest
# Add the libraries we need to run the application
RUN apk add libc6-compat gmp
COPY --from=haskell /opt/build/Main /Main
CMD ["/Main"]
推荐阅读
- microsoft-graph-api - 将 Azure B2B 用于我们的客户门户
- reactjs - 通过 useSelector 渲染项目列表哪个更高效?
- android - Kotlin:从 SD 卡中的文件夹中读取所有文件
- python - 传递值的形状是 (492, 7),索引意味着 (1, 7) 用于财务数据
- crystal-reports-2010 - Crystal报告如何获取符号右侧的数字(这是带有2位小数和千位分隔符的字符串)=
- reactjs - 组件重叠 - 显示一个在另一个之上 - 反应路由器 v5
- flutter - Flutter Unhandled Exception:接收数据时连接关闭
- node.js - GPU.js计算多对象xyz之间的距离?
- sql - UNION 的替代方案
- php - 基于商品数量的 Woocommerce Shipping Class 价格