Loading...
墨滴

楼仔

2021/05/04  阅读:11  主题:默认主题

ETCD系列教程2 - ETCD体验

ETCD安装

直接安装

以MAC系统为例,讲述2种按照方法,第一种很简单,是Mac自带的:

#用brew安装非常方便,没安装的自行安装Homebrew,通过下面命令可以查看安装包
brew search etcd
#安装
brew install etcd
#查看版本
etcd --version
#启动,如果没有--enable-v2=true,就不用使用v2的接口
etcd --enable-v2=true

不过这种方式可能会安装失败,我这把失败时提示日志目前没有权限,根据提示执行相关命令即可,我这边的提示如下:

sudo chown -R $(whoami) /usr/local/var/log

源码安装

我个人更推荐下面这种安装方式:

ETCD_VER=v3.4.14

# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}

rm -f /tmp/etcd-${ETCD_VER}-darwin-amd64.zip
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test

curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-darwin-amd64.zip -o /tmp/etcd-${ETCD_VER}-darwin-amd64.zip
unzip /tmp/etcd-${ETCD_VER}-darwin-amd64.zip -d /tmp && rm -f /tmp/etcd-${ETCD_VER}-darwin-amd64.zip
mv /tmp/etcd-${ETCD_VER}-darwin-amd64/* /tmp/etcd-download-test && rm -rf mv /tmp/etcd-${ETCD_VER}-darwin-amd64

#输出etcd版本
/tmp/etcd-download-test/etcd --version
/tmp/etcd-download-test/etcdctl version

#这里是把etcd和etcdctl copy到bin目录下面
cp /tmp/etcd-download-test/etcd /usr/local/bin
cp /tmp/etcd-download-test/etcdctl /usr/local/bin
然后执行:
#安装etcd
sh etcd_install.sh
#查看版本
etcd --version
#启动,如果没有--enable-v2=true,就不用使用v2的接口
etcd --enable-v2=true

集群部署

部署流程

下面我们可以部署一个etcd集群,我把代码还是写到文件中,第一个脚本为不支持在 Docs 外粘贴 block,内容如下(启动etcd需要很多参数,这些参数我都已经注释说明,更多参数详见:https://www.cnblogs.com/linuxws/p/11194403.html):

TOKEN=token-01
CLUSTER_STATE=new
NAME_1=etcd-01
NAME_2=etcd-02
NAME_3=etcd-03
HOST_1=127.0.0.1
HOST_2=127.0.0.1
HOST_3=127.0.0.1
PORT_API_1=2379
PORT_PEER_1=2380
PORT_API_2=2479
PORT_PEER_2=2480
PORT_API_3=2579
PORT_PEER_3=2580

CLUSTER=${NAME_1}=http://${HOST_1}:${PORT_PEER_1},${NAME_2}=http://${HOST_2}:${PORT_PEER_2},${NAME_3}=http://${HOST_3}:${PORT_PEER_3}

# For every machine
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
THIS_PORT_API=${PORT_API_1}
THIS_PORT_PEER=${PORT_PEER_1}
# 用于杀死进程
lsof -i:2379 | awk '{print $2}' | grep -v "PID" | uniq | xargs kill -9

# --enable-v2 支持v2接口,可以省略
# --data-dir 数据存储目录,可以省略
# --name 节点名称,必须
# --initial-advertise-peer-urls  数据在集群内进行交互的url,必须
# --listen-peer-urls  集群节点之间通信监听的url,必须
# --advertise-client-urls 客户通过该地址与本member交互信息,可以省略
# --listen-client-urls 监听客户端请求的url,必须
# --initial-cluster 初始启动的集群配置,必须
# --initial-cluster-state 初始化集群状态,取值为new和existing,可以省略
# --initial-cluster-token 集群初始化token,可以省略
etcd --enable-v2=true --data-dir=data.${THIS_NAME} --name ${THIS_NAME} \
        --initial-advertise-peer-urls http://${THIS_IP}:${THIS_PORT_PEER} --listen-peer-urls http://${THIS_IP}:${THIS_PORT_PEER} \
        --advertise-client-urls http://${THIS_IP}:${THIS_PORT_API} --listen-client-urls http://${THIS_IP}:${THIS_PORT_API} \
        --initial-cluster ${CLUSTER} \
        --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

第二个脚本需要把里面的内容替换如下:

# For every machine
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
THIS_PORT_API=${PORT_API_2}
THIS_PORT_PEER=${PORT_PEER_2}
# 用于杀死进程
lsof -i:2479 | awk '{print $2}' | grep -v "PID" | uniq | xargs kill -9

第三个脚本需要把里面的内容替换如下:

# For every machine
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
THIS_PORT_API=${PORT_API_3}
THIS_PORT_PEER=${PORT_PEER_3}
# 用于杀死进程
lsof -i:2579 | awk '{print $2}' | grep -v "PID" | uniq | xargs kill -9

有了这3个脚本,分别开3个窗口,分别执行,服务启动截图如下:

当这3个脚本全部启动后,集群部署完毕,我们检查一下3个节点的健康状态:

curl http://127.0.0.1:2379/health
curl http://127.0.0.1:2479/health
curl http://127.0.0.1:2579/health

如果都返回{"health":"true"},表示部署成功,下面我们查看一下部署的节点信息:

curl http://127.0.0.1:2379/v2/members

返回结果如下,其中peerURLs是节点互相通信访问的url,clientURLs是对外访问的url:

{
    "members":[
        {
            "id":"264ae6bc59e99892",
            "name":"etcd-01",
            "peerURLs":[
                "http://127.0.0.1:2380"
            ],
            "clientURLs":[
                "http://127.0.0.1:2379"
            ]
        },
        {
            "id":"dbafe5ad6b652eda",
            "name":"etcd-02",
            "peerURLs":[
                "http://127.0.0.1:2480"
            ],
            "clientURLs":[
                "http://127.0.0.1:2479"
            ]
        },
        {
            "id":"f570ae41f524bdcb",
            "name":"etcd-03",
            "peerURLs":[
                "http://127.0.0.1:2580"
            ],
            "clientURLs":[
                "http://127.0.0.1:2579"
            ]
        }
    ]
}

遇到问题

问题1:服务启动后,不能使用v2接口,比如执行“curl http://127.0.0.1:2379/v2/members”,提示“404 page not found”

问题原因:因为V3.4版本默认是V3接口,不支持V2

解决方案:需要在启动etcd时,加上“--enable-v2=true”,强制使用V2接口

问题2:服务启动失败,提示“conflicting environment variable "ETCD_ENABLE_V2" is shadowed by corresponding command-line flag (either unset environment variable or disable flag)”

问题原因:因为启动etcd时,参数“--enable-v2=true”导致,因为V3.4版本会读取该配置,所以提示配置重复。

解决方案:不能删除该参数,否则会引入其它问题,我是关闭所有窗口,然后重新启动etcd即可。

问题3:启动某个节点时,提示member已经存在

问题原因:因为之前启动过该节点,该member已经存在,不能初始化,只能加入已经存在的member

解决方案:需要将启动脚本中的“CLUSTER_STATE=new”改为“CLUSTER_STATE=existing”

常规操作

集群管理

我们在部署集群时,用到一些方法,这里我简单汇总一下:

// 版本检查,输出{"etcdserver":"3.4.14","etcdcluster":"3.4.0"}
curl http://127.0.0.1:2379/version
// 健康检查,输出{"health":"true"}
curl http://127.0.0.1:2379/health
// 查看集群节点
curl http://127.0.0.1:2379/v2/members

键值操作

设置键的值:

curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="hello world"

返回结果:

{
    "action":"set",
    "node":{
        "key":"/message",
        "value":"hello world",
        "modifiedIndex":43,
        "createdIndex":43
    }
}

读取键的值:

curl http://127.0.0.1:2379/v2/keys/message

返回结果:

{
    "action":"get",
    "node":{
        "key":"/message",
        "value":"hello world",
        "modifiedIndex":43,
        "createdIndex":43
    }
}

给键设置10s的超时时间:

curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="hello world" -d ttl=10

返回结果(prevNode是旧值):

{
    "action":"set",
    "node":{
        "key":"/message",
        "value":"hello world",
        "expiration":"2021-01-21T00:16:13.777434Z",
        "ttl":10,
        "modifiedIndex":44,
        "createdIndex":44
    },
    "prevNode":{
        "key":"/message",
        "value":"hello world",
        "modifiedIndex":43,
        "createdIndex":43
    }
}

获取该键值,超时后,就提示“key not found”:

watch通知

可以对key设置监听,当key的值有变化时,会通知监听的客户端,我们先在客户端A监听key:

curl http://127.0.0.1:2379/v2/keys/message?wait=true

然后在客户端B,修改该key的值:

curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="hello world2"

客户端A返回并退出,返回结果:

{
    "action":"set",
    "node":{
        "key":"/message",
        "value":"hello world2",
        "modifiedIndex":48,
        "createdIndex":48
    }
}

如果希望客户端A能持续监听,不退出,可以通过增加stream=true参数:

curl "http://127.0.0.1:2379/v2/keys/message?wait=true&stream=true"

当在客户端B执行如下时:

curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="hello world" -d ttl=10

客户端A会实时监听返回,比如当给key设置值,或者当key过期时,客户端A都会监听到:

目录操作

这个可用于配置管理,因为etcd数据结构是颗目录树,所以我们也可以PUT一个目录,目录里存放我们的服务的配置数据:

curl http://127.0.0.1:2379/v2/keys/animal -XPUT -d dir=true 

返回结果:

{
    "action":"set",
    "node":{
        "key":"/animal",
        "dir":true,
        "modifiedIndex":80,
        "createdIndex":80
    }
}

可以获取目录中的数据,如果增加参数recursive=true,可以递归罗列:

curl http://127.0.0.1:2379/v2/keys?recursive=true

返回结果:

{
    "action":"get",
    "node":{
        "dir":true,
        "nodes":[
            {
                "key":"/animal",
                "dir":true,
                "modifiedIndex":80,
                "createdIndex":80
            }
        ]
    }
}

下面我们举一个稍微复杂的示例,创建2个目录:

curl http://127.0.0.1:2379/v2/keys/animal -XPUT -d dir=true 
curl http://127.0.0.1:2379/v2/keys/animal/cat -XPUT -d value="a little cat"
curl http://127.0.0.1:2379/v2/keys/animal/dog -XPUT -d value="a big dog"
curl http://127.0.0.1:2379/v2/keys/tool -XPUT -d dir=true 
curl http://127.0.0.1:2379/v2/keys/tool/car -XPUT -d value="a small car"
curl http://127.0.0.1:2379/v2/keys/tool/ship -XPUT -d value="a big ship"

然后递归获取里面的数据:

curl http://127.0.0.1:2379/v2/keys?recursive=true

返回结果:

{
    "action":"get",
    "node":{
        "dir":true,
        "nodes":[
            {
                "key":"/tool",
                "dir":true,
                "nodes":[
                    {
                        "key":"/tool/car",
                        "value":"a small car",
                        "modifiedIndex":84,
                        "createdIndex":84
                    },
                    {
                        "key":"/tool/ship",
                        "value":"a big ship",
                        "modifiedIndex":85,
                        "createdIndex":85
                    }
                ],
                "modifiedIndex":83,
                "createdIndex":83
            },
            {
                "key":"/animal",
                "dir":true,
                "nodes":[
                    {
                        "key":"/animal/cat",
                        "value":"a little cat",
                        "modifiedIndex":81,
                        "createdIndex":81
                    },
                    {
                        "key":"/animal/dog",
                        "value":"a big dog",
                        "modifiedIndex":82,
                        "createdIndex":82
                    }
                ],
                "modifiedIndex":80,
                "createdIndex":80
            }
        ]
    }
}

我们也可以通过下面方式删除animal目录:

curl http://127.0.0.1:2379/v2/keys/animal?recursive=true -XDELETE

返回结果:

{
    "action":"delete",
    "node":{
        "key":"/animal",
        "dir":true,
        "modifiedIndex":86,
        "createdIndex":80
    },
    "prevNode":{
        "key":"/animal",
        "dir":true,
        "modifiedIndex":80,
        "createdIndex":80
    }
}

自动创建有序key

通过对一个目录发起POST请求,我们能够让创建的key的名字是有序的。自动创建有序key的这个功能在许多场景下都很有用,例如,用于实现一个对处理顺序有严格要求的队列等。

curl http://127.0.0.1:2379/v2/keys/queue -XPOST -d value=Jobl

返回结果:

{
    "action":"create",
    "node":{
        "key":"/queue/00000000000000000088",
        "value":"Jobl",
        "modifiedIndex":88,
        "createdIndex":88
    }
}

如果我们多执行几次,会发现key中的值是递增关系,但如果在并发情况下,就不一定每次都是递增1,看一下执行结果:

我们可以列举这个目录下所有的key,并排序输出:

curl -s "http://127.0.0.1:2379/v2/keys/queue?recursive=true&sort=true"

输出结果:

{
    "action":"get",
    "node":{
        "key":"/queue",
        "dir":true,
        "nodes":[
            {
                "key":"/queue/00000000000000000088",
                "value":"Jobl",
                "modifiedIndex":88,
                "createdIndex":88
            },
            {
                "key":"/queue/00000000000000000089",
                "value":"Jobl",
                "modifiedIndex":89,
                "createdIndex":89
            },
            {
                "key":"/queue/00000000000000000090",
                "value":"Jobl",
                "modifiedIndex":90,
                "createdIndex":90
            },
            {
                "key":"/queue/00000000000000000091",
                "value":"Jobl",
                "modifiedIndex":91,
                "createdIndex":91
            },
            {
                "key":"/queue/00000000000000000092",
                "value":"Jobl",
                "modifiedIndex":92,
                "createdIndex":92
            },
            {
                "key":"/queue/00000000000000000093",
                "value":"Jobl",
                "modifiedIndex":93,
                "createdIndex":93
            }
        ],
        "modifiedIndex":88,
        "createdIndex":88
    }
}

欢迎大家多多点赞,更多文章,请关注微信公众号“楼仔进阶之路”,点关注,不迷路~~

楼仔

2021/05/04  阅读:11  主题:默认主题

作者介绍

楼仔