A major benefit of virtual machines and containers is the way you can isolate an application from any other applications, and reason about it as being a separate entity with clear interfaces that you can throw away or rebuild at any moment. It's like being able to have a lot of physical machines, but at virtually no cost.
Why does it matter, and wouldn't you have the exact same benefits “if applications behaved better”? Well, it depends on what you mean by better behavior. Imagine your machine hosts a very old piece of proprietary software which wasn't updated for ages, and which works on Windows 2000 only. The same machine hosts a relational database, and you learn that the support for the database will end by the end of December. Unfortunately, newer versions of the database rely on features which are only available on Windows Server 2008. What do you do?
Or simply imagine a Linux server hosting a few dozens of services and applications. You have just upgraded one of the applications, and discovered that it requires OpenSSL 1.1.1 for one of its features. You remember that there are at least several services which too rely on OpenSSL, although you don't remember well which ones. You also remember your colleague cursing aloud a year ago when somebody tried to upgrade the server from OpenSSL 1.0.2 to OpenSSL 1.1.0 and it broke the LDAP for the entire company. What do you do?
In both examples, applications don't behave badly in a sense that they overwrite each one's files or try to be the first to grab a specific port. They, however, have different expectations about their environment. In order not to do that, they need to find a way to be completely environment-agnostic. They can do it by putting themselves in a container, or be distributed in a form of a virtual or hardware machine. Some companies actually did this for a while. If you purchase their software, they came to put their servers in your physical data center. And now, there are a lot of applications distributed in a form of Docker containers.
You see, it's all about the level of abstraction that you need in a specific context:
Sometimes, you have applications which require a different kernel to be used. One app may work on Windows. Another one is limited to Linux. In order to host them both, you need a separation in a form of physical machines or virtual machines.
Sometimes, two apps can run on the same kernel, but they require different operating systems. With containers, you can indeed run an application on Debian, while using Red Hat kernel. Nothing wrong with that. Obviously, you can also use the two other forms of isolation: virtualization or dedicated hardware.
Some other times, you have two apps which don't care about operating systems and system libraries. Their sole difference is within the packages they use: for example, one Python app may use a specific version of a Python library, whereas the other one needs another version. Here, you can use
venv for Python, or similar isolation technologies for other languages. Sometimes, the isolation comes for free, by design. In .NET, for instance, you can host together the applications which need different versions of assemblies, and even different versions of .NET itself.
The issue is with the element in the middle: the system dependencies. You believe that it's up to the operating system to provide some sort of isolation for the apps, in order to be able to provide different environment for each. The fact is, this is exactly what operating systems already do, in a form of user space—the thing which is used by Docker.
When you move from right to left, you notice that the isolation capability decreases, but the cost decreases as well. The isolation provided by .NET or
venv comes at no cost in terms of time required to set it up or resources wasted, whereas, at the opposite, physical machines are expensive, cannot magically appear in your data center, and require human intervention (to install them in the rack and plug them in). This is why one would generally chose the isolation approach towards the right, even if the approaches on the right provide a higher level of isolation.
Anything you can do with virtual machines and containers, you could do without them as well, by purchasing a lot of hardware. You get the same isolation, and the same way to automate deployments (thanks to PXE). The difference, however, is that physical hardware costs a lot, wastes space (you're limited to 42 bays per rack cage, not counting the space needed for network devices and UPS units), and is extremely slow to set up: it could take a few weeks from the moment you decide to purchase a machine to the moment where your new web app is fully deployed on it.
With virtualization and containers, however, you can have a new machine running for free on a matter of seconds if we talk about a VM, or milliseconds if we talk about containers. And this is a complete game changer, because it encouraged extreme isolation one could rarely see in the past.
Essentially, containers are exactly what you talk about abstracting away the environment from the applications. You believe that it's up to the applications to be self-isolated. Well, they are, through containers.
And then some tools don't play well together, simply because they can't. At some point, you find a Python 3 library that you want to test. So you install Python 3. Later on, you find a tool which requires Python 2, and expects Python 2 to be the default version. Do you switch to Python 2, and take the risk of breaking something which was installed when Python 3 was the default on your machine? Or do you spend the next two hours tweaking the tool to work on your environment?
Today, you would simply create a container, and play with the new technology within this container. It can even be an app you downloaded from a weird website you don't trust at all: if it's a virus, well, there is a chance it will make really nasty things inside your container. No big deal.
Ten years ago, it wasn't unusual (alas, it's still frequent even now) to see small companies hosting everything on the same server, because there is simply no enough money for additional hardware. You'll find the same machine running corporate LDAP, serving the website while also running the database it uses, hosting all the files distributed in a form of network shares, and doing dozens of other things. Chances are, in a case of a hardware failure or a hacker finding a way to get root access, the company is in a big, big trouble.
With virtual machines and containers, it's different. Not only isolation prevents bad things from propagating to other machines/containers, but you can also rebuild a virtual machine or a container relatively easy, knowing it will have either no impact on the environment, or the impact will be very limited. You can destroy all the containers hosting your corporate website, and LDAP will still work. Or you can take down network shares, and the website will still serve requests.