{% capture overview %}

This page shows you how to run a single-instance stateful application in Kubernetes using a PersistentVolume and a Deployment. The application is MySQL.

{% endcapture %}

{% capture objectives %}

  • Create a PersistentVolume referencing a disk in your environment.
  • Create a MySQL Deployment.
  • Expose MySQL to other pods in the cluster at a known DNS name.

{% endcapture %}

{% capture prerequisites %}

  • {% include task-tutorial-prereqs.md %}

  • For data persistence we will create a Persistent Volume that references a disk in your environment. See here for the types of environments supported. This Tutorial will demonstrate GCEPersistentDisk but any type will work. GCEPersistentDisk volumes only work on Google Compute Engine.

{% endcapture %}

{% capture lessoncontent %}

Set up a disk in your environment

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:

{% include code.html language="yaml" file="gce-volume.yaml" ghlink="/docs/tutorials/stateful-application/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 http://k8s.io/docs/tutorials/stateful-application/gce-volume.yaml

Deploy MySQL

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.

{% include code.html language="yaml" file="mysql-deployment.yaml" ghlink="/docs/tutorials/stateful-application/mysql-deployment.yaml" %}

  1. Deploy the contents of the YAML file:

    kubectl create -f http://k8s.io/docs/tutorials/stateful-application/mysql-deployment.yaml
    
  2. 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
    Selector:             app=mysql
    Replicas:             1 updated | 1 total | 0 available | 1 unavailable
    StrategyType:         Recreate
    MinReadySeconds:      0
    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
    
  3. 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
    
  4. Inspect the Persistent Volume:

    kubectl describe pv mysql-pv
    
    Name:            mysql-pv
    Labels:          <none>
    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
    No events.
    
  5. Inspect the PersistentVolumeClaim:

    kubectl describe pvc mysql-pv-claim
    
    Name:         mysql-pv-claim
    Namespace:    default
    Status:       Bound
    Volume:       mysql-pv
    Labels:       <none>
    Capacity:     20Gi
    Access Modes: RWO
    No events.
    

Accessing the MySQL instance

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 mysql -ppassword

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> 

Updating

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:

  • Don't scale the app. This setup is for single-instance apps only. The underlying PersistentVolume can only be mounted to one Pod. For clustered stateful apps, see the StatefulSet documentation.
  • Use 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.

Deleting a deployment

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

{% endcapture %}

{% capture whatsnext %}

{% endcapture %}

{% include templates/tutorial.md %}