Skip to content
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

Add Alpine compatibility #194

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

nil-malh
Copy link

@nil-malh nil-malh commented Mar 2, 2025

Alpine Compatibility: Static Linking with musl libc

Issue

When running the kafkactl native-image in Alpine Docker containers, it produces segmentation faults due to symbol incompatibilities between glibc and musl:

/# kafkactl
Error relocating /usr/local/bin/kafkactl: __strdup: symbol not found
Error relocating /usr/local/bin/kafkactl: __strtok_r: symbol not found
Error relocating /usr/local/bin/kafkactl: __isnan: symbol not found
Error relocating /usr/local/bin/kafkactl: __isnanf: symbol not found
Segmentation fault

Solution

This PR modifies our build process to create statically linked binaries with musl libc for Linux builds. This allows our application to run correctly in Alpine and other musl-based environments.

Specifically:

  1. Updated GitHub Actions workflows to install the correct musl cross-compiler (version 10.2.1) for Linux builds
  2. Modified the build commands to use --static --libc=musl options with GraalVM's native-image
  3. Configured proper toolchain paths with --gcc-toolchain=/usr/local/musl/x86_64-linux-musl-native

Changes

  • Updated Release workflow for static musl builds on Linux
  • Updated Pull Request workflow for static musl builds on Linux
  • Updated Push Main workflow for static musl builds on Linux
  • Added environment variable configuration to ensure proper compiler usage

Testing

The changes have been tested by building the application and running it in an Alpine container to verify that the segmentation faults no longer occur.

image

References

@loicgreffier loicgreffier changed the title [DRAFT] - Alpine Compatibility Add Alpine Compatibility Mar 17, 2025
@loicgreffier
Copy link
Collaborator

loicgreffier commented Mar 17, 2025

@nil-malh Build is currently failing for Windows and MacOS. I think it is related to buildArgs.add("--static") and buildArgs.add("--libc=musl") from the build.gradle, isn't it?

Args are already defined in GitHub Actions: gradlew nativeCompile -PnativeArgs="--static --libc=musl --gcc-toolchain=/usr/local/musl/x86_64-linux-musl-native"

Do we need to have them in the build.gradle?

@loicgreffier loicgreffier changed the title Add Alpine Compatibility Add Alpine compatibility Mar 17, 2025
@loicgreffier loicgreffier added the feature This issue adds a new feature label Mar 17, 2025
@loicgreffier
Copy link
Collaborator

loicgreffier commented Mar 17, 2025

@nil-malh Note that starting from Alpine v3.13, after installing apk add gcompat, I don't have issue running Kafkactl:

/test # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.13.12
PRETTY_NAME="Alpine Linux v3.13"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"

/test # ./kafkactl-1.14.0-linux-amd64 -V
Version 1.14.0

@nil-malh
Copy link
Author

nil-malh commented Mar 18, 2025

Args are already defined in GitHub Actions: gradlew nativeCompile -PnativeArgs="--static --libc=musl --gcc-toolchain=/usr/local/musl/x86_64-linux-musl-native"

Do we need to have them in the build.gradle?

Absolutely it was some leftovers from my testing on my machine I'll remove it from the build.gradle :).

@nil-malh
Copy link
Author

@nil-malh Note that starting from Alpine v3.13, after installing apk add gcompat, I don't have issue running Kafkactl:

Thanks for bringing this up ! I was unaware of gcompat thanks !
Regarding our approach, I believe both options are valid :

  1. Native musl binary: Provides the best performance and alignment with Alpine's philosophy. It eliminates dependencies to the C library not matter the implementation. But changes the way we build Kafkactl at the moment.

  2. Gcompat approach: Offers simplicity for users - they just install gcompat and run our existing binary. This is more pragmatic for users who may not be Alpine experts, though it adds runtime overhead and extra dependencies.

What do you want to do going forward ?

@loicgreffier
Copy link
Collaborator

@nil-malh It’s always better if the CLI works out of the box without additional installation. However I need to do some tests and answer the following questions:

  • What is the cost of embedding Musl: how many additional KiB, MiB in the CLI
  • With Musl embedded, does it still work fine on Ubuntu LTS (20, 22, 24) or would be need to provide an additional version of Kafkactl like kafkactl-1.14.0-linux-musl-amd64

@nil-malh
Copy link
Author

  • What is the cost of embedding Musl: how many additional KiB, MiB in the CLI
    The size impact of embedding Musl in the CLI is remarkably small - we're talking about an additional footprint of just a few hundred KiB in most cases. Musl is specifically designed to be lightweight and efficient

On the latest version of Kafkactl at date 0.14.0 : non musl-c : 87mb with muslc : 87.35mb

  • With Musl embedded, does it still work fine on Ubuntu LTS (20, 22, 24) or would be need to provide an additional version of Kafkactl like kafkactl-1.14.0-linux-musl-amd64

AFAIK, I am currently using a static build of Kafkactl inside my Ubuntu WSL and I don't have any issues at all !

@loicgreffier
Copy link
Collaborator

loicgreffier commented Mar 22, 2025

@nil-malh

I've tested static linking to Musl on latest Alpine, latest Debian, latest Ubuntu, Ubuntu 22.04 LTS and Ubuntu 20.04 LTS. Looks good.

I've open #203 to guide you through my following suggestions:

  • Our GraalVM GitHub Action can magically install Musl (Cf. https://github.com/marketplace/actions/github-action-for-graalvm#options native-image-musl option). Thus, we can omit the manual installation steps. It has to be activated only on Linux runners.
  • Unless I miss something, the -PnativeArgs= option seems not working. I've set buildArgs in the build.gradle, conditioned by a property. Looks good and easier to run static Musl build on localhost.

I think we can keep providing a single Kafkactl, statically linked to Musl. Some other apps are doing this as well which confirms my decision.

👉 I let you do a first update of your PR

@ThomasCAI-mlv We might need to discuss about moving to Alpine:

baseImage("ubuntu:22.04")
(@nil-malh ignore this is this PR for now)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature This issue adds a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Kafkactl cannot be executed in a Alpine Docker image
2 participants