Key concepts for stateful services in Kubernetes

Kubernetes Boston

Ryan Wallner - Technical Evangelist

@RyanWallner - ryan.wallner@clusterhq.com

 

Avid outdoorsman, works on containers

Previously at IBM and EMC

Worked on SDN/OpenStack/Containers

Who am i?

Agenda

  • ​​Containers
  • Sateful / Stateless
  • Managing State
  • Persistence / Volumes
  • Kubernetes Volumes
  • Demo
  • Q/A

CONTAINERs

  • Container = Process and Resource Isolation
  • Linux Process (think of as light weight app vm)
  • Does not need a hypervisor, shares a linux kernel
  • Enables new architectures such as Microservices

http://en.community.dell.com/cfs-file/__key/communityserver-wikis-components-files/00-00-00-01-55/lxc_2D00_vm.jpg

"Stateful" Container

  • Secrets - public/private keys, password, etc
  • Databases - databases, sharded, clustered.
  • Logs - to collect support bundles, run analytics for data mining, etc. 
  • Other - CI repo data, transcoded bits...

"Stateless" Container

  • Nothing to Disk
  • Web Front-End
  • Can stop and start as many containers as you like
  • Like http is stateless
  • Container is ephemeral
  • Does not care about what has happened or changed.

Use Case

Stateless Streaming

Stateless Web

Stateful DB

Twitter

K8s

Cluster

var Twitter = require('twitter');
var MongoClient = require('mongodb').MongoClient;
Server = require('mongodb').Server;

var client = new Twitter({
    consumer_key: process.env.CONSUMER_KEY,
    consumer_secret: process.env.CONSUMER_SECRET,
    access_token_key: process.env.ACCESS_TOKEN_KEY,
    access_token_secret: process.env.ACCESS_TOKEN_SECRET
});

client.stream('statuses/filter', {
    track: process.env.TWITTER_TRACK
}, function(stream) {
    stream.on('data', function(tweet) {
        console.log(tweet.text);
        var client = new MongoClient(new Server(process.env.MONGOSERVER, "27017", {
            auto_reconnect: true
        }, {
            numberOfRetries: 10,
            retryMilliseconds: 500
        }));
        client.open(function(err, client) {
            if (err) {
                console.log(err);
            } else {
                var db = client.db("test");
                db.collection('records', function(err, collection) {
                    collection.insert({
                        'tweet': tweet.text
                    }, {
                        safe: true
                    }, function(err, result) {
                        if (!err) {
                            console.log(result);
                        } else {
                            console.log(err);
                        }
                    });
                });
            }
        });
    });
    stream.on('error', function(error) {
        console.log("error getting tweets");
        console.log(error);
    });
});

Stateless Streaming Service

var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var app = express();
var PORT = 8080;

var fs = require('fs'); // this engine requires the fs module
app.engine('ntl', function(filePath, options, callback) { // define the template engine
    fs.readFile(filePath, function(err, content) {
        if (err) return callback(new Error(err));
        var rendered = content.toString().replace('#tweet#', '<style type="text/css"> body { background-color: #f3f3f3; }</style><div id="twt" style="display: inline-block; position: fixed; top: 0; bottom: 0; left: 0; right: 0; width: 500px; height: 200px; margin: auto; font-size:22pt; font-weight:bold; font-family: Helvetica Neue, sans-serif; letter-spacing: -1px; line-height: 1; background-color: #f3f3f3;"><p>' + options.tweet + '</p></div>');
        return callback(null, rendered);
    })
});
app.set('views', './views'); // specify the views directory
app.set('view engine', 'ntl'); // register the template engine

app.get('/', function(req, res) {
    console.log('Contacting MongoDB');
    // Connect to the db
    MongoClient.connect("mongodb://" + process.env.MONGOSERVER + ":27017/test", function(err, db) {
        if (!err) {
            console.log("We are connected to MongoDB");
            db.collection('records', function(err, collection) {
                if (!err) {
                    collection.find().toArray(function(err, docs) {
                        if (!err) {
                            db.close();
                            len = docs.length - 1;
                            res.render('index', {
                                tweet: docs[len].tweet
                            });
                        }
                    });
                }
            });
        } else {
            res.send('Cannot connect to MongoDB\n');
            console.log("Cannot connect to MongoDB");
            console.log(err);
        }
    });
});

app.listen(PORT);

Stateless Web Service

var jsonfile = require('jsonfile')
var util = require('util')

var file = '/tmp/data.json'
var obj = {name: 'Ryan'}

//Write file
jsonfile.writeFile(file, obj, function (err) {
  console.error(err)
})

//Read File
jsonfile.readFile(file, function(err, obj) {
  console.dir(obj)
})

Stateful JSON Service

If the container or node fails, what happens to /tmp/data.json?

 

Do we care what changes were tracked and what happend/changed during the container lifecycle? Yes. (Stateful)

Managing State

It's important through the entire devops lifecycle

Dev

CI/Test

Production

development:
  adapter: postgresql
  encoding: unicode
  database: myapp_development
  pool: 5
  username: myapp
  password: password1

test:
  adapter: postgresql
  encoding: unicode
  database: myapp_test
  pool: 5
  username: myapp
  password: password1
#!/bin/bash -x
export RAILS_ENV=test
bundle install

read -d '' database_yml <<"EOF"  
login: &login
  adapter: mysql2
  username: builds
  password: 
  encoding: utf8

test: &test
  database: my_project_test
  <<: *login
EOF

echo "$database_yml" > config/database.yml
rake db:create db:test:prepare
bundle exec rspec spec/
<VirtualHost *:80>
  ServerName application_name.rails.local
  DocumentRoot "/Users/rails/app/public"
  RailsEnv production ## This is the default
</VirtualHost>

Volumes & Kubernetes

PersistentVolume (PV)

storage resource provisioned by admin.

 

PersistentVolumeClaim (PVC)

user request for persistent volume.

 

VOLUMES

  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlockStore
  • nfs
  • iscsi
  • flocker
  • glusterfs
  • rbd
  • gitRepo
  • secret
  • persistentVolumeClaim

Volumes & Kubernetes

A "Volume" from the pool

Pool of Volumes

Directly Reference Volumes

Volume: "MyVolume"

Pool of Volumes

Container

POD

volumes:
    - name: www-root
      flocker:
        datasetName: MyVolume
flockerctl create -m name=MyVolume -s 1Tb

User

Admin

Utilize Volume Claims

Pool of Volumes

Container

POD

volumes:
    - name: mypd
      source:
        persistentVolumeClaim:
         accessMode: ReadWriteOnce
         claimRef:
           name: myclaim-1

Persistent

Volume

Claim

PersistentVolume (best match)

POST: 

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim-1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
POST:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv0001
spec:
  capacity:
    storage: 8Gi
  persistentDisk:
    pdName: "abc123"
    fsType: "ext4"

Admin

User

User

Demo

QA & THANK YOU

Thanks to Microsoft NERD

www.clusterhq.com

www.clusterhq.com/blog

 

We're Hiring!

www.clusterhq.com/careers/

 

@RyanWallner

ryan.wallner@clusterhq.com

@clusterhq

Key concepts for stateful services in Kubernetes

By Ryan Wallner

Key concepts for stateful services in Kubernetes

  • 2,842