ЁЯПл The SchoolтА║ЁЯдЦ ArgoCDтА║ЁЯФС рдзрдбрд╛ 12 тАФ GitOps рдордзрд▓реЗ Secrets + рд╢реЗрд╡рдЯрдЪреЗ рдЧреБрдгрдкрддреНрд░рдХ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯФС рдзрдбрд╛ 12 тАФ GitOps рдордзрд▓реЗ Secrets + рд╢реЗрд╡рдЯрдЪреЗ рдЧреБрдгрдкрддреНрд░рдХ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 12 тАФ рд╢реЗрд╡рдЯрдЪрд╛ рдзрдбрд╛! ┬╖ рдорд╛рдЧреАрд▓: lesson-11-helm-kustomize-envs


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рд╕рдЧрд│реЗ 12 рдзрдбреЗ тАФ рд╕рдВрдкреВрд░реНрдг course. рд╢реЗрд╡рдЯрдЪрд╛ рдбреНрд░реЕрдЧрди: secrets (рдПрдХрдореЗрд╡ рдЧреЛрд╖реНрдЯ рдЬреА рд╡рд╣реАрдд рдХрдзреАрдЪ рдЙрдШрдбрдкрдгреЗ рдмрд╕реВ рдирдпреЗ), рдЖрдгрд┐ рдордЧ рд╢реЗрд╡рдЯрдЪреЗ ArgoCD-рд╕рд╣-рд╡рд┐рд░реБрджреНрдз-ArgoCD-рд╢рд┐рд╡рд╛рдп рдЧреБрдгрдкрддреНрд░рдХ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╡рд╣реА ЁЯУЦ рдЕрджреНрднреБрдд рдЖрд╣реЗ рдХрд╛рд░рдг рддреА рд╕рдЧрд│реЗ рд╡рд╛рдЪреВ рд╢рдХрддрд╛рдд. рдЖрдгрд┐ рдиреЗрдордХреНрдпрд╛ рддреНрдпрд╛рдЪ рдХрд╛рд░рдгрд╛рдореБрд│реЗ рддреБрдореНрд╣реА рд▓реЙрдХрд░рдЪреА рдЪрд╛рд╡реА рддрд┐рдЪреНрдпрд╛ рдЖрдд рдХрдзреАрдЪ рдЪрд┐рдХрдЯрд╡реВ рдирдпреЗ. ЁЯФСтЭМ git рдордзреНрдпреЗ paste рдХреЗрд▓реЗрд▓рд╛ password рдореНрд╣рдгрдЬреЗ рдкреНрд░рд╕рд┐рджреНрдз рдХреЗрд▓реЗрд▓рд╛ password тАФ рдХрд╛рдпрдордЪрд╛ (git рдХрдзреАрдЪ рд╡рд┐рд╕рд░рдд рдирд╛рд╣реА, рддреБрдореНрд╣реА рддреЛ "delete" рдХреЗрд▓реНрдпрд╛рдирдВрддрд░рд╣реА).

рд╡рд╣реАрдЪреНрдпрд╛ рдЬрдЧрд╛рдд рдЪрд╛рд╡реНрдпрд╛ рд╣рд╛рддрд╛рд│рдгреНрдпрд╛рдЪреЗ рджреЛрди рдкреНрд░рд╛рдорд╛рдгрд┐рдХ рдорд╛рд░реНрдЧ:

  1. рд╡рд╣реАрдд рдЪрд┐рдХрдЯрд╡рд▓реЗрд▓реА рдХреБрд▓реВрдкрдмрдВрдж рдкреЗрдЯреА ЁЯФР (Sealed Secrets): рддреБрдореНрд╣реА рдЪрд╛рд╡реА рдЕрд╢рд╛ рдкреЗрдЯреАрдд рдмрдВрдж рдХрд░рддрд╛ рдЬреА рдлрдХреНрдд рддреБрдордЪреНрдпрд╛ рд╢рд╛рд│реЗрдЪрд╛ рдХреЗрдЕрд░рдЯреЗрдХрд░ рдЙрдШрдбреВ рд╢рдХрддреЛ, рдЖрдгрд┐ рддреА рдкреЗрдЯреА рд╡рд╣реАрдд рдЪрд┐рдХрдЯрд╡рддрд╛. рдкреЗрдЯреА рдЖрд╣реЗ рд╣реЗ рд╕рдЧрд│реНрдпрд╛рдВрдирд╛ рджрд┐рд╕рддреЗ; рддреА рдлрдХреНрдд cluster рдЪреНрдпрд╛ рдЖрддрд▓рд╛ robot рдЙрдШрдбреВ рд╢рдХрддреЛ. рдкреНрд░рд╕рд┐рджреНрдз рдХрд░рд╛рдпрд▓рд╛ рд╕реБрд░рдХреНрд╖рд┐рдд, рдЖрдгрд┐ рддрд░реАрд╣реА рдкреВрд░реНрдгрдкрдгреЗ GitOps.
  2. рдЪрд╛рд╡реА рдХреБрдареЗ рдареЗрд╡рд▓реА рдЖрд╣реЗ рд╣реЗ рд╕рд╛рдВрдЧрдгрд╛рд░реА рдЪрд┐рдареНрдареА ЁЯЧЭя╕П (External Secrets Operator): рд╡рд╣реАрдд рдлрдХреНрдд "рдмрдБрдХреЗрдЪреНрдпрд╛ рддрд┐рдЬреЛрд░реАрддреАрд▓ рдЪрд╛рд╡реА #42" (AWS Secrets Manager, VaultтАж) рдЗрддрдХреЗрдЪ рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЕрд╕рддреЗ. рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдЖрддрд▓рд╛ рдПрдХ рдорджрддрдиреАрд╕ рддреА рдЖрдгреВрди рдпреЛрдЧреНрдп рдЦреЛрд▓реАрдХрдбреЗ рджреЗрддреЛ. рд╡рд╣реА рд╕реНрд╡рдЪреНрдЫ рд░рд╛рд╣рддреЗ; рддрд┐рдЬреЛрд░реА рддрд┐рдЬреЛрд░реАрдЪреА рдХрд╛рдореЗ рдХрд░рддреЗ (rotation!).

k8s course рдЪреНрдпрд╛ рдзрдбрд╛ 06 рдордзрд▓рд╛рдЪ рдирд┐рдпрдо тАФ рд╕реВрдЪрдирд╛ рдлрд▓рдХ рд╡рд┐рд░реБрджреНрдз рд▓реЙрдХрд░рдЪреА рдЪрд╛рд╡реА тАФ рдЖрддрд╛ рдереЗрдЯ рд╡рд╣реАрд▓рд╛рдЪ рд▓рд╛рдЧреВ.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    bad["тЭМ plaintext password in git<br/>= published forever"]
    sealed["ЁЯФР Sealed Secrets<br/>encrypted box IN the book<br/>only the cluster can open"]
    eso["ЁЯЧЭя╕П External Secrets<br/>book holds a POINTER<br/>to a vault / AWS SM"]
    cluster["ЁЯПл cluster<br/>real Secret exists only here"]
    bad -.->|"1 never do this"| bad
    sealed -->|"2 decrypt inside"| cluster
    eso -->|"3 fetch inside"| cluster

тЭУ рдХрд╛рдп

ЁЯФз рдХрд╕реЗ (рдПрдХ рдХрд┐рдорд╛рди Sealed Secret, рд╕реБрд░реБрд╡рд╛рддреАрдкрд╛рд╕реВрди рд╢реЗрд╡рдЯрдкрд░реНрдпрдВрдд)

ЁЯФС рдвреЛрдмрд│ рдирд┐рдпрдо: plaintext Secret рдХрдзреАрдЪ commit рдХрд░реВ рдирдХрд╛ тАФ "рдлрдХреНрдд dev рд╕рд╛рдареА" рдореНрд╣рдгреВрдирд╣реА рдирд╛рд╣реА, base64 рдореНрд╣рдгреВрдирд╣реА рдирд╛рд╣реА (рддреЗ encoding рдЖрд╣реЗ, encryption рдирд╛рд╣реА). рдПрдХрддрд░ рддреЗ encrypt рдХрд░реВрди рд╡рд╣реАрдд рдареЗрд╡рд╛ (Sealed Secrets, SOPS) рдХрд┐рдВрд╡рд╛ рд╡рд╣реАрдд рдлрдХреНрдд рдПрдХ рдирд┐рд░реНрджреЗрд╢ рдареЗрд╡рд╛ (External Secrets Operator).

# 1) install the controller once per cluster (it generates the cluster's key pair):
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/controller.yaml
brew install kubeseal            # the CLI that encrypts with the cluster's PUBLIC key

# 2) write the plain Secret locally тАФ NEVER commit this file:
kubectl -n gitops-school create secret generic db-pass \
  --from-literal=password='SuperSecret123' --dry-run=client -o yaml > /tmp/db-pass.yaml

# 3) seal it тАФ the output is safe to commit:
kubeseal --format yaml < /tmp/db-pass.yaml > k8s/db-pass.sealed.yaml
rm /tmp/db-pass.yaml
git add k8s/db-pass.sealed.yaml && git commit -m "add db password (sealed)"

# 4) ArgoCD syncs the SealedSecret; the controller turns it into a real Secret in-cluster:
kubectl -n gitops-school get sealedsecret,secret db-pass

commit рдХреЗрд▓реЗрд▓реА file рдХрд╢реА рджрд┐рд╕рддреЗ рддреЗ argocd/examples/sealed-secret.example.yaml рджрд╛рдЦрд╡рддреЗ, рдЖрдгрд┐ argocd/examples/external-secret.example.yaml рд╣реЗ рддреНрдпрд╛рдЪреЗ ESO рд░реВрдк рдЖрд╣реЗ: AWS Secrets Manager рдордзрд▓реНрдпрд╛ рдПрдХрд╛ key рдЪреЗ рдирд╛рд╡ рджреЗрдгрд╛рд░реЗ рдЖрдгрд┐ operator рд▓рд╛ cluster рдЪреНрдпрд╛ рдЖрдд Secret рддрдпрд╛рд░ рдХрд░реВ рджреЗрдгрд╛рд░реЗ рдПрдХ ExternalSecret.

ЁЯЪк Push рдЪреЗ рдЪрд╛рд░ рдЦрдбреНрдбреЗ тАФ рдмреБрдЬрд▓реЗ

рдЦрдбреНрдбрд╛ (рдзрдбрд╛ 04) рдХрд╢рд╛рдиреЗ рдмреБрдЬрд▓рд╛ рдзрдбрд╛
1 тАФ рджреЛрди deploys рджрд░рдореНрдпрд╛рдирдЪрд╛ drift рдЕрджреГрд╢реНрдп рдЕрд╕рддреЛ ~3-рдорд┐рдирд┐рдЯрд╛рдВрдЪреНрдпрд╛ reconcile loop рд╡рд░ selfHeal 09
2 тАФ deployment credentials рдмрд╛рд╣реЗрд░реВрди рдпрд╛рд╡реЗ рд▓рд╛рдЧрддрд╛рдд robot рдЖрддреВрди pull рдХрд░рддреЛ; CI рдХрдбреЗ cluster рдордзреНрдпреЗ рд╢рд┐рд░рдгреНрдпрд╛рдЪрд╛ рдХреЛрдгрддрд╛рд╣реА рдорд╛рд░реНрдЧ рд░рд╛рд╣рдд рдирд╛рд╣реА 07
3 тАФ рдкреНрд░рддреНрдпреЗрдХ рдЬрд╛рд╕реНрддреАрдЪрд╛ cluster keys рдЖрдгрд┐ pipelines рд╡рд╛рдврд╡рддреЛ рдПрдХ рд╡рд╣реА, рдкреНрд░рддреНрдпреЗрдХ cluster рд▓рд╛ рдПрдХ robot, app-of-apps bootstrap 11
4 тАФ delivery log рдореНрд╣рдгрдЬреЗ live рд╕рддреНрдп рдирд╡реНрд╣реЗ Synced = рдЦреЛрд▓реА рдкрд╛рдирд╛рд╢реА рдЬреБрд│рддреЗ; sync history рд╕рд╛рдВрдЧрддреЗ рдХреЗрд╡реНрд╣рд╛ 10

ЁЯПБ рд╢реЗрд╡рдЯрдЪреЗ рдЧреБрдгрдкрддреНрд░рдХ

