Oggi, 29 marzo 2017, al CloudNativeCon + KubeCon Europe di Berlino il Technical Oversight Committee della Cloud Native Computing Foundation (CNCF) ha accettato containerd come progetto incubating. Da qui in poi il runtime di basso livello che esegue davvero un container smette di essere un dettaglio interno di Docker e diventa un componente con vita propria.
Contesto
Fino al 2016 dire “container” voleva dire Docker: un’unica installazione che teneva insieme interfaccia a riga di comando (CLI), daemon, build delle immagini, gestione dei registry, networking e l’esecuzione vera e propria dei processi isolati. Quando Kubernetes è diventato l’orchestratore di riferimento, quell’accorpamento ha cominciato a pesare. Kubernetes non ha bisogno della CLI di Docker né di docker build: gli serve avviare, fermare e sorvegliare i container, scaricare immagini, gestire i namespace. Tutto il resto, visto dal kubelet, è superficie che nessuno ha chiesto.
La risposta di Docker è stata ritagliare il proprio nucleo. Nel dicembre 2016 ha trasformato containerd — fino ad allora un componente interno limitato all’esecuzione — in un progetto autonomo con un perimetro definito: push e pull delle immagini, storage di basso livello, esecuzione e supervisione dei container, gestione dei network namespace. La build delle immagini, i volumi di logging e le funzioni più alte del daemon Docker sono rimaste fuori per scelta. La donazione di oggi al CNCF mette quel perimetro nelle mani di una fondazione neutrale.
Container Runtime Interface
A rendere possibile tutto questo è la Container Runtime Interface (CRI), arrivata come alpha in Kubernetes 1.5 il 19 dicembre 2016. CRI è un’API gRPC e Protocol Buffers con cui il kubelet parla al runtime, su una socket Unix dove il kubelet fa da client e il runtime (o uno shim al suo posto) fa da server.
L’API definisce due servizi:
RuntimeService, che gestisce il ciclo di vita di pod e container e le operazioni interattive (exec,attach,port-forward);ImageService, che scarica, ispeziona e rimuove le immagini.
Prima di CRI ogni runtime andava cablato dentro il kubelet ricompilandolo. Con CRI il kubelet parla un protocollo stabile, e chiunque lo implementi diventa un runtime usabile. In Kubernetes 1.5 anche Docker è stato spostato dietro questa interfaccia tramite uno shim integrato, il dockershim, che traduce le chiamate CRI verso il daemon Docker. Docker, insomma, è già uno dei runtime CRI possibili, non più l’unico.
Due implementazioni
Sul livello CRI crescono due risposte con baricentri diversi.
containerd espone una propria API gRPC e si appoggia interamente alle specifiche Open Container Initiative (OCI) e all’implementazione di riferimento runc per l’esecuzione vera e propria. L’ambizione è restare un runtime generale, usabile sotto Docker e sotto Kubernetes (con un layer di adattamento CRI) e in prospettiva anche da altri sistemi. La roadmap pubblicata fissa a giugno 2017 il completamento del lavoro verso la 1.0; il progetto dichiara di voler cercare la certificazione OCI quando sarà disponibile. Al progetto lavorano ingegneri di Alibaba, AWS, Google, IBM, Microsoft, Rancher, Red Hat e VMware.
CRI-O — nato come OCID, Open Container Initiative Daemon, nel settembre 2016 e poi rinominato — sta nell’incubatore Kubernetes ed è guidato da Red Hat. La scelta progettuale è opposta in ampiezza: implementare CRI e nient’altro. Non è un runtime generale con un adattatore CRI montato sopra; è un daemon il cui unico scopo è eseguire i carichi di Kubernetes. Anche lui si appoggia a runc (e in linea di principio a qualsiasi runtime conforme OCI) per l’esecuzione, scarica e spacchetta le immagini OCI, gestisce i pod sandbox, il ciclo di vita dei container e un server di streaming per kubectl exec, attach e logs. È ancora pre-1.0.
Punto critico
La differenza interessante non è quale binario sia più piccolo, ma dove ciascun progetto traccia il confine della propria responsabilità.
containerd scommette che esista un runtime riusabile e generale, e che CRI ne sia uno dei consumatori. Questo lo rende utile anche fuori da Kubernetes — è già il motore dentro Docker — al prezzo di mantenere una propria API accanto all’adattamento CRI. CRI-O scommette il contrario: un container in un cluster Kubernetes non ha bisogno di astrazioni oltre CRI, e ogni superficie in più è solo codice da mantenere. Il suo confine coincide esattamente con l’API che il kubelet già sa parlare.
Entrambe le scelte dipendono dalla stabilità delle specifiche OCI sottostanti, che a oggi non hanno ancora raggiunto la 1.0 definitiva: la runtime spec e la image spec sono allo stadio di release candidate. Finché quei due documenti non si assestano, qualsiasi runtime “conforme OCI” insegue un bersaglio ancora in movimento.
Implicazioni
Per chi gestisce cluster oggi la conseguenza pratica è limitata. Il percorso normale resta il kubelet con il dockershim davanti a Docker, e le immagini sono in formato OCI a prescindere da quale runtime le esegue. Cambia la direzione: CRI ha reso il runtime un componente sostituibile, e la donazione di containerd a una fondazione neutrale lo sottrae al controllo di un singolo vendor. Per un orchestratore che vuole restare indipendente dal fornitore, è una precondizione più che un dettaglio.
Conviene tenere distinti due livelli che spesso si confondono. CRI è il contratto tra kubelet e runtime. OCI è il contratto sul formato delle immagini e sul comportamento del runtime che esegue (runc e affini). containerd e CRI-O stanno in mezzo: parlano CRI verso l’alto e OCI verso il basso. È questo doppio aggancio a rendere i pezzi intercambiabili.
Limiti
Quanto sopra è una fotografia del 29 marzo 2017. containerd è in incubazione da oggi e la sua 1.0 è una roadmap, non una release. CRI-O è pre-1.0, e non ho ancora visto numeri di adozione in produzione da citare. Le specifiche OCI sono a release candidate. Affermazioni più forti — quale runtime diventerà il default in quale distribuzione, quando il dockershim verrà rimosso — oggi sarebbero previsioni e non fatti, e non ne faccio. Quello che è verificabile è il cambiamento strutturale: il runtime è stato estratto, standardizzato dietro CRI e affidato a una governance condivisa.
- https://www.cncf.io/announcements/2017/03/29/containerd-joins-cloud-native-computing-foundation/
- https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/
- https://github.com/opencontainers/runtime-spec
- https://github.com/opencontainers/image-spec
- https://github.com/kubernetes-incubator/cri-o
- https://github.com/containerd/containerd
- https://www.noze.it/insights/containerd-cri-o/
Immagine di copertina: Diagramma a tre colonne che confronta deployment tradizionale, macchine virtuali e containerizzazione, con i livelli hardware,… — diagramma di MoreInput, CC BY-SA 4.0 — https://commons.wikimedia.org/wiki/File:Containerization2.svg