Chapter 05Lesson 02~165 minutes

Kubernetes and OpenShift Deployment Patterns: Guided Hands-On Workflow

Render and inspect a pinned Community Build Helm release before any apply, then optionally deploy a bounded local namespace and collect Pod, PVC, Service, readiness, log, and rollback evidence.

helm templatevalues.yamlDry runNamespaceEvidence

Learning objectives

  • Pin the released SonarQube chart and current Community Build number rather than accepting moving defaults.
  • Create a local values file that makes edition, resources, persistence, security helpers, and exposure choices explicit.
  • Run Helm lint/template before apply and inspect the rendered workload, PVC, Service, Secret references, probes, and security contexts.
  • Optionally install only into a disposable local namespace after cluster identity, capacity, storage, and permissions are verified.
  • Collect first-failure evidence and perform a guarded rollback/uninstall without deleting unrelated namespaces, PVCs, or database state.

1. Lab contract: render is mandatory; cluster apply is optional

The mandatory path needs Helm 3 and local files only. It uses chart 2026.4.1 and Community Build 26.9.0.129388. A real cluster is optional. If you do have one, it must be local/disposable and you must verify the active context before mutation.

Abort criteria: stop if kubectl config current-context points to a shared, employer, customer, cloud-production, or unknown cluster; if the namespace already contains non-lab resources; if a default StorageClass is unknown; or if allocatable memory cannot safely accommodate SonarQube.

2. Preflight tools, release artifacts, and cluster identity

helm version
kubectl version --client
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update
helm show chart sonarqube/sonarqube --version 2026.4.1
helm pull sonarqube/sonarqube --version 2026.4.1 --untar --untardir ./sq-ch05-chart

# Only if a cluster is intentionally available:
kubectl config current-context
kubectl cluster-info
kubectl get nodes
kubectl get storageclass
kubectl get ns sq-ch05-lab --ignore-not-found

Store Chart.yaml, the chart package digest if available, and helm show chart output in the evidence packet.

3. Create one explicit render-only values file

Use fake values and keep the mandatory path internal. This file enables a PVC so learners can inspect persistence semantics, disables root helper containers to demonstrate restricted-namespace intent, and does not create public routing.

# values-sq-ch05.yaml
community:
  enabled: true
  buildNumber: "26.9.0.129388"
monitoringPasscode: "FAKE_LOCAL_MONITORING_ONLY"

initSysctl:
  enabled: false
initFs:
  enabled: false

persistence:
  enabled: true
  size: 5Gi
  accessMode: ReadWriteOnce

service:
  type: ClusterIP

resources:
  requests:
    cpu: 400m
    memory: 2048M
  limits:
    cpu: 800m
    memory: 6144M

ingress:
  enabled: false
OpenShift:
  enabled: false

Disabling the root helpers means the cluster administrator must already satisfy Elasticsearch/node and storage ownership prerequisites. A successful render does not prove those runtime prerequisites exist.

4. Lint and render before apply

mkdir -p sq-ch05-evidence
helm lint ./sq-ch05-chart/sonarqube -f values-sq-ch05.yaml   > sq-ch05-evidence/helm-lint.txt

helm template sq-ch05 sonarqube/sonarqube   --version 2026.4.1   --namespace sq-ch05-lab   -f values-sq-ch05.yaml   > sq-ch05-evidence/rendered-kubernetes.yaml

Rendering is intentionally first because it is deterministic and reviewable. It exposes what the chart intends to create without mutating a cluster.

5. Inspect the rendered object graph

grep -nE '^kind:|name: sq-ch05|securityContext:|runAsNonRoot:|allowPrivilegeEscalation:|PersistentVolumeClaim|storageClassName:|readinessProbe:|livenessProbe:|type: ClusterIP'   sq-ch05-evidence/rendered-kubernetes.yaml

Expected categories include a SonarQube workload, Service, configuration/Secret-related objects, and a PVC because persistence is enabled. Confirm no Ingress/Route was created and no init-sysctl/init-fs workload remains. If a chart release renders differently, record that instead of forcing the expected shape.

6. Render the OpenShift variant without touching OpenShift

helm template sq-ch05-os sonarqube/sonarqube   --version 2026.4.1   --namespace sq-ch05-lab   --set community.enabled=true   --set community.buildNumber=26.9.0.129388   --set monitoringPasscode=FAKE_LOCAL_MONITORING_ONLY   --set OpenShift.enabled=true   --set OpenShift.createSCC=false   --set persistence.enabled=true   > sq-ch05-evidence/rendered-openshift.yaml

Compare the security contexts and helper containers. Do not add arbitrary fixed UIDs/GIDs to “make OpenShift work”; the maintained chart is responsible for its supported OpenShift adaptation.

7. Render a production-shaped external-database contract

Create a values fragment that references a Secret name/key without containing the password itself:

jdbcOverwrite:
  enabled: true
  jdbcUrl: "jdbc:postgresql://sq-db.example.invalid:5432/sonar"
  jdbcUsername: "sonar_lab"
  jdbcSecretName: "sq-db-credentials"
  jdbcSecretPasswordKey: "password"

