openshift

What Actually Starts Your Container in OpenShift? A Deep Dive into CRI-O

Introduction

When a developer deploys an application to OpenShift, the process looks simple:

oc create deployment nginx --image=nginx

A few seconds later, a running container appears.

But what actually happens behind the scenes?

Many people assume:

  • The Scheduler starts the container.
  • The Kubelet creates the container.
  • Kubernetes directly runs the image.

That is not exactly correct.

The Scheduler and Kubelet have important roles, but the component that actually pulls the image, creates the container, and starts the workload is CRI-O.

CRI-O is the container runtime engine that powers OpenShift worker nodes.


The Container Startup Journey in OpenShift

The complete flow looks like this:

Developer
    |
    |
oc create deployment
    |
    |
API Server
    |
    |
    v
Scheduler
    |
    |
Assigns Pod to Worker Node
    |
    |
    v
Kubelet
    |
    |
Uses CRI Interface
    |
    |
    v
CRI-O
    |
    |
Pull Image
Create Container
Configure Runtime
Start Process
    |
    |
    v
Running Container

Each component has a specific responsibility.


Step 1: The API Server Receives the Request

Everything starts with the Kubernetes API Server.

Example:

oc create deployment web --image=nginx

The request creates Kubernetes objects:

  • Deployment
  • ReplicaSet
  • Pod

The API Server stores the desired state in etcd.

Example desired state:

apiVersion: v1
kind: Pod
metadata:
  name: web-pod
spec:
  containers:
  - name: nginx
    image: nginx

At this point:

No container exists yet.

Only a request describing what should exist.


Step 2: The Scheduler Chooses the Node

The Kubernetes Scheduler watches for Pods without an assigned node.

Example:

Pod:

web-pod

Node:

Not assigned

The Scheduler evaluates:

  • CPU availability
  • Memory capacity
  • Node labels
  • Taints and tolerations
  • Affinity rules

Example decision:

web-pod

        |
        v

Worker Node 3

The Scheduler updates the Pod:

spec:
  nodeName: worker-3

Important:

The Scheduler does not:

  • Pull images
  • Create containers
  • Start processes

It only makes the placement decision.


Step 3: The Kubelet Takes Control

Every OpenShift worker node runs a Kubelet process.

The Kubelet is responsible for ensuring:

“The containers running on this node match the desired Pod specification.”

The Kubelet continuously watches the API Server.

It sees:

Pod assigned:

web-pod

Node:

worker-3

Now the Kubelet begins the startup process.

However, the Kubelet does not directly create containers.

It communicates with the container runtime.

That runtime is CRI-O.


Step 4: Kubelet Talks to CRI-O Through CRI

Kubernetes does not directly depend on one container runtime.

Instead, it uses the:

Container Runtime Interface (CRI)

CRI is an API contract between Kubernetes and the runtime.

Architecture:

              Kubernetes

                  |
                  |
              Kubelet

                  |
                  |
             CRI Interface

                  |
                  |

                CRI-O

                  |
                  |

          Linux Container Runtime

                  |
                  |

              Container

The Kubelet sends commands like:

Create Pod Sandbox
Pull Image
Create Container
Start Container

through CRI.


Step 5: CRI-O Pulls the Container Image

The first job of CRI-O is obtaining the image.

Example:

Image:

registry.redhat.io/ubi9/nginx

CRI-O checks:

Is the image already present?

If not:

CRI-O
   |
   |
Container Registry
   |
   |
Download Image Layers

Images are stored locally on the worker node.

Example:

crictl images

Output:

IMAGE
nginx
ubi9
redis

Step 6: CRI-O Creates the Container

After downloading the image, CRI-O creates the container.

A container is not a virtual machine.

It is a Linux process with isolation.

CRI-O configures:

  • Linux namespaces
  • cgroups
  • mounts
  • networking
  • security policies

Example:

Container

+----------------------+
| Process              |
|                      |
| Namespace Isolation  |
|                      |
| Resource Limits      |
|                      |
+----------------------+

Step 7: CRI-O Starts the Container Process

The container image contains metadata describing what should run.

Example Dockerfile:

FROM nginx

CMD ["nginx","-g","daemon off;"]

The CMD becomes the main container process.

CRI-O launches:

nginx process
       |
       |
       v

Container Running

Now the Pod becomes:

oc get pods

Example:

NAME       STATUS
web-pod    Running

What Role Does the Linux Kernel Play?

CRI-O does not create isolation itself.

It uses Linux kernel features.

Important components:

Namespaces

Provide isolation:

  • Process namespace
  • Network namespace
  • Mount namespace
  • PID namespace

Example:

A container sees:

PID 1

but the host sees:

PID 25000

cgroups

Control resources.

Example:

Container limit:

resources:
 limits:
   cpu: "2"
   memory: 4Gi

CRI-O uses cgroups to enforce:

Maximum CPU:

2 cores

Maximum Memory:

4GB

CRI-O vs Docker

Many administrators ask:

“Does OpenShift use Docker?”

Modern OpenShift does not use Docker Engine as the default runtime.

OpenShift uses:

Kubernetes
    |
    |
Kubelet
    |
    |
CRI-O

Docker was replaced because Kubernetes only needed a CRI-compatible runtime.

Advantages of CRI-O:

  • Lightweight
  • Kubernetes-focused
  • Open standards based
  • Better integration with OpenShift security

Troubleshooting Container Startup Problems

When a Pod fails to start, follow the startup chain.

1. Check Pod Status

oc get pods

Example:

NAME      STATUS
app       ImagePullBackOff

2. Check Pod Events

oc describe pod app

Look for:

Failed to pull image

or:

FailedCreateContainer

3. Check CRI-O Status

On the worker node:

systemctl status crio

Example:

crio.service
Active: running

4. Use crictl

CRI-O can be inspected using:

crictl ps

Running containers:

CONTAINER ID
IMAGE
STATE

Images:

crictl images

Container Startup Timeline

A simplified timeline:

0s
|
| User creates Deployment
|
v

API Server stores desired state

|
v

Scheduler selects Worker Node

|
v

Kubelet detects assigned Pod

|
v

Kubelet calls CRI-O

|
v

CRI-O pulls image

|
v

CRI-O creates container

|
v

CRI-O starts process

|
v

Application Running

Technical Takeaway

The responsibility chain in OpenShift is:

ComponentResponsibility
API ServerStores desired state
SchedulerChooses worker node
KubeletManages Pod lifecycle
CRI-OCreates and starts containers
Linux KernelProvides isolation and resource control

The Scheduler does not start containers.

The Kubelet does not directly create containers.

CRI-O is the engine that turns a container image into a running process.

Just like a car needs an engine to move, every OpenShift worker node needs CRI-O to run containers.

Without CRI-O:

  • Images cannot be pulled
  • Containers cannot be created
  • Applications cannot start

Understanding CRI-O is essential for troubleshooting OpenShift deployments, designing secure clusters, and operating production Kubernetes platforms.

Leave a Reply

Your email address will not be published. Required fields are marked *