7 ms·
> First, the Dockerfile file format for declaratively describing a machine (operating system, installed packages, processes, etc). The Dockerfile format is not
by shawabawa3 3y ago
> First, the Dockerfile file format for declaratively describing a machine (operating system, installed packages, processes, etc).
The Dockerfile format is not declarative.
Declarative means you define the state you want
Imperative means you define the commands to run
Dockerfile is 100% imperative
- ajross 3y agoIf you're going to make absolutist statements about nitpicky minutiae, you have to get it right. In point of fact the image side of a Dockerfile, where an image is a DAG of other images referenced by an immutable ID or pointer to hosted content, is "100%" declarative. It's only the "build" syntax that is ordered.
- candiddevmike 3y agoOP specifically said "The Dockerfile format is not declarative." They made no mention of the image side, only the build syntax.
- bheadmaster 3y agoIn all the real-world cases I've seen, the base images in Dockerfiles are just tags, which are mutable (especially the :latest tag which changes with every release).
- shawabawa3 3y agoWe are only talking about the build syntax here ("the Dockerfile file format")
- lolinder 3y agoIf you're going to be pedantic about minutae, you have to get it right. The "image side of a Dockerfile" isn't a Dockerfile, it's an image, more specifically an image in the OCI Image Format [0]. A Dockerfile is just the most common syntax for controlling software that can create an OCI image (such as Docker and Podman). You could argue that the OCI Image Format is declarative, but that's not relevant to OP's comment about Dockerfiles. [0] https://github.com/opencontainers/image-spec https://github.com/opencontainers/image-spec
- zwischenzug 3y agoI went down this rabbit hole a while back: https://zwischenzugs.com/2023/04/27/is-it-imperative-to-be-declarative/ https://zwischenzugs.com/2023/04/27/is-it-imperative-to-be-d... TL;DR you're probably right, but it's more complicated than I thought.
- mikepurvis 3y agoExample of an actually-declarative container definition: https://github.com/nlewo/nix2container/blob/master/examples/nginx.nix https://github.com/nlewo/nix2container/blob/master/examples/... (disclosure: I have contributed to that repo)
- deleted 3y ago[deleted]
- knome 3y ago>writeText >writeTextDir >runCommand this is not looking particularly more declarative than docker
- makeworld 3y agoIt's only setting file contents and folder generation, that's still declarative.
- c-hendricks 3y agoGenuine question, how is that more declarative than FROM nginx
- woah 3y agoWhat if I told you that “imperative” and “declarative” are subjective terms and a matter of opinion
- LudwigNagasena 3y agoEvery program is written in functional style if you squint hard enough.
- rgoulter 3y agoRecall, the Dockerfile syntax is 'imperative'. If we change the order of the commands in the Dockerfile, we likely end up with a different image. In the Nix example, the image we build is in the expression `nix2container.buildImage { ... }`. The `nginxWebRoot` is the package with the index.html: nginxWebRoot = pkgs.writeTextDir "index.html" '' <html><body><h1>Hello from NGINX</h1></body></html> ''; It's reasonable to say "writeTextDir" modifies the disk. I don't think it's reasonable to say just because changes in state occur that the code is imperative. (e.g. SQL is a declarative language, but clearly allows modifying the database). Similarly: nginxVar = pkgs.runCommand "nginx-var" {} '' mkdir -p $out/var/log/nginx mkdir -p $out/var/cache/nginx ''; We can say the contents of the runCommand argument are run imperatively, sure. (Especially: if you change the order of the bash commands, you might get a different result). But unlike the Dockerfile, the order where we declare this nginxVar package doesn't matter. Or, say: the `copyToRoot` in the `nix2container.buildImage` takes in a list of packages where the contents are copied to root. The copying is an action; but the list of what to copy is not an action. -- And again, `copyToRoot` could be put after the `config` attribute. The mechanisms describing how the copying is done is elsewhere.
- gitgud 3y ago> Declarative means you define the state you want “FROM ubuntu:latest” is pretty declarative then…
- makeva 3y agoIt an imperative command telling to copy/import another image, not different from importing a library in any other imperative language
- digitalsushi 3y agoIt is the most declarative thing in a Dockerfile. It becomes an imperative solution very quickly. Here's an example Dockerfile: FROM McDonalds Cheeseburger HappyMeal RUN Get ketchup packets RUN Squirt ketchup on fries RUN Dump happymeal on plate The first is declarative, a menu, you point at what you want and software makes it so. The rest is a recipe, an imperative, a command, you tell software to perform, and software makes it so.
- steffres 3y agoI'd say both. Some is declarative, e.g. `FROM`, `ENV`, `EXPOSE`. While on the other hand `RUN`, `CMD`, etc. is fully imperative.
- aequitas 3y ago`ENV` is a bad example because it's effect differs greatly from where it's placed in the Dockerfile. Eg: before a RUN statement consuming it's value or after. `FROM` also has more use cases when using multi stage builds.
- steffres 3y agooh, you're right. I forgot that a key characteristic of anything being "declarative" is that order of statements should not matter. Acutally, come to think of it, since `RUN` may depend on any other Dockerfile statement (even `EXPOSE` might make a difference in code), does this mean that even a single imperative statement that is introduced in some language, makes the language imperative?
- tptacek 3y agoYou don't really get credit for being "both". There are maintainability and comprehensibility benefits to keeping anything imperative out of a language (you don't have to reason causally from one statement to the next), which is out the window when you introduce imperative elements. Also: in a Dockerfile, those imperative elements are the heart of the system.
- strogonoff 3y agoLike with functional programming, which at extremes is only declarative, there is a tendency to call “declarative” only those approaches that are perfectly declarative and incapable of imperativeness. However, not unlike purely functional programming, declarativeness is useless unless it is contaminated with real world at some boundary. If you pretend that COPY means with these files like those files on host machine as of execution time, RUN means with this command executed at build time, etc., then even Dockerfile becomes fairly declarative. Every declaration defines a new immutable layer with its own unique hash, it’s just that some declarations can easily be used in ways that make the outcome vary based on the state of the entire world as of build time. Just as in Ansible you can use “declarative” YAML in a very imperative lasagna of a setup, you can do the same with a Dockerfile, Nix, Haskell, or Python. You can also get pretty close to purely declarative bliss with any of them, but it will grow impractical before that point.
- tyingq 3y agoI feel like "not declarative" is fair enough if you look at how the important bits work in a Dockerfile. Like what software is installed, that's not usually some structured thing...but typically shell commands out to apk or apt-get. I get why it is the way it is, but if it were more declarative, it would be easier to manage Dockerfiles through changes, security updates, etc.
- strogonoff 3y agoYou can shell out in Nix and Haskell. If they are not “declarative” then what is [both declarative and useful, a.k.a. capable of interfacing with outside world]? Dockerfile is intentionally bare-bones. It just gives you RUN straight up, no scary hidden option, but it’s up to you how to use it. If you want to write imperative, you can. If you don’t write imperative, pin everything to a hash, shell out only to Dhall and Prolog, it can get very declarative… …at a cost. The fact that people do not tend to go this route means equally that 1) they are lazy, and 2) using RUN this way is pragmatic.
- tyingq 3y ago
- pmarreck 3y agoA Docker image is basically the cached result of a lucky, nondeterministic imperative build success from a Dockerfile. In comparison, a Nix file is actually declarative ("I want my result system to have this" as opposed to "Do this to get me towards my result system"), and is actually reproducible. But notice the sizes of both a Docker image (many megabytes) and a Nix file (a couple of K)...
- globular-toast 3y agoA many megabytes Dockerfile?! Where?
- pmarreck 3y agoYou're right, and I meant "Docker image", was fortunately able to edit before the window closed! (Edited it for clarification. Sorry, 2-year-old plus no breakfast or coffee, minus sleep = daddy brain...) A Dockerfile provides no guarantees that it will succeed, is what I was getting at- which is why people download Docker images to begin with, because the build product is guaranteed to work, since it's immutable at that point.