<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Day 5 on Eriks training corner</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/index.html</link><description>Recent content in Day 5 on Eriks training corner</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://training.caha.cloud/en/kubernetes-gt/day-05/index.xml" rel="self" type="application/rss+xml"/><item><title>Persistent storage</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/01-storage/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://training.caha.cloud/en/kubernetes-gt/day-05/01-storage/index.html</guid><description>&lt;h1 id="persistent-storage"&gt;Persistent storage&lt;/h1&gt;
&lt;p&gt;A container&amp;rsquo;s writable layer dies with the container, and &lt;code&gt;emptyDir&lt;/code&gt; dies with
the Pod. Anything that must outlive a reschedule has to come from outside the
cluster.&lt;/p&gt;
&lt;h2 id="three-objects"&gt;Three objects&lt;/h2&gt;
&lt;div class="mermaid align-center"&gt;graph LR
PV[PersistentVolume&amp;lt;br/&amp;gt;the actual storage&amp;lt;br/&amp;gt;cluster-scoped] --- B((bind))
PVC[PersistentVolumeClaim&amp;lt;br/&amp;gt;a request for storage&amp;lt;br/&amp;gt;namespaced] --- B
POD[Pod&amp;lt;br/&amp;gt;mounts the claim] --&amp;gt; PVC
SC[StorageClass&amp;lt;br/&amp;gt;creates PVs on demand] -.optional.-&amp;gt; PV&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PersistentVolume (PV)&lt;/strong&gt; - a piece of storage that exists. Cluster-scoped.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PersistentVolumeClaim (PVC)&lt;/strong&gt; - &amp;ldquo;I need 1 GiB, ReadWriteOnce&amp;rdquo;. Namespaced.
The Pod references the &lt;em&gt;claim&lt;/em&gt;, never the volume.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StorageClass&lt;/strong&gt; - a factory that creates PVs automatically (dynamic
provisioning).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;This cluster ships with no StorageClass and no CSI driver&lt;/strong&gt;, so at first
nothing is created automatically: you write the PV yourself, pointing at one of
the NFS exports on support01 (&lt;code&gt;/storage1&lt;/code&gt;, &lt;code&gt;/storage2&lt;/code&gt;, &lt;code&gt;/storage3&lt;/code&gt;). That is the
on-premise reality a cloud cluster hides from you - and Lab 35 fixes it by
installing a driver.&lt;/p&gt;</description></item><item><title>Stateful workloads</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/02-stateful-workloads/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://training.caha.cloud/en/kubernetes-gt/day-05/02-stateful-workloads/index.html</guid><description>&lt;h1 id="stateful-workloads"&gt;Stateful workloads&lt;/h1&gt;
&lt;p&gt;Stateless apps are easy: kill a Pod, start another. State changes the rules.&lt;/p&gt;
&lt;h2 id="should-it-run-in-kubernetes-at-all"&gt;Should it run in Kubernetes at all?&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run it in the cluster when&lt;/th&gt;
&lt;th&gt;Keep it outside when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;an &lt;strong&gt;operator&lt;/strong&gt; handles failover, backup, upgrades&lt;/td&gt;
&lt;td&gt;nobody on the team can operate the database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;storage is solid (CSI, snapshots, tested restores)&lt;/td&gt;
&lt;td&gt;storage is &amp;ldquo;an NFS share somebody set up&amp;rdquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;you want the same tooling for everything&lt;/td&gt;
&lt;td&gt;a managed service (Azure SQL, RDS) fits the need&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="the-building-blocks"&gt;The building blocks&lt;/h2&gt;
&lt;div class="mermaid align-center"&gt;graph TB
SS[StatefulSet or operator CR] --&amp;gt; P0[pod-0] &amp;amp; P1[pod-1]
P0 --&amp;gt; V0[(PVC data-0)]
P1 --&amp;gt; V1[(PVC data-1)]
V0 &amp;amp; V1 --&amp;gt; SC[StorageClass -&amp;gt; CSI]
PDB[PodDisruptionBudget] -.protects.-&amp;gt; P0 &amp;amp; P1
AA[anti-affinity / topology spread] -.spreads.-&amp;gt; P0 &amp;amp; P1&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;stable identity, one volume per replica&lt;/td&gt;
&lt;td&gt;StatefulSet &lt;code&gt;volumeClaimTemplates&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;replicas on different nodes / zones&lt;/td&gt;
&lt;td&gt;pod anti-affinity, &lt;code&gt;topologySpreadConstraints&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maintenance without losing quorum&lt;/td&gt;
&lt;td&gt;PodDisruptionBudget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slow start (recovery, replay)&lt;/td&gt;
&lt;td&gt;startupProbe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;more space&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allowVolumeExpansion: true&lt;/code&gt; + edit the PVC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;point-in-time copy&lt;/td&gt;
&lt;td&gt;VolumeSnapshot (CSI) / application backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failover, replication, upgrades&lt;/td&gt;
&lt;td&gt;an &lt;strong&gt;operator&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="access-modes-decide-the-design"&gt;Access modes decide the design&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RWO&lt;/strong&gt; (block storage, most cloud disks): one node at a time - one PVC per
replica, replication done by the application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RWX&lt;/strong&gt; (NFS, CephFS, Azure Files): shared - convenient, but two writers on
one database file is corruption, not high availability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="backups-are-part-of-the-design"&gt;Backups are part of the design&lt;/h2&gt;
&lt;p&gt;A replica is not a backup: a &lt;code&gt;DROP TABLE&lt;/code&gt; is replicated in milliseconds. Plan
&lt;strong&gt;application-consistent&lt;/strong&gt; backups (database dump, operator backups to object
storage) and &lt;strong&gt;test the restore&lt;/strong&gt; - Lab 39.&lt;/p&gt;</description></item><item><title>Monitoring with Prometheus and Grafana</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/03-observability/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://training.caha.cloud/en/kubernetes-gt/day-05/03-observability/index.html</guid><description>&lt;h1 id="monitoring-with-prometheus-and-grafana"&gt;Monitoring with Prometheus and Grafana&lt;/h1&gt;
&lt;h2 id="three-signals"&gt;Three signals&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;th&gt;Typical tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;how much, how fast, how often - over time&lt;/td&gt;
&lt;td&gt;Prometheus, Grafana&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;what exactly happened&lt;/td&gt;
&lt;td&gt;Loki, Elasticsearch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;where did the time go across services&lt;/td&gt;
&lt;td&gt;OpenTelemetry, Tempo, Jaeger&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prometheus-in-kubernetes"&gt;Prometheus in Kubernetes&lt;/h2&gt;
&lt;div class="mermaid align-center"&gt;graph LR
subgraph targets
KS[kube-state-metrics&amp;lt;br/&amp;gt;object state]
NE[node-exporter&amp;lt;br/&amp;gt;DaemonSet: CPU, disk, net]
KL[kubelet / cAdvisor&amp;lt;br/&amp;gt;container usage]
APP[your app /metrics]
end
P[Prometheus] --&amp;gt;|scrape every 30 s| KS &amp;amp; NE &amp;amp; KL &amp;amp; APP
P --&amp;gt;|rules fire| AM[Alertmanager] --&amp;gt; N[mail / Teams / PagerDuty]
G[Grafana] --&amp;gt;|PromQL| P&lt;/div&gt;&lt;p&gt;Prometheus &lt;strong&gt;pulls&lt;/strong&gt; metrics over HTTP and stores them as time series:
&lt;code&gt;metric_name{label=&amp;quot;value&amp;quot;} number&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Backup and disaster recovery</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/04-backup-dr/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://training.caha.cloud/en/kubernetes-gt/day-05/04-backup-dr/index.html</guid><description>&lt;h1 id="backup-and-disaster-recovery"&gt;Backup and disaster recovery&lt;/h1&gt;
&lt;h2 id="what-exactly-can-you-lose"&gt;What exactly can you lose?&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Contains&lt;/th&gt;
&lt;th&gt;Backed up by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;etcd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;every Kubernetes object of the cluster&lt;/td&gt;
&lt;td&gt;&lt;code&gt;etcdctl snapshot save&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resources&lt;/strong&gt; per namespace / app&lt;/td&gt;
&lt;td&gt;Deployments, Secrets, PVC definitions&amp;hellip;&lt;/td&gt;
&lt;td&gt;Velero, &lt;strong&gt;git&lt;/strong&gt; (GitOps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Volumes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the data&lt;/td&gt;
&lt;td&gt;Velero (file system backup or CSI snapshots), application backups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outside the cluster&lt;/td&gt;
&lt;td&gt;DNS, certificates, registry, cloud resources&lt;/td&gt;
&lt;td&gt;their own processes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="mermaid align-center"&gt;graph LR
subgraph cluster
E[(etcd)]
NS[namespace shop&amp;lt;br/&amp;gt;objects &amp;#43; PVCs]
end
E --&amp;gt;|etcdctl snapshot| F[snapshot file&amp;lt;br/&amp;gt;off the node!]
NS --&amp;gt;|Velero backup| OS[(S3 object storage&amp;lt;br/&amp;gt;Garage)]
G[git repo] --&amp;gt;|GitOps re-apply| NS&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;etcd snapshot&lt;/th&gt;
&lt;th&gt;Velero&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;whole cluster, all or nothing&lt;/td&gt;
&lt;td&gt;namespaces, labels, resource types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volumes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore to another cluster&lt;/td&gt;
&lt;td&gt;no (same cluster identity)&lt;/td&gt;
&lt;td&gt;yes - migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use&lt;/td&gt;
&lt;td&gt;control plane loss, fatal mistakes&lt;/td&gt;
&lt;td&gt;app restore, namespace deleted, migration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="velero"&gt;Velero&lt;/h2&gt;
&lt;p&gt;A controller in the cluster plus a CLI. Backups go to &lt;strong&gt;object storage&lt;/strong&gt;
(&lt;code&gt;BackupStorageLocation&lt;/code&gt;); volume data via CSI snapshots or the &lt;strong&gt;node agent&lt;/strong&gt;
(file-system backup with Kopia).&lt;/p&gt;</description></item><item><title>CRDs and operators</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/05-crds-operators/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://training.caha.cloud/en/kubernetes-gt/day-05/05-crds-operators/index.html</guid><description>&lt;h1 id="crds-and-operators"&gt;CRDs and operators&lt;/h1&gt;
&lt;h2 id="custom-resource-definitions"&gt;Custom Resource Definitions&lt;/h2&gt;
&lt;p&gt;A CRD teaches the API server a &lt;strong&gt;new kind&lt;/strong&gt;. After you apply one, &lt;code&gt;kubectl get&lt;/code&gt;,
&lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;explain&lt;/code&gt;, RBAC and every other tool work on it exactly as they do
on Pods - because to the API server there is no difference.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get crd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl api-resources &lt;span class="p"&gt;|&lt;/span&gt; grep -v -E &lt;span class="s1"&gt;&amp;#39;v1$|apps/v1&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# what is not core Kubernetes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl explain cluster.spec &lt;span class="c1"&gt;# works on CRDs too&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A CRD alone stores data and does nothing with it. It is a table with validation.&lt;/p&gt;</description></item><item><title>Production operations and best practices</title><link>https://training.caha.cloud/en/kubernetes-gt/day-05/06-production/index.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://training.caha.cloud/en/kubernetes-gt/day-05/06-production/index.html</guid><description>&lt;h1 id="production-operations-and-best-practices"&gt;Production operations and best practices&lt;/h1&gt;
&lt;p&gt;Everything in this course, as one checklist.&lt;/p&gt;
&lt;h2 id="platform"&gt;Platform&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Control plane&lt;/td&gt;
&lt;td&gt;3 control plane nodes (odd etcd quorum), or a managed service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upgrades&lt;/td&gt;
&lt;td&gt;one minor version at a time, every ~4 months; control plane first, then nodes (drain, upgrade kubelet, uncordon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Certificates&lt;/td&gt;
&lt;td&gt;kubeadm certs expire after 1 year: &lt;code&gt;kubeadm certs check-expiration&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backups&lt;/td&gt;
&lt;td&gt;etcd snapshots + Velero, &lt;strong&gt;tested restores&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity&lt;/td&gt;
&lt;td&gt;headroom for one node failure + rolling updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access&lt;/td&gt;
&lt;td&gt;OIDC for people, no shared admin kubeconfig, audit log to SIEM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policies&lt;/td&gt;
&lt;td&gt;PSA baseline everywhere, restricted where possible; admission policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;default-deny NetworkPolicies per namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability&lt;/td&gt;
&lt;td&gt;metrics, logs, alerts on symptoms, runbooks per alert&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="workloads"&gt;Workloads&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;td&gt;pinned tags/digests, scanned, non-root, minimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resources&lt;/td&gt;
&lt;td&gt;requests on everything, memory limits, quotas per namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;readiness + liveness (+ startup), no dependency checks in liveness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;td&gt;≥ 2 replicas, anti-affinity / spread, PDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config&lt;/td&gt;
&lt;td&gt;ConfigMaps/Secrets, nothing baked into images; secrets from a vault&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;own ServiceAccount, no token unless needed, least-privilege RBAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery&lt;/td&gt;
&lt;td&gt;GitOps (Argo CD / Flux), Helm or Kustomize, no &lt;code&gt;kubectl edit&lt;/code&gt; in prod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutdown&lt;/td&gt;
&lt;td&gt;handle SIGTERM, &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; fits the app&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="incidents"&gt;Incidents&lt;/h2&gt;
&lt;div class="mermaid align-center"&gt;graph LR
D[detect&amp;lt;br/&amp;gt;alert] --&amp;gt; T[triage&amp;lt;br/&amp;gt;impact, severity] --&amp;gt; M[mitigate&amp;lt;br/&amp;gt;rollback, scale, failover] --&amp;gt; R[resolve&amp;lt;br/&amp;gt;root cause fix] --&amp;gt; P[postmortem&amp;lt;br/&amp;gt;blameless, actions]&lt;/div&gt;&lt;p&gt;Mitigate first, investigate second: &lt;code&gt;kubectl rollout undo&lt;/code&gt; restores service
while you look for the cause. Write the postmortem: timeline, impact, root
cause, what went well, action items with owners.&lt;/p&gt;</description></item></channel></rss>