Containers and Virtualization
Before GKE and Cloud Run make sense, know what a container is and how it differs from a VM. Deployment evolved in three steps: physical servers → virtual machines → containers.
How deployment evolved
Physical servers. The old way: one app per physical computer. Each needed space, power, cooling, and network, plus its own OS, dependencies, and app. More capacity meant more machines - usually single-purpose (one for the database, one for the web server). Wasteful and slow to deploy, maintain, and scale.
Virtualization. Creating a virtual version of a physical resource (server, storage, network). A hypervisor is the software layer that breaks the OS's dependency on specific hardware and lets several virtual machines share one physical host. Kernel-based Virtual Machine (KVM) is a well-known hypervisor. VMs deploy faster, waste fewer resources, and are portable - a VM can be imaged and moved.
But a VM still bundles the app, its dependencies, and a full operating system together.
Every time a VM starts, its operating system has to boot - that is slow. Moving a VM between different hypervisor products is not easy either. Containers avoid both: they start and stop like OS processes, with no OS boot.
Packing many apps into one VM trades one problem for another: apps sharing dependencies are not isolated. One app can starve the others of resources, and a dependency upgrade for one app can break another. Locking dependencies or adding integration tests only papers over it - dependency conflicts cause novel failures that are hard to troubleshoot and slow development.
The VM-centric fix is one dedicated VM per app. That isolates them, but now every app runs its own full copy of the kernel. Scale that to hundreds of apps and the overhead is enormous.
What a container is
The efficient fix is to abstract at the level of the app and its dependencies - not the whole machine, not even the whole OS, just the user space (all the code above the kernel: the app and its dependencies). That is a container: an isolated user space for running application code, sharing the host kernel.
- No full OS - they carry only the app and its dependencies, so images are small.
- Fast start/stop - they start and stop OS processes; they do not boot a VM or initialize an OS per app.
- Tightly schedulable - because they share the kernel, they integrate efficiently with the underlying system.
Isolating apps by giving each its own VM means running a complete copy of the kernel per app. Containers give the same isolation while sharing a single kernel - that is the whole efficiency win.
Why developers like containers
You still develop on desktops, laptops, and servers as usual; the container engine makes the packaged dependencies available at runtime, and the final code executes on VMs.
- Code-centric - a portable way to deliver high-performing, scalable apps.
- Reliable and predictable - on a Linux-kernel base, code runs the same on a laptop or in production. An incremental change on top of a production image ships as a single file copy.
- Microservices-friendly - loosely coupled, fine-grained components let you scale and upgrade one part of an app without touching the whole.