DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

Production Kubernetes Helm Argo CD capstone lab

LEVEL 1
Raw Kubernetes YAML
        ↓
LEVEL 2
Helm packages/templates the Kubernetes YAML
        ↓
LEVEL 3
Argo CD watches Git and deploys Helm to Kubernetes
        ↓
Production-style GitOps
Enter fullscreen mode Exit fullscreen mode

I would structure the project like this.

JumpToTech Production Kubernetes + Helm + Argo CD Lab

Architecture

Developer
    │
    │ git push
    ▼
GitHub
    │
    │ watched by
    ▼
Argo CD
    │
    │ renders Helm chart
    ▼
Helm
    │
    │ produces Kubernetes manifests
    ▼
EKS Cluster
    │
    └── Namespace: jumptotech-prod
          │
          ├── ConfigMap
          ├── Secret
          ├── ServiceAccount
          │
          ├── Deployment
          │     │
          │     └── ReplicaSet
          │           │
          │           ├── Pod
          │           ├── Pod
          │           └── Pod
          │
          ├── Service
          │
          ├── PDB
          │
          ├── HPA
          │
          └── Ingress
                 │
                 ▼
          AWS Load Balancer
                 │
                 ▼
              Internet
Enter fullscreen mode Exit fullscreen mode

The application image will already be stored in ECR.


Part 1 — Repository structure

Have students create:

jumptotech-production/
│
├── app/
│   ├── index.html
│   └── Dockerfile
│
├── kubernetes/
│   ├── namespace.yaml
│   ├── configmap.yaml
│   ├── secret.yaml
│   ├── serviceaccount.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── pdb.yaml
│   ├── hpa.yaml
│   └── ingress.yaml
│
├── helm/
│   └── jumptotech/
│       ├── Chart.yaml
│       ├── values.yaml
│       └── templates/
│           ├── _helpers.tpl
│           ├── configmap.yaml
│           ├── serviceaccount.yaml
│           ├── deployment.yaml
│           ├── service.yaml
│           ├── pdb.yaml
│           ├── hpa.yaml
│           └── ingress.yaml
│
└── argocd/
    └── application.yaml
Enter fullscreen mode Exit fullscreen mode

This lets you teach the same application three times:

Raw YAML → Helm → Argo CD
Enter fullscreen mode Exit fullscreen mode

That repetition is valuable because students see exactly what each new tool solves.


PART 2 — Raw Kubernetes

1. Namespace

kubernetes/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: jumptotech-prod
  labels:
    environment: production
    managed-by: jumptotech
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f kubernetes/namespace.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get ns
Enter fullscreen mode Exit fullscreen mode

Explain:

Namespace provides a logical boundary for namespaced Kubernetes resources.

Architecture:

EKS Cluster
│
├── default
├── kube-system
└── jumptotech-prod
      └── our application
Enter fullscreen mode Exit fullscreen mode

2. ConfigMap

kubernetes/configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: jumptotech-config
  namespace: jumptotech-prod
data:
  APP_NAME: "JumpToTech"
  APP_ENV: "production"
  LOG_LEVEL: "info"
Enter fullscreen mode Exit fullscreen mode

Explain:

ConfigMap
    ↓
non-sensitive configuration

APP_ENV
LOG_LEVEL
API_URL
application settings
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f kubernetes/configmap.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get configmap -n jumptotech-prod

kubectl describe configmap jumptotech-config \
  -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

3. Secret

For a classroom demonstration:

kubernetes/secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: jumptotech-secret
  namespace: jumptotech-prod
type: Opaque
stringData:
  DB_USERNAME: "admin"
  DB_PASSWORD: "student-demo-password"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f kubernetes/secret.yaml
Enter fullscreen mode Exit fullscreen mode

Explain the difference:

ConfigMap
    ↓
non-sensitive data

Secret
    ↓
sensitive data
Enter fullscreen mode Exit fullscreen mode

But tell students:

Do not commit real production passwords to Git.

Even:

data:
  password: cGFzc3dvcmQ=
