Containers—orchestrated by Kubernetes, Docker Swarm and their ilk—abstract the OS, providing a way to run applications on multiple isolated systems, while sharing an OS, often binaries and libraries too. This made containers lightweight; they are often in megabytes and take a few milliseconds to start. In fact, you might be able to put twice or thrice as many applications on a single server with containers, as compared to VMs.
Much like VMs abstracted hardware, containers abstract software. Much like VMs took away the burden of server management, containers significantly reduce software overheads—bug fixing, patch updates, etc.—as they need to happen for one OS instance rather than for each instance in case of VMs. In fact, today, Kubernetes-orchestrated containers often run on top of VM-based infrastructure, as much of enterprise IT is still VM-based.
But, more recently, among progressive application engineering teams, containers have become the most preferred way to deploy applications in a multi-cloud environment. Especially for microservices-based applications, containers had distinct advantages over virtual machines across cost, efficiency, flexibility and speed of execution. Containers also made possible the ability to create an efficient and portable environment for development, testing and deployment.
Yet, they lack the unassailable security of VMs. Cloud go Containers have had to bear the consequences of process-level isolation, unlike boundaries of hardware virtualization that VMs had. I don’t mean to imply that Cloud go containers are not secure—they have container-level, cluster-level and kernel-level security.