etcd

command module
v0.2.0-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 14, 2013 License: Apache-2.0 Imports: 10 Imported by: 0

README

etcd

README version 0.2.0

Build Status

A highly-available key value store for shared configuration and service discovery. etcd is inspired by zookeeper and doozer, with a focus on:

  • Simple: curl'able user facing API (HTTP+JSON)
  • Secure: optional SSL client cert authentication
  • Fast: benchmarked 1000s of writes/s per instance
  • Reliable: Properly distributed using Raft

Etcd is written in Go and uses the Raft consensus algorithm to manage a highly-available replicated log.

See etcdctl for a simple command line client. Or feel free to just use curl, as in the examples below.

Contact

Getting Started

Getting etcd

The latest release is available as a binary at Github.

Building

You can build etcd from source:

git clone https://github.com/coreos/etcd
cd etcd
./build

This will generate a binary in the base directory called ./etcd.

NOTE: you need go 1.1+. Please check your installation with

go version
Running a single node

These examples will use a single node cluster to show you the basics of the etcd REST API. Let's start etcd:

./etcd -d node0 -n node0

This will bring up an etcd node listening on port 4001 for client communication and on port 7001 for server-to-server communication. The -d node0 argument tells etcd to write node configuration, logs and snapshots to the ./node0/ directory. The -n node0 tells the rest of the cluster that this node is named node0.

Usage

Setting the value to a key

Let’s set the first key-value pair to the node. In this case the key is /message and the value is Hello world.

curl -L http://127.0.0.1:4001/v2/keys/message -X PUT -d value="Hello world"
{"action":"set","key":"/message","value":"Hello world","modifiedIndex":2}

This response contains four fields. We will introduce three more fields as we try more commands.

  1. The action of the request; we set the value via a PUT request, thus the action is set.

  2. The key of the request; we set /message to Hello world, so the key field is /message. We use a file system like structure to represent the key-value pairs so each key starts with /.

  3. The current value of the key; we set the value toHello world.

  4. Modified Index is a unique, monotonically incrementing index created for each change to etcd. Requests that change the index include set, delete, update, create and compareAndSwap. Since the get and watch commands do not change state in the store, they do not change the index. You may notice that in this example the index is 2 even though it is the first request you sent to the server. This is because there are internal commands that also change the state like adding and syncing servers.

Get the value of a key

We can get the value that we just set in /message by issuing a GET request:

curl -L http://127.0.0.1:4001/v2/keys/message
{"action":"get","key":"/message","value":"Hello world","modifiedIndex":2}
Changing the value of a key

You can change the value of /message from Hello world to Hello etcd with another PUT request to the key:

curl -L http://127.0.0.1:4001/v1/keys/message -XPUT -d value="Hello etcd"
{"action":"set","key":"/message","prevValue":"Hello world","value":"Hello etcd","index":3}

Notice that the prevValue is set to the previous value of the key - Hello world. It is useful when you want to atomically set a value to a key and get its old value.

Deleting a key

You can remove the /message key with a DELETE request:

curl -L http://127.0.0.1:4001/v2/keys/message -XDELETE
{"action":"delete","key":"/message","prevValue":"Hello etcd","modifiedIndex":4}
Using key TTL

Keys in etcd can be set to expire after a specified number of seconds. You can do this by setting a TTL (time to live) on the key when send a PUT request:

curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -d ttl=5
{"action":"set","key":"/foo","value":"bar","expiration":"2013-11-12T20:21:22.629352334-05:00","ttl":5,"modifiedIndex":5}

Note the two new fields in response:

  1. The expiration is the time that this key will expire and be deleted.

  2. The ttl is the time to live for the key, in seconds.

NOTE: Keys can only be expired by a cluster leader so if a node gets disconnected from the cluster, its keys will not expire until it rejoins.

Now you can try to get the key by sending a GET request:

curl -L http://127.0.0.1:4001/v2/keys/foo

If the TTL has expired, the key will be deleted, and you will be returned a 100.

{"errorCode":100,"message":"Key Not Found","cause":"/foo","index":6}
Waiting for a change

We can watch for a change on a key and receive a notification by using long polling. This also works for child keys by passing recursive=true in curl.

In one terminal, we send a get request with wait=true :

curl -L http://127.0.0.1:4001/v2/keys/foo?wait=true

Now we are waiting for any changes at path /foo.

In another terminal, we set a key /foo with value bar:

curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar

The first terminal should get the notification and return with the same response as the set request.

{"action":"set","key":"/foo","value":"bar","modifiedIndex":7}

However, the watch command can do more than this. Using the the index we can watch for commands that has happened in the past. This is useful for ensuring you don't miss events between watch commands.

Let's try to watch for the set command of index 7 again:

curl -L http://127.0.0.1:4001/v2/keys/foo?wait=true\&waitIndex=7

The watch command returns immediately with the same response as previous.

Atomic Compare-and-Swap (CAS)

Etcd can be used as a centralized coordination service in a cluster and CompareAndSwap is the most basic operation to build distributed lock service.

This command will set the value of a key only if the client-provided conditions are equal to the current conditions.

The current comparable conditions are:

  1. prevValue - checks the previous value of the key.

  2. prevIndex - checks the previous index of the key.

  3. prevExist - checks existence of the key: if prevExist is true, it is a update request; if prevExist is false, it is a create request.

Here is a simple example. Let's create a key-value pair first: foo=one.

curl -L http://127.0.0.1:4001/v1/keys/foo -XPUT -d value=one

Let's try an invalid CompareAndSwap command first. We can provide the prevValue parameter to the set command to make it a CompareAndSwap command.

curl -L http://127.0.0.1:4001/v1/keys/foo?prevValue=two -XPUT -d value=three

This will try to compare the previous value of the key and the previous value we provided. If they are equal, the value of the key will change to three.

{"errorCode":101,"message":"Test Failed","cause":"[two != one] [0 != 8]","index":8}

which means CompareAndSwap failed.

Let's try a valid condition:

curl -L http://127.0.0.1:4001/v2/keys/foo?prevValue=one -XPUT -d value=two

The response should be

{"action":"compareAndSwap","key":"/foo","prevValue":"one","value":"two","modifiedIndex":9}

We successfully changed the value from “one” to “two” since we gave the correct previous value.

Listing a directory

In etcd we can store two types of things: keys and directories. Keys store a single string value. Directories store a set of keys and/or other directories.

In this example, let's first create some keys:

We already have /foo=two so now we'll create another one called /foo_dir/foo with the value of bar:

curl -L http://127.0.0.1:4001/v2/keys/foo_dir/foo -XPUT -d value=bar
{"action":"set","key":"/foo_dir/foo","value":"bar","modifiedIndex":10}

Now we can list the keys under root /:

curl -L http://127.0.0.1:4001/v2/keys/

We should see the response as an array of items:

{"action":"get","key":"/","dir":true,"kvs":[{"key":"/foo","value":"two","modifiedIndex":9},{"key":"/foo_dir","dir":true,"modifiedIndex":10}],"modifiedIndex":0}

Here we can see /foo is a key-value pair under / and /foo_dir is a directory. We can also recursively get all the contents under a directory by adding recursive=true.

curl -L http://127.0.0.1:4001/v2/keys/?recursive=true
{"action":"get","key":"/","dir":true,"kvs":[{"key":"/foo","value":"two","modifiedIndex":9},{"key":"/foo_dir","dir":true,"kvs":[{"key":"/foo_dir/foo","value":"bar","modifiedIndex":10}],"modifiedIndex":10}],"modifiedIndex":0}
Deleting a directory

Now let's try to delete the directory /foo_dir.

To delete a directory, we must add recursive=true.

curl -L http://127.0.0.1:4001/v2/keys/foo_dir?recursive=true -XDELETE
{"action":"delete","key":"/foo_dir","dir":true,"modifiedIndex":11}
Creating a hidden node

We can create a hidden key-value pair or directory by add a _ prefix. The hidden item will not be listed when sending a GET request for a directory.

First we'll add a hidden key named /_message:

curl -L http://127.0.0.1:4001/v2/keys/_message -XPUT -d value="Hello hidden world"
{"action":"set","key":"/_message","value":"Hello hidden world","modifiedIndex":12}

Next we'll add a regular key named /message:

curl -L http://127.0.0.1:4001/v2/keys/message -XPUT -d value="Hello world"
{"action":"set","key":"/message","value":"Hello world","modifiedIndex":13}

Now let's try to get a listing of keys under the root directory, /:

curl -L http://127.0.0.1:4001/v2/keys/
{"action":"get","key":"/","dir":true,"kvs":[{"key":"/foo","value":"two","modifiedIndex":9},{"key":"/message","value":"Hello world","modifiedIndex":13}],"modifiedIndex":0}

<<<<<<< HEAD which means foo=barbar is a key-value pair under /foo and foo_dir is a directory.

Here we see the /message key but our hidden /_message key is not returned.

aa047b124d7beefcfa3dc79f1791bf60980cbe6b

Advanced Usage

Transport security with HTTPS

Etcd supports SSL/TLS and client cert authentication for clients to server, as well as server to server communication.

First, you need to have a CA cert clientCA.crt and signed key pair client.crt, client.key. This site has a good reference for how to generate self-signed key pairs: http://www.g-loaded.eu/2005/11/10/be-your-own-ca/

For testing you can use the certificates in the fixtures/ca directory.

Let's configure etcd to use this keypair:

./etcd -n node0 -d node0 -clientCert=./fixtures/ca/server.crt -clientKey=./fixtures/ca/server.key.insecure -f

There are a few new options we're using:

  • -f - forces a new node configuration, even if an existing configuration is found. (WARNING: data loss!)
  • -clientCert and -clientKey specify the location of the cert and key files to be used for for transport layer security between the client and server.

You can now test the configuration using HTTPS:

curl --cacert ./fixtures/ca/server-chain.pem https://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -v

You should be able to see the handshake succeed.

...
SSLv3, TLS handshake, Finished (20):
...

And also the response from the etcd server:

{"action":"set","key":"/foo","prevValue":"bar","value":"bar","modifiedIndex":3}
Authentication with HTTPS client certificates

We can also do authentication using CA certs. The clients will provide their cert to the server and the server will check whether the cert is signed by the CA and decide whether to serve the request.

./etcd -n node0 -d node0 -clientCAFile=./fixtures/ca/ca.crt -clientCert=./fixtures/ca/server.crt -clientKey=./fixtures/ca/server.key.insecure -f

-clientCAFile is the path to the CA cert.

Try the same request to this server:

curl --cacert ./fixtures/ca/server-chain.pem https://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -v

The request should be rejected by the server.

...
routines:SSL3_READ_BYTES:sslv3 alert bad certificate
...

We need to give the CA signed cert to the server.

curl --key ./fixtures/ca/server2.key.insecure --cert ./fixtures/ca/server2.crt --cacert ./fixtures/ca/server-chain.pem -L https://127.0.0.1:4001/v1/keys/foo -XPUT -d value=bar -v

You should able to see:

...
SSLv3, TLS handshake, CERT verify (15):
...
TLS handshake, Finished (20)

And also the response from the server:

{"action":"set","key":"/foo","prevValue":"bar","value":"bar","modifiedIndex":3}

Clustering

Example cluster of three machines

Let's explore the use of etcd clustering. We use Raft as the underlying distributed protocol which provides consistency and persistence of the data across all of the etcd instances.

Let start by creating 3 new etcd instances.

We use -s to specify server port and -c to specify client port and -d to specify the directory to store the log and info of the node in the cluster

./etcd -s 127.0.0.1:7001 -c 127.0.0.1:4001 -d nodes/node1 -n node1

Note: If you want to run etcd on an external IP address and still have access locally, you'll need to add -cl 0.0.0.0 so that it will listen on both external and localhost addresses. A similar argument -sl is used to setup the listening address for the server port.

Let's join two more nodes to this cluster using the -C argument:

./etcd -s 127.0.0.1:7002 -c 127.0.0.1:4002 -C 127.0.0.1:7001 -d nodes/node2 -n node2
./etcd -s 127.0.0.1:7003 -c 127.0.0.1:4003 -C 127.0.0.1:7001 -d nodes/node3 -n node3

We can retrieve a list of machines in the cluster using the HTTP API:

curl -L http://127.0.0.1:4001/v1/machines

We should see there are three nodes in the cluster

http://127.0.0.1:4001, http://127.0.0.1:4002, http://127.0.0.1:4003

The machine list is also available via the main key API:

curl -L http://127.0.0.1:4001/v1/keys/_etcd/machines
[{"action":"get","key":"/_etcd/machines/node1","value":"raft=http://127.0.0.1:7001\u0026etcd=http://127.0.0.1:4001","index":1},{"action":"get","key":"/_etcd/machines/node2","value":"raft=http://127.0.0.1:7002\u0026etcd=http://127.0.0.1:4002","index":1},{"action":"get","key":"/_etcd/machines/node3","value":"raft=http://127.0.0.1:7003\u0026etcd=http://127.0.0.1:4003","index":1}]

We can also get the current leader in the cluster:

curl -L http://127.0.0.1:4001/v2/leader

The first server we set up should still be the leader unless it has died during these commands.

http://127.0.0.1:7001

Now we can do normal SET and GET operations on keys as we explored earlier.

curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar
{"action":"set","key":"/foo","value":"bar","modifiedIndex":4}
Killing Nodes in the Cluster

Now if we kill the leader of the cluster, we can get the value from one of the other two machines:

curl -L http://127.0.0.1:4002/v1/keys/foo

We can also see that a new leader has been elected:

curl -L http://127.0.0.1:4002/v1/leader
http://127.0.0.1:7002

or

http://127.0.0.1:7003
Testing Persistence

Next we'll kill all the nodes to test persistence. Type CTRL-C on each terminal and then rerun the same command you used to start each node.

Your request for the foo key will return the correct value:

curl -L http://127.0.0.1:4002/v1/keys/foo
{"action":"get","key":"/foo","value":"bar","index":4}
Using HTTPS between servers

In the previous example we showed how to use SSL client certs for client-to-server communication. Etcd can also do internal server-to-server communication using SSL client certs. To do this just change the -client* flags to -server*.

If you are using SSL for server-to-server communication, you must use it on all instances of etcd.

Contributing

See CONTRIBUTING for details on submitting patches and contacting developers via IRC and mailing lists.

Libraries and Tools

Tools

  • etcdctl - A command line client for etcd

Go libraries

Java libraries

Python libraries

Node libraries

Ruby libraries

C libraries

Clojure libraries

Chef Integration

Chef Cookbook

Projects using etcd

FAQ

What size cluster should I use?

Every command the client sends to the master is broadcast to all of the followers. The command is not committed until the majority of the cluster machines receive that command.

Because of this majority voting property, the ideal cluster should be kept small to keep speed up and be made up of an odd number of machines.

Odd numbers are good because if you have 8 machines the majority will be 5 and if you have 9 machines the majority will still be 5. The result is that an 8 machine cluster can tolerate 3 machine failures and a 9 machine cluster can tolerate 4 nodes failures. And in the best case when all 9 machines are responding the cluster will perform at the speed of the fastest 5 nodes.

Why SSLv3 alert handshake failure when using SSL client auth?

The crypto/tls package of golang checks the key usage of the certificate public key before using it. To use the certificate public key to do client auth, we need to add clientAuth to Extended Key Usage when creating the certificate public key.

Here is how to do it:

Add the following section to your openssl.cnf:

[ ssl_client ]
...
  extendedKeyUsage = clientAuth
...

When creating the cert be sure to reference it in the -extensions flag:

openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/node.crt -infiles node.csr

Project Details

Versioning

etcd uses semantic versioning. New minor versions may add additional features to the API however.

