etcd/README.md

470 lines
13 KiB
Markdown
Raw Normal View History

# etcd
2013-07-11 22:51:13 +04:00
2013-08-07 05:47:02 +04:00
[![Build Status](https://travis-ci.org/coreos/etcd.png)](https://travis-ci.org/coreos/etcd)
2013-07-23 21:14:29 +04:00
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
2013-07-23 22:01:02 +04:00
* Reliable: Properly distributed using Raft
2013-07-23 21:14:29 +04:00
2013-08-09 04:01:33 +04:00
Etcd is written in Go and uses the [raft][raft] consensus algorithm to manage a highly availably replicated log.
2013-07-23 21:14:29 +04:00
2013-08-09 04:01:33 +04:00
See [go-etcd][go-etcd] for a native Go client. Or feel free to just use curl, as in the examples below.
2013-07-23 21:14:29 +04:00
[raft]: https://github.com/coreos/go-raft
[go-etcd]: https://github.com/coreos/go-etcd
## Getting Started
2013-07-11 22:51:13 +04:00
### Building
2013-08-06 23:01:34 +04:00
To build etcd run the build script. This will generate a binary in the base directory called `./etcd`.
```
2013-08-06 23:01:34 +04:00
./build
```
### Running a single node
These examples will use a single node cluster to show you the basics of the etcd REST API. Lets start etcd:
2013-07-12 09:00:39 +04:00
2013-07-12 09:08:41 +04:00
```sh
./etcd -d node0
2013-07-12 09:08:41 +04:00
```
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.
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
## Usage
### Setting the value to a key
2013-07-12 09:00:39 +04:00
2013-07-12 09:32:40 +04:00
Lets set the first key-value pair to the node. In this case the key is `/message` and the value is `Hello world`.
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/message -d value="Hello world"
2013-07-12 09:00:39 +04:00
```
```json
{"action":"SET","key":"/message","value":"Hello world","newKey":true,"index":3}
```
This response contains five fields. We will introduce three more fields as we try more commands.
2013-07-12 09:32:40 +04:00
1. The action of the request; we set the value via a POST request, thus the action is `SET`.
2. The key of the request; we set `/message` to `Hello world!`, so the key field is `/message`.
2013-07-12 09:32:40 +04:00
Notice we use a file system like structure to represent the key-value pairs. So each key starts with `/`.
2013-07-12 09:00:39 +04:00
2013-07-12 09:32:40 +04:00
3. The current value of the key; we set the value to`Hello world`.
2013-07-12 09:00:39 +04:00
2013-07-12 09:32:40 +04:00
4. If we set a new key; `/message` did not exist before, so this is a new key.
2013-07-12 09:00:39 +04:00
5. Index is the unique internal log index of the set request. Requests that change the log index include `SET`, `DELETE` and `TESTANDSET`. The `GET`, `LIST` and `WATCH` commands do not change state in the store and so they do not change the index. You may notice that in this example the index is 3, although 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.
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
### Get the value of a key
2013-07-12 09:00:39 +04:00
Get the value that we just set in `/message` by issuing a GET:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/message
2013-07-12 09:00:39 +04:00
```
2013-07-12 09:32:40 +04:00
```json
2013-07-12 09:00:39 +04:00
{"action":"GET","key":"/message","value":"Hello world","index":3}
2013-07-12 09:32:40 +04:00
```
2013-08-09 06:50:28 +04:00
### Change the value of a key
2013-07-12 09:00:39 +04:00
Change the value of `/message` from `Hello world` to `Hello etcd` with another POST to the key:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/message -d value="Hello etcd"
2013-07-12 09:00:39 +04:00
```
```json
{"action":"SET","key":"/message","prevValue":"Hello world","value":"Hello etcd","index":4}
```
Notice that the `prevValue` is set to `Hello world`.
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
### Delete a key
2013-07-12 09:00:39 +04:00
Remove the `/message` key with a DELETE:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/message -X DELETE
2013-07-12 09:00:39 +04:00
```
```json
{"action":"DELETE","key":"/message","prevValue":"Hello etcd","index":5}
```
2013-08-09 06:50:28 +04:00
### Using key TTL
2013-07-12 09:00:39 +04:00
Keys in etcd can be set to expire after a specified number of seconds. That is done by setting a TTL (time to live) on the key when you POST:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/foo -d value=bar -d ttl=5
2013-07-12 09:00:39 +04:00
```
```json
{"action":"SET","key":"/foo","value":"bar","newKey":true,"expiration":"2013-07-11T20:31:12.156146039-07:00","ttl":4,"index":6}
```
Note the last two new fields in response:
2013-07-12 09:00:39 +04:00
1. The expiration is the time that this key will expire and be deleted.
2013-07-12 09:00:39 +04:00
2. The ttl is the time to live of the key.
2013-07-12 09:00:39 +04:00
Now you can try to get the key by sending:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/foo
2013-07-12 09:00:39 +04:00
```
2013-07-23 21:43:29 +04:00
If the TTL has expired, the key will be deleted, and you will be returned a 404.
2013-07-12 09:00:39 +04:00
```json
{"errorCode":100,"message":"Key Not Found","cause":"/foo"}
2013-07-12 09:08:41 +04:00
```
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
### Watching a prefix
2013-07-12 09:00:39 +04:00
We can watch a path prefix and get notifications if any key change under that prefix.
2013-07-12 09:00:39 +04:00
In one terminal, we send a watch request:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/watch/foo
2013-07-12 09:00:39 +04:00
```
2013-07-12 09:32:40 +04:00
Now, we are watching at the path prefix `/foo` and wait for any changes under this path.
2013-07-12 09:00:39 +04:00
2013-07-12 09:32:40 +04:00
In another terminal, we set a key `/foo/foo` to `barbar` to see what will happen:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/foo/foo -d value=barbar
2013-07-12 09:00:39 +04:00
```
The first terminal should get the notification and return with the same response as the set request.
2013-07-12 09:00:39 +04:00
```json
{"action":"SET","key":"/foo/foo","value":"barbar","newKey":true,"index":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.
2013-07-12 09:00:39 +04:00
Let's try to watch for the set command of index 6 again:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/watch/foo -d index=7
2013-07-12 09:00:39 +04:00
```
The watch command returns immediately with the same response as previous.
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
### Atomic Test and Set
2013-07-12 09:00:39 +04:00
Etcd can be used as a centralized coordination service in a cluster and `TestAndSet` is the most basic operation to build distributed lock service. This command will set the value only if the client provided `prevValue` is equal the current key value.
2013-07-12 09:00:39 +04:00
Here is a simple example. Let's create a key-value pair first: `testAndSet=one`.
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/testAndSet -d value=one
2013-07-12 09:00:39 +04:00
```
Let's try an invaild `TestAndSet` command.
2013-07-15 03:22:07 +04:00
We can give another parameter prevValue to set command to make it a TestAndSet command.
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/testAndSet -d prevValue=two -d value=three
2013-07-12 09:00:39 +04:00
```
This will try to test if the previous of the key is two, it is change it to three.
2013-07-15 03:22:07 +04:00
```json
{"errorCode":101,"message":"The given PrevValue is not equal to the value of the key","cause":"TestAndSet: one!=two"}
2013-07-12 09:08:41 +04:00
```
2013-07-12 09:00:39 +04:00
which means `testAndSet` failed.
2013-07-12 09:00:39 +04:00
2013-07-12 09:32:40 +04:00
Let us try a vaild one.
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/testAndSet -d prevValue=one -d value=two
2013-07-12 09:00:39 +04:00
```
The response should be
2013-07-12 09:00:39 +04:00
```json
{"action":"SET","key":"/testAndSet","prevValue":"one","value":"two","index":10}
```
We successfully changed the value from “one” to “two”, since we give the correct previous value.
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
### Listing a directory
2013-07-12 09:00:39 +04:00
Last we provide a simple List command to list all the keys under a prefix path.
Let us create some keys first.
2013-07-12 09:32:40 +04:00
We already have `/foo/foo=barbar`
2013-07-12 09:00:39 +04:00
2013-07-12 09:32:40 +04:00
We create another one `/foo/foo_dir/foo=barbarbar`
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/foo/foo_dir/bar -d value=barbarbar
2013-07-12 09:00:39 +04:00
```
Now list the keys under `/foo`
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/foo/
2013-07-12 09:00:39 +04:00
```
2013-07-14 06:35:58 +04:00
We should see the response as an array of items
2013-07-12 09:00:39 +04:00
```json
2013-07-14 06:35:58 +04:00
[{"action":"GET","key":"/foo/foo","value":"barbar","index":10},{"action":"GET","key":"/foo/foo_dir","dir":true,"index":10}]
2013-07-12 09:00:39 +04:00
```
2013-07-12 09:32:40 +04:00
which meas `foo=barbar` is a key-value pair under `/foo` and `foo_dir` is a directory.
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
## Advanced Usage
### Transport security with HTTPS
2013-07-23 23:02:36 +04:00
Etcd supports SSL/TLS and client cert authentication for clients to server, as well as server to server communication
2013-07-23 22:37:39 +04:00
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:
2013-07-23 23:59:18 +04:00
```url
http://www.g-loaded.eu/2005/11/10/be-your-own-ca/
```
2013-08-11 21:49:26 +04:00
For testing you can use the certificates in the `fixtures/ca` directory.
Next, lets configure etcd to use this keypair:
2013-07-23 22:37:39 +04:00
```sh
2013-08-11 21:49:26 +04:00
./etcd -n node0 -d node0 -clientCert=./fixtures/ca/server.crt -clientKey=./fixtures/ca/server.key.insecure -f
2013-07-23 22:37:39 +04:00
```
2013-07-23 23:59:18 +04:00
2013-08-09 00:49:39 +04:00
`-f` forces new node configuration if existing configuration is found (WARNING: data loss!)
2013-07-27 23:20:40 +04:00
`-clientCert` and `-clientKey` are the key and cert for transport layer security between client and server
2013-07-23 22:37:39 +04:00
You can now test the configuration using https:
2013-07-23 23:59:18 +04:00
```sh
2013-08-11 21:49:26 +04:00
curl --cacert fixtures/ca/ca.crt https://127.0.0.1:4001/v1/keys/foo -F value=bar
2013-07-23 22:37:39 +04:00
```
You should be able to see the handshake succeed.
2013-07-23 22:37:39 +04:00
```
...
SSLv3, TLS handshake, Finished (20):
...
```
2013-07-23 22:37:39 +04:00
And also the response from the etcd server.
2013-07-23 22:37:39 +04:00
```json
{"action":"SET","key":"/foo","value":"bar","newKey":true,"index":3}
```
2013-08-09 06:50:28 +04:00
### 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.
2013-07-23 22:37:39 +04:00
```sh
2013-08-11 21:49:26 +04:00
./etcd -n node0 -d node0 -clientCAFile=./fixtures/ca/ca.crt -clientCert=./fixtures/ca/server.crt -clientKey=./fixtures/ca/server.key.insecure -f
2013-07-23 22:37:39 +04:00
```
```-clientCAFile``` is the path to the CA cert.
Try the same request to this server:
2013-07-23 23:59:18 +04:00
```sh
2013-08-11 21:49:26 +04:00
curl --cacert fixtures/ca/ca.crt https://127.0.0.1:4001/v1/keys/foo -F value=bar
2013-07-23 22:37:39 +04:00
```
The request should be rejected by the server.
2013-07-23 22:37:39 +04:00
```
...
routines:SSL3_READ_BYTES:sslv3 alert bad certificate
...
```
We need to give the CA signed cert to the server.
2013-07-23 23:59:18 +04:00
```sh
curl -L https://127.0.0.1:4001/v1/keys/foo -d value=bar -v --key myclient.key --cert myclient.crt -cacert clientCA.crt
2013-07-23 22:37:39 +04:00
```
You should able to see
```
...
SSLv3, TLS handshake, CERT verify (15):
...
TLS handshake, Finished (20)
```
And also the response from the server:
2013-07-23 22:37:39 +04:00
```json
{"action":"SET","key":"/foo","value":"bar","newKey":true,"index":3}
```
2013-08-09 06:50:28 +04:00
## Clustering
### Example cluster of three machines
2013-08-09 06:50:28 +04:00
Let's explore the use of etcd clustering. We use go-raft as the underlying distributed protocol which provides consistency and persistence of the data across all of the etcd instances.
2013-07-12 09:00:39 +04:00
Let start by creating 3 new etcd instances.
2013-07-12 09:32:40 +04:00
2013-07-12 09:00:39 +04:00
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
```sh
./etcd -s 7001 -c 4001 -d nodes/node1
```
Let the join two more nodes to this cluster using the -C argument:
2013-07-12 09:00:39 +04:00
```sh
2013-08-01 01:47:53 +04:00
./etcd -c 4002 -s 7002 -C 127.0.0.1:7001 -d nodes/node2
./etcd -c 4003 -s 7003 -C 127.0.0.1:7001 -d nodes/node3
2013-07-12 09:00:39 +04:00
```
Get the machines in the cluster:
2013-07-14 06:26:51 +04:00
```sh
curl -L http://127.0.0.1:4001/machines
2013-07-14 06:26:51 +04:00
```
We should see there are three nodes in the cluster
```
2013-07-15 02:32:46 +04:00
0.0.0.0:4001,0.0.0.0:4002,0.0.0.0:4003
2013-07-14 06:26:51 +04:00
```
The machine list is also available via this API:
2013-08-01 01:47:53 +04:00
```sh
2013-08-01 06:41:26 +04:00
curl -L http://127.0.0.1:4001/v1/keys/_etcd/machines
2013-08-01 01:47:53 +04:00
```
```json
[{"action":"GET","key":"/machines/node1","value":"0.0.0.0,7001,4001","index":4},{"action":"GET","key":"/machines/node3","value":"0.0.0.0,7002,4002","index":4},{"action":"GET","key":"/machines/node4","value":"0.0.0.0,7003,4003","index":4}]
```
The key of the machine is based on the ```commit index``` when it was added. The value of the machine is ```hostname```, ```raft port``` and ```client port```.
2013-07-14 06:26:51 +04:00
Also try to get the current leader in the cluster
```
curl -L http://127.0.0.1:4001/leader
2013-07-14 06:26:51 +04:00
```
The first server we set up should be the leader, if it has not dead during these commands.
```
0.0.0.0:7001
```
Now we can do normal SET and GET operations on keys as we explored earlier.
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4001/v1/keys/foo -d value=bar
2013-07-12 09:00:39 +04:00
```
2013-07-12 09:00:39 +04:00
```json
{"action":"SET","key":"/foo","value":"bar","newKey":true,"index":5}
```
2013-08-09 06:50:28 +04:00
### Killing Nodes in the Cluster
2013-07-12 09:00:39 +04:00
Let's kill the leader of the cluster and get the value from the other machine:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4002/v1/keys/foo
2013-07-12 09:00:39 +04:00
```
2013-07-14 06:26:51 +04:00
A new leader should have been elected.
```
curl -L http://127.0.0.1:4001/leader
2013-07-14 06:26:51 +04:00
```
```
0.0.0.0:7002 or 0.0.0.0:7003
```
You should be able to see this:
2013-07-12 09:00:39 +04:00
```json
{"action":"GET","key":"/foo","value":"bar","index":5}
```
It succeeded!
2013-07-12 09:00:39 +04:00
2013-08-09 06:50:28 +04:00
### Testing Persistence
OK. Next let us kill all the nodes to test persistence. And restart all the nodes use the same command as before.
2013-07-12 09:00:39 +04:00
Your request for the `foo` key will return the correct value:
2013-07-12 09:00:39 +04:00
```sh
curl -L http://127.0.0.1:4002/v1/keys/foo
2013-07-12 09:00:39 +04:00
```
2013-07-12 09:00:39 +04:00
```json
{"action":"GET","key":"/foo","value":"bar","index":5}
```
2013-07-23 22:37:39 +04:00
2013-08-09 06:50:28 +04:00
### Using HTTPS between servers
2013-07-23 23:02:36 +04:00
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*```.
2013-07-23 22:37:39 +04:00
If you are using SSL for server to server communication, you must use it on all instances of etcd.
## Libraries and Tools
**Tools**
- [etcdctl](https://github.com/coreos/etcdctl) - A command line client for etcd
**Go libraries**
- [go-etcd](https://github.com/coreos/go-etcd)
**Ruby libraries**
- [iconara/etcd-rb](https://github.com/iconara/etcd-rb)
- [jpfuentes2/etcd-ruby](https://github.com/jpfuentes2/etcd-ruby)
- [ranjib/etcd-ruby](https://github.com/ranjib/etcd-ruby)
**Chef Cookbook**
- [spheromak/etcd-cookbook](https://github.com/spheromak/etcd-cookbook)
2013-08-11 22:08:09 +04:00
## Project Details
### Versioning
etcd uses [semantic versioning][semver].
When we release v1.0.0 of etcd we will promise not to break the "v1" REST API.
New minor versions may add additional features to the API however.
You can get the version of etcd by requesting the root path of etcd:
```sh
curl -L http://127.0.0.1:4001
```
During the v0 series of releases we may break the API as we fix bugs and get feedback.
[semver]: http://semver.org/