Evolution of application deployment over the past 20 years.
Configure your local and remote lab environments.
Covers the resource types that are included with Kubernetes.
•Pod
•Job
Using helm to manage Kubernetes resources
Example microservice application.
Kubernetes manifests to deploy the demo application.
Explore how custom resources can add functionality
Install additional software to enhance the deployment.
Improving the DevX when working with Kubernetes.
How to safely upgrade your clusters and nodes.
Implement CI/CD for your applications (with GitOps!)
In Kubernetes, a StatefulSet
is used to manage stateful applications. Unlike Deployments
, StatefulSets
maintain a sticky identity for each of their Pods
.
These Pods
are created from the same spec but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.
Official docs: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
We will create and examine a StatefulSet
to understand it's behavior.
First, we'll create a namespace for these examples and set it as the default.
# task 01-create-namespace
# - Create a namespace for these examples and set as default.
kubectl apply -f Namespace.yaml
kubens 04--statefulset
Next, we will apply a minimal StatefulSet
configuration:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-minimal
spec:
serviceName: nginxs
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.26.0
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
# We haven't covered PersistentVolumeClaims yet... we can ignore for now
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 100Mi
# Service.nginxs.yaml ("headless" service)
apiVersion: v1
kind: Service
metadata:
name: nginxs
spec:
type: ClusterIP
clusterIP: None
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
# Service.nginx.yaml (Standard clusterIP Service)
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
Because each pod in the StatefulSet has a unique identity, we need to be able to connect to them independently.
The way this is handled in Kubernetes is through a "headless service", whichi is a ClusterIP with clusterIP: None
.
With a headless service, Kubernetes does NOT provision a single IP to load balance across the pods.
# task 02-apply-minimal
# - Apply a minimal StatefulSet configuration.
kubectl apply -f Service.nginx.yaml
kubectl apply -f Service.nginxs.yaml
kubectl apply -f StatefulSet.nginx-minimal.yaml
To connect to one of the pods from within the clsuter we could then use the fully qualified domain name of:
nginx-minimal-0.nginxs.04-statefulset.svc.cluster.local
The minimal StatefulSet
example doesn't really demonstrate how one might use a StatefulSet
in the real world so we let's delete it.
# task 03-delete-minimal
# - Delete the minimal StatefulSet configuration.
kubectl delete -f StatefulSet.nginx-minimal.yaml
One method you might utilize the identity of a StatefulSet is to use an Init Container to generate a unique config for each replica.
For example, you might set pod 0
to be the primary replica of a database while pods 1-N
might be read replicas.
We will demonstrate a simple version of this by using an init container to write the pods ordinal (number) to a file for the primary pod to read. The Kubernetes docs provide a similar example showing how a MySQL StatefulSet might do this.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-with-init-container
spec:
serviceName: nginxs
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
initContainers:
- name: populate-default-html
image: nginx:1.26.0
command:
- bash
- "-c"
- |
set -ex
[[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo "<h1>Hello from pod $ordinal</h1>" > /usr/share/nginx/html/index.html
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
containers:
- name: nginx
image: nginx:1.26.0
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 100Mi
# task 04-apply-with-init
# - Apply a statefulset configuration with an init container.
kubectl apply -f Service.nginx.yaml
kubectl apply -f Service.nginxs.yaml
kubectl apply -f StatefulSet.nginx-with-init-container.yaml
Each pod in the StatefulSet will then serve a file containing:
<h1>Hello from pod N</h1>
Finally, clean up by deleting the namespace, which will also delete all resources within it.
# task 05-delete-namespace
# - Delete the namespace to clean up.
kubectl delete -f Namespace.yaml