Why you still meet DeploymentConfigs
You inherit a project where oc get deploy shows nothing, yet pods are running. They belong to a DeploymentConfig, OpenShift's older version of a Deployment. Sooner or later someone asks you to replace it - without dropping traffic.
What you need to know already: Deployments, ReplicaSets and rolling updates (15.16, 17.24); selectors and endpoints (15.26, 16.1); ImageStreams and image triggers (30.18); Argo CD drift and ignoreDifferences (26.12); Helm and Argo hooks (25.26, 26.9).
The words you need first
- DeploymentConfig (
dc) - the OpenShift-only, deprecated ancestor of Deployment. - ReplicationController (
rc) - the ancestor of ReplicaSet; DCs still use it. - Deployer pod - a helper pod that performs one DC rollout.
- Lifecycle hook - a command a DC runs in a pod before, during or after a rollout (e.g. a database migration).
The OpenShift-only Deployment
Before Kubernetes had a Deployment, OpenShift had the DeploymentConfig (dc, apps.openshift.io/v1). You will find them in any project older than a few years and in old templates. They are deprecated since OpenShift 4.14; the API server says so every time one is created:
# ledger-ui = the DeploymentConfig mission's app, in project store
oc apply -f ledger-ui-dc.yaml
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
deploymentconfig.apps.openshift.io/ledger-ui created
("v4.10000+" is Red Hat's way of saying: no removal date announced - but no new features and no reason to create new ones.) The DeploymentConfig cluster capability can even be disabled at install time. New work uses Deployments.
How a DC works
A DC keeps one ReplicationController per revision (ledger-ui-1, ledger-ui-2) and labels its pods with deploymentconfig=<name> and deployment=<name>-<revision>. TRIGGERED BY lists what starts a new revision: config (the DC changed) and image(...) (that image stream tag moved):
oc get dc
NAME REVISION DESIRED CURRENT TRIGGERED BY
ledger-ui 2 2 2 config,image(ledger-ui:latest)
oc get rc
NAME DESIRED CURRENT READY AGE
ledger-ui-1 0 0 0 3d
ledger-ui-2 2 2 2 2h
oc get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
ledger-ui-2-4kq8x 1/1 Running 0 2h app=ledger-ui,deployment=ledger-ui-2,deploymentconfig=ledger-ui
ledger-ui-2-z9n2c 1/1 Running 0 2h app=ledger-ui,deployment=ledger-ui-2,deploymentconfig=ledger-ui
| DeploymentConfig | Deployment | |
|---|---|---|
| API | apps.openshift.io/v1, deprecated | apps/v1, upstream |
| manages | ReplicationControllers <name>-<revision> | ReplicaSets <name>-<hash> |
| rollout driven by | a deployer pod <name>-<rev>-deploy | the controller manager |
| triggers | spec.triggers: ConfigChange, ImageChange (built in) | none upstream; OpenShift adds the image.openshift.io/triggers annotation |
| lifecycle hooks | pre / mid / post hooks (run a pod with the new image, e.g. migrations) | none - use a Job, an init container, or Argo/Helm hooks |
| selector | a plain map | matchLabels / matchExpressions |
| custom strategy | yes | no (Recreate / RollingUpdate) |
| when a node dies mid-rollout | the deployer pod can get stuck | controller-manager is HA |
The last row is why Red Hat moved on: the deployer pod made rollouts depend on a pod being scheduled, and the proportional-scaling, pause/resume and progressDeadlineSeconds behaviour of Deployments (17.24) is what everyone expects now.
DC commands you will see in old runbooks (rollout latest = start a new revision now):
oc rollout latest dc/ledger-ui
deploymentconfig.apps.openshift.io/ledger-ui rolled out
oc logs dc/ledger-ui
oc rollout history dc/ledger-ui
oc rollout undo dc/ledger-ui
Image triggers on a Deployment
What people miss most is the ImageChange trigger. OpenShift gives Deployments, StatefulSets, DaemonSets, CronJobs and Jobs the same thing through an annotation that the image trigger controller watches. oc set triggers deploy/NAME --from-image=IS:TAG -c CONTAINER writes that annotation (-c = which container's image to update); oc set triggers deploy/NAME alone lists the triggers:
oc set triggers deploy/ledger-ui --from-image=ledger-ui:latest -c ledger-ui
deployment.apps/ledger-ui triggers updated
oc get deploy ledger-ui -o jsonpath='{.metadata.annotations.image\.openshift\.io/triggers}{"\n"}'
[{"from":{"kind":"ImageStreamTag","name":"ledger-ui:latest"},"fieldPath":"spec.template.spec.containers[?(@.name==\"ledger-ui\")].image"}]
oc set triggers deploy/ledger-ui
NAME TYPE VALUE AUTO
deployments/ledger-ui config true
deployments/ledger-ui image ledger-ui:latest (ledger-ui) true
When ledger-ui:latest moves, the controller writes the new digest into containers[?(@.name=="ledger-ui")].image and the Deployment rolls - exactly the DC ImageChange trigger. --manual sets "paused":"true" (the trigger stops following).
GitOps note: an Argo CD app that manages this Deployment will see the controller's image change as drift. Either let Argo ignore that field (ignoreDifferences on the container image) or do promotion in Git instead of with image streams - pick one source of truth.
Migrating a DC to a Deployment
The mapping, field by field:
| DeploymentConfig | Deployment |
|---|---|
apiVersion: apps.openshift.io/v1, kind: DeploymentConfig | apps/v1, Deployment |
spec.selector: {app: x} | spec.selector.matchLabels: {app: x} |
spec.strategy.type: Rolling + rollingParams | RollingUpdate + rollingUpdate.maxSurge/maxUnavailable |
Recreate | Recreate |
spec.triggers: ImageChange | the image.openshift.io/triggers annotation |
spec.triggers: ConfigChange | nothing - Deployments always roll on template change |
| lifecycle hooks | a Job / init container |
spec.test | nothing |
The steps that avoid downtime:
- Create the Deployment next to the DC with the same pod template (labels included). If the Service selects
app=ledger-ui, the new pods join it immediately; if it selectsdeploymentconfig=ledger-ui, change the Service to a label both share. - Wait until the Deployment is Ready and in the endpoints.
oc scale dc/ledger-ui --replicas=0, thenoc delete dc ledger-ui(its RCs and pods go with it).- Move Routes/HPAs/PDBs/monitoring that referenced the DC's labels.
Keep one thing in mind: oc delete dc without --cascade=orphan deletes the ReplicationControllers and their pods - after step 2, that is what you want.
What you can now do
- Recognise a DeploymentConfig and read it: revisions, RCs, triggers.
- Give a Deployment the same image trigger with
oc set triggers. - Migrate a DC to a Deployment in the order that never empties the Service.