ЁЯОТ рд╣рд╛рддрд╛рдиреЗ ЁЯУо CI/CD push ЁЯдЦ GitOps pull (ArgoCD)
рдкреБрдиреНрд╣рд╛ рдХрд░рддрд╛ рдпреЗрдгрд╛рд░реЗ deploys тЭМ рддреБрдордЪреА рдЖрдард╡рдг тЬЕ pipeline тЬЕ reconcile loop
рджреЛрди deploys рджрд░рдореНрдпрд╛рдирдЪрд╛ drift ЁЯШ▒ рдЕрджреГрд╢реНрдп тЭМ рдЕрджреГрд╢реНрдп тЬЕ рдХрд╛рд╣реА рдорд┐рдирд┐рдЯрд╛рдВрдд рдУрд│рдЦрд▓рд╛ + рдЙрд▓рдЯрд╡рд▓рд╛
Deployment credentials ЁЯФС рдкреНрд░рддреНрдпреЗрдХ laptop рд╡рд░ тЪая╕П CI рдмрд╛рд╣реЗрд░реВрди рдорд┐рд│рд╡рддреЗ (OIDC рд╕рд╣ рдЕрд▓реНрдкрд╛рдпреБрд╖реА) тЬЕ cluster рдХрдзреАрдЪ рд╕реЛрдбрдд рдирд╛рд╣реАрдд
"рдЖрддреНрддрд╛ рдХрд╛рдп рдЪрд╛рд▓реВ рдЖрд╣реЗ?" ЁЯд╖ тЪая╕П CI рдиреЗ рдЬреЗ рдкрд╛рдард╡рд▓реЗ рддреЗ тЬЕ рд╡рд╣реА тАФ рдиреЗрд╣рдореА рдЦрд░реА
Rollback рдЖрдард╡рдгреАрддреВрди рдкреБрдиреНрд╣рд╛ рд░рдВрдЧрд╡рд╛ тЪая╕П рдЬреБрдиреА pipeline рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд╡рд╛ тЬЕ git revert, 10 рд╕реЗрдХрдВрдж
History shell history тЪая╕П CI logs тЬЕ git (desired) + ArgoCD sync history (actual)
10 clusters тЭМ 10├Ч рд╡реЗрджрдирд╛ тЭМ CI рдордзреНрдпреЗ 10 keys тЬЕ 10 agents, рдПрдХрдЪ рд╡рд╣реА

рдкреНрд░рдЧрд▓реНрдн setup рдореНрд╣рдгрдЬреЗ рджреЛрдиреНрд╣реА robots: courier ЁЯУо рдЕрдЬреВрдирд╣реА рдкреНрд░рддреНрдпреЗрдХ image test рдЖрдгрд┐ build рдХрд░рддреЛ (CI рдХрдзреАрдЪ рдЬрд╛рдд рдирд╛рд╣реА!) тАФ рдкрдг рддреНрдпрд╛рдЪреА рд╢реЗрд╡рдЯрдЪреА рдкрд╛рдпрд░реА kubectl apply рдРрд╡рдЬреА рдирд╡рд╛ image tag рд╡рд╣реАрдд commit рдХрд░рдгреЗ рдЕрд╢реА рдмрджрд▓рддреЗ. рдХреЗрдЕрд░рдЯреЗрдХрд░ ЁЯдЦ рд╕рдЧрд│реЗ рдкреНрд░рддреНрдпрдХреНрд╖ deploying рдХрд░рддреЛ. Courier build рдХрд░рддреЛ, рдХреЗрдЕрд░рдЯреЗрдХрд░ deploy рдХрд░рддреЛ тАФ рдкреНрд░рддреНрдпреЗрдХ рдЬрдг рддреНрдпрд╛рд▓рд╛ рдЬреЗ рд╕рд░реНрд╡рд╛рдд рдЪрд╛рдВрдЧрд▓реЗ рдЬрдорддреЗ рддреЗрдЪ рдХрд░рддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛ (git рдХрдзреАрдЪ рдХрд╛ рд╡рд┐рд╕рд░рдд рдирд╛рд╣реА рддреЗ рдкрд╛рд╣рд╛)

# prove to yourself that "deleting" a committed secret doesn't work:
echo "password=SuperSecret123" > oops.txt
git add oops.txt && git commit -m "oops" -q
git rm oops.txt -q && git commit -m "remove secret" -q
git show HEAD~1:oops.txt        # ЁЯШ▒ still right there, forever
git reset --hard HEAD~2 -q      # (cleanup тАФ we never pushed, so we got lucky)

