首页 > 解决方案 > 为什么我的 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

标签: dockerhaskellhaskell-stack

解决方案


该图像旨在与Haskell Stack 的 Docker 集成一起使用。一个非常合理的路径是使用该路径在主机系统目录中构建二进制文件,然后使用该 Dockerfile 的后半部分将二进制文件打包成 Docker 映像。

如果您查看构建的内容,它是一个动态链接的二进制文件,具有非默认依赖项。如果我更改ubuntualpine(临时)并更改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"]

推荐阅读