June 6, 2024·4 min read·engineeringkuberneteshistoryarchitecture

Ten years of Kubernetes changed what belongs in the core

Kubernetes's tenth anniversary traces a shift from a container orchestrator with built-in integrations to an extensible API and controller system. Its history also records the compatibility costs of that change.

The first Kubernetes commit reached GitHub on June 6, 2014. Ten years later, the project's anniversary article describes a system whose relationship with its integrations has changed substantially. A project initially designed around Docker now has a runtime interface, a storage interface and a large ecosystem of controllers built around custom resources. Its history is partly the history of deciding which responsibilities the core project should retain.

The anniversary account puts the original commit at 250 files and 47,501 lines of Go, shell and Markdown. Those numbers describe a starting point, but the more useful comparison is architectural. Early implementations made specific integrations directly available. The mature project increasingly supplies contracts through which independently maintained implementations provide those capabilities. That change supports a broader ecosystem while creating compatibility and maintenance obligations at each interface.

I value Kubernetes most for its API machinery and level-triggered reconciliation. A controller can observe the current state and resume convergence after an interruption. Custom resources let that design apply beyond the built-in workload objects. The anniversary is a useful occasion to examine how the project made that extensibility practical and what it has cost users during transitions.

Containers made the orchestration problem accessible

The article traces the context back before Kubernetes. It describes work on Linux container resource accounting and Google's experience with Borg and Omega. Docker's public introduction in 2013 made containers accessible to more developers. That usability changed the audience for orchestration, because portable and repeatable application packaging left a separate problem of managing many such applications.

Kubernetes emerged from engineers who recognized that need for an open-source orchestration system. The article dates the initial work to the fall of 2013, followed by the first public commit and the DockerCon announcement in June 2014. This sequence matters because it separates the ideas inherited from earlier systems from the new project's public implementation and community.

The same account dates version 1.0 to July 21, 2015, alongside Google's announcement that Kubernetes would be donated to the newly formed Cloud Native Computing Foundation. Public governance and a broader contributor community became part of the project's development. The technical system and the organization responsible for maintaining it grew together.

Extensibility became a concrete architecture

The anniversary highlights runtime pluggability through the Container Runtime Interface in Kubernetes 1.5. That provided a direction beyond the original direct relationship with Docker. The architectural importance is a common contract between kubelet and runtime implementations. Kubernetes can retain responsibility for workload management while different runtimes satisfy the execution contract.

Storage followed a comparable pattern through the Container Storage Interface, which the article places at general availability in 1.13. Provider-specific storage implementations can live outside core Kubernetes. This shifts integration work toward independently maintained drivers while preserving a standard relationship with cluster components. Supporting different storage systems no longer requires every implementation to remain part of the same core codebase.

Custom resources enabled a broader kind of extension. The article records the replacement of ThirdPartyResources with CustomResourceDefinitions in 1.7 and CRD general availability in 1.16. A custom resource gives a controller an API representation for the state it manages. That lets specialized systems adopt the Kubernetes interaction model without asking the project to add every domain to its built-in APIs.

The consequence is a different way to understand Kubernetes's scope. It supplies a system for exposing and reconciling declared state, while controllers determine what that state means for particular infrastructure or applications. The built-in workload kinds remain useful, but they are no longer the limit of what the API machinery can coordinate.

Compatibility makes those changes expensive

The retrospective also names difficult transitions. Widely used beta APIs disappeared in 1.22, and dockershim deprecation and removal caused user confusion. The article explicitly acknowledges that communication problem. These examples belong in an anniversary account because an extensible architecture still has users depending on yesterday's interfaces and integration arrangements.

API maturity does not eliminate future maintenance decisions. A beta API can become widely deployed before its lifecycle reaches a removal boundary. Likewise, a compatibility component can become part of an operator's assumed system even when the project intends another integration path. Removing either requires more than agreement among maintainers about the destination architecture.

I see that operational friction as part of evaluating the design, not an incidental communication issue. The desired contracts need to be understandable before older arrangements disappear. Users need a clear account of which component now provides the behavior they depended on. The retrospective's acknowledgment of difficult transitions is more informative than a history containing only successful graduations.

The cloud migration applies the same lesson

The May cloud-provider migration report gives a current example of how extensive that replacement work can be. Removing built-in provider integrations required cloud controller managers and generic control-plane network proxying. It also required kubelet credential provider plugins and migration of storage operations to CSI drivers. The integration problem crossed several components with different responsibilities.

Storage made compatibility especially explicit. The report describes translating existing in-tree volume APIs into equivalent CSI operations. That preserves the API while moving the implementation behind a different contract. It demonstrates why the removal of a large amount of code can require substantial new machinery, existing resource definitions must continue to refer to behavior the cluster can provide.

The anniversary article identifies the forthcoming 1.31 release as the culmination of that migration. As of June, it remains a release ahead, with the code migration already reported. The architectural pattern is established, core components shed provider-specific implementations while external components maintain the integrations through shared contracts.

This distribution of responsibility also changes project maintenance. The anniversary account discusses the move to the community-owned registry.k8s.io in response to the costs of distributing images. Keeping a widely used system available includes operating its delivery infrastructure as well as maintaining its APIs. At ten years, Kubernetes's engineering responsibilities include both the contracts external implementations depend on and the community systems that make the software available to its users.