# then, for real practice: run the Sealed Secret steps from the How section above.

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

Sealed Secret рдорд╛рд░реНрдЧ: kubectl -n gitops-school get sealedsecret,secret db-pass рджреЛрдиреНрд╣реА objects рджрд╛рдЦрд╡рддреЗ, рдЖрдгрд┐ kubectl -n gitops-school get secret db-pass -o jsonpath='{.data.password}' | base64 -d value рдЫрд╛рдкрддреЗ тАФ рддрд░ git show HEAD:k8s/db-pass.sealed.yaml рдлрдХреНрдд ciphertext рджрд╛рдЦрд╡рддреЗ. "git рдХрдзреАрдЪ рд╡рд┐рд╕рд░рдд рдирд╛рд╣реА" demo: "delete" рдХреЗрд▓реНрдпрд╛рдирдВрддрд░рд╣реА git show HEAD~1:oops.txt password рдЫрд╛рдкрддреЗрдЪ.

ЁЯз╣ рд╕реНрд╡рдЪреНрдЫрддрд╛

Course рд╕рдВрдкрд▓рд╛ тАФ рд╕рдЧрд│реЗ рдХрд╛рдвреВрди рдЯрд╛рдХрд╛: kubectl -n argocd delete application hello-school, kubectl delete namespace gitops-school, ArgoCD uninstall рдХрд░рд╛ (рдзрдбрд╛ 06 рдЪреА рд╕реНрд╡рдЪреНрдЫрддрд╛), рдЖрдгрд┐ install рдХреЗрд▓рд╛ рдЕрд╕рд▓реНрдпрд╛рд╕ kubectl delete -f https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/controller.yaml. Cloud cluster рдЖрд╣реЗ? рддреЛ рдирд╖реНрдЯ рдХрд░рд╛ тАФ рддреЛрдкрд░реНрдпрдВрдд nodes рдЖрдгрд┐ load balancers рдЪреЗ bill рд▓рд╛рдЧрдд рд░рд╛рд╣рддреЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдкреНрд░рддреНрдпреЗрдХ platform рд╕рд╛рдареА рдПрдХ pattern рдирд┐рд╡рдбрд╛ рдЖрдгрд┐ review рдордзреНрдпреЗ рддреЛ рдХрд╛рдЯреЗрдХреЛрд░рдкрдгреЗ рдкрд╛рд│рд╛: рдХрдореА secrets рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЫреЛрдЯреНрдпрд╛ teams рд╕рд╛рдареА Sealed Secrets рдХрд┐рдВрд╡рд╛ SOPS, rotation рдЖрдгрд┐ рдХреЗрдВрджреНрд░реАрдп audit рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЕрд╕рддреАрд▓ рддреЗрд╡реНрд╣рд╛ managed store рд╕рд╣ External Secrets. рдХреЛрдгрддрд╛рд╣реА рдорд╛рд░реНрдЧ рдШреНрдпрд╛, рдЦрд░реЗ Secret рдлрдХреНрдд cluster рдЪреНрдпрд╛ рдЖрддрдЪ рдЕрд╕рддреЗ.

ЁЯОУ рддреБрдореНрд╣реА рдХрд░реВрди рджрд╛рдЦрд╡рд▓реЗ тАФ рд╕рдВрдкреВрд░реНрдг рдкреНрд░рд╡рд╛рд╕

рд╣рд╛рддрд╛рдиреЗ рдкреЛрд╣реЛрдЪрд╡рдгреЗ ЁЯОТ тЖТ drift ЁЯкС тЖТ courier robot ЁЯУо тЖТ рддреНрдпрд╛рдЪреНрдпрд╛ рдорд░реНрдпрд╛рджрд╛ ЁЯЪк тЖТ рд╡рд╣реА ЁЯУЦ тЖТ рдХреЗрдЕрд░рдЯреЗрдХрд░ ЁЯдЦ тЖТ рдШрд░рд╛рдЪреЗ рдирд┐рдпрдо ЁЯУП тЖТ self-heal рд╢реА рд▓рдврд╛рдИ рд╣рд░рдгреЗ тЖйя╕П тЖТ 10-рд╕реЗрдХрдВрджрд╛рдВрдЪреЗ rollbacks тПк тЖТ рдЕрдиреЗрдХ рдЦреЛрд▓реНрдпрд╛рдВрд╕рд╛рдареА recipes ЁЯУЪ тЖТ рдХреБрд▓реВрдкрдмрдВрдж рдкреЗрдЯреНрдпрд╛ ЁЯФР.

