| ▲ | bmitch3020 6 hours ago |
| I've seen countless attempts to replace "docker build" and Dockerfile. They often want to give tighter control to the build, sometimes tightly binding to a package manager. But the Dockerfile has continued because of its flexibility. Starting from a known filesystem/distribution, copying some files in, and then running arbitrary commands within that filesystem mirrored so nicely what operations has been doing for a long time. And as ugly as that flexibility is, I think it will remain the dominant solution for quite a while longer. |
|
| ▲ | kccqzy 2 hours ago | parent | next [-] |
| > But the Dockerfile has continued because of its flexibility. The flip side is that the world still hasn’t settled on a language-neutral build tool that works for all languages. Therefore we resort to running arbitrary commands to invoke language-specific package managers. In an alternate timeline where everyone uses Nix or Bazel or some such, docker build would be laughed out of the window. |
| |
| ▲ | muvlon 2 hours ago | parent | next [-] | | As a Nix evangelist, I have to say: Nix is really not capable of replacing languag-specific package managers. > running arbitrary commands to invoke language-specific package managers. This is exactly what we do in Nix. You see this everywhere in nixpkgs. What sets apart Nix from docker is not that it works well at a finer granularity, i.e. source-file-level, but that it has real hermeticity and thus reliable caching. That is, we also run arbitrary commands, but they don't get to talk to the internet and thus don't get to e.g. `apt update`. In a Dockerfile, you can `apt update` all you want, and this makes the build layer cache a very leaky abstraction. This is merely an annoyance when working on an individual container build but would be a complete dealbreaker at linux-distro-scale, which is what Nix operates at. | | |
| ▲ | kccqzy an hour ago | parent | next [-] | | Fundamentally speaking, the key point is really just hermeticity and reliable caching. Running arbitrary commands is never the problem anyways. What makes gcc a blessed command but the compiler for my own language an "arbitrary" command anyways? And in languages with insufficient abstraction power like C and Go, you often need to invoke a code generation tool to generate the sources; that's an extremely arbitrary command. These are just non-problems if you have hermetic builds and reliable caching. | |
| ▲ | poly2it 40 minutes ago | parent | prev [-] | | Well, arbitrary granularity is possible with Nix, but the build systems of today simply do not utilise it. I've for example written an experimental C build system for Nix which handles all compiler orchestration and it works great, you get minimal recompilations and free distributed builds. It would be awesome if something like this was actually available for major languages (Rust?). Let me know if you're working on or have seen anything like this! |
| |
| ▲ | brightball 2 hours ago | parent | prev [-] | | Reminds me of the “Electric cars in reverse” video where the guy envisions a world where all vehicles are electric and tries to make the argument for gas engines. | | |
|
|
| ▲ | __MatrixMan__ 5 hours ago | parent | prev | next [-] |
| There are some hurdles preventing that flow from achieving reproducible builds. As the bad guys get more sophisticated, it's going to become more and more important that one party can say "we trust this build hash" and a separate party to say "us too". That's not going to work if both parties get different hashes when they build the image, which won't happen as long as file modification timestamps (and other such hazards) are part of what gets hashed. |
| |
| ▲ | bmitch3020 an hour ago | parent [-] | | Recent versions of buildkit have added support for SOURCE_DATE_EPOC. I've been making the images reproducible before that with my own tooling, regctl image mod [1] to backdate the timestamps. It's not just the timestamps you need to worry about. Tar needs to be consistent with the uid vs username, gzip compression depends on implementations and settings, and the json encoding can vary by implementation. And all this assumes the commands being run are reproducible themselves. One issue I encountered there was how alpine tracks their package install state from apk, which is a tar file that includes timestamps. There are also timestamps in logs. Not to mention installing packages needs to pin those package versions. All of this is hard, and the Dockerfile didn't make it easy, but it is possible. With the right tools installed, reproducing my own images has a documented process [2]. [1]: https://regclient.org/cli/regctl/image/mod/ [2]: https://regclient.org/install/#reproducible-builds |
|
|
| ▲ | miladyincontrol 5 hours ago | parent | prev | next [-] |
| The lack of docker registry-like solutions really does seem to be the chokepoint for many alternatives. Personally I love using mkosi and while it has all the composability and deployment options I'd care for, its clear not everyone wants to build starting only with a blank set of OS templates. |
|
| ▲ | whateveracct 5 hours ago | parent | prev | next [-] |
| Nix is exceptionally good at making docker containers. |
| |
| ▲ | stabbles 2 hours ago | parent | next [-] | | Does Nix do one layer per dependency? Does it run into >=128 layers issues? In Spack [1] we do one layer per package; it's appealing, but I never checked if besides the layer limit it's actually bad for performance when doing filesystem operations. [1] https://spack.readthedocs.io/en/latest/containers.html | |
| ▲ | Spivak 4 hours ago | parent | prev | next [-] | | Yes but then you're committed to using Nix which doesn't work so well the moment you need some software not packaged by Nix. Want to throw a requirements.txt in there? No no, why would you even ask that? Meanwhile docker says yeah sure just run pip install, why should I care? | | |
| ▲ | okso 4 hours ago | parent | next [-] | | LLMs are getting very good at packaging software using Nix. | | |
| ▲ | mort96 3 hours ago | parent | next [-] | | Then you're committing to maintaining a package for that software. Like all LLM boosters, you've ignored the fact that the largest time sink in many kinds of software is not initial development, but perpetual maintenance. | |
| ▲ | CuriouslyC 4 hours ago | parent | prev [-] | | This. I wouldn't have touched Nix when you needed someone who was really good at Nix to keep it working, but agents make it viable to use in a number of place. |
| |
| ▲ | gnull 2 hours ago | parent | prev | next [-] | | Packaging for nix is exceptionally easy once you learn it. And once something is packaged, it's solved for all, it's not going to randomly break. If you care about getting it to work with minimal effort right now more thar about it being sustainable later, then sure. | |
| ▲ | nothrabannosir 2 hours ago | parent | prev | next [-] | | Nix doesn't make sense if all you're going to use it for is building Docker images. It only makes sense if you're all in in the first place. Then Docker images are free. | |
| ▲ | whateveracct 4 hours ago | parent | prev [-] | | I use software from pretty much every language with Nix. And I package it myself too when needed. Including Python often :) |
| |
| ▲ | mikepurvis 4 hours ago | parent | prev [-] | | Especially if you use nix2container to take control over the layer construction and caching. |
|
|
| ▲ | zbentley 6 hours ago | parent | prev | next [-] |
| > the Dockerfile has continued because of its flexibility I wish we had standardized on something other than shell commands, though. Puppet or terraform or something more declarative would have been such a better alternative to “everyone cargo cults ‘RUN apt-get upgrade’ onto the top of their dockerfiles”. Like, the layer/stage/caching behavior is fine. I just wish the actual execution parts had been standardized using something at a higher level of abstraction than shell. |
| |
| ▲ | bheadmaster 6 hours ago | parent | next [-] | | > Puppet or terraform or something more declarative would have been such a better alternative Until you need to do something that isn't covered with its DSL, and you extend it with an external command execution declaration... At which point people will just write bash scripts anyway and use your declarative language as a glorified exec. | | |
| ▲ | sofixa 4 hours ago | parent [-] | | If you have 90-95% of everyone's needs (installing packages, compiling, putting files) covered in your DSL, and it has strong consistency and declarativeness, it's not that big of a problem if you need an escape hatch from time to time. Terraform, Puppet, Ansible, SaltStack show this pretty well, and the vast majority of them that isn't bash scripts is better and more maintainable than their equivalents in pure bash would be. | | |
| ▲ | bheadmaster 25 minutes ago | parent [-] | | The problem is, ironically, that each DSL has its own execution platform, and is not designed for testability. Bash scripts may be hard to maintain, but at least you can write tests for them. In Azure YAML I had an odd bug because I used succeeded() instead of not(failed()) as a condition. I had no way of testing the pipeline without executing it. And each DSL has its own special set of sharp edges. At least Bash's common edges are well known. |
|
| |
| ▲ | avsm 6 hours ago | parent | prev | next [-] | | Docker broke out the build layer into a separate component called BuildKit (see HN discussion recently https://news.ycombinator.com/item?id=47166264). However, Dockerfiles are so popular because they run shell commands and permit 'socially' extending someone else shell commands; tacking commands onto the end of someone else's shell script is a natural process. /bin/sh is unreasonably effective at doing anything you need to a filesystem, and if the shell exposes a feature, it has probably been used in a Dockerfile somewhere. Every other solution, especially declarative ones, tend to come up short when _layering_ images quickly and easily. However, I agree they're good if you control the entire declarative spec. | |
| ▲ | mihaelm 6 hours ago | parent | prev | next [-] | | I'd say LLB is the "standard", Dockerfile is just one of human-friendly frontends, but you can always make one yourself or use an alternative. For example, Dagger uses BuildKit directly for building its containers instead of going through a Dockerfile. | |
| ▲ | mort96 3 hours ago | parent | prev | next [-] | | Dockerfile has the flexibility to do what you want though, no? Use a base image with terraform or puppet or opentofu or whatever pre-installed, then your Dockerfile can just run the right command to apply some declarative config file from the build context. And if you want something weird that's not supported by your particular tool of choice, you have the escape hatch of running arbitrary commands in the Dockerfile. What more do you want? | |
| ▲ | cpuguy83 an hour ago | parent | prev | next [-] | | Give https://github.com/project-dalec/dalec a look.
It is more declarative. Has explicit abstractions for packages, caching, language level integrations, hermetic builds, source packages, system packages, and minimal containers. Its a Buildkit frontend, so you still use "docker build". | |
| ▲ | harrall 4 hours ago | parent | prev | next [-] | | Declarative methods existed before Docker for years and they never caught on. They sounded nice on paper but the work they replaced was somehow more annoying. I moved over to Docker when it came out because it used shell. | |
| ▲ | toast0 5 hours ago | parent | prev | next [-] | | Oof, not terraform please. If you use foreach and friends, dependency calculations are broken, because dependency happens before dynamic rules are processed. I'd get much better results it I used something else to do the foreach and gave terraform only static rules. | |
| ▲ | esseph 5 hours ago | parent | prev [-] | | The more you try and abstract from the OS, the more problems you're going to run into. | | |
| ▲ | zbentley 3 hours ago | parent [-] | | Bash is pretty darn abstracted from the OS, though. Puppet vs Bash is more about abstraction relative to the goal. If your dockerfile says “ensure package X is installed at version Y” that’s a lot clearer (and also more easy to make performant/cached and deterministic) than “apt-get update; apt-get install $transitive-at-specific-version; apt-get install $the-thing-you-need-atspecific-version”. I’m not thrilled at how distro-locked the shell version makes you, and how easy it is for accidental transitive changes to occur too. But neither of those approaches is at a particularly low abstraction level relative to the OS itself; files and system calls are more or less hidden away in both package-manager-via-bash and puppet/terraform/whatever. |
|
|
|
| ▲ | phplovesong 4 hours ago | parent | prev [-] |
| You can pretty much replace "docker build" with "go build". But as long as people want to use scripting languages (like php, python etc) i guess docker is the neccessary evil. |
| |
| ▲ | garganzol 4 hours ago | parent | next [-] | | Go is just one language, while Dockerfile gives you access to the whole universe with myriads of tools and options from early 1970s and up to the future. I don't know how you can compare or even "replace" Docker with Go; they belong to different categories. | |
| ▲ | well_ackshually 3 hours ago | parent | prev | next [-] | | >You can pretty much replace "docker build" with "go build". I'll tell that to my CI runner, how easy is it for Go to download the Android SDK and to run Gradle? Can I also `go sonarqube` and `go run-my-pullrequest-verifications` ? Or are you also going to tell me that I can replace that with a shitty set of github actions ? I'll also tell Microsoft they should update the C# definition to mark it down as a scripting language. And to actually give up on the whole language, why would they do anything when they could tell every developer to write if err != nil instead Just because you have an extremely narrow view of the field doesn't mean it's the only thing that matters. | |
| ▲ | osigurdson 4 hours ago | parent | prev | next [-] | | In some situations, yes, others no. For instance if you want to control memory or cpu using a container makes sense (unless you want to use cgroups directly). Also if running Kubernetes a container is needed. | | |
| ▲ | matrss 4 hours ago | parent [-] | | You have to differentiate container images, and "runtime" containers. You can have the former without the latter, and vice versa. They are entirely orthogonal things. E.g. systemd exposes a lot of resource control as well as sandboxing options, to the point that I would argue that systemd services can be very similar to "traditional" runtime containers, without any image involved. |
| |
| ▲ | yunwal 3 hours ago | parent | prev | next [-] | | > You can pretty much replace "docker build" with "go build". Interesting. How does go build my python app? | |
| ▲ | aobdev 4 hours ago | parent | prev | next [-] | | Wasn’t this the same argument for .jar files? | |
| ▲ | speedgoose 4 hours ago | parent | prev [-] | | It doesn't sound like Golang is going to dominate and replace everything else, so Docker is there to stay. |
|