介绍

etcd是由CoreOS开发的开源项目,其是一个高可用的Key/Value存储系统,主要用于共享配置和服务发现,本文将对etcd集群的配置和基本使用进行介绍

etcd集群部署

由于etcd集群使用raft协议保证高可用,所以etcd集群至少要配置3台机器,如下采用systemd方式进行部署

  • 机器1(my_etcd_1)配置
[Unit]
Description=etcd
Documentation=https://github.com/coreos/etcd
Conflicts=etcd.service
Conflicts=etcd2.service

[Service]
Type=notify
Restart=always
RestartSec=5s
LimitNOFILE=40000
TimeoutStartSec=0

ExecStart=/usr/local/bin/etcd \
	--name my_etcd_1 \
	--data-dir /data/etcd \
	--listen-client-urls https://0.0.0.0:2379 \
	--advertise-client-urls https://192.168.60.21:2379 \
	--listen-peer-urls https://0.0.0.0:2380 \
	--initial-advertise-peer-urls https://192.168.60.21:2380 \
	--initial-cluster my_etcd_1=https://192.168.60.21:2380,my_etcd_2=https://192.168.60.22:2380,my_etcd_3=https://192.168.60.23:2380 \
	--initial-cluster-token 9477af68bbee1b9ae037d6fd9e7efefd \
	--initial-cluster-state new \
	--cert-file=/etc/pki/etcd/server.pem \
	--key-file=/etc/pki/etcd/server-key.pem \
	--client-cert-auth \
	--trusted-ca-file=/etc/pki/etcd/ca.pem \
	--peer-cert-file=/etc/pki/etcd/peer.pem \
	--peer-key-file=/etc/pki/etcd/peer-key.pem \
	--peer-client-cert-auth \
	--peer-trusted-ca-file=/etc/pki/etcd/ca.pem

[Install]
WantedBy=multi-user.target
  • 机器2(my_etcd_2)配置
[Unit]
Description=etcd
Documentation=https://github.com/coreos/etcd
Conflicts=etcd.service
Conflicts=etcd2.service

[Service]
Type=notify
Restart=always
RestartSec=5s
LimitNOFILE=40000
TimeoutStartSec=0

ExecStart=/usr/local/bin/etcd \
	--name my_etcd_2 \
	--data-dir /data/etcd \
	--listen-client-urls https://0.0.0.0:2379 \
	--advertise-client-urls https://192.168.60.22:2379 \
	--listen-peer-urls https://0.0.0.0:2380 \
	--initial-advertise-peer-urls https://192.168.60.22:2380 \
	--initial-cluster my_etcd_1=https://192.168.60.21:2380,my_etcd_2=https://192.168.60.22:2380,my_etcd_3=https://192.168.60.23:2380 \
	--initial-cluster-token 9477af68bbee1b9ae037d6fd9e7efefd \
	--initial-cluster-state new \
	--cert-file=/etc/pki/etcd/server.pem \
	--key-file=/etc/pki/etcd/server-key.pem \
	--client-cert-auth \
	--trusted-ca-file=/etc/pki/etcd/ca.pem \
	--peer-cert-file=/etc/pki/etcd/peer.pem \
	--peer-key-file=/etc/pki/etcd/peer-key.pem \
	--peer-client-cert-auth \
	--peer-trusted-ca-file=/etc/pki/etcd/ca.pem

[Install]
WantedBy=multi-user.target
  • 机器3(my_etcd_3)配置
[Unit]
Description=etcd
Documentation=https://github.com/coreos/etcd
Conflicts=etcd.service
Conflicts=etcd2.service

[Service]
Type=notify
Restart=always
RestartSec=5s
LimitNOFILE=40000
TimeoutStartSec=0

ExecStart=/usr/local/bin/etcd \
	--name my_etcd_3 \
	--data-dir /data/etcd \
	--listen-client-urls https://0.0.0.0:2379 \
	--advertise-client-urls https://192.168.60.23:2379 \
	--listen-peer-urls https://0.0.0.0:2380 \
	--initial-advertise-peer-urls https://192.168.60.23:2380 \
	--initial-cluster my_etcd_1=https://192.168.60.21:2380,my_etcd_2=https://192.168.60.22:2380,my_etcd_3=https://192.168.60.23:2380 \
	--initial-cluster-token 9477af68bbee1b9ae037d6fd9e7efefd \
	--initial-cluster-state new \
	--cert-file=/etc/pki/etcd/server.pem \
	--key-file=/etc/pki/etcd/server-key.pem \
	--client-cert-auth \
	--trusted-ca-file=/etc/pki/etcd/ca.pem \
	--peer-cert-file=/etc/pki/etcd/peer.pem \
	--peer-key-file=/etc/pki/etcd/peer-key.pem \
	--peer-client-cert-auth \
	--peer-trusted-ca-file=/etc/pki/etcd/ca.pem