You can get the version of etcd by issuing a request to /version:

curl -L http://127.0.0.1:4001/version

During the pre-v1.0.0 series of releases we may break the API as we fix bugs and get feedback.

License

etcd is under the Apache 2.0 license. See the LICENSE file for details.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
mod
mod is the entry point to all of the etcd modules.
mod is the entry point to all of the etcd modules.
v1
v2
v2
third_party
bitbucket.org/kardianos/osext
Extensions to the standard "os" package.
Extensions to the standard "os" package.
code.google.com/p/go.net/dict
Package dict implements the Dictionary Server Protocol as defined in RFC 2229.
Package dict implements the Dictionary Server Protocol as defined in RFC 2229.
code.google.com/p/go.net/html
Package html implements an HTML5-compliant tokenizer and parser.
Package html implements an HTML5-compliant tokenizer and parser.
code.google.com/p/go.net/html/atom
Package atom provides integer codes (also known as atoms) for a fixed set of frequently occurring HTML strings: tag names and attribute keys such as "p" and "id".
Package atom provides integer codes (also known as atoms) for a fixed set of frequently occurring HTML strings: tag names and attribute keys such as "p" and "id".
code.google.com/p/go.net/idna
Package idna implements IDNA2008 (Internationalized Domain Names for Applications), defined in RFC 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
Package idna implements IDNA2008 (Internationalized Domain Names for Applications), defined in RFC 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
code.google.com/p/go.net/ipv4
Package ipv4 implements IP-level socket options for the Internet Protocol version 4.
Package ipv4 implements IP-level socket options for the Internet Protocol version 4.
code.google.com/p/go.net/ipv6
Package ipv6 implements IP-level socket options for the Internet Protocol version 6.
Package ipv6 implements IP-level socket options for the Internet Protocol version 6.
code.google.com/p/go.net/netutil
Package netutil provides network utility functions, complementing the more common ones in the net package.
Package netutil provides network utility functions, complementing the more common ones in the net package.
code.google.com/p/go.net/proxy
Package proxy provides support for a variety of protocols to proxy network data.
Package proxy provides support for a variety of protocols to proxy network data.
code.google.com/p/go.net/publicsuffix
Package publicsuffix provides a public suffix list based on data from http://publicsuffix.org/.
Package publicsuffix provides a public suffix list based on data from http://publicsuffix.org/.
code.google.com/p/go.net/spdy
Package spdy implements the SPDY protocol (currently SPDY/3), described in http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3.
Package spdy implements the SPDY protocol (currently SPDY/3), described in http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3.
code.google.com/p/go.net/websocket
Package websocket implements a client and server for the WebSocket protocol as specified in RFC 6455.
Package websocket implements a client and server for the WebSocket protocol as specified in RFC 6455.
code.google.com/p/goprotobuf/proto
Package proto converts data structures to and from the wire format of protocol buffers.
Package proto converts data structures to and from the wire format of protocol buffers.
code.google.com/p/goprotobuf/protoc-gen-go/generator
The code generator for the plugin for the Google protocol buffer compiler.
The code generator for the plugin for the Google protocol buffer compiler.
github.com/coreos/go-systemd/activation
Package activation implements primitives for systemd socket activation.
Package activation implements primitives for systemd socket activation.
github.com/coreos/go-systemd/dbus
Integration with the systemd D-Bus API.
Integration with the systemd D-Bus API.
github.com/coreos/go-systemd/journal
Package journal provides write bindings to the systemd journal
Package journal provides write bindings to the systemd journal
github.com/gorilla/context
Package gorilla/context stores values shared during a request lifetime.
Package gorilla/context stores values shared during a request lifetime.
github.com/gorilla/mux
Package gorilla/mux implements a request router and dispatcher.
Package gorilla/mux implements a request router and dispatcher.
github.com/stretchr/testify/mock
Provides a system by which it is possible to mock your objects and verify calls are happening as expected.
Provides a system by which it is possible to mock your objects and verify calls are happening as expected.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL