SDN開発エンジニアを目指した活動ブログ

〜SDNなオープンソース製品を実際に使って試してみる〜

etcdの分散Key-Valuesストアを試してみる

CoreOSが提供するetcdの動作をお手軽に試してみました。
なお、etcdとは、分散Key-Valuesストアを使い,各種設定をノード間で共有するメカニズムだそうです。

etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines.
It’s open-source and available on GitHub. etcd gracefully handles master elections during network partitions
and will tolerate machine failure, including the master.

f:id:ttsubo:20151227155040p:plain

◼️ まずは、環境準備から ...

まずは、golang環境を準備しておきます。
Getting Started - The Go Programming Language

そして、Mac OS X環境で、etcdを動作させる場合、こちらのGetting Startedに従って作業します。
github.com

$ curl -L  https://github.com/coreos/etcd/releases/download/v2.2.2/etcd-v2.2.2-darwin-amd64.zip -o etcd-v2.2.2-darwin-amd64.zip
$ unzip etcd-v2.2.2-darwin-amd64.zip
$ cd etcd-v2.2.2-darwin-amd64
$ ./etcd
2015-12-27 15:42:15.142251 I | etcdmain: etcd Version: 2.2.2
2015-12-27 15:42:15.142330 I | etcdmain: Git SHA: b4bddf6
2015-12-27 15:42:15.142346 I | etcdmain: Go Version: go1.5.1
2015-12-27 15:42:15.142349 I | etcdmain: Go OS/Arch: darwin/amd64
2015-12-27 15:42:15.142360 I | etcdmain: setting maximum number of CPUs to 8, total number of available CPUs is 8
2015-12-27 15:42:15.142364 W | etcdmain: no data-dir provided, using default data-dir ./default.etcd
2015-12-27 15:42:15.142982 I | etcdmain: listening for peers on http://localhost:2380
2015-12-27 15:42:15.143166 I | etcdmain: listening for peers on http://localhost:7001
2015-12-27 15:42:15.143335 I | etcdmain: listening for client requests on http://localhost:2379
2015-12-27 15:42:15.143499 I | etcdmain: listening for client requests on http://localhost:4001
2015-12-27 15:42:15.144404 I | etcdserver: name = default
2015-12-27 15:42:15.144433 I | etcdserver: data dir = default.etcd
2015-12-27 15:42:15.144447 I | etcdserver: member dir = default.etcd/member
2015-12-27 15:42:15.144451 I | etcdserver: heartbeat = 100ms
2015-12-27 15:42:15.144453 I | etcdserver: election = 1000ms
2015-12-27 15:42:15.144467 I | etcdserver: snapshot count = 10000
2015-12-27 15:42:15.144474 I | etcdserver: advertise client URLs = http://localhost:2379,http://localhost:4001
2015-12-27 15:42:15.144478 I | etcdserver: initial advertise peer URLs = http://localhost:2380,http://localhost:7001
2015-12-27 15:42:15.144487 I | etcdserver: initial cluster = default=http://localhost:2380,default=http://localhost:7001
2015-12-27 15:42:15.145942 I | etcdserver: starting member ce2a822cea30bfca in cluster 7e27652122e8b2ae
2015-12-27 15:42:15.146367 I | raft: ce2a822cea30bfca became follower at term 0
2015-12-27 15:42:15.146404 I | raft: newRaft ce2a822cea30bfca [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0]
2015-12-27 15:42:15.146415 I | raft: ce2a822cea30bfca became follower at term 1
2015-12-27 15:42:15.146776 I | etcdserver: starting server... [version: 2.2.2, cluster version: to_be_decided]
2015-12-27 15:42:15.147874 N | etcdserver: added local member ce2a822cea30bfca [http://localhost:2380 http://localhost:7001] to cluster 7e27652122e8b2ae
2015-12-27 15:42:15.147934 E | etcdserver: cannot monitor file descriptor usage (cannot get FDUsage on darwin)
2015-12-27 15:42:15.548221 I | raft: ce2a822cea30bfca is starting a new election at term 1
2015-12-27 15:42:15.548284 I | raft: ce2a822cea30bfca became candidate at term 2
2015-12-27 15:42:15.548299 I | raft: ce2a822cea30bfca received vote from ce2a822cea30bfca at term 2
2015-12-27 15:42:15.548316 I | raft: ce2a822cea30bfca became leader at term 2
2015-12-27 15:42:15.548330 I | raft: raft.node: ce2a822cea30bfca elected leader ce2a822cea30bfca at term 2
2015-12-27 15:42:15.548736 I | etcdserver: published {Name:default ClientURLs:[http://localhost:2379 http://localhost:4001]} to cluster 7e27652122e8b2ae
2015-12-27 15:42:15.548760 I | etcdserver: setting up the initial cluster version to 2.2
2015-12-27 15:42:15.550378 N | etcdserver: set the initial cluster version to 2.2

◼️ オープンソース"go-etcd"を活用して、etcdの分散Key-Valuesストアを試してみる

それでは、オープンソース"go-etcd"を活用して、golangから、etcdを試してみます。

github.com

0. etcdのKey-Valuesストアを確認する
別ターミナルから、etcdctlコマンドで、etcdのストアデータを確認してみます。
まだ、etcdを起動したばかりなので、何も表示されないですけど。

$ ./etcdctl --peers "http://127.0.0.1:4001" ls

1. etcdにサンプルデータをセットしてみる
オープンソース"go-etcd"を活用して、手軽にKey-Valuesストアをセットできるサンプルコードを準備しておきます。

package main

import (
    "github.com/coreos/go-etcd/etcd"
    "log"
)

var etcdServers = []string{
    "http://127.0.0.1:4001",
}

func SetStatus_for_server(client *etcd.Client, status string){
    // Set "active" to path of "/state/server"
    _, err := client.Set("/state/server", status, 0)
    if err != nil {
        log.Fatal(err)
    }
}

func SetStatus_for_network(client *etcd.Client, status string){
    // Set "active" to path of "/state/network"
    _, err := client.Set("/state/network", status, 0)
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    // Create etcd client
    client := etcd.NewClient(etcdServers)
    SetStatus_for_server(client, "active")
    SetStatus_for_network(client, "down")
}

サンプルコードを実行して、etcdにサンプルデータをセットしてみます。
エラーが表示されなければOKです。

$ go run go-etcd-set.go

2. etcdctlコマンドで、再度、Key-Valuesストアを確認する
今度は、"/state"という結果が表示されました。

$ ./etcdctl --peers "http://127.0.0.1:4001" ls
/state

ちなみに、etcdのKey-Valuesストアデータは、ディレクトリ構造(=パス)で管理されております。
/state配下のパスを確認する場合は、"--recursive"を指定します。

$ ./etcdctl --peers "http://127.0.0.1:4001" ls --recursive
/state
/state/server
/state/network

続いて、Key-ValuesストアのValuesを確認してみます。
まずは、"server"が"active"の状態であることを確認します。

$ ./etcdctl --peers "http://127.0.0.1:4001" get /state/server
active

つぎに、"network"が"down"の状態であることを確認します。

$ ./etcdctl --peers "http://127.0.0.1:4001" get /state/network
down

3. もっと、お手軽にKey-ValuesストアのValuesを確認してみる
オープンソース"go-etcd"を活用して、手軽にKey-Valuesストアを確認できるサンプルコードを準備しておきます。

package main

import (
    "github.com/coreos/go-etcd/etcd"
    "log"
    "fmt"
)

var etcdServers = []string{
    "http://127.0.0.1:4001",
}

func main() {
    client := etcd.NewClient(etcdServers)
    resp, err := client.Get("/state", false, true)
    if err != nil {
        log.Fatal(err)
    }
    for _, n := range resp.Node.Nodes {
        fmt.Printf("%s: %s\n", n.Key, n.Value)
    }
}

サンプルコードを実行して、Key-ValuesストアのValuesを確認してみます。

$ go run go-etcd-get.go 
/state/server: active
/state/network: down

4. etcdのKey-ValuesストアのValuesを監視してみる
ここからは、応用編です。
Key-ValuesストアのValuesが外部要因によって変更された場合、そのValuesをモニタ監視するサンプルコードを準備しておきます。
12/28追記:パス情報が複数変更された場合でも、モニタ監視できるようサンプルコードを修正しました。

package main

import (
    "os"
    "os/signal"
    "github.com/coreos/go-etcd/etcd"
    "log"
)

var etcdServers = []string{
    "http://127.0.0.1:4001",
}

func main() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    etcdResponseChan := make(chan *etcd.Response)
    client := etcd.NewClient(etcdServers)
    go func(){
        _, err := client.Watch("/state", 0, true, etcdResponseChan, nil)
        if err != nil {
            log.Fatal(err)
        }
    }()
    log.Println("Waiting for an update...")

    go func(){
        for {
            response := <-etcdResponseChan
            log.Printf("Got updated state : path=[%s], value=[%s]\n",
                        response.Node.Key, response.Node.Value)
        }
    }()
    <-c
}

サンプルコードを実行して、Valuesをモニタ監視できるよう準備しておきます。

$ go run ./go-etcd-watch.go 
2015/12/27 16:46:16 Waiting for an update...

続いて、別ターミナルから、"network"を"active"の状態に更新します。
更新方法は、なんでも良いのですが、curlコマンドで更新してみます。

$ curl -L http://127.0.0.1:4001/v2/keys/state/network -XPUT -d value='active'
{"action":"set","node":{"key":"/state/network","value":"active","modifiedIndex":6,"createdIndex":6},"prevNode":{"key":"/state/network","value":"down","modifiedIndex":5,"createdIndex":5}}

すると、先ほどモニタ監視として実行しておいたサンプルコードの起動画面にて、"network"が"active"の状態に更新されたことが検出できました。

$ go run ./go-etcd-watch.go 
2015/12/27 16:46:16 Waiting for an update...
2015/12/27 16:47:07 Got updated state : path=[/state/network], value=[active]

事後確認として、Key-ValuesストアのValuesを確認してみます。

$ go run go-etcd-get.go 
/state/server: active
/state/network: active

◼️ 終わりに

以上で、オープンソース"go-etcd"を活用による、etcdでの分散Key-Valuesストアの動作を試してみました。
etcdの分散Key-Valuesストア活用によるクラウド基盤において、いろいろな用途に応用できそうな手応えを感じることができました。