рдЖрддрд╛ рддреБрдордЪреНрдпрд╛рдХрдбреЗ deployment рдЪреА рд╕рдВрдкреВрд░реНрдг рдХрдерд╛ рдЖрд╣реЗ тАФ рдЖрдгрд┐ Kubernetes course рд╕реЛрдмрдд, runtime рдЪреА рд╕рдВрдкреВрд░реНрдг рдХрдерд╛рд╣реА. рдЬрд╛, рдХрд╛рд╣реАрддрд░реА рдмрдирд╡рд╛, рдЖрдгрд┐ рдЧреГрд╣рдкрд╛рда robots рдирд╛ рд╡рд╛рд╣реВ рджреНрдпрд╛. ЁЯдЦЁЯОТ

git checkout main

ЁЯФС Lesson 12 тАФ Secrets in GitOps + the final scorecard

ЁЯУН You are here: Lesson 12 of 12 тАФ the final lesson! ┬╖ Previous: lesson-11-helm-kustomize-envs


ЁЯУж What's in this branch

All 12 lessons тАФ the complete course. The last dragon: secrets (the one thing that must never sit plainly in the book), then the final with-vs-without-ArgoCD scorecard.

ЁЯзТ Explain like I'm 5

The book ЁЯУЦ is wonderful because everyone can read it. Which is exactly why you must never glue the locker key inside it. ЁЯФСтЭМ A password pasted into git is a password published тАФ forever (git never forgets, even after you "delete" it).

Two honest ways to handle keys in a book-world:

  1. A locked box glued into the book ЁЯФР (Sealed Secrets): you lock the key in a box that only your school's caretaker can open, and glue the BOX into the book. Everyone can see there's a box; only the robot inside the cluster can open it. Safe to publish, still fully GitOps.
  2. A note saying where the key is kept ЁЯЧЭя╕П (External Secrets Operator): the book only says "key #42 from the bank vault" (AWS Secrets Manager, VaultтАж). A helper inside the school fetches it and hands it to the right room. The book stays clean; the vault does vault things (rotation!).

Same rule as the k8s course, lesson 06 тАФ notice board vs locker key тАФ now applied to the book itself.

ЁЯЧ║я╕П Diagram

flowchart LR
    bad["тЭМ plaintext password in git<br/>= published forever"]
    sealed["ЁЯФР Sealed Secrets<br/>encrypted box IN the book<br/>only the cluster can open"]
    eso["ЁЯЧЭя╕П External Secrets<br/>book holds a POINTER<br/>to a vault / AWS SM"]
    cluster["ЁЯПл cluster<br/>real Secret exists only here"]
    bad -.->|"1 never do this"| bad
    sealed -->|"2 decrypt inside"| cluster
    eso -->|"3 fetch inside"| cluster

тЭУ What

ЁЯФз How (a minimal Sealed Secret, end to end)

ЁЯФС Rule of thumb: never commit a plaintext Secret тАФ not "just for dev", not base64 (that is encoding, not encryption). Either encrypt it into the book (Sealed Secrets, SOPS) or keep only a pointer in the book (External Secrets Operator).

# 1) install the controller once per cluster (it generates the cluster's key pair):
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/controller.yaml
brew install kubeseal            # the CLI that encrypts with the cluster's PUBLIC key

# 2) write the plain Secret locally тАФ NEVER commit this file:
kubectl -n gitops-school create secret generic db-pass \
  --from-literal=password='SuperSecret123' --dry-run=client -o yaml > /tmp/db-pass.yaml

# 3) seal it тАФ the output is safe to commit:
kubeseal --format yaml < /tmp/db-pass.yaml > k8s/db-pass.sealed.yaml
rm /tmp/db-pass.yaml
git add k8s/db-pass.sealed.yaml && git commit -m "add db password (sealed)"

# 4) ArgoCD syncs the SealedSecret; the controller turns it into a real Secret in-cluster:
kubectl -n gitops-school get sealedsecret,secret db-pass

