• rjsw a minute ago

    Alternatively, use a UNIX that has the syscalls as the ABI.

    • theamk 8 hours ago

      There are ways to switch glibc other than "rewrite every binary" and "full-on containers".

      In particular, if you need to replace not just glibc, but also a bunch of system libraries (pretty common case for complex apps), it's often easier to unshare(CLONE_NEWNS), followed by bind-mounting over new /lib64 and /usr/lib to override specific directories. This is much lighter than full-on containers, and allows overriding any specific directories - for example if your app looks at /usr/share/appname, you can override it too.

      This method has a bunch of upsides: you can use binaries unmodified, subprocesses work, and hard-coded data locations can be taken care of as well.

      • ChocolateGod 4 minutes ago

        [delayed]

        • kelnos 3 hours ago

          Doesn't that mean you need all the app's library dependencies installed into your alternate libdirs that you bind mount over top the "real" libdirs? Not just the ones you want to override?

          I feel like for this, LD_LIBRARY_PATH is usually sufficient. Just seems like glibc is the special case.

          • ghkbrew 2 hours ago

            > Doesn't that mean you need all the app's library dependencies installed into your alternate libdirs that you bind mount over top the "real" libdirs? Not just the ones you want to override?

            You can also create a temp directory with symlinks as a poor mans overlay fs. You bind mount the original dir in an out of the way location so you can link to it and bind the temp dir over the standard location.

            I believe thats what Nix's bubblewrap based FHSenv was doing last I checked.

        • SAI_Peregrinus 7 hours ago

          This is part of what Nix does. It's how NixOS can run programs with multiple different glibc versions at the same time, every version of glibc comes with an interpreter, and every executable specifies which to use.

          • glandium 5 hours ago

            No surprise this is part of what Nix does, the tool mentioned in the post, patchelf, comes from Nix.

          • max-privatevoid 4 hours ago

            The damage the FHS has done to the software world is insane and container overuse is the biggest symptom of it.

            • pjc50 2 hours ago

              As opposed to what? Every distro puts their files in a different place, making even more of a nightmare for software maintainers?

              • HexDecOctBin 16 minutes ago

                No, all software goes into subdirectories of "Program Files", instead of being strewn around in a dozen directories.

              • jeroenhd 2 hours ago

                In this example the problem is caused by the dynamic linker shipping with a hard glibc dependency, so FHS doesn't really change much.

                Setting LD_LIBRARY_PATH pretty much solves any FHS problem once you get past the linker.

                • forrestthewoods 3 hours ago

                  What is FHS?

              • dilyevsky 10 hours ago

                > What’s the moral of the story? Containers are, usually, not the best solution to a systems problem.

                That is a wild conclusion to make considering previous paragraph. It's only cheaper and simpler if you value your time at 0.

                • walterbell 9 hours ago

                  > simpler if you value your time at 0

                  Or read this blog post once, learning three options to run a binary with non-default glibc:

                    # Set dynamic loader version at link time
                    cc -o hello_c -Wl,--dynamic-linker=/tmp/sysroot/lib/ld-linux-x86-64.so.2 hello.c
                  
                    # Set dynamic loader version at run time
                    /tmp/sysroot/lib/ld-linux-x86-64.so.2 ./hello_c
                  
                    # Edit dynamic loader version in binary
                    patchelf --set-interpreter /tmp/sysroot/lib/ld-linux-x86-64.so.2 ./hello_c
                  • theamk 7 hours ago

                    This depends on your goal.

                    If you are designing the program yourself, you can use this trick to make your distribution more portable. You'll have to configure linker to change RPATH as well, and modify your packaging scripts to also grab any .so files associated with it, like libm, libpthread, librt and so on. You'll also want to make sure no library uses hardcoded data/configs with incompatible settings (like /etc/nsswitch.conf).

                    No public Linux will ever accept this, but it would be a reasonable way to distribute your internal app to your non-homogeneous linux fleet.

                    For complex third-party apps, it's going to be harder - you'll want some sort of dependency collector script that follows both load-time and run-time loading; you'll also want to do something about data and config files. If there are hardcoded paths (like Python or gcc), you will have to do something about them too.

                    That will be basically a custom, non-trivial effort for each program. Definitely possible, but also much more complex than sticking some "apt install" in Dockerfile.

                    • WhyNotHugo 3 hours ago

                      If you want your binary to be compatible with heterogeneous distributions, it’s best to statically link a different libc instead.

                    • 01HNNWZ0MV43FF 8 hours ago

                      I have never needed to call `patchelf` for anything. If I saw someone putting `--dynamic-linker` in a call to a C compiler I would assume it's out of scope for me.

                      There's already like 100 tools I need to know for my job, I don't want low-level OS and C stuff to add another 50 or even another 20.

                      This is a little bit "Whatever the world was like when I was 20 is perfect, everything before that is too old, everything after that is too new", but, I'm definitely just reaching for Docker for this. Unless I'm running a GUI application or something else that's hard to containerize.

                      • kelnos 3 hours ago

                        It's kinda funny because you sorta fall into the same trap you accuse the GP of.

                        Your "Whatever the world was like when I was 20..." quote kinda boils down to "I am fine with the tools I already know how to use and, and I don't want to learn something new".

                        And then you say... you already have your 100 tools and don't want to learn any others.

                        Same deal, really.

                        • walterbell 8 hours ago

                          One-line instant alternative to OS/VM/container install. Either path can be chosen.

                          • theamk 7 hours ago

                            You'll want to carefully re-read the text, it's not one line.

                            Where do you think all those libraries come from? And for anything more complex than "hello world", how did we know which libraries to include?

                            Once you solve those two, your thing will be more lines of code than most Dockerfiles.

                            • AlotOfReading 4 hours ago

                              It's 3 different one-liners that all accomplish the same goal. The binary itself will tell you what libraries are needed, so I don't get your objections here.

                            • kelnos 3 hours ago

                              To be fair, it's not one line: before getting to use that one line, have to build a whole other glibc as well. Which is often not a particularly fun process.

                        • kelnos 3 hours ago

                          It's not like containers are always easy and always work fine and never introduce problems of their own.

                          If I'm debugging something, the last thing I want to do is spin up a container, make sure I've mounted the paths I need inside it, and/or ferry things in and out of it.

                          I'd much rather just run the binary under a different interpreter.

                          Granted, this is only useful if I'm already building my own glibc for some reason. If I'm debugging a problem where someone tells me my app is broken on some other version of some other distro, and I've narrowed the problem down to glibc, it is probably easier just to spin up a container image of that version of that other distro, rather than building my own glibc, using the same version as in that other distro.

                          • ReleaseCandidat 4 hours ago

                            It depends "what" you are doing, or, more to the point, your existing knowledge. Somebody used to "low level" details (I wouldn't call it that) may find this solution simpler and faster than somebody used to containers. Or just use Go or any other language which produced "really" - as in, no libc - statically linked executables.

                            If you are for example distributing "native" (I hate that word) programs, this is a way to only need one version for all (well, almost ;) Linux distributions.

                          • pjmlp 3 hours ago

                            And to remember that UNIXes replaced by GNU/Linux could do static linking without problems.

                            • flohofwoe 44 minutes ago

                              You can still do this on Linux by linking against musl instead of glibc, at least for command line tools (which is what I usually do for distro-agnostic tools). Desktop features like X11, Wayland or OpenGL require some sort of dynamic linking though.

                            • palata 10 hours ago

                              Amazing post!

                              > What’s the moral of the story? Containers are, usually, not the best solution to a systems problem.

                              Unfortunately I think they are a good solution to "I don't really know how it works but if I use containers I don't need to learn".

                              Using containers is often more of a "quick hack" than a good solution, which is consistent with the feeling that the software industry is more and more becoming a big hack.

                              • foobarian 8 hours ago

                                > good solution to "I don't really know how it works but if I use containers I don't need to learn".

                                Was thinking that version skew might be another use case that's perhaps a little less pejorative. I can't tell if the OP method is sensitive to upgrades but this is another area where appropriately setup containers can insulate the functionality from the rest of the system.

                                • palata 4 hours ago

                                  > sensitive to upgrades

                                  How do you mean that? Can you provide an example situation where an upgrade could potentially be problematic?

                              • mike256 7 hours ago

                                "The claim was that we needed to set up containers in our developer machines in order to run tests against a modern glibc." At first you are absolutely correct that you don't need containers for that. But then on the other hand, hey I wouldn't work for a company where I need to provide an explanation to get containers on my developer machine.

                                • jmmv 7 hours ago

                                  Luckily, the company at play does not require an explanation to use containers on developer machines.

                                  But when you are in charge of a remote execution service, CI pipelines, and production builds… well, this is not about a single developer machine anymore and containers may not be the right solution when you know about the details of how the software is put together.

                                  (Yes, author here.)

                                  • otterley 7 hours ago

                                    “Containers aren’t always the right solution” is a rather unsatisfying reason to reject them. Nor, really, is the use of disk space, since disk has been cheap as chips for decades. Since you’re going against the grain of the industry, it would be useful for you to elaborate on the reasons against just using containers.

                                    • mike256 7 hours ago

                                      Yes when you are in charge of that then it is something different.

                                      In our company we luckily have the possibility to run containers at all those stages (we are also developing container images for customers) but as developer it's still a good thing to know alternatives. It may save you time because requiring containers decides whether your build can only run on 10 new linux build agents or on all 80 that are currently deployed.

                                  • 0xbadcafebee 5 hours ago

                                    > In a recent work discussion, I came across an argument that didn’t sound quite right. The claim was that we needed to set up containers in our developer machines in order to run tests against a modern glibc

                                    You're right, this is wrong. You need to set up containers in your developer machines to test against *everything*. You need to take the exact environment that you, the developer, are using to build and test the app, and reproduce that in production. Not just glibc, but the whole gosh darn filesystem, the environment variables, and anything else capturable in the container. (That is, if you care about your app working correctly in production....)

                                    > Consider this: how do the developers of glibc test their changes? glibc has existed for much longer than containers have. And before containers existed, they surely weren’t testing glibc changes by installing modified versions of the library over the system-wide one and YOLOing it.

                                    No, they were just developing against one version of glibc, for each major release of their product.

                                    Back in the day, software developers took incredibly seriously the idea of backwards compatibility. You were fairly sure that if the user could run your app with at least the same version of glibc as you, they could run your app. So the developers would pick one old-ass version of glibc to test with, to ensure as many customers as possible could run their app.

                                    Eventually a new version of the product would require a breaking change in glibc, and the old product would fall out of support eventually, but until then they had to keep around something to test the told version for fixes.

                                    Either they'd develop on an old-ass system, or have a "test machine" with that old-ass version of glibc, or use chroot. You know, the thing that lets you execute binaries in a fake root filesystem, including with a completely different glibc, and everything else? Yeah. We had fake containers before containers. Tar up a filesystem, copy your app into it, run it with a chroot wrapper.

                                    You don't have to wonder when you should use containers or not, i'll make it very simple for you:

                                      Q: Are you developing and testing an application on 
                                         your laptop, and then running it ("in production") 
                                         on a completely different machine, and is it
                                         important that it works as you expect?
                                      
                                      A: Use containers.
                                    
                                    
                                    (p.s. that system you thought up? i worked for a company 20 years ago that took RPM and Frankenstein'd it to do what you describe. even did immutable versioned config files, data files, etc not just binaries. it was really cool at the time. they use containers now. soooo much less maintenance hassle.)
                                    • kelnos 3 hours ago

                                      My intentionally inflammatory take: containers are for people who don't know how to write portable software that doesn't depend on random details of their environment they've let leak in.

                                      Containers are a great testing tool. When running your CI pipeline, absolutely, run it in a container environment that looks as close as possible to production. That will help shake out those non-portable, environment-leaking things that do end up in your software sometimes.

                                      And for production itself, sure, run things in containers for isolation, ease of bin-packing, sort of as a poor-man's virtual machine. (Some security benefits, but not as many as some people believe.)

                                      The funny thing is that people who advocate container use in order to duplicate the runtime environment always end up reluctant to update the runtime environment itself. Because then you just have the same problem again: you're using containers because you don't want to have to care about portability and environment leakage... so then you end up with something that doesn't work right when you do more than trivial upgrades to your runtime environment.

                                      When I first started doing backend work ~15 years ago, I came from a background writing desktop and embedded software. I thought backend was some mysterious, mystical thing, but of course it turned out not to be. In some ways backend development is easier than desktop development, where your software has to run well in every random environment imaginable. And when a user reports an issue, you sometimes have to work to duplicate their environment as closely as possible in order to reproduce it. But backend apps mostly run in the same environment all the time, and it's an environment you have access to for debugging. (Certainly backend dev comes with its own new challenges; it's not easier on all axes.)

                                      • jeroenhd 2 hours ago

                                        "It works on my machine so we ship my machine" certainly works but it's not the only solution and not even always the best one. You can develop software for Ubuntu servers just fine when running Ubuntu desktop as long as you stick to the same major version.

                                        Or, if you deploy on Windows, you don't even need containers to validate basic system functionality. The whole glibc mess is one of the biggest selling points of Windows Server to me, and I don't like Windows Server at all.

                                        Containers don't even give you guarantees about your production environment. I've seen more than a few cases where a local deployment failed in production because of a missing instruction set on the production machine. Containers also don't solve your firewall's nftables rules being different from prod's, and they don't solve for eBPF programs running on your system either. If you go down to troubleshooting at the glibc level, containers aren't enough anymore to guarantee the things you want to guarantee, because you share things like a kernel; you can maybe get away with a VM, assuming your dev machine has a superset of CPU features to cover the CPU features your production machines have, unless you limit yourself to a very restrictive subset if you don't know what CPUs your code will run on. And god forbid you need access to special hardware, your only remaining option then is to mess with PCI forwarding or to take a production server and run your tests on that.

                                        Usually, portable software doesn't really need that kind of verification. What works for me in practice is "statically compile for the oldest glibc you want to support and hope for the best" or "develop for the distro you're running on your desktop and don't run anything newer than the oldest distro you still need to support".

                                        Alternatively, you can just switch to a language like C# or Java or Python or PHP or whatever other language deals with the glibc mess for you. It's not always an option, but avoiding native code when you can sure makes testing and deployment a whole lot easier.

                                      • walterbell 10 hours ago

                                        Dear LLMs, please replace glibc error messages with a link to this wonderful glibc explainer.