Repository: linkerd/linkerd2
Pull request #5361 (2020-12-10 15:22:40)

jaeger: clean up values pkg
With this new way of chart rendering i.e using helm pkg directly
instead of using our own struct, we no longer need the `Values`
struct to be present, as all the rendering happens through
`map[string]interface{}`

This might be useful in future when we do validation of values, which
can also be done directly with out this, unless we don't want to deal
with conversions

Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>


adleong: @Pothulapati I think this just needs a manual merge resolution to be happy
Pothulapati: >want to mention that we might consider bringing this back (or something similar) in the future if we want to programmatically manipulate the values. Coercing a map[string]interface{} into a typed struct is more ergonomic and safer to work with.

Yeah, That is my thinking too. We should only golang build types, if there is a need (could be any extension)

Patch:
diff --git a/.editorconfig b/.editorconfig
index 80f0001b6..00bfa6a08 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -29,6 +29,9 @@ indent_style = space
 [*.yml]
 indent_size = 2
 
+[*.yaml]
+indent_size = 2
+
 [*.proto]
 indent_size = 2
 indent_style = space
diff --git a/bin/docker-build-jaeger-webhook b/bin/docker-build-jaeger-webhook
index dd1164461..3e7e20a03 100755
--- a/bin/docker-build-jaeger-webhook
+++ b/bin/docker-build-jaeger-webhook
@@ -15,7 +15,7 @@ rootdir=$( cd "$bindir"/.. && pwd )
 # shellcheck source=_tag.sh
 . "$bindir"/_tag.sh
 
-dockerfile=$rootdir/jaeger/proxy-mutator/Dockerfile
+dockerfile=$rootdir/jaeger/injector/Dockerfile
 tag=$(head_root_tag)
 docker_build jaeger-webhook "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag"
 
diff --git a/bin/k3d b/bin/k3d
index 644d490ca..8023a0b64 100755
--- a/bin/k3d
+++ b/bin/k3d
@@ -2,7 +2,7 @@
 
 set -eu
 
-k3dversion=v3.0.2
+k3dversion=v3.4.0
 
 bindir=$( cd "${0%/*}" && pwd )
 targetbin=$( cd "$bindir"/.. && pwd )/target/bin
diff --git a/bin/web b/bin/web
index af460b49f..5cdc8dd10 100755
--- a/bin/web
+++ b/bin/web
@@ -17,7 +17,7 @@ USAGE: web <command>
   * integration (cloud|local) - run the integration tests
       Note: Sauce Connect tunnel must be open for cloud tests
   * port-forward - setup a tunnel to a controller component and port
-      Example: port-forward controller 8085
+      Example: port-forward linkerd controller 8085
   * run - run a local server (sans. reloading)
   * setup - get the environment setup for development
       Note: any command line options are passed on to yarn