argocd/examples/sealed-secret.example.yaml shows what the committed file looks like, and argocd/examples/external-secret.example.yaml is the ESO equivalent: an ExternalSecret that names a key in AWS Secrets Manager and lets the operator create the Secret inside the cluster.

ЁЯЪк The four gaps of push тАФ closed

Gap (lesson 04) Closed by Lesson
1 тАФ drift between deploys is invisible selfHeal on a ~3-minute reconcile loop 09
2 тАФ deployment credentials must come from outside the robot pulls from inside; CI keeps no way into the cluster 07
3 тАФ every extra cluster multiplies keys and pipelines one book, one robot per cluster, app-of-apps bootstrap 11
4 тАФ the delivery log is not the live truth Synced = room matches page; sync history says when 10

ЁЯПБ The final scorecard

ЁЯОТ by hand ЁЯУо CI/CD push ЁЯдЦ GitOps pull (ArgoCD)
Repeatable deploys тЭМ your memory тЬЕ pipeline тЬЕ reconcile loop
Drift between deploys ЁЯШ▒ invisible тЭМ invisible тЬЕ detected + reverted in minutes
Deployment credentials ЁЯФС every laptop тЪая╕П obtained from outside by CI (short-lived with OIDC) тЬЕ never leave the cluster
"What's running right now?" ЁЯд╖ тЪая╕П what CI sent тЬЕ the book тАФ always true
Rollback repaint from memory тЪая╕П re-run old pipeline тЬЕ git revert, 10 seconds
History shell history тЪая╕П CI logs тЬЕ git (desired) + ArgoCD sync history (actual)
10 clusters тЭМ 10├Ч pain тЭМ 10 keys in CI тЬЕ 10 agents, same book

The mature setup is BOTH robots: the courier ЁЯУо still tests and builds every image (CI never goes away!) тАФ but its last step changes from kubectl apply to committing the new image tag into the book. The caretaker ЁЯдЦ does all actual deploying. Courier builds, caretaker deploys тАФ each doing what it's best at.

ЁЯзк Try it (see why git never forgets)

# prove to yourself that "deleting" a committed secret doesn't work:
echo "password=SuperSecret123" > oops.txt
git add oops.txt && git commit -m "oops" -q
git rm oops.txt -q && git commit -m "remove secret" -q
git show HEAD~1:oops.txt        # ЁЯШ▒ still right there, forever
git reset --hard HEAD~2 -q      # (cleanup тАФ we never pushed, so we got lucky)

# then, for real practice: run the Sealed Secret steps from the How section above.

тЬЕ Verify тАФ what you should see

Sealed Secret path: kubectl -n gitops-school get sealedsecret,secret db-pass shows both objects, and kubectl -n gitops-school get secret db-pass -o jsonpath='{.data.password}' | base64 -d prints the value тАФ while git show HEAD:k8s/db-pass.sealed.yaml shows only ciphertext. The "git never forgets" demo: git show HEAD~1:oops.txt still prints the password after the "delete".

ЁЯз╣ Clean up

Course over тАФ remove everything: kubectl -n argocd delete application hello-school, kubectl delete namespace gitops-school, uninstall ArgoCD (lesson 06's clean-up), and kubectl delete -f https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/controller.yaml if you installed it. Cloud cluster? Destroy it тАФ nodes and load balancers bill until you do.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: pick one pattern per platform and enforce it in review: Sealed Secrets or SOPS for small teams with few secrets, External Secrets with a managed store when rotation and central audit matter. Either way the real Secret exists only inside the cluster.

ЁЯОУ You made it тАФ the whole journey

Hand-delivery ЁЯОТ тЖТ drift ЁЯкС тЖТ courier robot ЁЯУо тЖТ its limits ЁЯЪк тЖТ the book ЁЯУЦ тЖТ the caretaker ЁЯдЦ тЖТ house rules ЁЯУП тЖТ losing a fight with self-heal тЖйя╕П тЖТ 10-second rollbacks тПк тЖТ recipes for many rooms ЁЯУЪ тЖТ locked boxes ЁЯФР.

You now hold the complete deployment story тАФ and with the Kubernetes course, the complete runtime story too. Go build something, and let the robots carry the homework. ЁЯдЦЁЯОТ

git checkout main
тЖР Previoushelm kustomize envsFinished! Take the quiz тЖТcheck what stuck

This page is the lesson's README from the lesson-12-secrets-and-compare branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.