[FLI-856] Support Alpine/Musl
markphelps opened this issue ยท 31 comments
Currently the engine is only compiled against GLibc which means the clients will not work in Alpine env
We should create musl compatible versions, targeting x86_64-unknown-linux-musl
and aarch64-unknown-linux-musl
I think there are two paths we could take:
- Continue to bundle these new libs along with the glibc versions so that the native clients (ie
flipt-client-java
) should 'just work' on all architectures - Create new musl/alpine versions of each
flipt-client-x
library, putting the responsibility on the user/integrator to select the correct SDK for their use case.
I'm leaning on 2 as it might be very difficult for us to figure out which version of the fliptengine
Clib to 'load' at runtime when the clients initialize (ie likely not all languages allow you to know which libc
your os supports/requires). Also each additional fliptengine
we package increases the overall library side by avg of 20-30mb (perhaps we can find a way to slim these down though?)
I'd be interested to know what others think, also if there are any examples of other SDKs built with FFI and run on both libc and musl based OSes
From SyncLinear.com | FLI-856
SDKs to Support
- Go (go.flipt.io/flipt-client@musl-vx.x.x)
- Node (now uses wasm)
- Python
- Ruby
- Dart
- Java
See: https://github.com/flipt-io/flipt-client-sdks/actions/runs/7962787687/job/21736987230?pr=140#step:5:1395 for POC of failing test using musl
based docker image
cc @piclemx
@markphelps I would go with the second option also.
What would option 2 look like in practice?
For example, if I import the go
library, would I have to choose between an glibc version or a musl version? That would be somewhat problematic in scenarios where local development environments are glibc, but the production environment is musl.
@markphelps Spent some time looking into this.
I think a good example repo to look at is the confluent-kafka-go library since they use the librdkafka
C-library under the hood. This is also generally true for all their SDKs except for the Java one.
For the Go SDK, it looks like they use Go build tags to support musl vs libc, which seems like a better option than different imports.
If you look at the librdkafka_vendor
directory each included version of the library is 13MB+, so doesn't look like that's a huge issue.
Also, see the build_*.go
files (e.g. build_darwin_amd64.go) for how they use build tags to include the right version of the library. The file platform and arch file suffixes act as implicit build tags that match against GOOS
and GOARCH
.
It seems like the biggest downside to this approach is that it will download all the different pre-built binaries during the go mod download
phase, but the final binary should be statically linked to only the version the platform that you're building for needs. Obviously, it would be nice to make the pre-built libraries smaller to save on download costs, but given that's usually cached, I wouldn't over optimize yet.
Some additional useful reading:
Just checking in on this. We'd love to use the client-side evaluation SDK, but this is a blocker for us (not just musl, but also automatic arch selection).
He @jalaziz sorry for the delay in replying. We can definitely take a look at building a MUSL compatible build and packaging like you mentioned above. Or if you are able to contribute we would ofcourse โค๏ธ that as well!
We are also actively exploring leaning more into WASM, and building a proper WASM impl of the evaluator engine (not just for JS like we have now) and then using WASM runtimes in the various languages we support to call the evaluator. The main benefit of this approach would be that it would be architecture independent.
First one would likely be Go since that's the language we use the most internally. Would this work for you and your team?
He @jalaziz sorry for the delay in replying. We can definitely take a look at building a MUSL compatible build and packaging like you mentioned above. Or if you are able to contribute we would ofcourse โค๏ธ that as well!
We are also actively exploring leaning more into WASM, and building a proper WASM impl of the evaluator engine (not just for JS like we have now) and then using WASM runtimes in the various languages we support to call the evaluator. The main benefit of this approach would be that it would be architecture independent.
First one would likely be Go since that's the language we use the most internally. Would this work for you and your team?
That would work great. Our main use case is Go as well.
I don't know if I'll have time to contribute the changes for the approach outlined above, but I'll definitely try!
I'm implementing Flipt at alpine based Java microservices for my company. Is there some workaround or a roadmap planned for the Java version? This seems to be a big blocker for now
I'm implementing Flipt at alpine based Java microservices for my company. Is there some workaround or a roadmap planned for the Java version? This seems to be a big blocker for now
I can get a Java version of the sdk built that works on musl. Will work on it today and keep you posted
@willyw0nka we just released https://central.sonatype.com/artifact/io.flipt/flipt-client-java-musl
could you try it and let me know if it works for you?
Hi @markphelps , I'm a colleage of @willyw0nka and tried your solution. It still doesn't work for us.
I integrated the dependency and built the project just fine, but when I tried to run it on my development machine (OSX) it throws me these errors.
nested exception is java.lang.UnsatisfiedLinkError: Unable to load library 'fliptengine': dlopen(libfliptengine.dylib, 0x0009): tried: 'libfliptengine.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibfliptengine.dylib' (no such file), '/Users/david/.sdkman/candidates/java/17.0.10-tem/bin/./libfliptengine.dylib' (no such file), '/Users/david/.sdkman/candidates/java/17.0.10-tem/bin/../lib/libfliptengine.dylib' (no such file), '/usr/lib/libfliptengine.dylib' (no such file, not in dyld cache), 'libfliptengine.dylib' (no such file) dlopen(libfliptengine.dylib, 0x0009): tried: 'libfliptengine.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibfliptengine.dylib' (no such file), '/Users/david/.sdkman/candidates/java/17.0.10-tem/bin/./libfliptengine.dylib' (no such file), '/Users/david/.sdkman/candidates/java/17.0.10-tem/bin/../lib/libfliptengine.dylib' (no such file), '/usr/lib/libfliptengine.dylib' (no such file, not in dyld cache), 'libfliptengine.dylib' (no such file) dlopen(/Users/david/Library/Frameworks/fliptengine.framework/fliptengine, 0x0009): tried: '/Users/david/Library/Frameworks/fliptengine.framework/fliptengine' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/david/Library/Frameworks/fliptengine.framework/fliptengine' (no such file), '/Users/david/Library/Frameworks/fliptengine.framework/fliptengine' (no such file) dlopen(/Library/Frameworks/fliptengine.framework/fliptengine, 0x0009): tried: '/Library/Frameworks/fliptengine.framework/fliptengine' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Library/Frameworks/fliptengine.framework/fliptengine' (no such file), '/Library/Frameworks/fliptengine.framework/fliptengine' (no such file) dlopen(/System/Library/Frameworks/fliptengine.framework/fliptengine, 0x0009): tried: '/System/Library/Frameworks/fliptengine.framework/fliptengine' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/System/Library/Frameworks/fliptengine.framework/fliptengine' (no such file), '/System/Library/Frameworks/fliptengine.framework/fliptengine' (no such file, not in dyld cache)
Next thing I tried was to run the app.jar inside our Alpine Linux docker image and the errors are the same as with the regular java version.
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'flagController' defined in URL [jar:file:/app.jar!/BOOT-INF/classes!/org/company/services/nexus/medias/controller/FlagController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'featureFlagService' defined in class path resource [org/company/services/featureflags/config/FeatureFlagConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.company.services.featureflags.service.FeatureFlagService]: Factory method 'featureFlagService' threw exception; nested exception is java.lang.UnsatisfiedLinkError: Error relocating /root/.cache/JNA/temp/jna8663586253287976616.tmp: swapcontext: symbol not found
hope you can help us.
Hey @SultanICQ thanks for the detailed reply!
I think there's two issues going on here:
- for the mac local dev setup, I failed to include the mac version of the libraries (dylibs) in the musl build of our SDK. I'm rectifying that in the next version of the SDK (commit here: a02a947)
- for the alpine version, I think we also copied the wrong library (the glibc version instead of the musl version), this should also be fixed in the next release of the SDK.
I'll be sure to test this as well before declaring the next version good
Hi @markphelps no worries. If you need me to test something dont doubt to reach me.
I just released flipt-client-java-musl-v0.0.2
which should have fixed both issues above โ๐ป
it seems to take a bit for it to make it to maven central however, so maybe can try in 30m or so? Im about to be traveling for the next couple hours but can follow up later today
No worries! :)
I just finished testing it and it worked like a charm in both scenarios. Well done!
I just finished testing it and it worked like a charm in both scenarios. Well done!
Thanks for your patience and for testing @SultanICQ !!
Hi @markphelps, today one of our developers with a windows machine tested the new library and, as everybody would expect, it didn't work because the windows libraries are not included.
Could it be possible for the musl version to add also de win dlls?
Hey @SultanICQ yes I will work on getting a windows version built this week
@SultanICQ does your colleague need 32-bit or 64-bit Windows support?
oh nevermind i see x86-64 in the stack trace
Work being done over ๐๐ป #433
Hey @SultanICQ we just released 0.2.0-rc.1
that should have Windows support. I say should because I cant easily test since I don't have a Windows machine. We're working on getting more comprehensive testing after release via GitHub Actions though.
Can you have your coworker try 0.2.0-rc.1
on Windows? Thanks!
Hi @markphelps !
I'll prepare a test for my colleage to try. I assume she'll do the test on monday at the earliest.
I keep you posted.
Hi @markphelps ,
I deleted my previous message, maybe you have received an email from github.
With Windows now there is no library error but now there is a subtle error with the evaluateBoolean response. I see that now is not returning a Result.
So I think that overall it works but I have to adapt my code with this method signature change.
I keep you posted.
Hi @markphelps ,
I deleted my previous message, maybe you have received an email from github.
With Windows now there is no library error but now there is a subtle error with the evaluateBoolean response. I see that now is not returning a Result.
So I think that overall it works but I have to adapt my code with this method signature change.
I keep you posted.
Yes apologies we simplified the response type by not returning the wrapped Result type re: #353
Since these are pre 1.0.0 releases we decided to bump the minor version for this change in method signature.. sorry about that
Well, all good and working fine with this new version from my side.
Nice work!!
@jalaziz @piclemx we just released
flipt-client-go@musl-v0.0.1
, see #332 for detailsCan you try:
go install go.flipt.io/flipt-client@musl-v0.0.1
It should support both x86 and arm on Linux
Just getting back to this, but unfortunately a musl vs glibc dependency won't work for us long term. While all our built docker images are Alpine, we generally test locally on our machines which use glibc. It would be great if we could install one dependency that is universal.
As I mentioned in a previous post, that should be doable with Go and build tags. Yes that means downloading both the musl and glibc libraries when building, but I don't think that's a major issue.
Gotcha. I'll create a universal version to try this approach and ping you once ready