Un conteneur contient en général une application qui met, la plupart du temps, un microservice à disposition. Dans la pratique, un grand nombre de conteneurs doivent, en général, être contrôlés ensemble pour réaliser une application complète. La gestion coordonnée de grands groupes de conteneurs est appelée orchestration.
Même s’il est possible d’effectuer l’orchestration avec Docker et des outils comme Docker Swarm, Kubernetes a réussi à s’imposer comme une alternative à Docker. Kubernetes regroupe plusieurs conteneurs dans un pod. Ces pods fonctionnent sur des machines appelées nœuds qui peuvent être aussi bien des machines virtuelles que physiques.
L’un des principaux problèmes de Docker résidait dans l’architecture monolithique du logiciel. Le daemon docker fonctionnait avec des privilèges administrateurs et était responsable d’un grand nombre de tâches : du téléchargement des images des conteneurs à la création de nouvelles images en passant par l’exécution de l’environnement d’exécution. Cet amalgame de tâches normalement indépendantes va à l’encontre du principe de développement de logiciel : la « séparation des préoccupations » (Separation of concerns) et provoque notamment des problèmes de sécurité. Des efforts ont donc été faits pour découpler les différentes composantes.
Lors de la création de Kubernetes, le daemon Kubernetes kubelet a été doté d’un environnement d’exécution codé en dur. Mais la nécessité de prendre en charge d’autres environnements d’exécution s’est rapidement fait sentir. Une modularisation des aspects individuels promettait un développement simplifié et plus de sécurité. Afin de rendre différents environnements d’exécution compatibles avec Kubernetes, une interface a été définie : Container Runtime Interface (CRI). CRI-O est une implémentation spécifique de cette interface.