简体   繁体   English

为什么我的 Haskell 程序不能在带有 Alpine 或 Scratch 的 Docker 中运行?

[英]Why doesn't my Haskell program run in Docker with Alpine or Scratch?

I'm having a few issues try to build a Docker container that runs one Haskell application indefinitely.我在尝试构建一个无限期运行一个 Haskell 应用程序的 Docker 容器时遇到了一些问题。 For starters, I'd like to use a base image that provides a program I need to use from my code.首先,我想使用一个基本图像,它提供了一个我需要从我的代码中使用的程序。 It is based on scratch linux.它基于scratch linux。 However, when I build my Haskell program and copy it to that container, I get an error:但是,当我构建 Haskell 程序并将其复制到该容器时,出现错误:

standard_init_linux.go:211: exec user process caused "no such file or directory" standard_init_linux.go:211: exec 用户进程导致“没有这样的文件或目录”

Next, I would like to keep my build process, and file structure very simple if possible.接下来,如果可能的话,我想保持我的构建过程和文件结构非常简单。 I have just one script in Haskell in Main.hs and it has one dependency on process.我在 Main.hs 中的 Haskell 中只有一个脚本,并且它依赖于进程。 If it's possible and reasonable to avoid both a stack and a cabal file as well as subdirectories and all the that, it'd be nice if the build directive where just in the Docker or in the Haskell file.如果避免堆栈和 cabal 文件以及子目录和所有这些是可能且合理的,那么如果构建指令仅在 Docker 或 Haskell 文件中,那就太好了。

However I have an issue with the build in that the stack ghc line takes several minutes to download ghc and process and build everything and that line reexecutes whenever I make a small code change.但是,我在构建时遇到了一个问题,即堆栈 ghc 行需要几分钟才能下载 ghc 并处理和构建所有内容,每当我对代码进行小的更改时,该行都会重新执行。 This makes development very difficult.这使得开发非常困难。

What's a better process for running a simple Haskell script in a Docker image?在 Docker 镜像中运行一个简单的 Haskell 脚本的更好的过程是什么?

Here is my simplified Docker image:这是我的简化 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"]

A simplified version of the Haskell program. Haskell 程序的简化版本。

import System.Process (readProcess)
import Control.Monad (forever)
main = forever $ do
    output <- readProcess "/bin/ls" [] ""
    print output

That image is intended to be used with Haskell Stack's Docker integration .该映像旨在与Haskell Stack 的 Docker 集成一起使用 One very reasonable path is just to use that path to build a binary in a host-system directory, and then use the second half of that Dockerfile to package the binary into a Docker image.一种非常合理的路径是使用该路径在主机系统目录中构建二进制文件,然后使用该 Dockerfile 的后半部分将二进制文件打包成 Docker 映像。

If you look at what gets built, it's a dynamically linked binary that has a non-default dependency.如果您查看构建的内容,它是一个动态链接的二进制文件,具有非默认依赖项。 If I change ubuntu to alpine (temporarily) and change ENTRYPOINT to CMD then I can run如果我将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)

This also will not start with the musl libc that's packaged in the Alpine image (it's not obvious why), so you need to install the GNU libc compatibility package as well as the libgmp package.这也不会从打包在 Alpine 映像中的 musl libc 开始(原因并不明显),因此您需要安装 GNU libc 兼容包以及 libgmp 包。

(Since it's a dynamically linked binary, you also can't run it in a FROM scratch image, unless you're willing to hand-install GNU libc and the other supporting libraries you need. (因为它是一个动态链接的二进制文件,所以您也不能在FROM scratch映像中运行它,除非您愿意手动安装 GNU libc 和您需要的其他支持库。

For the build stage, as the image name suggests, it includes a complete copy of LTS Haskell 15.4 , but it takes some poking around in the image to find it.对于构建阶段,顾名思义,它包含LTS Haskell 15.4的完整副本,但需要在图像中四处寻找才能找到它。

$ docker run --rm -it fpco/stack-build:lts-15.4 sh

In this shell, you can find the Stack installation in /home/stackage/.stack ;在这个 shell 中,您可以在/home/stackage/.stack找到 Stack 安装; pointing the STACK_ROOT environment variable at that directory will make the stack command find it.STACK_ROOT环境变量指向该目录将使stack命令找到它。 That avoids the need to download ghc and the rest of the LTS Haskell environment again on rebuild.这避免了在重建时再次下载 ghc 和 LTS Haskell 环境的其余部分的需要。 Once you've done that, the rest of your Dockerfile works pretty much as you've shown.完成此操作后,Dockerfile 的其余部分就如您所展示的那样工作。

That leaves us with the final 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"]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM