puppetdb: sneaking clojure into operations

109
Puppet DB The life and times of Clojure/West 2013

Upload: grimradical

Post on 25-Jun-2015

1.165 views

Category:

Technology


8 download

TRANSCRIPT

Page 2: PuppetDB: Sneaking Clojure into Operations

[email protected]@grim_radical [github twitter freenode]

Page 3: PuppetDB: Sneaking Clojure into Operations

Let’s talk about...

Page 4: PuppetDB: Sneaking Clojure into Operations
Page 5: PuppetDB: Sneaking Clojure into Operations

Immutabilityis great!

Page 6: PuppetDB: Sneaking Clojure into Operations

Immutability allows for invariants, which help you reason about correctness

Page 7: PuppetDB: Sneaking Clojure into Operations

Immutability prevents spooky action at a distance

Page 8: PuppetDB: Sneaking Clojure into Operations

Immutability fosters modular, composable abstractions

Page 9: PuppetDB: Sneaking Clojure into Operations

(not a tough sell to Clojurists*)

*clojurians?

Page 10: PuppetDB: Sneaking Clojure into Operations

That’s great for develoment, but how

about operations?

Page 11: PuppetDB: Sneaking Clojure into Operations

Immutability for infrastructure?Because operations is in the same boat as development

Page 12: PuppetDB: Sneaking Clojure into Operations

Everyone who’s got their app running on a fleet of servers has experienced spooky action at a distance

Page 13: PuppetDB: Sneaking Clojure into Operations

Known, good state is critical for reliable upgrades

Page 14: PuppetDB: Sneaking Clojure into Operations

A lack of predictability in your systemsruins automation and abstraction

Page 15: PuppetDB: Sneaking Clojure into Operations

The problem is that:

Systems are inherently mutable!

But ideally:

Systems should behave as though they weren’t!

Page 16: PuppetDB: Sneaking Clojure into Operations

façade of immutability. Immutability

Page 17: PuppetDB: Sneaking Clojure into Operations

façade of immutability. Immutability

-- The Joy of Clojure

façade of immutability. Immutability requires that we layer over and abstract the parts of our system that provide unrestrained mutability.

Computer systems are in many ways open systems, providing the keys to the vault if one is so inclined to grab them. But in order to foster an air of immutability in our own systems, it's of utmost importance to create a

Page 18: PuppetDB: Sneaking Clojure into Operations
Page 19: PuppetDB: Sneaking Clojure into Operations

Describe how you’d like your systems to look, and Puppet does all the hard work for you!

Page 20: PuppetDB: Sneaking Clojure into Operations

file { “/etc/issue”: content => “Got an issue? Here’s a tissue!”,}

file { “/etc/motd”: content => template(“Welcome to $hostname!”),}

Page 21: PuppetDB: Sneaking Clojure into Operations

file { "/etc/sudoers": owner => root, group => root, mode => 440, source => "puppet:///modules/sudo/sudoers"}

Page 22: PuppetDB: Sneaking Clojure into Operations

package { 'ntp': ensure => installed, } service { 'ntpd': ensure => running, enable => true, subscribe => File['/etc/ntp.conf'], } file { '/etc/ntp.conf': ensure => file, require => Package['ntp'], source => "puppet:///modules/ntp/ntp.conf", }

Page 23: PuppetDB: Sneaking Clojure into Operations

class ntp {

package { 'ntp': ensure => installed, } service { 'ntpd': ensure => running, enable => true, subscribe => File['/etc/ntp.conf'], } file { '/etc/ntp.conf': ensure => file, require => Package['ntp'], source => "puppet:///modules/ntp/ntp.conf", }

}

Page 24: PuppetDB: Sneaking Clojure into Operations

node “webserver.mydomain.com” { include ntp } node “appserver.mydomain.com” { include ntp }

node “database.mydomain.com” { include ntp }

Page 25: PuppetDB: Sneaking Clojure into Operations

class ssh {

@@sshkey { $hostname: type => dsa, key => $sshdsakey } Sshkey <<| |>>

}

Page 26: PuppetDB: Sneaking Clojure into Operations

Dir “/tmp/foo”

File “/tmp/foo/bar”

Dir “/tmp”

User “deepak”

Page 27: PuppetDB: Sneaking Clojure into Operations

Dir “/tmp/foo”

File “/tmp/foo/bar”

Dir “/tmp” User “deepak”

Page 28: PuppetDB: Sneaking Clojure into Operations

Dir “/tmp/foo”

File “/tmp/foo/bar”

Dir “/tmp” User “deepak”

Page 29: PuppetDB: Sneaking Clojure into Operations
Page 30: PuppetDB: Sneaking Clojure into Operations
Page 31: PuppetDB: Sneaking Clojure into Operations

Idempotent, and only does what’s necessary

Page 32: PuppetDB: Sneaking Clojure into Operations

Compensates for the inherent mutability of systems

Page 33: PuppetDB: Sneaking Clojure into Operations

Combats spooky action at a distance with automatic repair

Page 34: PuppetDB: Sneaking Clojure into Operations

Brings predictability to your systems

Page 35: PuppetDB: Sneaking Clojure into Operations

A foundation of predictability and reliability lets you perform higher-level operations on your infrastructure

Page 36: PuppetDB: Sneaking Clojure into Operations

V OLUME

Page 37: PuppetDB: Sneaking Clojure into Operations

every resourceevery parameter

every relationshipevery fact

for every nodeupdated all the time

Page 38: PuppetDB: Sneaking Clojure into Operations

Users leverage this data to do higher-order things with their infrastructure

Page 39: PuppetDB: Sneaking Clojure into Operations

key distributionmonitoring

clustered servicesmaster/slave replication

load balancersshared filesystems

firewall rules...

Page 40: PuppetDB: Sneaking Clojure into Operations

Infrastructure as code

Page 41: PuppetDB: Sneaking Clojure into Operations

Infrastructure as data

Page 42: PuppetDB: Sneaking Clojure into Operations

User demand:

Store as much data as we can!Much better queryability!

Oh yeah, but:

Don’t slow down the system!Don’t compromise reliability!

Page 43: PuppetDB: Sneaking Clojure into Operations

We can rebuild it, we have the technology!

Page 44: PuppetDB: Sneaking Clojure into Operations

Speed is important

Parsing, validating, and manipulating incoming data is

computationally expensive

Page 45: PuppetDB: Sneaking Clojure into Operations

Speed is important

The slower central storage is, the less agile sysadmins can be. That

can cost money and uptime!

Page 46: PuppetDB: Sneaking Clojure into Operations

Reliability is important

If it’s a critical part of managing infrastructure, it’s got to be solid!

Page 47: PuppetDB: Sneaking Clojure into Operations

Deployment is important

Should be easy to package, install, configure, use, monitor,

and upgrade.

Page 48: PuppetDB: Sneaking Clojure into Operations

Wait, isn’t Puppet written in Ruby?

Page 49: PuppetDB: Sneaking Clojure into Operations

Lessons learned from writing the rest of our software in Ruby

It’s...not speedy. Object creation, method calls, garbage collection,

etc. “Magical” APIs amplify the problem.

Page 50: PuppetDB: Sneaking Clojure into Operations

Lessons learned from writing the rest of our software in Ruby

Can only use one core! Workarounds compromise performance or simplicity

Page 51: PuppetDB: Sneaking Clojure into Operations

Lessons learned from writing the rest of our software in Ruby

Mutable state all over the damn place!

Page 52: PuppetDB: Sneaking Clojure into Operations

Lessons learned from writing the rest of our software in Ruby

Many struggles with the runtime. :(

Page 53: PuppetDB: Sneaking Clojure into Operations

-- Jeff Gagliardi

Page 54: PuppetDB: Sneaking Clojure into Operations
Page 55: PuppetDB: Sneaking Clojure into Operations

1. It’s fast

Page 56: PuppetDB: Sneaking Clojure into Operations

2. JVM libraries & Tools

Page 57: PuppetDB: Sneaking Clojure into Operations

3. State & Parallelism

Page 58: PuppetDB: Sneaking Clojure into Operations

PuppetDB

Page 59: PuppetDB: Sneaking Clojure into Operations

PuppetDB

Definitely Better!

Page 60: PuppetDB: Sneaking Clojure into Operations

Fast, safe storageof catalogs, facts, and events

like, *way* faster!

Page 61: PuppetDB: Sneaking Clojure into Operations

HTTP APIsfor resource, fact, node, report retrieval

plenty of data, justa “curl” away!

Page 62: PuppetDB: Sneaking Clojure into Operations

Storage &Querying

Page 63: PuppetDB: Sneaking Clojure into Operations

Storage &Querying

Page 64: PuppetDB: Sneaking Clojure into Operations

CQRS

Page 65: PuppetDB: Sneaking Clojure into Operations

CommandQueryResponsibilitySeparation

use a different model to update information than the model you

use to read information

Page 66: PuppetDB: Sneaking Clojure into Operations

Writes

Page 67: PuppetDB: Sneaking Clojure into Operations

CQRSwrite pipeline

async, parallel, MQ-based, with automatic retry

Page 68: PuppetDB: Sneaking Clojure into Operations

{ :command "replace catalog" :version 2 :payload {...}}

Page 69: PuppetDB: Sneaking Clojure into Operations

/commands MQ Parse

Delayed

Dead Letter Office

Process

UUID

Page 70: PuppetDB: Sneaking Clojure into Operations

(defmulti process-command!  (fn [{:keys [command version] :or {version 1}} _]    [command version]))

(defmethod process-command! ["replace catalog" 1] [command options]  (replace-catalog* command options))

Page 71: PuppetDB: Sneaking Clojure into Operations

(defmulti process-command!  (fn [{:keys [command version] :or {version 1}} _]    [command version]))

(defmethod process-command! ["replace catalog" 2]  [command options]  (replace-catalog* command options))

(defmethod process-command! ["replace catalog" 1]  [command options] (-> command (update-in [:payload] catalog-v1->v2)   (replace-catalog* options))

Page 72: PuppetDB: Sneaking Clojure into Operations

Command processors must be retry-aware

expect failure, because it *will* happen.

Page 73: PuppetDB: Sneaking Clojure into Operations

(demo)

Page 74: PuppetDB: Sneaking Clojure into Operations

Reads

Page 75: PuppetDB: Sneaking Clojure into Operations

Queriesare expressed in their own “language”

domain specific, AST-based query language

Page 76: PuppetDB: Sneaking Clojure into Operations

["and", ["=", "type", "User"], ["=", "title", "nick"]]

Page 77: PuppetDB: Sneaking Clojure into Operations

["and", ["=", ["fact", "operatingsystem"], "Debian"], ["<", ["fact", "uptime_seconds"], 10000]]

Page 78: PuppetDB: Sneaking Clojure into Operations

["and", ["=", "name", "ipaddress"], ["in", "certname", ["extract", "certname", ["select-resources", ["and", ["=", "type", "Class"], ["=", "title", "Apache"]]]]

Page 79: PuppetDB: Sneaking Clojure into Operations

["or", ["=", "certname", "foo.com"], ["=", "certname", "bar.com"], ["=", "certname", "baz.com"]]

Page 80: PuppetDB: Sneaking Clojure into Operations

We use core.match to walk the tree, compiling it to SQL

Page 81: PuppetDB: Sneaking Clojure into Operations

AST-based API lets users write their own languages

ah, you’ve got to love open source!

Page 82: PuppetDB: Sneaking Clojure into Operations

(Package[httpd] and country=fr)or country=us

Package["mysql-server"]and architecture=amd64

Erik Dalén, Spotifyhttps://github.com/dalen/puppet-puppetdbquery

Page 83: PuppetDB: Sneaking Clojure into Operations

AST-based API lets us more safely manipulate queries

Page 84: PuppetDB: Sneaking Clojure into Operations

(def query-app  (app    [&]    {:get (fn [{:keys [params] :as request}]             (perform-query (params "query")))}))

(def resources-app  (app   []   query-app

   [type title &]   (comp query-app (partial http-q/restrict-resource-query-to-type type) (partial http-q/restrict-resource-query-to-title title))

   [type &]   (comp query-app (partial http-q/restrict-resource-query-to-type type))))

Page 85: PuppetDB: Sneaking Clojure into Operations

(defn restrict-resource-query-to-type  [type query]  (conj ["and"          ["=" "type" type]]        query))

Page 86: PuppetDB: Sneaking Clojure into Operations

Storage

Page 87: PuppetDB: Sneaking Clojure into Operations

Relational Database, embedded or PostgreSQL

because they’re actually pretty fantastic at ad-hoc queries,

aggregation, windowing, etc. while maintaining safety

Page 88: PuppetDB: Sneaking Clojure into Operations
Page 89: PuppetDB: Sneaking Clojure into Operations

Relational Database, embedded or PostgreSQL

we use arrays, recursive queries, indexing inside complex

structures

Page 90: PuppetDB: Sneaking Clojure into Operations

Relational Database, embedded or PostgreSQL

schema is oriented towards the types of queries we encounter

Page 91: PuppetDB: Sneaking Clojure into Operations

Deployment

Page 92: PuppetDB: Sneaking Clojure into Operations

PuppetDB Server

HTTP MQ

DB Workers

DLO

Page 93: PuppetDB: Sneaking Clojure into Operations

PuppetDB Server

HTTP MQ

DBWorkers DLO

Page 94: PuppetDB: Sneaking Clojure into Operations

PuppetDB Server

HTTP MQ

DBWorkers DLOHTTPProxy(SSL)

Page 95: PuppetDB: Sneaking Clojure into Operations

Codez

Page 96: PuppetDB: Sneaking Clojure into Operations

(defn process-commands!  "Connect to an MQ an continually, sequentially process commands.

If the MQ consumption timeout is reached without any new data, the function will terminate."  [connection endpoint discarded-dir options-map]  (let [producer (mq-conn/producer connection)        publish (partial mq-producer/publish producer endpoint)        on-message (produce-message-handler publish discarded-dir options-map)        mq-error (promise)        consumer (mq-conn/consumer connection                                     {:endpoint endpoint                                      :on-message on-message                                      :transacted true                                      :on-failure #(deliver mq-error (:exception %))})]    (mq-cons/start consumer)

    ;; Block until an exception is thrown by the consumer thread    (try      (deref mq-error)      (throw @mq-error)      (finally        (mq-cons/close consumer)))))

Page 97: PuppetDB: Sneaking Clojure into Operations

(defmacro with-error-delivery  "Executes body, and delivers an exception to the provided promise if one is thrown."  [error & body]  `(try     ~@body     (catch Exception e#       (deliver ~error e#))))

Page 98: PuppetDB: Sneaking Clojure into Operations

(defn filter-mbean  "Converts an mbean to a map. For attributes that can't be converted to JSON, return a string representation of the value."  [mbean]  {:post [(map? %)]}  (into {} (for [[k v] mbean]             (cond              ;; Nested structures should themselves be filtered              (map? v)              [k (filter-mbean v)]

              ;; Cheshire can serialize to JSON anything that              ;; implements the JSONable protocol              (satisfies? JSONable v)              [k v]

              :else              [k (str v)]))))

Page 99: PuppetDB: Sneaking Clojure into Operations

(defmacro with-test-broker  "Constructs and starts an embedded MQ, and evaluates `body` inside a `with-open` expression that takes care of connection cleanup and MQ tear-down.

`name` - The name to use for the embedded MQ

`conn-var` - Inside of `body`, the variable named `conn-var` contains an active connection to the embedded broker.

Example:

(with-test-broker \"my-broker\" the-connetion ;; Do something with the connection (prn the-connection)) "  [name conn-var & body]  `(let [dir# (fs/absolute-path (fs/temp-dir))         broker-name# ~name         conn-str# (str "vm://" ~name)         ^BrokerService broker# (mq/build-embedded-broker broker-name# dir#)]

     (.setUseJmx broker# false)     (.setPersistent broker# false)     (mq/start-broker! broker#)

     (try       (with-open [~conn-var (mq/connect! conn-str#)]         ~@body)       (finally         (mq/stop-broker! broker#)         (fs/delete-dir dir#)))))

Page 100: PuppetDB: Sneaking Clojure into Operations

Results

Page 101: PuppetDB: Sneaking Clojure into Operations

Thousands of production deployments

Small shops with a dozen hosts,large shops with thousands of hosts, intercontinental deployments...

Page 102: PuppetDB: Sneaking Clojure into Operations
Page 103: PuppetDB: Sneaking Clojure into Operations

Thousands of deployments,Hundreds of threads per install,Zero deadlocks,Zero bugs involving clojure state

companion Ruby code has ~10x the defect rate

Page 104: PuppetDB: Sneaking Clojure into Operations

Users dig the uberjar, makes deployment straightforward and less error-prone

Page 105: PuppetDB: Sneaking Clojure into Operations

Nobody cares that it’s Clojure. Users just want stuff to work.

Page 106: PuppetDB: Sneaking Clojure into Operations

Monitoring and instrumentation is a big deal. Users want easy ways to consume metrics.

Page 107: PuppetDB: Sneaking Clojure into Operations

If you build it, they will come.

Page 109: PuppetDB: Sneaking Clojure into Operations

[email protected]@grim_radical [github twitter freenode]

We’re hiring!