
There are various ways to deploy an app.
- From GitHub (https://github.com)
- From Docker Hub (https://hub.docker.com)
- From an image
- From an image stream
- From a build
- From a template
- From a JSON or YAML file (templates)
An image is basically an object that can be used to deploy an operating system to an OpenShift container, such as a Linux Red Hat operating system. These are typically small, lightweight operating systems that just contain the core features needed to run the operating system in an OpenShift container. Often, images come precompiled with certain features. For example, a Python image would contain the requirements needed to run a Python application in the container. A Node.js image would contain the requirements needed to run a Node.js application in the container.
A deployment includes a certain image and the deployment creates a replica set (which is the number of pods that should be created) and then the replica set should spawn pods, and each pod should include a container. The container runs the operating system.
When deploying an application using a YAML file that has kind: Deployment, the application will be created as a deployment, not a deployment config. In this scenario, a replica set will be used to create the pods.
When deploying an application using a YAML file that has kind: DeploymentConfig (or using oc new-app command with the --as-deployment-config flag), the application will be created as a deployment config, not a deployment. In this scenario, a replication controller will be used to create the pods.
If you are not familiar with the oc command, refer to OpenShift - Getting Started with the oc command.
A YAML file can be used to create an object, such as a config map, a deployment, a project, a pod, a route, a secret, a service, et cetera. These files are known as templates.
In this example, let's say you have a Python FastAPI uvicorn app that contains the following.
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def home():
return "Hello World"
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8080
And a requirements.txt file that contains the following.
fastapi==0.115.6
uvicorn==0.34.0
And you create a config map named my-config-map in namespace my-project that contains app.py and requirements.txt
]$ oc create configmap my-config-map --namespace my-project --from-file app.py --from-file requirements.txt
configmap/my-config-map created
The config map should now contain app.py and requirements.txt
]$ oc get configmap my-config-map --namespace my-project --output yaml
apiVersion: v1
data:
app.py: |
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def home():
return "Hello World"
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8080)
requirements.txt: |
fastapi==0.115.6
uvicorn==0.34.0
kind: ConfigMap
metadata:
creationTimestamp: "2025-05-09T01:39:56Z"
name: my-config-map
namespace: my-project
resourceVersion: "517596296"
uid: 92028a99-53c8-4353-9f98-40f7e11569fc
For example, let's say you have a YAML file named deployment.yml that contains the following markup. Notice in this example that the deployment uses Python image registry.redhat.io/ubi8/python-311 (Python version 3.11) and mounts app.py and requirements.txt in the config map in the container and uses pip to install the requirements in the container and then uses python to run app.py.
- use kind: Deployment to create a Deployment > Replica Set > Pods
- use kind: DeploymentConfig to create a Deployment Config > Replication Controller > Pods
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-project
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- command:
- /bin/sh
- -c
- pip install --upgrade pip; pip install --requirement /opt/app-root/src/requirements.txt;
python /opt/app-root/src/app.py
image: registry.redhat.io/ubi8/python-311
name: my-app
ports:
- containerPort: 8080
protocol: TCP
volumeMounts:
- mountPath: /opt/app-root/src/app.py
name: my-config-map
subPath: app.py
- mountPath: /opt/app-root/src/requirements.txt
name: my-config-map
subPath: requirements.txt
volumes:
- configMap:
defaultMode: 420
name: my-config-map
name: my-config-map
The oc apply or oc create command with the -f or --filename option can be used to create the deployment using the template JSON or YAML file.
The oc replace command can be used to replace a deployment using a new or updated template JSON or YAML file.
The oc edit command can be used to update a deployments template YAML file.
~]$ oc create --filename deployment.yml
deploymentconfig.apps/my-app created
If kind: Deployment was used, the oc get deployments command can be used to list the deployments.
]$ oc get deployments --namespace my-project
NAME READY UP-TO-DATE AVAILABLE AGE
my-app 1/1 1 1 52s
If kind: DeploymentConfig was used, the oc get deploymentconfig (or oc get dc) command can be used to list the deployment configs.
~]$ oc get deploymentconfigs --namespace my-project
NAME REVISION DESIRED CURRENT TRIGGERED BY
my-app 1 1 1 config,image(my-app:latest)
If kind: Deployment was used, the oc get replicaset (or oc get rs) command can be used to list the replica sets.
~]# oc get replicaset --namespace my-project
NAME DESIRED CURRENT READY AGE
my-app-5b9879db6d 1 1 1 205d
If kind: DeploymentConfig was used, the oc get replicationcontroller (or oc get rc) command can be used to list the replication controllers.
Replicas is the number of pods that should be created for the deployment.
~]# oc get replicationcontroller --namespace my-project
NAME DESIRED CURRENT READY AGE
my-app-5b9879db6d 1 1 1 205d
The oc get pods command can be used to determine if the pods are up and running.
~]$ oc get pods --namespace my-project
NAME READY STATUS RESTARTS AGE
my-app-rhz7c 1/1 Running 0 23s
Did you find this article helpful?
If so, consider buying me a coffee over at