[Install]
WantedBy=multi-user.target

注:

  • 启动单个机器上的etcd服务,将被hang住,这是正常现象,直到所有3台机器的etcd服务都启动后,systemd会根据其service文件中”Type=notify”配置,将其放到后台运行
  • 本集群配置使用TLS加密,可使用CFSSL工具生成相关证书文件: CFSSL使用官方指导

etcd使用

读/写key

  • etcdctl
$ alias my_etcdctl="etcdctl --endpoints=https://192.168.60.21:2379 --ca-file=/etc/pki/etcd/ca.pem --cert-file=/etc/pki/etcd/client.pem --key-file=/etc/pki/etcd/client.pem"
$ my_etcdctl set /message hello
hello
$ my_etcdctl set /name kulong0105
hello
$ my_etcdctl ls
/message
/name
$ my_etcdctl get /message
hello
$ my_etcdctl rm /name
$ my_etcdctl rm /message
PrevNode.Value: hello
$ my_etcdctl get /message
Error:  100: Key not found (/message) [13]
$
  • curl
$ alias my_curl="curl --cacert /etc/pki/etcd/ca.pem --cert /etc/pki/etcd/client.pem --key /etc/pki/etcd/client-key.pem"
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/message -d value="Hello"
{"action":"set","node":{"key":"/message","value":"Hello","modifiedIndex":14,"createdIndex":14}}
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/name -d value="kulong0105"
{"action":"set","node":{"key":"/name","value":"kulong0105","modifiedIndex":15,"createdIndex":15}}
$ my_curl https://192.168.60.21:2379/v2/keys/
{"action":"get","node":{"dir":true,"nodes":[{"key":"/message","value":"Hello","modifiedIndex":14,"createdIndex":14},{"key":"/name","value":"kulong0105","modifiedIndex":15,"createdIndex":15}]}}
$ my_curl https://192.168.60.21:2379/v2/keys/ | python -m json.tool
{
    "action": "get",
    "node": {
        "dir": true,
        "nodes": [
            {
                "createdIndex": 18,
                "key": "/message",
                "modifiedIndex": 18,
                "value": "hello"
            },
            {
                "createdIndex": 19,
                "key": "/name",
                "modifiedIndex": 19,
                "value": "kulong0105"
            }
        ]
    }
}
$ my_curl https://192.168.60.21:2379/v2/keys/name
{"action":"get","node":{"key":"/name","value":"kulong0105","modifiedIndex":15,"createdIndex":15}}
$ my_curl -X DELETE https://192.168.60.21:2379/v2/keys/name
{"action":"delete","node":{"key":"/name","modifiedIndex":16,"createdIndex":15},"prevNode":{"key":"/name","value":"kulong0105","modifiedIndex":15,"createdIndex":15}}
$ my_curl -X DELETE https://192.168.60.21:2379/v2/keys/
{"errorCode":107,"message":"Root is read only","cause":"/","index":16}
$ my_curl -X DELETE https://192.168.60.21:2379/v2/keys/message
{"action":"delete","node":{"key":"/message","modifiedIndex":17,"createdIndex":14},"prevNode":{"key":"/message","value":"Hello","modifiedIndex":14,"createdIndex":14}}
$ my_curl -X DELETE https://192.168.60.21:2379/v2/keys/message
{"errorCode":100,"message":"Key not found","cause":"/message","index":17}
$

Test and Set

  • etcdctl
$ alias my_etcdctl="etcdctl --endpoints=https://192.168.60.21:2379 --ca-file=/etc/pki/etcd/ca.pem --cert-file=/etc/pki/etcd/client.pem --key-file=/etc/pki/etcd/client.pem"
$ my_etcdctl  set /name "kulong0105"
kulong0105
$ my_etcdctl set /name "lucky" --swap-with-value "kulong0106"
Error:  101: Compare failed ([kulong0106 != kulong0105]) [37]
$ my_etcdctl set /name "lucky" --swap-with-value "kulong0105"
lucky
$ my_etcdctl get /name
lucky
$
  • curl
