Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 623
Victor Polevoy Avatar asked Mar 04 '18 18:03

Victor Polevoy


1 Answers

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.

like image 123
Victor Polevoy Avatar answered Nov 08 '22 10:11

Victor Polevoy