Enter fullscreen mode Exit fullscreen mode

doesn't make the password secure merely because it is Base64.

For a real production GitOps architecture you would normally integrate a secrets-management solution rather than committing plaintext credentials.


4. ServiceAccount

kubernetes/serviceaccount.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jumptotech-sa
  namespace: jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Explain:

Person
   ↓
User identity

Workload
   ↓
ServiceAccount
Enter fullscreen mode Exit fullscreen mode

ServiceAccount answers:

"Who is this workload?"

It does not automatically mean:

"This workload can do everything."

Authorization is separate, such as RBAC.


5. Deployment

This is the most important file.

kubernetes/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jumptotech-web
  namespace: jumptotech-prod
  labels:
    app: jumptotech-web

spec:
  replicas: 3

  selector:
    matchLabels:
      app: jumptotech-web

  template:
    metadata:
      labels:
        app: jumptotech-web

    spec:
      serviceAccountName: jumptotech-sa

      containers:
        - name: web
          image: YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/jumptotech-web:v1

          ports:
            - name: http
              containerPort: 80
              protocol: TCP

          envFrom:
            - configMapRef:
                name: jumptotech-config

          env:
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: jumptotech-secret
                  key: DB_USERNAME

            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: jumptotech-secret
                  key: DB_PASSWORD

          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"

            limits:
              cpu: "500m"
              memory: "256Mi"

          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 5
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 3

          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 15
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3
Enter fullscreen mode Exit fullscreen mode

Spend significant class time on this file.


6. Understand Deployment

Deployment
replicas: 3
     │
     ▼
ReplicaSet
     │
 ┌───┼────┐
 ▼   ▼    ▼
Pod Pod  Pod
Enter fullscreen mode Exit fullscreen mode

The Deployment doesn't directly "run three containers."

It declares the desired state.

The Deployment controller manages ReplicaSets, and the ReplicaSet maintains the desired number of Pods.


7. Requests and Limits

Inside the Deployment:

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"

  limits:
    cpu: "500m"
    memory: "256Mi"
Enter fullscreen mode Exit fullscreen mode

Teach:

REQUEST
   ↓
Scheduler uses it when deciding
whether a Node has capacity

LIMIT
   ↓
maximum resource constraint
for the container
Enter fullscreen mode Exit fullscreen mode

CPU:

1000m = 1 CPU
500m  = 0.5 CPU
100m  = 0.1 CPU
Enter fullscreen mode Exit fullscreen mode

Then connect it to scheduling:

New Pod
request:
CPU 100m
RAM 128Mi
      │
      ▼
Scheduler
      │
      ├── Node A → enough capacity
      ├── Node B → not enough
      └── Node C → enough
                  │
                  ▼
               Node C
                  │
                  ▼
                kubelet
                  │
                  ▼
              Container
Enter fullscreen mode Exit fullscreen mode

8. Readiness Probe

readinessProbe:
  httpGet:
    path: /
    port: http
Enter fullscreen mode Exit fullscreen mode

Question:

Is this Pod ready to receive traffic?

Pod Running
    │
    ▼
Readiness
    │
 ┌──┴───┐
YES     NO
 │       │
 ▼       ▼
Ready   Not Ready
for
traffic
Enter fullscreen mode Exit fullscreen mode

Important:

Readiness failure doesn't normally restart the container.


9. Liveness Probe

livenessProbe:
  httpGet:
    path: /
    port: http
Enter fullscreen mode Exit fullscreen mode

Question:

Is this container/application still healthy enough to keep running?

Repeated liveness failures can cause kubelet to restart the container.

Liveness fails
      ↓
failure threshold reached
      ↓
kubelet
      ↓
restart container
Enter fullscreen mode Exit fullscreen mode

10. Apply Deployment

kubectl apply -f kubernetes/serviceaccount.yaml
kubectl apply -f kubernetes/deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get deployment -n jumptotech-prod
kubectl get rs -n jumptotech-prod
kubectl get pods -n jumptotech-prod
kubectl get pods -n jumptotech-prod -o wide
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe pod POD_NAME -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Students should locate:

Image
Ports
Environment
Requests
Limits
Readiness
Liveness
ServiceAccount
Events
Enter fullscreen mode Exit fullscreen mode

11. Service

kubernetes/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: jumptotech-service
  namespace: jumptotech-prod

spec:
  type: ClusterIP

  selector:
    app: jumptotech-web

  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: http
Enter fullscreen mode Exit fullscreen mode

Explain:

Service
selector:
app=jumptotech-web
       │
       ▼
Find matching Pod labels
       │
 ┌─────┼─────┐
 ▼     ▼     ▼
Pod   Pod   Pod
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f kubernetes/service.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get svc -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl get endpointslice \
  -n jumptotech-prod \
  -l kubernetes.io/service-name=jumptotech-service
Enter fullscreen mode Exit fullscreen mode

12. Test before ALB

kubectl port-forward \
  service/jumptotech-service \
  8080:80 \
  -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Browser:

http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

This proves:

Deployment
    ↓
Pods
    ↓
Service
    ↓
Application
Enter fullscreen mode Exit fullscreen mode

works before adding another layer.


13. PDB

kubernetes/pdb.yaml

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: jumptotech-pdb
  namespace: jumptotech-prod

spec:
  minAvailable: 2

  selector:
    matchLabels:
      app: jumptotech-web
Enter fullscreen mode Exit fullscreen mode

Architecture:

3 replicas

Pod A
Pod B
Pod C

PDB:
minAvailable: 2
Enter fullscreen mode Exit fullscreen mode

This helps preserve availability during supported voluntary disruptions, such as certain maintenance/eviction operations.

It does not prevent every crash or infrastructure failure.

Apply:

kubectl apply -f kubernetes/pdb.yaml

kubectl get pdb -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

14. HPA

Now introduce automatic scaling.

kubernetes/hpa.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: jumptotech-hpa
  namespace: jumptotech-prod

spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: jumptotech-web

  minReplicas: 3
  maxReplicas: 10

  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
Enter fullscreen mode Exit fullscreen mode

Explain:

Low traffic

Pod Pod Pod


CPU increases
     ↓
HPA observes metric
     ↓
desired replicas increases
     ↓

Pod Pod Pod Pod Pod ...


Traffic decreases
     ↓
scale down
Enter fullscreen mode Exit fullscreen mode

For resource-based CPU HPA to work as expected, the cluster needs resource metrics available, commonly through Metrics Server, and CPU requests matter for utilization calculations.

Check first:

kubectl top pods -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl apply -f kubernetes/hpa.yaml
kubectl get hpa -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

15. Ingress

kubernetes/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jumptotech-ingress
  namespace: jumptotech-prod

  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip

spec:
  ingressClassName: alb

  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: jumptotech-service
                port:
                  number: 80
Enter fullscreen mode Exit fullscreen mode

Before applying:

kubectl get deployment \
  aws-load-balancer-controller \
  -n kube-system
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl apply -f kubernetes/ingress.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get ingress -n jumptotech-prod

kubectl describe ingress \
  jumptotech-ingress \
  -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Teach the two flows separately.

Configuration:

Ingress YAML
     ↓
Kubernetes API
     ↓
AWS Load Balancer Controller
     ↓
AWS APIs
     ↓
ALB / target groups / rules
Enter fullscreen mode Exit fullscreen mode

Runtime traffic is different:

Customer
   ↓
ALB
   ↓
configured backend targets
   ↓
Pods/application
Enter fullscreen mode Exit fullscreen mode

The controller is not sitting in the middle of every customer HTTP request.


PART 3 — Why do we need Helm?

Now ask students:

What happens if we need DEV, TEST and PROD?

Without Helm we may start duplicating files:

deployment-dev.yaml
deployment-test.yaml
deployment-prod.yaml

service-dev.yaml
service-test.yaml
service-prod.yaml

ingress-dev.yaml
ingress-test.yaml
ingress-prod.yaml
Enter fullscreen mode Exit fullscreen mode

