This page shows you how to run a single-instance stateful application in Kubernetes using a PersistentVolume and a Deployment. The application is MySQL.
You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube, or you can use one of these Kubernetes playgrounds:
GCEPersistentDisk
but any type will work. GCEPersistentDisk
volumes only work on Google Compute Engine.You can use any type of persistent volume for your stateful app. See Types of Persistent Volumes for a list of supported environment disks. For Google Compute Engine, run:
gcloud compute disks create --size=20GB mysql-disk
Next create a PersistentVolume that points to the mysql-disk
disk just created. Here is a configuration file for a PersistentVolume
that points to the Compute Engine disk above:
gce-volume.yaml
|
---|
|
Notice that the pdName: mysql-disk
line matches the name of the disk
in the Compute Engine environment. See the
Persistent Volumes
for details on writing a PersistentVolume configuration file for other
environments.
Create the persistent volume:
kubectl create -f https://k8s.io/docs/tasks/run-application/gce-volume.yaml
You can run a stateful application by creating a Kubernetes Deployment and connecting it to an existing PersistentVolume using a PersistentVolumeClaim. For example, this YAML file describes a Deployment that runs MySQL and references the PersistentVolumeClaim. The file defines a volume mount for /var/lib/mysql, and then creates a PersistentVolumeClaim that looks for a 20G volume. This claim is satisfied by any volume that meets the requirements, in this case, the volume created above.
Note: The password is defined in the config yaml, and this is insecure. See Kubernetes Secrets for a secure solution.
mysql-deployment.yaml
|
---|
|
Deploy the contents of the YAML file:
kubectl create -f https://k8s.io/docs/tasks/run-application/mysql-deployment.yaml
Display information about the Deployment:
kubectl describe deployment mysql
Name: mysql
Namespace: default
CreationTimestamp: Tue, 01 Nov 2016 11:18:45 -0700
Labels: app=mysql
Annotations: deployment.kubernetes.io/revision=1
Selector: app=mysql
Replicas: 1 desired | 1 updated | 1 total | 0 available | 1 unavailable
StrategyType: Recreate
MinReadySeconds: 0
Pod Template:
Labels: app=mysql
Containers:
mysql:
Image: mysql:5.6
Port: 3306/TCP
Environment:
MYSQL_ROOT_PASSWORD: password
Mounts:
/var/lib/mysql from mysql-persistent-storage (rw)
Volumes:
mysql-persistent-storage:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: mysql-pv-claim
ReadOnly: false
Conditions:
Type Status Reason
---- ------ ------
Available False MinimumReplicasUnavailable
Progressing True ReplicaSetUpdated
OldReplicaSets: <none>
NewReplicaSet: mysql-63082529 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
33s 33s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
List the pods created by the Deployment:
kubectl get pods -l app=mysql
NAME READY STATUS RESTARTS AGE
mysql-63082529-2z3ki 1/1 Running 0 3m
Inspect the Persistent Volume:
kubectl describe pv mysql-pv
Name: mysql-pv
Labels: <none>
Annotations: pv.kubernetes.io/bound-by-controller=yes
StorageClass:
Status: Bound
Claim: default/mysql-pv-claim
Reclaim Policy: Retain
Access Modes: RWO
Capacity: 20Gi
Message:
Source:
Type: GCEPersistentDisk (a Persistent Disk resource in Google Compute Engine)
PDName: mysql-disk
FSType: ext4
Partition: 0
ReadOnly: false
Events: <none>
Inspect the PersistentVolumeClaim:
kubectl describe pvc mysql-pv-claim
Name: mysql-pv-claim
Namespace: default
StorageClass:
Status: Bound
Volume: mysql-pv
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed=yes
pv.kubernetes.io/bound-by-controller=yes
Capacity: 20Gi
Access Modes: RWO
Events: <none>
The preceding YAML file creates a service that
allows other Pods in the cluster to access the database. The Service option
clusterIP: None
lets the Service DNS name resolve directly to the
Pod’s IP address. This is optimal when you have only one Pod
behind a Service and you don’t intend to increase the number of Pods.
Run a MySQL client to connect to the server:
kubectl run -it --rm --image=mysql:5.6 mysql-client -- mysql -h <pod-ip> -p <password>
This command creates a new Pod in the cluster running a MySQL client and connects it to the server through the Service. If it connects, you know your stateful MySQL database is up and running.
Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
mysql>
The image or any other part of the Deployment can be updated as usual
with the kubectl apply
command. Here are some precautions that are
specific to stateful apps:
strategy:
type: Recreate
in the Deployment configuration
YAML file. This instructs Kubernetes to not use rolling
updates. Rolling updates will not work, as you cannot have more than
one Pod running at a time. The Recreate
strategy will stop the
first pod before creating a new one with the updated configuration.Delete the deployed objects by name:
kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv
Also, if you are using Compute Engine disks:
gcloud compute disks delete mysql-disk
Learn more about Deployment objects.
Learn more about Deploying applications