What is a function? What’s actually required to run a function on a machine? Why is it so hard, and why is it getting easier?
Software deployment used to mean dedicating an entire physical machine to a single application (I’m not going back to time-sharing just yet). This approach, while straightforward, led to significant resource waste as servers often ran at just 10-15% utilization. Each application required its own operating system, hardware maintenance, and physical space, making scaling both expensive and time-consuming. This is an over-simplification, but serves the purpose of talking about a “unit” of an application.
Virtual machines emerged as the first popular, major abstraction, enabling multiple isolated environments to run on a single physical server. Through hypervisor technology, organizations could run different operating systems and applications on the same hardware, dramatically improving resource utilization. However, each VM still carried the overhead of a full operating system, consuming significant memory and storage resources. This era gave birth to the cloud as we know it—Amazon's EC2 service, launched in 2006, was essentially a VM rental service.
Containers represented the next leap forward, introducing a lighter-weight abstraction that shares the host operating system's kernel. They weren’t entirely a new idea (Unix had similar concepts like chroot for years, and cgroups had been out for a while too), but because Docker made them usable. Containers showed that what we really wanted wasn't to divide machines, but to package programs. Unlike VMs, containers package just the application and its dependencies, making them faster to start and more resource-efficient.