This becomes difficult to maintain.

Helm gives us:

Templates
   +
Values
   =
Rendered Kubernetes YAML
Enter fullscreen mode Exit fullscreen mode

A simple analogy:

Kubernetes YAML = completed form

Helm template = reusable form

values.yaml = answers inserted
into the form
Enter fullscreen mode Exit fullscreen mode

PART 4 — Create Helm Chart

Run:

helm create jumptotech
Enter fullscreen mode Exit fullscreen mode

For the class, simplify the generated chart and use:

helm/jumptotech/
├── Chart.yaml
├── values.yaml
└── templates/
    ├── _helpers.tpl
    ├── configmap.yaml
    ├── serviceaccount.yaml
    ├── deployment.yaml
    ├── service.yaml
    ├── pdb.yaml
    ├── hpa.yaml
    └── ingress.yaml
Enter fullscreen mode Exit fullscreen mode

16. Chart.yaml

apiVersion: v2
name: jumptotech
description: JumpToTech production Kubernetes application
type: application
version: 0.1.0
appVersion: "1.0.0"
Enter fullscreen mode Exit fullscreen mode

Explain:

Chart.yaml
    ↓
information about Helm chart
Enter fullscreen mode Exit fullscreen mode

17. values.yaml

This becomes the central configuration.

replicaCount: 3

image:
  repository: YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/jumptotech-web
  tag: "v1"
  pullPolicy: IfNotPresent

serviceAccount:
  create: true
  name: jumptotech-sa

service:
  type: ClusterIP
  port: 80

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

config:
  appName: JumpToTech
  environment: production
  logLevel: info

probes:
  readiness:
    path: /
    initialDelaySeconds: 5

  liveness:
    path: /
    initialDelaySeconds: 15

pdb:
  enabled: true
  minAvailable: 2

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

ingress:
  enabled: true
  className: alb
  scheme: internet-facing
  targetType: ip
Enter fullscreen mode Exit fullscreen mode

Now students should see the point.

Instead of editing templates repeatedly:

values.yaml
     ↓
Helm templates
     ↓
Kubernetes manifests
Enter fullscreen mode Exit fullscreen mode

18. Helm Deployment template

templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Release.Name }}

spec:
  replicas: {{ .Values.replicaCount }}

  selector:
    matchLabels:
      app: {{ .Release.Name }}

  template:
    metadata:
      labels:
        app: {{ .Release.Name }}

    spec:
      serviceAccountName: {{ .Values.serviceAccount.name }}

      containers:
        - name: web

          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

          imagePullPolicy: {{ .Values.image.pullPolicy }}

          ports:
            - name: http
              containerPort: 80

          envFrom:
            - configMapRef:
                name: {{ .Release.Name }}-config

          resources:
{{ toYaml .Values.resources | indent 12 }}

          readinessProbe:
            httpGet:
              path: {{ .Values.probes.readiness.path }}
              port: http
            initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}

          livenessProbe:
            httpGet:
              path: {{ .Values.probes.liveness.path }}
              port: http
            initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
Enter fullscreen mode Exit fullscreen mode

Now explain:

{{ .Values.replicaCount }}
Enter fullscreen mode Exit fullscreen mode

means:

Go to values.yaml and get replicaCount.

And:

{{ .Values.image.repository }}
Enter fullscreen mode Exit fullscreen mode

gets:

image:
  repository: ...
Enter fullscreen mode Exit fullscreen mode

19. Helm Service

templates/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-service

spec:
  type: {{ .Values.service.type }}

  selector:
    app: {{ .Release.Name }}

  ports:
    - name: http
      port: {{ .Values.service.port }}
      targetPort: http
Enter fullscreen mode Exit fullscreen mode

20. Helm ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config

data:
  APP_NAME: {{ .Values.config.appName | quote }}
  APP_ENV: {{ .Values.config.environment | quote }}
  LOG_LEVEL: {{ .Values.config.logLevel | quote }}
Enter fullscreen mode Exit fullscreen mode

