Skip to content

chore: Build for x86_64-unknown-linux-musl with all features and optimized binary size #689

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from Aug 1, 2019

Conversation

ghost
Copy link

@ghost ghost commented Jul 29, 2019

This PR enables all features, including those with native dependencies, for x86_64-unknown-linux-musl target. The resulting binary is fully statically linked, LTO-optimized, and stripped.

The build toolchain is based on Clang and libc++ from LLVM project, in addition to musl.

libc++ is used by rdkafka and leveldb. Although it should be possible to link GNU libstdc++ statically, it makes the resulting binaries licensed under LGPL, which might be not desirable as Vector's license is Apache 2. In contrast, libc++ is MIT/UIUC licensed.

Clang is used not only because it is easy to pair with libc++, but also because it enables cross-language linker plugin LTO. Linker plugin LTO is different from LTO codegen option in Rust. Both types of LTO can be used together to reduce binary size and improve runtime performance.

Here are the measurements of the binary size with different LTO combinations for x86_64-unknown-linux-musl target:

Rust codegen LTO Linker plugin LTO Original binary size, MB Stripped binary size, MB Compressed stripped binary size, MB
26.78 17.34 6.59
lto=thin 27.34 18.17 6.54
lto 26.98 18.33 6.58
lto=thin 25.76 17.58 6.67
lto=thin lto=thin 27.35 18.18 6.54
lto 20.95 14.70 5.96
lto lto 20.11 14.85 5.80

From the results listed above it can be seen that Rust codegen LTO type lto and linker plugin LTO type lto deliver the best size of compressed stripped binary. It theoretically should also have the best performance, as LTO reduces functions call overhead by inlining small functions.

For comparison, a similar table for x86_64-unknown-linux-gnu:

Rust codegen LTO Linker plugin LTO Original binary size, MB Stripped binary size, MB Compressed stripped binary size, MB
34.50 17.10 6.20
lto=thin 33.40 17.29 6.27
lto 28.64 14.37 5.55

As a result, Rust codegen LTO type lto and linker plugin LTO type lto are used for x86_64-unknown-linux-musl. Rust LTO type is not set in Cargo.toml, but is added at the build time because LTO significantly increases build times, and in my opinion it is better to start from enabling it only for certain build types in CI.

Stripping of Vector binary is added for all target platforms because it can't hurt, but only reduce resulting binaries size.

Ref #661, #630, #654, #669

@binarylogic
Copy link
Contributor

@a-rodin this is fantastic! Thank you for doing this. I spent a considerable amount of time trying to do exactly this and was unsuccessful. In fact, I've been reaching out to our network to try and find someone to help with this, so I'm really happy to see this done. 😄

I'll have @LucioFranco review and then we can merge and get it fully integrated into our releases.

@binarylogic binarylogic requested a review from LucioFranco July 29, 2019 20:21
Copy link
Contributor

@LucioFranco LucioFranco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fantastic! I pulled this down locally and ran the build, everything looks good. I'm very happy this was even possible! I have a few questions but nothing blocking.

I say we :shipit:

@binarylogic I think we will have to build the new image and push it to docker hub before we can merge this?

# Nightly Rust is used because at the moment only nightly uses recent enough
# LLVM version that supports libc++ with musl. It can be changed to a stable
# version after Rust 1.38 release.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving a note here: we should open an issue for this.

cc @binarylogic

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LucioFranco I was just trying to compile a musl Vector on a Linux x86_64 machine with cargo build --target x86_64-unknown-linux-musl --release --verbose using Rust stable 1.36.0 and hit an openssl error. Is this problem related to this issue?

Any consideration of using Rust native rustls? See https://jbp.io/2019/07/01/rustls-vs-openssl-performance.html. This works well for rusoto https://github.com/rusoto/rusoto/blob/master/rusoto/core/README.md#usage-with-rustls

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@moderation The OpenSSL error shouldn't be related to differences between nightly and stable Rust. Probably pkg-config is installed on your system, but returns empty CFLAGS and LIBS for openssl package. In that case setting environment variables OPENSSL_STATIC=1 and OPENSSL_INCLUDE_DIR/OPENSSL_LIB_DIR might help. See sfackler/rust-openssl#604 and https://github.com/timberio/vector/blob/master/distribution/docker/alpine/Dockerfile#L10-L13 for an example.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a-rodin thanks for the tip. I'm now able to get through to the linking stage with env OPENSSL_STATIC=1 OPENSSL_INCLUDE_DIR="/usr/include" OPENSSL_LIB_DIR="/usr/lib/x86_64-linux-gnu" cargo build --target x86_64-unknown-linux-musl --release --verbose. I get error: linking with cc failed: exit code: 1 following by a large stack trace / error dump. Closer. Maybe need to wait for newer Rust releases.

Copy link
Author

@ghost ghost Aug 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@moderation I've seen linker crashing when building native rdkafka dependency without rdkafka/cmake_build feature, it is unrelated to Rust at all and caused by linking unused dynamic library for librdkafka. You can try cargo build --target x86_64-unknown-linux-musl --release --verbose --features="default rdkafka/cmake_build", in that case the library that makes C++ linker crash would just not be created at all.

# In order to be able to use the latest stable release with musl,
# we need to backport unreleased changes from this PR:
# https://github.com/libressl-portable/portable/pull/529/files.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the implications of this? Is there any follow up that we might need to do here?

Copy link
Author

@ghost ghost Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

program_invocation_short_name is a global variable that contains short name of the program. It is not a part of standard C library, but a GNU extension. It is provided by musl though. The original code in LibreSSL checks on Linux whether C library is glibc and generates a compile-time error if it is not, even if program_invocation_short_name is available. With the patch the error is not thrown and program_invocation_short_name from musl is used.

Because the issue is fixed in upstream by the mentioned PR, it should be possible to remove this patching after the next release of LibreSSL.

@binarylogic binarylogic added the needs: docs Needs documentation updates label Jul 31, 2019
@binarylogic
Copy link
Contributor

Everything looks great here. I'm going to merge and follow up with documentation changes.

@binarylogic binarylogic merged commit d2df9ba into vectordotdev:master Aug 1, 2019
@binarylogic binarylogic removed the needs: docs Needs documentation updates label Aug 1, 2019
@binarylogic binarylogic mentioned this pull request Aug 2, 2019
@binarylogic binarylogic mentioned this pull request Aug 10, 2019
@ghost ghost mentioned this pull request Feb 15, 2020
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants