DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

project: StatefulSet with replica's

statefulset-read-write-lab/

├── 01-mysql-headless.yaml
├── 02-mysql-init-configmap.yaml
├── 03-mysql-statefulset.yaml
├── 04-label-primary.sh
├── 05-mysql-write-service.yaml
├── 06-mysql-read-service.yaml
├── 07-mysql-client.yaml

└── apply-all.sh

Image

Image

Image

                ┌─────────────┐
                │  App / User │
                └──────┬──────┘
                       │
        ┌──────────────┴──────────────┐
        │                              │
 WRITE Service                    READ Service
(mysql-write)                  (mysql-read)
        │                              │
   mysql-0 (Primary)       mysql-1, mysql-2
        │                              │
        └───────── Persistent Volumes ─┘
Enter fullscreen mode Exit fullscreen mode


1️⃣ WHY StatefulSet + Headless (Concept)

Why StatefulSet?

Databases need:

  • Stable hostname
  • Stable storage
  • Ordered pods

StatefulSet gives:

mysql-0 (Primary)
mysql-1 (Replica)
mysql-2 (Replica)
Enter fullscreen mode Exit fullscreen mode

Why Headless Service?

StatefulSet cannot work correctly without it.

Headless gives DNS like:

mysql-0.mysql-headless
mysql-1.mysql-headless
mysql-2.mysql-headless
Enter fullscreen mode Exit fullscreen mode

This is mandatory.


2️⃣ Headless Service (Step 1)

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
Enter fullscreen mode Exit fullscreen mode

What this means

  • clusterIP: None → no load balancing
  • DNS is created per pod
  • Required for StatefulSet identity

Apply:

kubectl apply -f mysql-headless.yaml
Enter fullscreen mode Exit fullscreen mode

3️⃣ StatefulSet – 3 Pods (Step 2)

ConfigMap (Init DB)

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-init
data:
  init.sql: |
    CREATE DATABASE appdb;
Enter fullscreen mode Exit fullscreen mode

StatefulSet YAML

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-headless
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
        role: replica
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: root
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: init
          mountPath: /docker-entrypoint-initdb.d
      volumes:
      - name: init
        configMap:
          name: mysql-init
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f mysql-statefulset.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

You will see:

mysql-0
mysql-1
mysql-2
Enter fullscreen mode Exit fullscreen mode

4️⃣ Define PRIMARY vs REPLICAS (Step 3)

Make mysql-0 PRIMARY

kubectl label pod mysql-0 role=primary --overwrite
Enter fullscreen mode Exit fullscreen mode

Replicas already labeled:

role=replica
Enter fullscreen mode Exit fullscreen mode

Verify:

kubectl get pods --show-labels
Enter fullscreen mode Exit fullscreen mode

5️⃣ WRITE Service (Primary Only)

apiVersion: v1
kind: Service
metadata:
  name: mysql-write
spec:
  selector:
    app: mysql
    role: primary
  ports:
  - port: 3306
Enter fullscreen mode Exit fullscreen mode

Meaning

  • Only mysql-0 matches
  • All WRITE traffic goes to Primary
  • Safe & correct DB pattern

6️⃣ READ Service (Replicas Only)

apiVersion: v1
kind: Service
metadata:
  name: mysql-read
spec:
  selector:
    app: mysql
    role: replica
  ports:
  - port: 3306
Enter fullscreen mode Exit fullscreen mode

Meaning

  • Load balances across mysql-1 & mysql-2
  • Scales reads
  • Protects primary from overload

7️⃣ Test Pod (Client)

apiVersion: v1
kind: Pod
metadata:
  name: mysql-client
spec:
  containers:
  - name: client
    image: mysql:8.0
    command: ["sleep", "3600"]
Enter fullscreen mode Exit fullscreen mode

8️⃣ CONNECT & SWITCH (IMPORTANT PART)

WRITE (Primary)

kubectl exec -it mysql-client -- \
mysql -h mysql-write -uroot -proot
Enter fullscreen mode Exit fullscreen mode
CREATE TABLE appdb.test (id INT);
Enter fullscreen mode Exit fullscreen mode

✔ Goes ONLY to mysql-0


READ (Replicas)

kubectl exec -it mysql-client -- \
mysql -h mysql-read -uroot -proot
Enter fullscreen mode Exit fullscreen mode
SELECT * FROM appdb.test;
Enter fullscreen mode Exit fullscreen mode

✔ Load-balanced across mysql-1 & mysql-2


9️⃣ How Switching Works (Explain Clearly)

Action Service Used Pod
Insert / Update mysql-write mysql-0
Select mysql-read mysql-1 / mysql-2

🔥 Interview Explanation (Must Say This)

We use StatefulSet for stable DB identity
Headless Service for pod-level DNS
Write Service points only to Primary
Read Service load balances replicas
PVC ensures no data loss

Top comments (0)