21. Helm ServiceAccount

{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.serviceAccount.name }}
{{- end }}
Enter fullscreen mode Exit fullscreen mode

Now introduce conditional rendering:

if true
   ↓
create object

if false
   ↓
don't render object
Enter fullscreen mode Exit fullscreen mode

22. Helm PDB

{{- if .Values.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: {{ .Release.Name }}-pdb

spec:
  minAvailable: {{ .Values.pdb.minAvailable }}

  selector:
    matchLabels:
      app: {{ .Release.Name }}
{{- end }}
Enter fullscreen mode Exit fullscreen mode

23. Helm HPA

{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ .Release.Name }}-hpa

spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ .Release.Name }}

  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}

  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
Enter fullscreen mode Exit fullscreen mode

24. Helm Ingress

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Release.Name }}-ingress

  annotations:
    alb.ingress.kubernetes.io/scheme: {{ .Values.ingress.scheme }}
    alb.ingress.kubernetes.io/target-type: {{ .Values.ingress.targetType }}

spec:
  ingressClassName: {{ .Values.ingress.className }}

  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ .Release.Name }}-service
                port:
                  number: {{ .Values.service.port }}
{{- end }}
Enter fullscreen mode Exit fullscreen mode

PART 5 — Never install a Helm chart before checking it

This is an important production habit.

First:

helm lint helm/jumptotech
Enter fullscreen mode Exit fullscreen mode

Then:

helm template jumptotech \
  helm/jumptotech
Enter fullscreen mode Exit fullscreen mode

Explain what happened:

values.yaml
      +
templates/
      ↓
HELM RENDERING
      ↓
ordinary Kubernetes YAML
Enter fullscreen mode Exit fullscreen mode

Helm doesn't replace Kubernetes.

Helm generates/manages Kubernetes manifests/releases. Kubernetes still runs the workloads.


25. Install with Helm manually

Before introducing Argo CD:

helm upgrade --install jumptotech \
  helm/jumptotech \
  --namespace jumptotech-prod \
  --create-namespace
Enter fullscreen mode Exit fullscreen mode

Check:

helm list -n jumptotech-prod

kubectl get all -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Then:

helm status jumptotech \
  -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Now students understand Helm independently.


PART 6 — DEV vs PROD

Create:

values-dev.yaml
values-prod.yaml
Enter fullscreen mode Exit fullscreen mode

Example DEV:

replicaCount: 1

config:
  environment: development

resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 200m
    memory: 128Mi

autoscaling:
  enabled: false
Enter fullscreen mode Exit fullscreen mode

PROD:

replicaCount: 3

config:
  environment: production

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70
Enter fullscreen mode Exit fullscreen mode

Now:

Same Helm templates

      ┌── values-dev.yaml
      │
Chart ┤
      │
      └── values-prod.yaml
Enter fullscreen mode Exit fullscreen mode

That's one of the major benefits students need to understand.


PART 7 — Why Argo CD?

Now ask:

We have Helm. Why do we need Argo CD?

Without Argo CD:

Developer
   ↓
git push
   ↓
Someone runs:
helm upgrade ...
   ↓
Cluster
Enter fullscreen mode Exit fullscreen mode

With GitOps:

Developer
   ↓
git push
   ↓
GitHub
   ↓
Argo CD detects desired state
   ↓
Helm chart rendered
   ↓
Kubernetes
Enter fullscreen mode Exit fullscreen mode

Git becomes the source of desired deployment configuration.


PART 8 — Argo CD Application

Assuming Argo CD is already installed, create:

argocd/application.yaml

apiVersion: argoproj.io/v1alpha1
kind: Application

metadata:
  name: jumptotech-production
  namespace: argocd

spec:
  project: default

  source:
    repoURL: https://github.com/jumptotechschooldevops/YOUR_REPOSITORY.git
    targetRevision: main
    path: helm/jumptotech

    helm:
      valueFiles:
        - values-prod.yaml

  destination:
    server: https://kubernetes.default.svc
    namespace: jumptotech-prod

  syncPolicy:
    automated:
      prune: true
      selfHeal: true

    syncOptions:
      - CreateNamespace=true
Enter fullscreen mode Exit fullscreen mode

Explain each important field.

repoURL
   ↓
Where is Git repository?

targetRevision
   ↓
Which branch/tag/commit?

path
   ↓
Where is Helm chart?

destination
   ↓
Which cluster?

namespace
   ↓
Where should resources go?
Enter fullscreen mode Exit fullscreen mode

26. selfHeal

This is a fantastic live demonstration.

Git says:

replicas: 3
Enter fullscreen mode Exit fullscreen mode

Someone manually runs:

kubectl scale deployment jumptotech \
  --replicas=1 \
  -n jumptotech-prod
Enter fullscreen mode Exit fullscreen mode

Now:

Git desired state
3 replicas

Cluster actual state
1 replica
Enter fullscreen mode Exit fullscreen mode

Argo CD detects drift.

With automated self-healing configured, it can reconcile the cluster back toward the Git-defined state.

Teach:

GIT = Desired State

CLUSTER = Actual State

Argo CD
    ↓
compares
    ↓
reconciles differences
Enter fullscreen mode Exit fullscreen mode

27. prune

Suppose Git previously contained:

service.yaml
deployment.yaml
old-configmap.yaml
Enter fullscreen mode Exit fullscreen mode

Then you remove the managed old ConfigMap from Git.

With:

prune: true
Enter fullscreen mode Exit fullscreen mode

Argo CD can remove the resource that is no longer part of the application's desired state.

Be careful with this in real production environments: automated pruning is powerful.


PART 9 — Complete CI/CD + GitOps architecture

Now students should be able to draw:

                 DEVELOPER
                     │
                  git push
                     │
             ┌───────▼────────┐
             │     GitHub     │
             └───────┬────────┘
                     │
          ┌──────────┴──────────┐
          │                     │
          ▼                     ▼
     GitHub Actions          Argo CD
          │                     │
      CI Pipeline            GitOps CD
          │                     │
     Test / Scan                │
          │                     │
     Docker Build               │
          │                     │
          ▼                     │
         ECR                    │
          │                     │
       Image:v2                 │
                                │
                        Helm templates
                              +
                           values
                                │
                                ▼
                           EKS Cluster
                                │
                         jumptotech-prod
                                │
                         ┌──────┴──────┐
                         │             │
                    Deployment      Service
                         │             │
                    ReplicaSet         │
                         │             │
                   Pods/Containers ◄───┘
                         ▲
                         │
                       ALB
                         ▲
                         │
                      Customer
Enter fullscreen mode Exit fullscreen mode

There's an important distinction here:

CI builds the artifact.

Code
 ↓
Test
 ↓
Docker build
 ↓
Image
 ↓
ECR
Enter fullscreen mode Exit fullscreen mode

GitOps CD changes desired deployment state and reconciles it.

Git desired state
 ↓
Argo CD
 ↓
Helm
 ↓
Kubernetes
Enter fullscreen mode Exit fullscreen mode

PART 10 — Production files students should recognize

Object/File Main purpose
Namespace Logical resource boundary
ConfigMap Non-sensitive configuration
Secret Sensitive configuration object
ServiceAccount Workload identity
Role Namespaced RBAC permissions
RoleBinding Assign Role permissions
Deployment Desired stateless application deployment
ReplicaSet Maintains Pod replica count
Pod Workload execution unit
Service Stable service discovery/access
EndpointSlice Service backend addresses
Ingress HTTP/HTTPS routing configuration
NetworkPolicy Pod network traffic policy
PDB Availability during voluntary disruptions
HPA Horizontal workload scaling
PVC Persistent storage request
StatefulSet Stateful workloads
Job Run-to-completion workload
CronJob Scheduled Job
Chart.yaml Helm chart metadata
values.yaml Helm configuration
templates/* Helm Kubernetes templates
Argo Application Defines a GitOps-managed application

Top comments (0)