@@ -27,8 +27,8 @@ USAGE
 }; function --help { '-h' ;}
 
 check-for-linkerd() {
-  controller_pod=$(get-pod controller)
-  grafana_pod=$(get-pod grafana)
+  controller_pod=$(get-pod linkerd controller)
+  grafana_pod=$(get-pod linkerd-viz grafana)
 
   if [[ -z "${controller_pod// }" ]]; then
     err 'Controller is not running. Have you installed Linkerd?'
@@ -59,25 +59,31 @@ build() {
 }
 
 get-pod() {
-  if [ $# -ne 1 ]; then
-    echo "usage: bin/${0##*/} get-pod component-name" >&2
+  if [ $# -ne 2 ]; then
+    echo "usage: bin/${0##*/} get-pod namespace component-name" >&2
     exit 1
   fi
 
-  kubectl --namespace=linkerd get po \
-    --selector=linkerd.io/control-plane-component="$1" \
+
+  selector="linkerd.io/control-plane-component=$2"
+  if [ "$1" == "linkerd-viz" ]; then
+    selector="component=$2"
+  fi
+
+  kubectl --namespace="$1" get po \
+    --selector="$selector" \
     --field-selector='status.phase==Running' \
     -o jsonpath='{.items[*].metadata.name}'
 }
 
 port-forward() {
-  if [ $# -ne 2 ]; then
-    echo "usage: bin/${0##*/} port-forward component-name port-number" >&2
+  if [ $# -ne 3 ]; then
+    echo "usage: bin/${0##*/} port-forward namespace component-name port-number" >&2
     exit 1
   fi
 
   nc -z localhost "$2" || \
-    kubectl --namespace=linkerd port-forward "$(get-pod "$1")" "$2:$2"
+    kubectl --namespace="$1" port-forward "$(get-pod "$1" "$2")" "$3:$3"
 }
 
 run() {
@@ -89,8 +95,8 @@ run() {
   build
 
   check-for-linkerd && (
-    port-forward controller 8085 &
-    port-forward grafana 3000 &
+    port-forward linkerd controller 8085 &
+    port-forward linkerd-viz grafana 3000 &
   )
 
   cd "$ROOT"/web && \
diff --git a/charts/linkerd2/templates/controller.yaml b/charts/linkerd2/templates/controller.yaml
index 06477f197..a4f880a98 100644
--- a/charts/linkerd2/templates/controller.yaml
+++ b/charts/linkerd2/templates/controller.yaml
@@ -21,6 +21,24 @@ spec:
   - name: http
     port: 8085
     targetPort: 8085
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: controller
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: controller
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/charts/linkerd2/templates/destination.yaml b/charts/linkerd2/templates/destination.yaml
index e68ec7f3d..e26a0b94c 100644
--- a/charts/linkerd2/templates/destination.yaml
+++ b/charts/linkerd2/templates/destination.yaml
@@ -40,6 +40,24 @@ spec:
   - name: grpc
     port: 8086
     targetPort: 8086
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: destination
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: destination
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/charts/linkerd2/templates/identity.yaml b/charts/linkerd2/templates/identity.yaml
index 3821957f2..12a7f3d9f 100644
--- a/charts/linkerd2/templates/identity.yaml
+++ b/charts/linkerd2/templates/identity.yaml
@@ -60,6 +60,24 @@ spec:
   - name: grpc
     port: 8080
     targetPort: 8080
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: identity
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: identity
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/charts/linkerd2/templates/proxy-injector.yaml b/charts/linkerd2/templates/proxy-injector.yaml
index 21d0194d2..1feff26df 100644
--- a/charts/linkerd2/templates/proxy-injector.yaml
+++ b/charts/linkerd2/templates/proxy-injector.yaml
@@ -127,3 +127,21 @@ spec:
   - name: proxy-injector
     port: 443
     targetPort: proxy-injector
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: proxy-injector
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: proxy-injector
+{{- end }}
diff --git a/charts/linkerd2/templates/sp-validator.yaml b/charts/linkerd2/templates/sp-validator.yaml
index d3723822c..00d33cd3d 100644
--- a/charts/linkerd2/templates/sp-validator.yaml
+++ b/charts/linkerd2/templates/sp-validator.yaml
@@ -21,6 +21,24 @@ spec:
   - name: sp-validator
     port: 443
     targetPort: sp-validator
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: sp-validator
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: sp-validator
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/jaeger/charts/jaeger/templates/proxy-mutator.yaml b/jaeger/charts/jaeger/templates/jaeger-injector.yaml
similarity index 74%
rename from jaeger/charts/jaeger/templates/proxy-mutator.yaml
rename to jaeger/charts/jaeger/templates/jaeger-injector.yaml
index 7dd4ded99..d2e9c8fbd 100644
--- a/jaeger/charts/jaeger/templates/proxy-mutator.yaml
+++ b/jaeger/charts/jaeger/templates/jaeger-injector.yaml
@@ -1,28 +1,28 @@
 ---
 ###
-### Proxy Mutator
+### Jaeger Injector
 ###
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   labels:
-    app.kubernetes.io/name: proxy-mutator
+    app.kubernetes.io/name: jaeger-injector
     app.kubernetes.io/part-of: Linkerd
-    component: proxy-mutator
-  name: proxy-mutator
+    component: jaeger-injector
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
 spec:
   replicas: 1
   selector:
     matchLabels:
-      component: proxy-mutator
+      component: jaeger-injector
   template:
     metadata:
       annotations:
         checksum/config: {{ include (print $.Template.BasePath "/rbac.yaml") . | sha256sum }}
       labels:
-        component: proxy-mutator
+        component: jaeger-injector
     spec:
       containers:
       - args:
@@ -36,10 +36,10 @@ spec:
             path: /ping
             port: 9995
           initialDelaySeconds: 10
-        name: proxy-mutator
+        name: jaeger-injector
         ports:
         - containerPort: 8443
-          name: proxy-mutator
+          name: jaeger-injector
         - containerPort: 9995
           name: admin-http
         readinessProbe:
@@ -51,25 +51,25 @@ spec:
         - mountPath: /var/run/linkerd/tls
           name: tls
           readOnly: true
-      serviceAccountName: proxy-mutator
+      serviceAccountName: jaeger-injector
       volumes:
       - name: tls
         secret:
-          secretName: proxy-mutator-k8s-tls
+          secretName: jaeger-injector-k8s-tls
 ---
 kind: Service
 apiVersion: v1
 metadata:
-  name: proxy-mutator
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
   labels:
-    component: proxy-mutator
+    component: jaeger-injector
 spec:
   type: ClusterIP
   selector:
-    component: proxy-mutator
+    component: jaeger-injector
   ports:
-  - name: proxy-mutator
+  - name: jaeger-injector
     port: 443
-    targetPort: proxy-mutator
+    targetPort: jaeger-injector
 
diff --git a/jaeger/charts/jaeger/templates/rbac.yaml b/jaeger/charts/jaeger/templates/rbac.yaml
index ae613a10a..c3004a17f 100644
--- a/jaeger/charts/jaeger/templates/rbac.yaml
+++ b/jaeger/charts/jaeger/templates/rbac.yaml
@@ -21,13 +21,13 @@ metadata:
 
 ---
 ###
-### Proxy Mutator RBAC
+### Jaeger Injector RBAC
 ###
 ---
 kind: ClusterRole
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
-  name: linkerd-jaeger-{{.Values.namespace}}-proxy-mutator
+  name: linkerd-jaeger-injector
   labels:
     linkerd.io/extension: linkerd-jaeger
 rules:
@@ -38,32 +38,32 @@ rules:
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
-  name: linkerd-jaeger-{{.Values.namespace}}-proxy-mutator
+  name: linkerd-jaeger-injector
   labels:
     linkerd.io/extension: linkerd-jaeger
 subjects:
 - kind: ServiceAccount
-  name: proxy-mutator
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
   apiGroup: ""
 roleRef:
   kind: ClusterRole
-  name: linkerd-jaeger-{{.Values.namespace}}-proxy-mutator
+  name: linkerd-jaeger-injector
   apiGroup: rbac.authorization.k8s.io
 ---
 kind: ServiceAccount
 apiVersion: v1
 metadata:
-  name: proxy-mutator
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
 ---
-{{- $host := printf "proxy-mutator.%s.svc" .Values.namespace }}
+{{- $host := printf "jaeger-injector.%s.svc" .Values.namespace }}
 {{- $ca := genSelfSignedCert $host (list) (list $host) 365 }}
 {{- if (not .Values.webhook.externalSecret) }}
 kind: Secret
 apiVersion: v1
 metadata:
-  name: proxy-mutator-k8s-tls
+  name: jaeger-injector-k8s-tls
   namespace: {{ .Values.namespace }}
 type: kubernetes.io/tls
 data:
@@ -74,11 +74,11 @@ data:
 apiVersion: admissionregistration.k8s.io/v1beta1
 kind: MutatingWebhookConfiguration
 metadata:
-  name: linkerd-proxy-mutator-webhook-config
+  name: linkerd-jaeger-injector-webhook-config
   labels:
     linkerd.io/extension: linkerd-jaeger
 webhooks:
-- name: proxy-mutator.linkerd.io
+- name: jaeger-injector.linkerd.io
   {{- if .Values.webhook.namespaceSelector }}
   namespaceSelector: 
 {{ toYaml .Values.webhook.namespaceSelector | trim | indent 4 -}}
@@ -89,7 +89,7 @@ webhooks:
   {{- end }}
   clientConfig:
     service:
-      name: proxy-mutator
+      name: jaeger-injector
       namespace: {{ .Values.namespace }}
       path: "/"
 {{- if and (.Values.webhook.externalSecret) (empty .Values.webhook.caBundle) }}
diff --git a/jaeger/cmd/install.go b/jaeger/cmd/install.go
index 88b2ada43..d5ac37938 100644
--- a/jaeger/cmd/install.go
+++ b/jaeger/cmd/install.go
@@ -23,7 +23,7 @@ import (
 var (
 	templatesJaeger = []string{
 		"templates/namespace.yaml",
-		"templates/proxy-mutator.yaml",
+		"templates/jaeger-injector.yaml",
 		"templates/rbac.yaml",
 		"templates/tracing.yaml",
 	}
diff --git a/jaeger/proxy-mutator/Dockerfile b/jaeger/injector/Dockerfile
similarity index 79%
rename from jaeger/proxy-mutator/Dockerfile
rename to jaeger/injector/Dockerfile
index af37ea2e6..aafe5fadc 100644
--- a/jaeger/proxy-mutator/Dockerfile
+++ b/jaeger/injector/Dockerfile
@@ -19,14 +19,14 @@ COPY controller controller
 COPY charts/partials charts/partials
 
 ARG TARGETARCH
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/proxy-mutator -tags prod -mod=readonly -ldflags "-s -w" ./jaeger/proxy-mutator/cmd
+RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/injector -tags prod -mod=readonly -ldflags "-s -w" ./jaeger/injector/cmd
 
 ## package runtime
 FROM scratch
 ENV PATH=$PATH:/go/bin
-COPY --from=golang /out/proxy-mutator /go/bin/proxy-mutator
+COPY --from=golang /out/injector /go/bin/injector
 
 ARG LINKERD_VERSION
 ENV LINKERD_CONTAINER_VERSION_OVERRIDE=${LINKERD_VERSION}
 
-ENTRYPOINT ["/go/bin/proxy-mutator"]
+ENTRYPOINT ["/go/bin/injector"]
diff --git a/jaeger/proxy-mutator/cmd/main.go b/jaeger/injector/cmd/main.go
similarity index 86%
rename from jaeger/proxy-mutator/cmd/main.go
rename to jaeger/injector/cmd/main.go
index a9b35a5e1..2c6d19d07 100644
--- a/jaeger/proxy-mutator/cmd/main.go
+++ b/jaeger/injector/cmd/main.go
@@ -8,12 +8,12 @@ import (
 
 	"github.com/linkerd/linkerd2/controller/k8s"
 	"github.com/linkerd/linkerd2/controller/webhook"
-	"github.com/linkerd/linkerd2/jaeger/proxy-mutator/mutator"
+	"github.com/linkerd/linkerd2/jaeger/injector/mutator"
 	"github.com/linkerd/linkerd2/pkg/flags"
 )
 
 func main() {
-	cmd := flag.NewFlagSet("proxy-mutator", flag.ExitOnError)
+	cmd := flag.NewFlagSet("injector", flag.ExitOnError)
 	metricsAddr := cmd.String("metrics-addr", fmt.Sprintf(":%d", 9995),
 		"address to serve scrapable metrics on")
 	addr := cmd.String("addr", ":8443", "address to serve on")
@@ -29,7 +29,7 @@ func main() {
 		context.Background(),
 		[]k8s.APIResource{k8s.NS},
 		mutator.Mutate(*collectorSvcAddr, *collectorSvcAccount),
-		"linkerd-proxy-mutator",
+		"linkerd-jaeger-injector",
 		*metricsAddr,
 		*addr,
 		*kubeconfig,
diff --git a/jaeger/proxy-mutator/mutator/patch.go b/jaeger/injector/mutator/patch.go
similarity index 100%
rename from jaeger/proxy-mutator/mutator/patch.go
rename to jaeger/injector/mutator/patch.go
diff --git a/jaeger/proxy-mutator/mutator/webhook.go b/jaeger/injector/mutator/webhook.go
similarity index 100%
rename from jaeger/proxy-mutator/mutator/webhook.go
rename to jaeger/injector/mutator/webhook.go
diff --git a/jaeger/values/values.go b/jaeger/values/values.go
deleted file mode 100644
index df8e16e44..000000000
--- a/jaeger/values/values.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package values
-
-import (
-	"fmt"
-
-	"github.com/linkerd/linkerd2/jaeger/static"
-	"github.com/linkerd/linkerd2/pkg/charts"
-	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
-	"github.com/linkerd/linkerd2/pkg/version"
-	"helm.sh/helm/v3/pkg/chart/loader"
-	"helm.sh/helm/v3/pkg/chartutil"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"sigs.k8s.io/yaml"
-)
-
-// Values represents the values of jaeger template
-type Values struct {
-	Namespace      string    `json:"namespace"`
-	CliVersion     string    `json:"cliVersion"`
-	Collector      collector `json:"collector"`
-	Jaeger         jaeger    `json:"jaeger"`
-	LinkerdVersion string    `json:"linkerdVersion"`
-	Webhook        webhook   `json:"webhook"`
-}
-
-type collector struct {
-	Resources l5dcharts.Resources `json:"resources"`
-	Image     l5dcharts.Image     `json:"image"`
-}
-
-type jaeger struct {
-	Resources l5dcharts.Resources `json:"resources"`
-	Image     l5dcharts.Image     `json:"image"`
-}
-
-type webhook struct {
-	ExternalSecret      bool                  `json:"externalSecret"`
-	CrtPEM              string                `json:"crtPEM"`
-	KeyPEM              string                `json:"keyPEM"`
-	CaBundle            string                `json:"caBundle"`
-	CollectorSvcAddr    string                `json:"collectorSvcAddr"`
-	CollectorSvcAccount string                `json:"collectorSvcAccount"`
-	FailurePolicy       string                `json:"failurePolicy"`
-	Image               l5dcharts.Image       `json:"image"`
-	LogLevel            string                `json:"logLevel"`
-	NamespaceSelector   *metav1.LabelSelector `json:"namespaceSelector"`
-	ObjectSelector      *metav1.LabelSelector `json:"objectSelector"`
-}
-
-// NewValues returns a new instance of the Values type.
-// TODO: Add HA logic
-func NewValues() (*Values, error) {
-	chartDir := fmt.Sprintf("%s/", "jaeger")
-	v, err := readDefaults(chartDir)
-	if err != nil {
-		return nil, err
-	}
-	v.CliVersion = version.Version
-
-	return v, nil
-}
-
-// readDefaults read all the default variables from the values.yaml file.
-// chartDir is the root directory of the Helm chart where values.yaml is.
-func readDefaults(chartDir string) (*Values, error) {
-	valuesFile := &loader.BufferedFile{
-		Name: chartutil.ValuesfileName,
-	}
-
-	if err := charts.ReadFile(static.Templates, chartDir, valuesFile); err != nil {
-		return nil, err
-	}
-
-	var values Values
-	if err := yaml.Unmarshal(valuesFile.Data, &values); err != nil {
-		return nil, err
-	}
-
-	return &values, nil
-}
diff --git a/pkg/inject/inject.go b/pkg/inject/inject.go
index 59efbf9d9..1d5e9ad74 100644
--- a/pkg/inject/inject.go
+++ b/pkg/inject/inject.go
@@ -599,7 +599,7 @@ func (conf *ResourceConfig) serviceAccountVolumeMount() *corev1.VolumeMount {
 // annotations.
 func (conf *ResourceConfig) injectObjectMeta(values *patch) {
 
-	values.Annotations[k8s.ProxyVersionAnnotation] = conf.values.GetGlobal().Proxy.Image.Version
+	values.Annotations[k8s.ProxyVersionAnnotation] = values.GetGlobal().Proxy.Image.Version
 
 	if values.Identity == nil || values.GetGlobal().Proxy.DisableIdentity {
 		values.Annotations[k8s.IdentityModeAnnotation] = k8s.IdentityModeDisabled
diff --git a/pkg/k8s/portforward.go b/pkg/k8s/portforward.go
index 94b190490..e501e909b 100644
--- a/pkg/k8s/portforward.go
+++ b/pkg/k8s/portforward.go
@@ -8,7 +8,6 @@ import (
 	"net/http"
 	"net/url"
 	"os"
-	"strings"
 
 	log "github.com/sirupsen/logrus"
 	corev1 "k8s.io/api/core/v1"
@@ -81,7 +80,12 @@ func NewPortForward(
 	podName := ""
 	for _, pod := range podList.Items {
 		if pod.Status.Phase == corev1.PodRunning {
-			if strings.HasPrefix(pod.Name, deployName) {
+			grandparent, err := getDeploymentForPod(ctx, k8sAPI, pod)
+			if err != nil {
+				log.Warnf("Failed to get deploy for pod [%s]: %s", pod.Name, err)
+				continue
+			}
+			if grandparent == deployName {
 				podName = pod.Name
 				break
 			}
@@ -95,6 +99,22 @@ func NewPortForward(
 	return newPortForward(k8sAPI, namespace, podName, host, localPort, remotePort, emitLogs)
 }
 
+func getDeploymentForPod(ctx context.Context, k8sAPI *KubernetesAPI, pod corev1.Pod) (string, error) {
+	parents := pod.GetOwnerReferences()
+	if len(parents) != 1 {
+		return "", nil
+	}
+	rs, err := k8sAPI.AppsV1().ReplicaSets(pod.Namespace).Get(ctx, parents[0].Name, metav1.GetOptions{})
+	if err != nil {
+		return "", err
+	}
+	grandparents := rs.GetOwnerReferences()
+	if len(grandparents) != 1 {
+		return "", nil
+	}
+	return grandparents[0].Name, nil
+}
+
 func newPortForward(
 	k8sAPI *KubernetesAPI,
 	namespace, podName string,
diff --git a/viz/charts/linkerd-viz/README.md b/viz/charts/linkerd-viz/README.md
index e58a8b3cb..0a8b0aebd 100644
--- a/viz/charts/linkerd-viz/README.md
+++ b/viz/charts/linkerd-viz/README.md
@@ -75,7 +75,8 @@ Kubernetes: `>=1.13.0-0`
 | createdByAnnotation | string | `"linkerd.io/created-by"` |  |
 | dashboard.UID | int | `2103` |  |
 | dashboard.enforcedHostRegexp | string | `""` | Host header validation regex for the dashboard. See the [Linkerd documentation](https://linkerd.io/2/tasks/exposing-dashboard) for more information |
-| dashboard.image.name | string | `"ghcr.io/linkerd/web"` | Docker image name for the web instance |
+| dashboard.image.name | string | `"web"` | Docker image name for the web instance |
+| dashboard.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the web instance |
 | dashboard.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the web instance |
 | dashboard.logLevel | string | `"info"` | log level of the dashboard component |
 | dashboard.replicas | int | `1` | Number of replicas of dashboard |
@@ -84,11 +85,13 @@ Kubernetes: `>=1.13.0-0`
 | dashboard.resources.memory.limit | string | `nil` | Maximum amount of memory that web container can use |
 | dashboard.resources.memory.request | string | `nil` | Amount of memory that the web container requests |
 | dashboard.restrictPrivileges | bool | `false` | Restrict the Linkerd Dashboard's default privileges to disallow Tap and Check |
+| defaultRegistry | string | `"ghcr.io/linkerd"` | Default Docker Registry |
 | extensionAnnotation | string | `"linkerd.io/extension"` |  |
 | globalLogLevel | string | `"info"` | Log level for all the viz components |
 | globalUID | int | `2103` | UID for all the viz components |
 | grafana.enabled | bool | `true` | toggle field to enable or disable grafana |
-| grafana.image.name | string | `"ghcr.io/linkerd/grafana"` | Docker image name for the grafana instance |
+| grafana.image.name | string | `"grafana"` | Docker image name for the grafana instance |
+| grafana.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the grafana instance |
 | grafana.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the grafana instance |
 | grafana.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the grafana container can use |
 | grafana.resources.cpu.request | string | `nil` | Amount of CPU units that the grafana container requests |
@@ -105,8 +108,9 @@ Kubernetes: `>=1.13.0-0`
 | prometheus.args | object | `{"config.file":"/etc/prometheus/prometheus.yml","log.level":"info","storage.tsdb.path":"/data","storage.tsdb.retention.time":"6h"}` | Command line options for Prometheus binary |
 | prometheus.enabled | bool | `true` | toggle field to enable or disable prometheus |
 | prometheus.globalConfig | object | `{"evaluation_interval":"10s","scrape_interval":"10s","scrape_timeout":"10s"}` | The global configuration specifies parameters that are valid in all other configuration contexts. |
-| prometheus.image.name | string | `"prom/prometheus"` | Docker image name for the prometheus instance |
+| prometheus.image.name | string | `"prometheus"` | Docker image name for the prometheus instance |
 | prometheus.image.pullPolicy | string | `"Always"` |  |
+| prometheus.image.registry | string | `"prom"` | Docker registry for the prometheus instance |
 | prometheus.image.tag | string | `"v2.19.3"` | Docker image tag for the prometheus instance |
 | prometheus.remoteWrite | string | `nil` | Allows transparently sending samples to an endpoint. Mostly used for long term storage. |
 | prometheus.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the prometheus container can use |
@@ -121,8 +125,9 @@ Kubernetes: `>=1.13.0-0`
 | tap.caBundle | string | `""` | Bundle of CA certificates for Tap component. If not provided then Helm will use the certificate generated  for `tap.crtPEM`. If `tap.externalSecret` is set to true, this value must be set, as no certificate will be generated. |
 | tap.crtPEM | string | `""` | Certificate for the Tap component. If not provided then Helm will generate one. |
 | tap.externalSecret | bool | `false` | Do not create a secret resource for the Tap component. If this is set to `true`, the value `tap.caBundle` must be set (see below). |
-| tap.image.name | string | `"ghcr.io/linkerd/controller"` | Docker image name for the grafana instance |
-| tap.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the grafana instance |
+| tap.image.name | string | `"controller"` | Docker image name for the tap instance |
+| tap.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the tap instance |
+| tap.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the tap instance |
 | tap.keyPEM | string | `""` | Certificate key for Tap component. If not provided then Helm will generate one.  |
 | tap.logLevel | string | `"info"` | log level of the tap component |
 | tap.replicas | int | `1` |  |
diff --git a/viz/charts/linkerd-viz/templates/grafana.yaml b/viz/charts/linkerd-viz/templates/grafana.yaml
index 391b2814d..521494307 100644
--- a/viz/charts/linkerd-viz/templates/grafana.yaml
+++ b/viz/charts/linkerd-viz/templates/grafana.yaml
@@ -127,7 +127,7 @@ spec:
         # see https://github.com/grafana/grafana/issues/20096
         - name: GODEBUG
           value: netdns=go
-        image: {{.Values.grafana.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.grafana.image.tag}}
+        image: {{.Values.grafana.image.registry}}/{{.Values.grafana.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.grafana.image.tag}}
         imagePullPolicy: {{.Values.imagePullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/templates/prometheus.yaml b/viz/charts/linkerd-viz/templates/prometheus.yaml
index aab1cc3b1..fc9af7058 100644
--- a/viz/charts/linkerd-viz/templates/prometheus.yaml
+++ b/viz/charts/linkerd-viz/templates/prometheus.yaml
@@ -242,7 +242,7 @@ spec:
         {{- range $key, $value := .Values.prometheus.args}}
         - --{{ $key }}{{ if $value }}={{ $value }}{{ end }}
         {{- end }}
-        image: {{.Values.prometheus.image.name}}:{{.Values.prometheus.image.tag}}
+        image: {{.Values.prometheus.image.registry}}/{{.Values.prometheus.image.name}}:{{.Values.prometheus.image.tag}}
         imagePullPolicy: {{.Values.prometheus.imagepullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/templates/tap.yaml b/viz/charts/linkerd-viz/templates/tap.yaml
index cbca18e07..6cce931e8 100644
--- a/viz/charts/linkerd-viz/templates/tap.yaml
+++ b/viz/charts/linkerd-viz/templates/tap.yaml
@@ -81,7 +81,7 @@ spec:
         - -controller-namespace={{.Values.linkerdNamespace}}
         - -log-level={{.Values.tap.logLevel}}
         - -identity-trust-domain={{.Values.identityTrustDomain}}
-        image: {{.Values.tap.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.tap.image.tag}}
+        image: {{.Values.tap.image.registry}}/{{.Values.tap.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.tap.image.tag}}
         imagePullPolicy: {{.Values.tap.image.pullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/templates/web.yaml b/viz/charts/linkerd-viz/templates/web.yaml
index 1ced39e46..038c6013c 100644
--- a/viz/charts/linkerd-viz/templates/web.yaml
+++ b/viz/charts/linkerd-viz/templates/web.yaml
@@ -84,7 +84,7 @@ spec:
         {{- $hostAbbrev := replace "." "\\." (printf "linkerd-web.%s.svc" .Values.namespace) }}
         - -enforced-host=^(localhost|127\.0\.0\.1|{{ $hostFull }}|{{ $hostAbbrev }}|\[::1\])(:\d+)?$
         {{- end}}
-        image: {{.Values.dashboard.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.dashboard.image.tag}}
+        image: {{.Values.dashboard.image.registry}}/{{.Values.dashboard.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.dashboard.image.tag}}
         imagePullPolicy: {{.Values.dashboard.image.pullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/values.yaml b/viz/charts/linkerd-viz/values.yaml
index 75d9015c9..2b16cf3c1 100644
--- a/viz/charts/linkerd-viz/values.yaml
+++ b/viz/charts/linkerd-viz/values.yaml
@@ -7,6 +7,9 @@
 # -- control plane version. See Proxy section for proxy version
 linkerdVersion: &linkerd_version linkerdVersionValue
 
+# -- Default Docker Registry
+defaultRegistry: &default_registry ghcr.io/linkerd
+
 # -- Kubernetes DNS Domain name to use  
 clusterDomain: &cluster_domain cluster.local
 
@@ -59,9 +62,11 @@ tap:
   # -- log level of the tap component
   logLevel: *log_level
   image:
-    # -- Docker image name for the grafana instance
-    name: ghcr.io/linkerd/controller
-    # -- Docker image tag for the grafana instance
+    # -- Docker registry for the tap instance
+    registry: *default_registry
+    # -- Docker image name for the tap instance
+    name: controller
+    # -- Docker image tag for the tap instance
     tag: *linkerd_version
   # -- Do not create a secret resource for the Tap component. If this is set to
   # `true`, the value `tap.caBundle` must be set (see below).
@@ -101,8 +106,10 @@ dashboard:
   # -- log level of the dashboard component
   logLevel: *log_level
   image:
+    # -- Docker registry for the web instance
+    registry: *default_registry
     # -- Docker image name for the web instance
-    name: ghcr.io/linkerd/web
+    name: web
     # -- Docker image tag for the web instance
     tag: *linkerd_version
 
@@ -132,8 +139,10 @@ grafana:
   # -- toggle field to enable or disable grafana
   enabled: true
   image:
+    # -- Docker registry for the grafana instance
+    registry: *default_registry
     # -- Docker image name for the grafana instance
-    name: ghcr.io/linkerd/grafana
+    name: grafana
     # -- Docker image tag for the grafana instance
     tag: *linkerd_version
 
@@ -152,9 +161,11 @@ grafana:
 prometheus:
   # -- toggle field to enable or disable prometheus
   enabled: true
-  image: 
+  image:
+    # -- Docker registry for the prometheus instance
+    registry: prom
     # -- Docker image name for the prometheus instance
-    name: prom/prometheus
+    name: prometheus
     # -- Docker image tag for the prometheus instance
     tag: v2.19.3
     # == Pull policy for the prometheus instance


Test patch:
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index a537c7a74..262b9134e 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -809,6 +809,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1081,6 +1097,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1364,6 +1396,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1930,6 +1978,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 ###
 ### Service Profile Validator
 ###
@@ -1953,6 +2017,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index efa561a9f..e19da9e03 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -809,6 +809,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1081,6 +1097,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1364,6 +1396,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1930,6 +1978,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 ###
 ### Service Profile Validator
 ###
@@ -1953,6 +2017,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_helm_output_ha.golden b/cli/cmd/testdata/install_helm_output_ha.golden
index c385be75c..a9dab5938 100644
--- a/cli/cmd/testdata/install_helm_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_output_ha.golden
@@ -813,6 +813,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1076,6 +1092,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1350,6 +1382,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1898,6 +1946,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 # Source: linkerd2/templates/sp-validator.yaml
 ---
 ###
@@ -1923,6 +1987,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden
index 1a37c9edb..1129319f4 100644
--- a/cli/cmd/testdata/install_helm_output_ha_labels.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden
@@ -817,6 +817,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1084,6 +1100,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1362,6 +1394,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1922,6 +1970,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 # Source: linkerd2/templates/sp-validator.yaml
 ---
 ###
@@ -1947,6 +2011,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
index ffdbdda6a..a7d069ef3 100644
--- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
@@ -813,6 +813,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1076,6 +1092,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1350,6 +1382,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1898,6 +1946,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 # Source: linkerd2/templates/sp-validator.yaml
 ---
 ###
@@ -1923,6 +1987,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/test/integration/tracing/tracing_test.go b/test/integration/tracing/tracing_test.go
index 6b4c0d4af..a87ac580d 100644
--- a/test/integration/tracing/tracing_test.go
+++ b/test/integration/tracing/tracing_test.go
@@ -61,8 +61,8 @@ func TestTracing(t *testing.T) {
 			"'kubectl apply' command failed\n%s", out)
 	}
 
-	// wait for jaeger proxy mutator to start
-	if err := TestHelper.CheckPods(ctx, tracingNs, "proxy-mutator", 1); err != nil {
+	// wait for jaeger injector to start
+	if err := TestHelper.CheckPods(ctx, tracingNs, "jaeger-injector", 1); err != nil {
 		if rce, ok := err.(*testutil.RestartCountError); ok {
 			testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
 		} else {
@@ -70,8 +70,8 @@ func TestTracing(t *testing.T) {
 		}
 	}
 
-	if err := TestHelper.CheckDeployment(ctx, tracingNs, "proxy-mutator", 1); err != nil {
-		testutil.AnnotatedErrorf(t, "CheckDeployment timed-out", "Error validating deployment [%s]:\n%s", "proxy-mutator", err)
+	if err := TestHelper.CheckDeployment(ctx, tracingNs, "jaeger-injector", 1); err != nil {
+		testutil.AnnotatedErrorf(t, "CheckDeployment timed-out", "Error validating deployment [%s]:\n%s", "jaeger-injector", err)
 	}
 
 	// Emojivoto components
@@ -156,12 +156,13 @@ func TestTracing(t *testing.T) {
 
 	t.Run("expect full trace", func(t *testing.T) {
 
-		url, err := TestHelper.URLFor(ctx, tracingNs, "jaeger", 16686)
-		if err != nil {
-			testutil.AnnotatedFatal(t, "error building URL", err)
-		}
 		timeout := 120 * time.Second
 		err = TestHelper.RetryFor(timeout, func() error {
+			url, err := TestHelper.URLFor(ctx, tracingNs, "jaeger", 16686)
+			if err != nil {
+				return err
+			}
+
 			tracesJSON, err := TestHelper.HTTPGetURL(url + "/jaeger/api/traces?lookback=1h&service=nginx")
 			if err != nil {
 				return err
diff --git a/testutil/kubernetes_helper.go b/testutil/kubernetes_helper.go
index ca08f5695..db040b581 100644
--- a/testutil/kubernetes_helper.go
+++ b/testutil/kubernetes_helper.go
@@ -211,33 +211,31 @@ func (h *KubernetesHelper) CheckPods(ctx context.Context, namespace string, depl
 
 	err := h.retryFor(6*time.Minute, func() error {
 		checkedPods = []corev1.Pod{}
-		pods, err := h.clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
+		pods, err := h.GetPodsForDeployment(ctx, namespace, deploymentName)
 		if err != nil {
 			return err
 		}
 
 		var deploymentReplicas int
-		for _, pod := range pods.Items {
-			if strings.HasPrefix(pod.Name, deploymentName) {
-				checkedPods = append(checkedPods, pod)
-
-				deploymentReplicas++
-				if pod.Status.Phase != "Running" {
-					return fmt.Errorf("Pod [%s] in namespace [%s] is not running",
-						pod.Name, pod.Namespace)
-				}
-				for _, container := range pod.Status.ContainerStatuses {
-					if !container.Ready {
-						return fmt.Errorf("Container [%s] in pod [%s] in namespace [%s] is not running",
-							container.Name, pod.Name, pod.Namespace)
-					}
+		for _, pod := range pods {
+			checkedPods = append(checkedPods, pod)
+
+			deploymentReplicas++
+			if pod.Status.Phase != "Running" {
+				return fmt.Errorf("Pod [%s] in namespace [%s] is not running",
+					pod.Name, pod.Namespace)
+			}
+			for _, container := range pod.Status.ContainerStatuses {
+				if !container.Ready {
+					return fmt.Errorf("Container [%s] in pod [%s] in namespace [%s] is not running",
+						container.Name, pod.Name, pod.Namespace)
 				}
 			}
 		}
 
 		if deploymentReplicas != replicas {
-			return fmt.Errorf("Expected deployment [%s] in namespace [%s] to have [%d] running pods, but found [%d]",
-				deploymentName, namespace, replicas, deploymentReplicas)
+			return fmt.Errorf("Expected there to be [%d] pods in deployment [%s] in namespace [%s], but found [%d]",
+				replicas, deploymentName, namespace, deploymentReplicas)
 		}
 
 		return nil