VP.
VP.

Reputation: 16695

Unable to run a Docker image with a Rust executable

I am trying to create an image with my binary file (written in Rust) but I get different errors. This is my Dockerfile:

FROM scratch
COPY binary /
COPY .env /
COPY cert.pem /etc/ssl/
ENV RUST_BACKTRACE 1
CMD /binary

Building finishes fine but when I try to run it I get this:

$ docker run binary
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"/bin/sh\": stat /bin/sh: no such file or directory": unknown.
ERRO[0000] error waiting for container: context canceled 

And this:

$ docker run binary /binary
standard_init_linux.go:195: exec user process caused "no such file or directory"

I have no idea what to do. The error message looks very odd to me. According to the official Docker documentation it must work.

System info: latest Arch Linux and Docker:

Docker version 18.02.0-ce, build fc4de447b5

I tested with a C++ program and it works fine, with both clang and gcc.

It does not work with scratch, alpine, busybox ,or bash-based images, but it does work with postgresql, ubuntu, and debian images. The exact problem is something related to Rust and lightweight docker images - everything works okay otherwise.

Upvotes: 18

Views: 6348

Answers (2)

Paul Razvan Berg
Paul Razvan Berg

Reputation: 21368

In my case, the issue was that I was passing an invalid executable name:

CMD ["liquidator"]

liquidator was the name of the Docker image, but I needed this:

CMD ["hifi-liquidator"]

Basically the CMD must be the same as the "name" field in the Cargo.toml file.

Upvotes: 0

VP.
VP.

Reputation: 16695

As @Oleg Sklyar pointed out, the problem is that the Rust binary is dynamically-linked.

This may be a bit confusing because many people who have heard of Rust have also heard that Rust binaries are statically-linked, but this refers to the Rust code in crates: crates are linked statically because they are all known at the moment of compilation. This does not refer to existing C dynamic libraries that the program may link to, such as libc and other must-have libraries. Often times, these libraries can also be built as statically-linked artifacts (see the end of this post). To check whether your program or library is dynamically-linked, you can use ldd utility:

$ ldd target/release/t
    linux-vdso.so.1 (0x00007ffe43797000)
    libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fa78482d000)
    librt.so.1 => /usr/lib/librt.so.1 (0x00007fa784625000)
    libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fa784407000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007fa7841f0000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007fa783e39000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fa784ca2000)

You'll need these libraries in your Docker image. You will also need the interpreter; to get its path you can use objdump utility:

$ LANG=en objdump -s -j .interp target/release/t

target/release/t:     file format elf64-x86-64

Contents of section .interp:
 0270 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 0280 7838362d 36342e73 6f2e3200           x86-64.so.2.  

Copy the files into the expected directories and everything works okay.

There is also a second option which is to use the rust-musl-builder docker image. There are some problems with postgresql and diesel but for most of projects it would be good. It works by producing a statically-linked executable which you may just copy and use. This option is much more preferred than using an interpreter and dynamic libraries if you want to provide a docker image with less size and without having all that useless extra data such as interpreter, unused libraries and so on.

Upvotes: 18

Related Questions