OnCallReady

Lesson 30.21 · OpenShift · 10 min read

DeploymentConfig vs Deployment

In plain words

Imagine an old family car that still runs fine. Nobody sells parts for new features any more, the manufacturer says "it will keep working, but please buy the new model", and the new model does the same job with better brakes. Some features the old car had built in, like the automatic headlights, you now get as an add-on for the new one.

The DeploymentConfig is the old car: an OpenShift-only object, deprecated since 4.14, that manages ReplicationControllers through a deployer pod and has built-in image and config triggers plus lifecycle hooks. The Deployment is the new model from upstream Kubernetes. OpenShift gives Deployments the image trigger as an annotation, image.openshift.io/triggers, set with oc set triggers. Migration is a side-by-side swap with no downtime.

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

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
DeploymentConfigDeployment
APIapps.openshift.io/v1, deprecatedapps/v1, upstream
managesReplicationControllers <name>-<revision>ReplicaSets <name>-<hash>
rollout driven bya deployer pod <name>-<rev>-deploythe controller manager
triggersspec.triggers: ConfigChange, ImageChange (built in)none upstream; OpenShift adds the image.openshift.io/triggers annotation
lifecycle hookspre / mid / post hooks (run a pod with the new image, e.g. migrations)none - use a Job, an init container, or Argo/Helm hooks
selectora plain mapmatchLabels / matchExpressions
custom strategyyesno (Recreate / RollingUpdate)
when a node dies mid-rolloutthe deployer pod can get stuckcontroller-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:

DeploymentConfigDeployment
apiVersion: apps.openshift.io/v1, kind: DeploymentConfigapps/v1, Deployment
spec.selector: {app: x}spec.selector.matchLabels: {app: x}
spec.strategy.type: Rolling + rollingParamsRollingUpdate + rollingUpdate.maxSurge/maxUnavailable
RecreateRecreate
spec.triggers: ImageChangethe image.openshift.io/triggers annotation
spec.triggers: ConfigChangenothing - Deployments always roll on template change
lifecycle hooksa Job / init container
spec.testnothing

The steps that avoid downtime:

  1. 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 selects deploymentconfig=ledger-ui, change the Service to a label both share.
  2. Wait until the Deployment is Ready and in the endpoints.
  3. oc scale dc/ledger-ui --replicas=0, then oc delete dc ledger-ui (its RCs and pods go with it).
  4. 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

Why it helps

Any OpenShift project a few years old has DeploymentConfigs, and "migrate DCs to Deployments" is a very typical platform-team ticket before an upgrade or a move to GitOps. You need the field mapping (selector, strategy, triggers, hooks), and the zero-downtime order: create the Deployment next to the DC with matching labels, wait for endpoints, then scale down and delete the DC.

Knowing the differences also explains incidents in legacy apps: a rollout stuck because the deployer pod could not be scheduled when a node died, or pods whose Service selects deploymentconfig=ledger-ui and would drop out of the Service after migration. And the GitOps note matters: an image trigger rewriting the Deployment's image looks like drift to Argo CD, so you pick one source of truth.

FAQ

Are DeploymentConfigs going to be removed?

They are deprecated since OpenShift 4.14: every creation prints a warning, they get no new features, and the DeploymentConfig capability can be disabled at install time. Red Hat has not announced a removal version, hence the odd "unavailable in v4.10000+" wording. They still work, but new work should use Deployments, and existing ones should be migrated when convenient.

What did DeploymentConfigs have that Deployments lack?

Built-in triggers for image and config changes, lifecycle hooks (pre, mid and post pods running with the new image, often for migrations), custom strategies, and spec.test. For triggers, OpenShift offers the image.openshift.io/triggers annotation on Deployments, StatefulSets, DaemonSets, CronJobs and Jobs. For hooks, use a Job, an init container, or Helm or Argo CD hooks. Config changes always roll a Deployment anyway.

Why did Red Hat move away from DeploymentConfigs?

Mainly reliability and consistency. A DC rollout is driven by a deployer pod, so it depends on that pod being scheduled and surviving; if the node dies mid-rollout, the rollout can get stuck. Deployments are driven by the HA controller manager and have the behaviour everyone expects: proportional scaling, pause and resume, progressDeadlineSeconds, rollout undo. Using the upstream object also makes manifests portable.

How does the image trigger annotation work with GitOps?

The image trigger controller watches the ImageStreamTag and writes the new digest into the container's image field when the tag moves. To Argo CD that is a live change to a field declared in git, so the app becomes OutOfSync and self-heal may revert it. Either tell Argo CD to ignore that field with ignoreDifferences on the container image, or remove the trigger and promote images through git. Pick one source of truth.

What happens to pods when I delete a DeploymentConfig?

By default, oc delete dc cascades: it deletes the DC's ReplicationControllers and their pods. That is what you want after the replacement Deployment is serving, and dangerous before. --cascade=orphan leaves the RCs and pods running. In a migration, first confirm the new Deployment's pods are ready and in the Service's endpoints, then scale the DC to zero and delete it.

In an interview Mid

What is the difference between a DeploymentConfig and a Deployment, and how do you migrate without downtime?

A DeploymentConfig (apps.openshift.io/v1) is OpenShift's older, deprecated since 4.14 version: it manages ReplicationControllers (<name>-<revision>) through a deployer pod, has built-in ConfigChange and ImageChange triggers and pre/mid/post lifecycle hooks. A Deployment (apps/v1) manages ReplicaSets from the HA controller manager, with matchLabels selectors, pause/resume and progressDeadlineSeconds. The deployer pod is the weakness: a rollout can stall when it cannot be scheduled.

Mapping: Rolling -> RollingUpdate; the ImageChange trigger -> oc set triggers deploy/NAME --from-image=IS:TAG -c CONTAINER (an annotation); lifecycle hooks -> a Job or init container; ConfigChange -> nothing (Deployments always roll on template change).

Migration that never empties the Service:

  1. Create the Deployment next to the DC with the same pod template and labels; make sure the Service selects a label both share (not deploymentconfig=...).
  2. Wait until it is Ready and in the endpoints.
  3. oc scale dc/NAME --replicas=0, then oc delete dc NAME.
  4. Move HPAs, PDBs, Routes and monitoring that used the DC's labels.

Also asked: How would you plan the migration of many DeploymentConfigs across teams? · How do you give a Deployment the image-change trigger a DeploymentConfig had? · What replaces DeploymentConfig lifecycle hooks on a Deployment?

Practise this lesson in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.