Unit #3
Statefulsets


use ←↑↓→ or <space>

Where Deployments fail

 
 
 
 
 
 
 
 
 
 

Load Balancing

Not all apps are compatible!

 
 
 
 

Predictable hostnames

Needed for none load-balancing apps

Not possible with Deployments

Something...
state-ier

Statefulsets

Intended for "stateful" applications

---
apiVersion: apps/v1
kind: StatefulSet         # Different kind
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql      # Must match Service object
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: "db"
          image: "ten7/flight-deck-db:develop"
          ports:
            - containerPort: 3306
              name: mysql
              protocol: TCP
(Scroll down.)

Like a Deployment

kubectl get statefulsets

kubectl describe statefulset name

kubectl edit statefulset name

Fault tolerant

New pod assumes "place" of failed pod

Includes hostname and volumes

Do we need a Service?

Yes!

Service advertises network accessibility

But not load balancing

Headless Services

Advertises access

No load balancing

---
apiVersion: v1
kind: Service
metadata:
  name: mysql       # Matches Statefulset.serviceName
spec:
  clusterIP: None
  ports:
    - name: mysql
      port: 3306
      protocol: TCP
  selector:         # Matches Statefulset.selector
    app: mysql

How is this headless?

clusterIP: None

Disables any load balancing

Looks weird, but okay!

Storage

 
 
 
 
 

Volumes

Managed disk storage

Allocated as an object, mounted in pod

Allocation

  1. Persistent Volume Claim (PVC)
  2. Volume Claim template on Statefulset

Volume Claim Template

Allocates disk for each replica

Disk reattached if pod is deleted, restarted

Best for MySQL replicated setup

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:

# Rest of spec goes here.

  volumeClaimTemplates:
    - metadata:
        name: vol-mysql    # Name of Claim
      spec:
        accessModes:
          - ReadWriteOnce  # Shareability
        resources:
          requests:        # Requirements of request
            storage: 10Gi
                
(Scroll down.)

Shareability

Specified by accessModes

Most managed k8s do not support multi-writer!

Requests

Specifies how to allocate the disk

Size, class, other host-specific options

Mounting

Volume must be mounted into pod

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: "db"
          image: "ten7/flight-deck-db:develop"
          ports:
            - containerPort: 3306
              name: mysql
              protocol: TCP
          volumeMounts:                        # Mount volumes...
            - mountPath: /var/lib/mysql        # ...at this path...
              name: vol-mysql                  # ...with this claim.
  volumeClaimTemplates:
    - metadata:
        name: vol-mysql
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
(Scroll down.)

Permissions

Who owns the disk?

The unix root account

Causes problems if containers run as non-root

Masking ownership

Mount disk as group...

...and run the user as part of that group.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      securityContext:    # Specify security options
        fsGroup: 1000     # Mask disk with GID 1000
      containers:
        - image: ten7/flight-deck-drupal:latest
          name: web
          ports:
            - containerPort: 80
securityContext

Allows us to set special permission options

fsGroup masks disk, applies GID to user

Brute-force method

Run a container as root first...

And chown the files.

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:                    # Runs before containers
        - name: "fix-pvc-permissions"
          image: "alpine:3.9"
          command:                       # chmod all files
            - "sh"
            - "-c"
            - "chown -R 1000:1000 /var/lib/mysql"
          volumeMounts:
            - mountPath: /var/lib/mysql  # Same mountpoint
              name: vol-mysql
      containers:
        - name: "db"
          image: "ten7/flight-deck-db:develop"
          ports:
            - containerPort: 3306
              name: mysql
              protocol: TCP
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: vol-mysql
  volumeClaimTemplates:
    - metadata:
        name: vol-mysql
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
(Scroll down.)

Managing Volumes

 
 
 
 

Claims aren't disks!

They request storage...

...not embody it!

PersistantVolumes

Represent fulfilled claims

Are not deleted with claim!

Listing

kubectl get persistentvolumes

...or...

kubectl get pv

$ kubectl --kubeconfig="/path/to/kubeconfig.yml" get pv
NAME                      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                        STORAGECLASS       REASON   AGE
pvc-abcdef-0123-4567-89   10Gi       RWO            Delete           Bound    default/vol-mysql-mysql-0                   do-block-storage            79d
(Scroll right.)

Deleting volumes

Must be done explicitly

Avoid the portal!

Delete through kubectl instead

May leave "dangling" PVs

Make a backup first

Kubernetes has no "undo".

$ kubectl --kubeconfig="/path/to/kubeconfig.yml" delete pv pvc-abcdef-0123-4567-89
(Scroll right.)

Lab #3

  1. Create Statefulset YAML
  2. Observe differences to Deployments
  3. Add persistent volume