简体   繁体   中英

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. 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. However, when I build my Haskell program and copy it to that container, I get an error:

standard_init_linux.go:211: exec user process caused "no such file or directory"

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. 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.

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. This makes development very difficult.

What's a better process for running a simple Haskell script in a Docker image?

Here is my simplified Docker image:

# 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.

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 . 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.

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

$ 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.

(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.

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.

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

In this shell, you can find the Stack installation in /home/stackage/.stack ; pointing the STACK_ROOT environment variable at that directory will make the stack command find it. That avoids the need to download ghc and the rest of the LTS Haskell environment again on rebuild. Once you've done that, the rest of your Dockerfile works pretty much as you've shown.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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