Overview
This guide demonstrates how to deploy a MySQL database on Kubernetes using MicroK8s. The deployment creates multiple MySQL replicas behind a LoadBalancer service, making the database accessible from outside the cluster.
Prerequisites
Before you begin, ensure you have the following:
- MicroK8s installed and running on your system
- MetalLB addon enabled for LoadBalancer support
- kubectl or
microk8s kubectlconfigured - Basic understanding of Kubernetes concepts (Pods, Deployments, Services)
Key Kubernetes Concepts
| Concept | Description |
|---|---|
| Deployment | Manages a set of identical Pods, ensuring the specified number of replicas are running |
| ReplicaSet | Created by Deployment to maintain the desired number of Pod replicas |
| Service | Exposes Pods to network traffic and provides load balancing |
| LoadBalancer | Service type that provisions an external IP address for accessing the application |
MySQL Deployment Architecture
The deployment consists of two main Kubernetes resources:
- Deployment: Manages 3 MySQL Pod replicas with the container configuration
- Service: Exposes MySQL on port 3306 via LoadBalancer
Production Warning: This basic deployment uses plaintext passwords and lacks persistent storage. For production environments, use Kubernetes Secrets for credentials and PersistentVolumeClaims for data persistence.
Create deployment file
Save the following YAML manifest as mysql-deployment.yaml. This file defines both the Deployment and Service resources in a single file, separated by ---.
Understanding the Deployment Specification
apiVersion: apps/v1: Uses the stable apps API group for Deploymentskind: Deployment: Declares this resource as a Deployment controllerreplicas: 3: Specifies 3 identical Pod instances will be maintainedselector.matchLabels: Defines how the Deployment finds Pods to managetemplate: Pod template that defines the container specification
1apiVersion: apps/v12kind: Deployment3metadata:4 name: mysql-deployment5spec:6 replicas: 37 selector:8 matchLabels:9 app: mysql10 template:11 metadata:12 labels:13 app: mysql14 spec:15 containers:16 - name: mysql17 image: mysql:latest18 ports:19 - containerPort: 330620 env:21 - name: MYSQL_ROOT_PASSWORD22 value: "123"23---24apiVersion: v125kind: Service26metadata:27 name: mysql-service28spec:29 selector:30 app: mysql31 ports:32 - protocol: TCP33 port: 330634 targetPort: 330635 type: LoadBalancerService Configuration Details
| Field | Value | Description |
|---|---|---|
selector.app | mysql | Routes traffic to Pods with label app: mysql |
port | 3306 | External port the Service listens on |
targetPort | 3306 | Port on the container to forward traffic to |
type | LoadBalancer | Provisions an external IP via MetalLB |
Deploy the Application
Apply the manifest to your MicroK8s cluster:
1microk8s kubectl apply -f mysql-deployment.yamlVerify Deployment Status
Check that all resources were created successfully:
1microk8s kubectl get deployment mysql-deployment2microk8s kubectl get service mysql-serviceExpected output shows 3/3 replicas ready and an external IP assigned to the service.
Try to connect to MySQL
Once the LoadBalancer assigns an external IP (in this example 10.11.0.51), you can connect to MySQL from any machine that can reach your cluster network:
1mysql -u root -p -h 10.11.0.51 -P 3306Connection Parameters Explained
| Parameter | Description |
|---|---|
-u root | Connect as the MySQL root user |
-p | Prompt for password (enter the password from MYSQL_ROOT_PASSWORD) |
-h 10.11.0.51 | Host IP address (your LoadBalancer external IP) |
-P 3306 | MySQL port number |
Finding Your External IP: Run microk8s kubectl get svc mysql-service to
see the EXTERNAL-IP assigned by MetalLB.
Verify Database Connectivity
Once connected, verify the MySQL instance is working:
1-- Show MySQL version2SELECT VERSION();3
4-- List available databases5SHOW DATABASES;6
7-- Check server status8STATUS;Some of information about the deployment
Use these commands to monitor and debug your MySQL deployment:
1microk8s kubectl get pods --watch2microk8s kubectl get pods -o wide3microk8s kubectl logs mysql-deployment-c4fccb7d9-fr5tw4microk8s kubectl describe services mysql-serviceCommand Reference
| Command | Purpose |
|---|---|
get pods --watch | Real-time monitoring of Pod status changes |
get pods -o wide | Shows Pod IPs and node placement |
logs <pod-name> | View container logs for debugging |
describe services | Detailed Service info including endpoints |
Understanding Pod States
When running kubectl get pods, you may see these states:
| State | Description |
|---|---|
Pending | Pod accepted but container not yet created |
ContainerCreating | Image being pulled and container starting |
Running | Container executing successfully |
CrashLoopBackOff | Container crashed and Kubernetes is restarting it |
Error | Container terminated with an error |
Scaling the Deployment
Kubernetes makes it easy to scale your MySQL deployment up or down. Scaling changes the number of Pod replicas managed by the Deployment.
Scale Using kubectl
1# Scale to 5 replicas2microk8s kubectl scale deployment mysql-deployment --replicas=53
4# Verify the scaling operation5microk8s kubectl get deployment mysql-deployment6
7# Scale down to 2 replicas8microk8s kubectl scale deployment mysql-deployment --replicas=2How Scaling Works
When you scale a Deployment:
- Scaling Up: New Pods are scheduled on nodes with available resources
- Scaling Down: Kubernetes terminates Pods based on priority (pending pods first, then newest pods)
- Load Balancing: The Service automatically distributes traffic across all healthy Pods
Note on Database Scaling: While this demonstrates Kubernetes scaling, running multiple MySQL instances without proper replication configuration will result in separate, independent databases. For true MySQL clustering, consider MySQL Group Replication or Galera Cluster.
Troubleshooting
Common Issues and Solutions
Pod Stuck in Pending State
1# Check why the pod is pending2microk8s kubectl describe pod <pod-name>3
4# Common causes:5# - Insufficient CPU/memory resources6# - No available nodes7# - PersistentVolumeClaim not boundCrashLoopBackOff Error
1# Check container logs for error details2microk8s kubectl logs <pod-name> --previous3
4# Common MySQL startup failures:5# - Invalid MYSQL_ROOT_PASSWORD6# - Permission issues on data directory7# - Port already in useService Has No External IP
1# Verify MetalLB is enabled2microk8s enable metallb3
4# Check MetalLB IP pool configuration5microk8s kubectl get configmap -n metallb-system6
7# View service events8microk8s kubectl describe svc mysql-serviceCleanup Resources
To remove the MySQL deployment and free up cluster resources:
1# Delete all resources defined in the manifest2microk8s kubectl delete -f mysql-deployment.yaml3
4# Or delete resources individually5microk8s kubectl delete deployment mysql-deployment6microk8s kubectl delete service mysql-service7
8# Verify cleanup9microk8s kubectl get all -l app=mysqlProduction Considerations
For production deployments, consider these enhancements:
Use Kubernetes Secrets for Credentials
Instead of plaintext passwords in the Deployment manifest, create a Secret:
1# Create a secret for MySQL root password2microk8s kubectl create secret generic mysql-secret \3 --from-literal=root-password='YourSecurePassword123!'Then reference it in your Deployment:
1env:2 - name: MYSQL_ROOT_PASSWORD3 valueFrom:4 secretKeyRef:5 name: mysql-secret6 key: root-passwordAdd Persistent Storage
To persist data across Pod restarts, add a PersistentVolumeClaim:
1apiVersion: v12kind: PersistentVolumeClaim3metadata:4 name: mysql-pv-claim5spec:6 accessModes:7 - ReadWriteOnce8 resources:9 requests:10 storage: 20GiConfigure Resource Limits
Define CPU and memory constraints to prevent resource contention:
1containers:2 - name: mysql3 image: mysql:8.04 resources:5 requests:6 cpu: "500m"7 memory: "1Gi"8 limits:9 cpu: "1000m"10 memory: "2Gi"Add Health Probes
Kubernetes uses probes to determine container health:
1containers:2 - name: mysql3 image: mysql:8.04 livenessProbe:5 exec:6 command: ["mysqladmin", "ping", "-h", "localhost"]7 initialDelaySeconds: 308 periodSeconds: 109 timeoutSeconds: 510 readinessProbe:11 exec:12 command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]13 initialDelaySeconds: 514 periodSeconds: 215 timeoutSeconds: 1| Probe Type | Purpose |
|---|---|
| livenessProbe | Restarts container if it becomes unresponsive |
| readinessProbe | Removes Pod from Service endpoints until ready |
Next Steps
- Persistent Storage: See MySQL with PVC for data persistence
- Load Balancer Setup: Configure MetalLB LoadBalancer for external access