Ask HN: Docker vs simple DLLs?
What I did understand so far is "Docker is useful if you have multiple applications with different versions of the same dependencies running on the same machine, as well as for deploying onto any machine".
MY QUESTION: (Why) don't dynamically linked libraries (.dll) already solve this problem?
I come from the .NET world and there I can run any number of applications all with their respective dependencies, e.g. NuGet packages, as .dll files in their own isolated directories - doesn't this already solve the problem of isolation and avoid dependency-conflicts? This is also easy to ship to any machine as I just compile the whole thing for the respective architecture and then copy and paste it over, set it up as a service/daemon, whatever.
To me a standalone application is simply a directory with an executable and all dependency-dlls in the same directory / some subdirectory. This is already a software-wise isolated application (obv not on the hardware side but lets leave that out for a second), of which I can run multiple on the same system. Why do I need docker on top of that?
I am probably misunderstanding something fundamental here so for the 21375th time, what is an idiot-proof explanation for the need of docker in light of dlls?
64 comments
[ 3.2 ms ] story [ 137 ms ] threadMost people aren’t disciplined though.
Maybe your application requires xyz lib to be installed via apt or something similar
Now docker is useful, as it can maintain that environment easily
Now imagine you work in a company with many different teams, and just want a similar way of deploying all services without requiring knowledge of how the application dependency tree looks, just use docker everywhere.
That is why imo you see docker used in places it doesn't really offer a benefit, because a standardized way of deploying is a benefit in itself.
1) what if I want to run your app on my Linux server and I dont have dotnet set up. I dont want to deal with installing and maintaining dontnet. And I dont trust your setup script
2) You want run a database and message as part of your application, and they aren't dotnet.
Worth pointing out that if you don't trust their setup script, then you probably shouldn't trust their Docker container either.
For example a database container a application container a web server.
Maybe you’re confusing docker with something like flatpak. .Net applications AFAIK are already simple to package and run so the benefit there is limited.
Context I’m a web engineer.
Docker, and the tools on top of that, can make these problems easier
The IP address situation can be handled with the "unshare" and "ip" commands. Unshare allows putting a process in a new namespace and ip allows configuring the interfaces and moving them between namespaces.
Both of these solutions do not require any container runtime which is a big advantage efficiency wise!
I am not saying that using docker for these purposes is bad though, I just wanted to mention some lightweight alternatives in case anyone was interested.
DLLs have the unmistable stench of C++, COM and deep plumbing around them. Docker smells of json-over-http and familiar things.
Then you have lads who use python who can use virtualenv, and then the ruby ones who use rbenv and then you have the CPP developers which are usually fine but on Tuesday Joe messed up the linker settings and all of a sudden we're not statically linked, thanks Joe.
And then our Java team forgot to tell the sysadmins that they upgraded the JVM version.
The thing with docker is that you just get a complete package with everything delivered directly by the developer and it's very hard to mess up.
Also docker can run applications that aren't shipped by people in your company and also does more stuff (like more sandoboxing and it also helps you with the resource management).
That's just a sing for bad communication/practices/documentation and processes, and why don't the dev's test the application on the dev/test-environment?
BTW: Why can the JAVA-team even install something on the dev/test/prod-env? That's completely against the rules.
They did, it failed, then the JVM was upgraded for dev/test (but not yet for prod as that was still running the prev-JVM version of the code).
See first error, dev/test has to be a exact mirror of production, otherwise it makes no sense. And if not even the admins from dev/test/production can communicate with each other...you really have deep rooted problem.
Containerisation allows reproducibility, in a self-documenting manner. Delivery can be automated, depended upon to run, in a totally system configuration agnostic manner. It allows projects to deliver their software in a way that it can be ran, instantly, with a single command (image download not withstanding).
I've worked heavily in service software engineering for over a decade now, and with docker since 2013, and it has changed everything. There will be some people who don't fully understand it, but perhaps they don't have the problems that it solves and thus don't need it.
Alternatively, they /could/ just be suffering without realising there is a solution.
Containerization largely eliminates this need for communication. There are new problems - security of running containers, image sizes, building images safely and so on, but this problem disappears.
This is a good way to fix those problems with communication/practices/documentation and processes.
No it's not because the dev who made the database-container forgot to inform you that you need the new jdbc.jar etc...it's the same just with one more layer.
The downside is: you are also, at the same time, excluding yourself from standard patch management. Whereas in ye olde times for a situation like Heartbleed or log4shell the 24/7 on-call sysop team would run a simple "apt-get upgrade openssl" and restart all affected daemons, under modern "devops"/docker-centric models you need to wait for the developers or, worse, the container image vendor to deliver updated versions.
It also really makes a difference if you are combining some tasks and ownership in a DevOps concept where it's not just developers handing off some binary and telling the operations team "good luck eh". That classic setup mostly brings finger-pointing problems and not really much of a solution.
What a patch management concept would look like is much a more modern version of the server thing: you have some system that allows you to query all the servers "what is your current version of thing X" and if you then want to change that you build an updated machine image and deploy that to all those servers. Cattle, not pets.
DevOps has always been more about companies attempting to "save" money by shoe-horning together roles that require completely different skill sets.
I have to vehemently disagree with this statement. I had 10 years in IT before I did my bootcamp and quickly found myself in deployment engineer roles by accident. Most of my bootcamp cohort ended up at smaller companies wearing many hats and learning quickly where a good half of them are in deployment type roles by necessity.
After an additional 10 years in this industry, I'm finding invariably that it's the FAANGs and companies hiring fresh grads out of the best schools where engineers aren't learning anything about the underlying systems. It's the feature factories where people put their headphones on, the heads down and code like their life depends on it.
They do things like write microservices that implement basic functionality of opensource loadbalancers, because they've never heard of nginx or haproxy before, don't know how it solves their specific problem and wouldn't know how to configure it. When all you do is write code, every problem looks like it should be solved by writing code.
Before the overlap between development and operations there were multiple teams involved in every step, everything was slow and expensive and you'd be lucky to deploy once a week. After making sure that where overlap exists the people that have the skills to work in that area can do what they need to do (i.e. build, verify, release and instrument the systems they are responsible for) we get every team releasing every system multiple times per day in small increments. This is both faster, easier for everyone, and worth the ROI on people and tech far more than trying to invent borders and fiefdoms between hyper-specialised teams.
Now if you use a wide variety of programs written in a wide variety of languages (e.g. a C database server and a Ruby web app and a JS daemon), then Docker is a very efficient way of staticizing your app just like you did with your .Net dlls or like you would by statically linking a Rust app.
We went from hosting on prem to directly azure web apps.
Something in between I think is where docker would be helpful.
It probably makes sense for your business though.
Different languages get a different amount of boost here. Ruby is an example which strongly benefits from docker deploys. You don't need to worry about what version of ruby (if any) will be installed on the host. And you don't need to worry about getting your gem dependencies installed on the host or figure out how to bundle them into your app code.
I think older dot net apps required the runtime to be installed on the host. Docker would have helped then.
The other big draw of docker is uniformity in deployment (the shipping container metaphor). Deploying code of any language and third party apps can be very similar. This is a powerful enough idea that giant abstractions like kubernetes have come to exist. This would be much more fiddly if each application needed to be special cased.
You can of course achieve this by using other tools, but you'd effectively de re-implementing a container runtime with that.
Docker does this for software. A docker image packaging an application has a well-defined interface to start/stop/configure/log the application, regardless of which language it was written in.
This way infrastructure tools (for example container orchestration like Kubernetes, docker-compose, swarm,... but also CI/CD tools and local development environments) can work with all sorts of applications in the same way. Docker (or containers) provide a uniform interface to all of these tools.
As a dev, containers allow you to care a lot less about what’s installed on the server and in which version. You’ve already solved that in a different way for your setup, which is fine. I’d say the trick in your case isn’t the DLLs specifically (I’m not sure why you focus on that aspect), it’s the fact that .NET allows you to create a standalone application, including (in modern times) the .NET runtime. If that’s all you need, you’ve already minimized your dependencies on the OS, and you’re unlikely to benefit from using containers.
However, if you need anything else on the server (another runtime, a database server, a mail server), Docker could still help. Having the whole tech stack pinned in version control is really nice, and having the ability to make any changes to one’s setup at whim is very liberating.
For admins, there’s a different advantage. Especially when multiple technologies are in use somewhere, containers are a standard interface to host any kind of server application the same way. You don’t need to know or care that one technology just copies a directory, the other one requires a specific configuration of Apache and the third one requires setting up an extra cron job — just start the container and you’re done.
But it’s always important to remember that there’s no shame in not using a technology that HN has deemed cool, particularly if it doesn’t solve a problem you have.
Ah that was because in the video I watched on docker, the guy gave the example of one application using node v14 and another application using node v9 on the same machine, so I was like why aren't they just separate precompiled files like nuget packages.
Great explanation though, thanks!
Don't blame yourself, it took me quite awhile to figure out the benefits as well.
Docker, or containerization in general is not a system that only helps you manage dependencies and isolation. It's about dependencies management, isolation (to a degree), fast and expectable deployment, automatic scaling and so on.
Now days, application backend are expected to run on multiple nodes. Sure, you can manually code your app to automatically deploy/update itself on multiple servers and so on, but that is a huge task, and the maintenance responsibility comes with it is even bigger.
Containerization platform such as Docker and Kubernetes provides a standard operating environment for operators and developers to manage the lifecycle of a scalable application, IMO that's where the gold is. Dependency management and isolation etc is just a natural part of that.
Of course, you're still responsible in creating a scalable application, but once you did that, deploying the application will be as simple as running a `git push`, then a remote system takes over, creating a container image according to your Dockerfile, and then deploy those containers onto necessary amount of servers. It all happens automatically, like a dream :)
At this point you might respond with “I could zip up all that state and still make sure it unzips to a single folder” But that leads us to
2) Most production software has at least one dependency that needs isolation. Even if you’re good about making sure that you follow the rules to make folders self-isolating, eventually you’ll have to take a dependency made by someone who isn’t following the rules.
Docker lets you isolate a complete environment, not only your own application files.
Your app might be expecting database X running on port Y, a specific OS version, Kafka running, etc...
Containers allow to configure all these "boxes" easily. Your co-worker can test the application with exactly the same "system" as you. And you can deploy it in that same "system" too.
Not everyone who packages an app is willing to lock all their dependencies into a specific version.
Look at dependencies that are usually coming from the system package manager, such as OpenSSL, pandoc, libicu, the timezone database, and whatnot. Sometimes those require updates. Apps may want to pick up those updates. I understand every app packager who says, „I don’t want to cut a new release every time an OpenSSL vulnerability gets patched, or whenever some country decides to update their time zone.“
So vendors may prefer their app to pick up system-level packages rather than ship those along with the app. Docker will update those packages for you every time you build your image, and you don’t depend on the app vendor to cut a release whenever you do. At the same time, containerization helps isolate versioning conflicts that stem from those common dependencies.
It has all the dependencies it needs, there is nothing else executing, the files are laid out just the perfect way, every configuration file or environment variable just magically appears.
It's very cheap to create and get rid of this happy place in contrast to dedicating an entire VM or bare metal. It's also cheap to move containers around so you could orchestrate them on multiple hosts.
Even though they raise others, they really answer to a lot of questions around developing, deploying and monitoring software reliably. That's why it's usually worth the hussle.
Likewise Docker volumes and mount points. The software gets an abstracted filesystem that's actually a "volume". Could be a chunk of local filesystem, an NFS mount, whatever. The software doesn't need to know. It just acts like it's the only software on the "system" and writes to any path that it can find. It can't corrupt any other containers also running on the same host by writing global configuration files or making "system-wide" changes because those system wide changes are contained to the container.
Docker also abstracts a ton of sysadmin/deployment type tasks. I have 1 container of app X running on host A. There's a load spike. I want a second instance of app X running on host B. Ok. Load spike is over, I want to get rid of the instance of app X on host B. Docker makes this kind of dynamic deployment/destruction automatable via APIs and there's huge libraries of software out there to do it. I don't know about the .NET packaging/deployment (NuGet, etc.) and whether that's as easily done.
It sounds like you know a lot about .NET, and I know very little. So it's entirely possible that Microsoft has solved the network and storage abstraction in ways I don't know about. In that case, if you don't NEED Docker, because you have this homogenous environment and effective isolation controls built into it, be comfortable in your lack of need.
I don't know if there is a comparable technology for Windows. If you really want to appreciate the difference I think that you have to invest some time building and running docker containers and thinking if you really could do the same by switching DLLs. Don't forget the deployment story.
Just FYI, for compatibility reasons, the .NET runtime uses the .dll extension for all libraries, even on Linux. Despite the extension, these files aren’t true Windows DLL files. They might have a stub PE header (not completely sure), but otherwise, they only contain what’s called Intermediate Language code that has to be JIT compiled by the runtime. The principle is similar to .jar files in Java, in case you’re familiar with those.
The .NET toolchain has a mode where it bundles all dependencies (.dll files) and your code (more .dll files) together with the runtime for the target platform (for example, Linux) into a redistributable directory. I think that’s what the OP is referring to.
A container image can be seen the same way a machine image can be used: you ship an image of a solution instead of some files and instructions and 'hope for the best'.
If you imagine your application and it's required environment as a complete machine as a starting point, and then running/scaling that by simply having the only requirement on any host be "it must be able to run this image". That's a much smaller and more standard interface than individual files.
If you were to just make a 'smaller' version of that you'd end up with chroot/jails and if you want to use cgroups on linux for isolation you'd end up with containers (i.e. Docker containers). It's not the only method or the best method, but it's the easiest and most widely supported method.
Going even 'smaller' you could simply build static binaries with everything included and tell the OS that when it gets started it should do so with no permissions to access anything else on the host.
Smaller beyond that is unikernels where your application is the OS and runs bare on a host (be it bare metal or virtual).
The downside of the last two is that all the 'extra' files like assets and temporary data needs to be embedded inside that single file. The downside of chroot and jail are that there is no cross-host packaging method and it usually just works on one specific OS or host.
Virtual machine images and container images have the same benefits but the virtual machine has the drawback that it is much bigger, much slower to start up (generally) and has a much larger attack surface and maintenance overhead.
What containers aren't is:
Technically containers also require you to be stateless and read-only. This nearly universally leads to better applications, better operations and better security. That gets us to the cattle vs. pets part of containers: the idea is that you ship containers, not whatever happens to be "inside" those containers. As long as a container is "good", any host that can run containers should be able to perform exactly the same to the point where running it on your laptop, on a mainframe and on a standard bare metal server does exactly the same thing. If you then wipe the server, install a different OS that can run containers and start the container again, it still does exactly what it should do.