The hostname is intentionally invalid and must not be deployed. The point is to prove that the chart receives an external DB endpoint and a Secret reference. Production uses a supported, reachable database with backups and least-privilege credentials.

8. Optional bounded local-cluster deployment

If—and only if—the current context is a disposable local cluster with enough memory, create the isolated namespace and install a test-only H2-backed instance. Keep persistence disabled in this optional execution path to avoid implying the local cluster is production-shaped.

kubectl create namespace sq-ch05-lab
kubectl label namespace sq-ch05-lab   pod-security.kubernetes.io/enforce=baseline --overwrite

helm upgrade --install sq-ch05 sonarqube/sonarqube   --version 2026.4.1   --namespace sq-ch05-lab   --set community.enabled=true   --set community.buildNumber=26.9.0.129388   --set monitoringPasscode=FAKE_LOCAL_MONITORING_ONLY   --set persistence.enabled=false
Why baseline here? The released chart still has root-oriented helper containers enabled by default. A full restricted namespace requires those helpers disabled and their node/filesystem work handled elsewhere. Do not weaken a production namespace merely to satisfy a lab.

9. Observe Pods, events, logs, Service, and readiness

kubectl -n sq-ch05-lab get all
kubectl -n sq-ch05-lab get events --sort-by=.lastTimestamp
kubectl -n sq-ch05-lab get pod -o wide
kubectl -n sq-ch05-lab describe pod -l app=sonarqube
kubectl -n sq-ch05-lab logs -l app=sonarqube --all-containers=true --tail=200
kubectl -n sq-ch05-lab get svc -o wide
helm -n sq-ch05-lab status sq-ch05
helm -n sq-ch05-lab get values sq-ch05 --all

Preserve the first failing event/log before a restart. Readiness means Kubernetes may route traffic; it does not prove database backup, TLS, scanner success, quality-gate behavior, or disaster recovery.

10. Access only through a local port-forward

kubectl -n sq-ch05-lab port-forward service/sq-ch05-sonarqube 9000:9000

Use http://127.0.0.1:9000 from the same machine. Do not switch the Service to LoadBalancer, create a public Ingress, or disable TLS verification to make the lab easier.

11. Challenge: diagnose from ownership, not from YAML volume

Observation First owner Evidence
PVC Pending StorageClass/provisioner PVC events + StorageClass
Init sysctl denied Pod Security/node prerequisite Pod events + namespace policy + chart values
Pod OOMKilled resource sizing/JVM/runtime container state + limits + logs
DB hostname unresolved cluster DNS/network/DB endpoint logs + Service/DNS/network policy
Service works internally, browser cannot reach it edge routing/exposure Service type + Ingress/Route/Gateway state

12. Guarded rollback and cleanup

For an optional local install, preserve evidence first, then remove the Helm release and only the named lab namespace:

helm -n sq-ch05-lab get manifest sq-ch05 > sq-ch05-evidence/installed-manifest.yaml
helm -n sq-ch05-lab history sq-ch05 > sq-ch05-evidence/helm-history.txt
kubectl -n sq-ch05-lab get all,pvc,secret,configmap -o wide > sq-ch05-evidence/final-inventory.txt

helm uninstall sq-ch05 -n sq-ch05-lab
kubectl get namespace sq-ch05-lab
# Delete only after you verify this namespace is the disposable lab:
kubectl delete namespace sq-ch05-lab

Do not delete cluster-wide StorageClasses, CRDs, ingress controllers, SCCs, nodes, or unrelated namespaces as part of this chapter.

Knowledge check

Why does the mandatory workflow use helm template before helm upgrade --install?

Why is community.buildNumber explicitly set?

What does a Pending PVC usually tell you?

Why is the optional local deployment labeled test-only?

What must be preserved before uninstalling a failed release?

Next lesson

From one workflow to deliberate platform choices

Lesson 3 compares Helm versus raw manifests, Kubernetes versus OpenShift, external DB versus test-only H2, persistence and edge-routing choices, and single-node versus Data Center architecture.

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against current SonarSource, Helm-chart repository, and Kubernetes primary material on 2026-09-07. Mandatory examples use SonarQube Community Build 26.9.0.129388 with the latest released SonarQube Helm chart 2026.4.1. That chart release originally defaults Community Build to 26.7.0.124771, so examples deliberately set community.buildNumber=26.9.0.129388. The released chart documents non-OpenShift Kubernetes support for 1.32–1.35 and OpenShift support for 4.17–4.20. SonarQube Server Developer/Enterprise and the separate Data Center chart are commercial boundaries and are not required. No SonarScanner execution, CI provider, third-party plugin, enterprise identity provider, managed Kubernetes service, public DNS, or paid infrastructure is required by the mandatory Chapter 05 path. Re-check the chart release, supported platform matrix, Community Build number, database compatibility, and upgrade notes before using these examples later.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.