{% 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)
      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
        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.



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 %}