$ alias my_curl="curl --cacert /etc/pki/etcd/ca.pem --cert /etc/pki/etcd/client.pem --key /etc/pki/etcd/client-key.pem"
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/name -d value="kulong0105"
{"action":"set","node":{"key":"/name","value":"kulong0105","modifiedIndex":40,"createdIndex":40}}
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/name?prevValue=kulong0106 -d value=lucky
{"errorCode":101,"message":"Compare failed","cause":"[kulong0106 != kulong0105]","index":40}
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/name?prevValue=kulong0105 -d value=lucky
{"action":"compareAndSwap","node":{"key":"/name","value":"lucky","modifiedIndex":41,"createdIndex":40},"prevNode":{"key":"/name","value":"kulong0105","modifiedIndex":40,"createdIndex":40}}
$ my_curl https://192.168.60.21:2379/v2/keys/name
{"action":"get","node":{"key":"/name","value":"lucky","modifiedIndex":41,"createdIndex":40}}
$

设置TTL

  • etcdctl
$ alias my_etcdctl="etcdctl --endpoints=https://192.168.60.21:2379 --ca-file=/etc/pki/etcd/ca.pem --cert-file=/etc/pki/etcd/client.pem --key-file=/etc/pki/etcd/client.pem"
$ my_etcdctl set /time none --ttl 10 && sleep 11 && my_etcdctl get /time
10
Error:  100: Key not found (/time) [27]
$
  • curl
$ alias my_curl="curl --cacert /etc/pki/etcd/ca.pem --cert /etc/pki/etcd/client.pem --key /etc/pki/etcd/client-key.pem"
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/time?ttl=10 -d value=none && sleep 11 && my_curl https://192.168.60.21:2379/v2/keys/time
{"action":"set","node":{"key":"/time","value":"none","expiration":"2018-06-11T11:30:42.497377652Z","ttl":10,"modifiedIndex":22,"createdIndex":22}}
{"errorCode":100,"message":"Key not found","cause":"/time","index":23}
$

创建目录

  • etcd
$ alias my_etcdctl="etcdctl --endpoints=https://192.168.60.21:2379 --ca-file=/etc/pki/etcd/ca.pem --cert-file=/etc/pki/etcd/client.pem --key-file=/etc/pki/etcd/client.pem"
$ my_etcdctl mkdir /testdir
$ my_etcdctl set /testdir/name kulong0105
kulong0105
$ my_etcdctl ls /testdir
/testdir/name
$ my_etcdctl rmdir /testdir
Error:  108: Directory not empty (/testdir)
$ my_etcdctl rm /testdir/name
PrevNode.Value: kulong0105
$ my_etcdctl rmdir /testdir
$
  • curl
$ alias my_curl="curl --cacert /etc/pki/etcd/ca.pem --cert /etc/pki/etcd/client.pem --key /etc/pki/etcd/client-key.pem"
$ my_curl -X PUT https://192.168.60.21:2379/v2/keys/testdir/name -d value="kulong0105"
{"action":"set","node":{"key":"/testdir/name","value":"kulong0105","modifiedIndex":30,"createdIndex":30}}
$ my_curl  https://192.168.60.21:2379/v2/keys/testdir
{"action":"get","node":{"key":"/testdir","dir":true,"nodes":[{"key":"/testdir/name","value":"kulong0105","modifiedIndex":30,"createdIndex":30}],"modifiedIndex":30,"createdIndex":30}}
$ my_curl  https://192.168.60.21:2379/v2/keys/testdir/name
{"action":"get","node":{"key":"/testdir/name","value":"kulong0105","modifiedIndex":30,"createdIndex":30}}
$ my_curl -X DELETE https://192.168.60.21:2379/v2/keys/testdir?recursive=true
{"action":"delete","node":{"key":"/testdir","dir":true,"modifiedIndex":47,"createdIndex":45},"prevNode":{"key":"/testdir","dir":true,"modifiedIndex":45,"createdIndex":45}}
$ my_curl  https://192.168.60.21:2379/v2/keys/testdir/name
{"errorCode":100,"message":"Key not found","cause":"/testdir/name","index":31}
$ my_curl  https://192.168.60.21:2379/v2/keys/testdir
{"errorCode":100,"message":"Key not found","cause":"/testdir","index":47}
$
$

注: 使用curl命令,可能出现出现错误

$ curl --cacert /etc/pki/etcd/ca.pem --cert /etc/pki/etcd/server.pem --key /etc/pki/etcd/server-key.pem  https://192.168.60.21:2379/v2/keys/name
curl: (58) unable to load client key: -8178 (SEC_ERROR_BAD_KEY)
$

这可能是由于SSL证书加密没有使用RSA算法导致, 解决方法:

  • 编译curl使用OpenSSL模块,不是默认的NSS模块
  • 生成证书时使用RSA加密算法

参考:

参考