golang etcd容器构建与客户端操作踩坑实操
1.问题说明
在用 go-zero 实现相关服务时一直报错,从报错信息看应该是 etcd 的容器有问题,应该是之前的构建哪里出错了,所以重新构建 etcd 容器应用。
记录下主要的踩坑情况:
- 1.连接 etcd 容器没有问题,但是写入数据时一直报错,报超时错,可以看下面的操作记录,但本人就构建了一个单节点的 etcd,也用不到 etcd 集群功能
# ./etcdctl --endpoints=0.0.0.0:2379 --write-out=table endpoint health
{"level":"warn","ts":"2024-08-01T10:50:35.003109+0800","logger":"client","caller":"v3@v3.5.12/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc0002e2a80/0.0.0.0:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \"error reading server preface: read tcp 127.0.0.1:49164->127.0.0.1:2379: read: connection reset by peer\""}
+--------------+--------+--------------+---------------------------+
| ENDPOINT | HEALTH | TOOK | ERROR |
+--------------+--------+--------------+---------------------------+
| 0.0.0.0:2379 | false | 5.007223857s | context deadline exceeded |
+--------------+--------+--------------+---------------------------+
Error: unhealthy cluster
- 2.通过 golang 实现 etcd 客户端时,一直报依赖的问题,最后发现 v3.3 问题很多,后面就切 v3.5了,之后依赖问题解决
下面记录下整体的工作流。
2. etcd 容器构建
具体操作参考:
以下是具体的一些操作。
这里的容器用到的镜像是:quay.io/coreos/etcd:v3.5.12
,所以你需要 docker pull 先拉取下来。
接下来是具体的一些容器构建的工作。
# 创建目录,数据与配置目录
mkdir -p /data/containers/etcd/{data,config}
# 创建配置文件
vi /data/containers/etcd/config/etcd.conf.yml
---
name: etcd-s1
data-dir: /var/etcd
listen-client-urls: http://0.0.0.0:2379
advertise-client-urls: http://0.0.0.0:2379
listen-peer-urls: http://0.0.0.0:2380
initial-advertise-peer-urls: http://0.0.0.0:2380
initial-cluster: etcd-s1=http://0.0.0.0:2380
initial-cluster-token: etcd-cluster
initial-cluster-state: new
logger: zap
log-level: info
#log-outputs: stderr
---
说明:
特殊参数说明:
name: etcd member 名称,可根据实际情况修改
data-dir: etcd 数据目录,可根据实际情况修改
listen-client-urls: client 流量监听地址,没特殊需求按文档填写即可
advertise-client-urls: 该 member 向外部通告的客户端 url 列表,单节点部署时不需要修改,集群部署模式需修改为容器所在节点对外提供服务的 IP
listen-peer-urls: peer 流量监听地址,没特殊需求按文档填写即可
initial-advertise-peer-urls: 该 member 向同一集群内其他 member 通告的 peer url 列表,单节点部署时不需要修改,集群部署模式需修改为容器所在节点对外提供服务的 IP
initial-cluster: 初始化集群节点信息,单节点部署时不需要修改,集群部署模式需要填写集群中所有 member 的信息
initial-cluster-token: 初始化集群时使用的 token,随便写
initial-cluster-state: 初始化集群状态,可选的值为 new 或者 existing,通常采用 new
# 编写 compose 文件
ubuntu:/data/containers/etcd# vi docker-compose.yaml
---
version: '3'
services:
etcd:
container_name: etcd-s1
image: quay.io/coreos/etcd:v3.5.12
command: /usr/local/bin/etcd --config-file=/var/lib/etcd/conf/etcd.conf.yml
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/data:/var/etcd
- ${DOCKER_VOLUME_DIRECTORY:-.}/config/etcd.conf.yml:/var/lib/etcd/conf/etcd.conf.yml
- "/etc/localtime:/etc/localtime:ro"
ports:
- 2379:2379
- 2380:2380
restart: always
networks:
app_net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.238.238.0/24
---
启动容器, 在 compose 文件目录
docker-compose up -d
root@ubuntu:/data/containers/etcd# docker-compose up -d
WARNING: Some networks were defined but are not used by any service: app_net
Creating network "etcd_default" with the default driver
Creating etcd-s1 ... done
root@ubuntu:/data/containers/etcd# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24162db8e44b quay.io/coreos/etcd:v3.5.12 "/usr/local/bin/etcd…" 4 seconds ago Up 2 seconds 0.0.0.0:2379-2380->2379-2380/tcp, :::2379-2380->2379-2380/tcp etcd-s1
顺便测试下容器,用到 etcdctl 工具,下载参见参考文档:
root@ubuntu:~/etcd/etcd-v3.5.12-linux-amd64# ./etcdctl --endpoints=0.0.0.0:2379 --write-out=table endpoint health
+--------------+--------+-------------+-------+
| ENDPOINT | HEALTH | TOOK | ERROR |
+--------------+--------+-------------+-------+
| 0.0.0.0:2379 | true | 31.864411ms | |
+--------------+--------+-------------+-------+
root@ubuntu:~/etcd/etcd-v3.5.12-linux-amd64# ./etcdctl --endpoints=0.0.0.0:2379 put foo bar
OK
root@ubuntu:~/etcd/etcd-v3.5.12-linux-amd64# ./etcdctl --endpoints=0.0.0.0:2379 get foo
foo
bar
走到这里,说明你的 etcd 单节点容器目前可用了,恭喜你。
3.goalng etcd 客户端实现
先看看具体的实现:
package main
import (
"context"
"fmt"
"time"
"go.etcd.io/etcd/client/v3"
)
// demo, etcd client put/get
func main() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"172.30.5.240:2379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
// handle error!
fmt.Printf("connect to etcd failed, err:%v\n", err)
return
}
fmt.Println("connect to etcd success")
defer cli.Close()
// put
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
_, err = cli.Put(ctx, "test", fmt.Sprintf("test-val: %v", time.Now().Format(time.RFC3339)))
cancel()
if err != nil {
fmt.Printf("put to etcd failed, err:%v\n", err)
return
}
// get
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
resp, err := cli.Get(ctx, "test")
cancel()
if err != nil {
fmt.Printf("get from etcd failed, err:%v\n", err)
return
}
for _, ev := range resp.Kvs {
fmt.Printf("%s:%s\n", ev.Key, ev.Value)
}
}
我的依赖 - go.mod:
module go-etcd
go 1.19
require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
go.etcd.io/etcd/client/v3 v3.5.15 // indirect // 这里需要注意版本问题,v3.3问题挺多,建议用3.5后的
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
以下是运行结果:
connect to etcd success
test:test-val: 2024-08-01T11:13:06+08:00
恭喜你,已经掌握 golang etcd 的客户端实现。