文档版本 v3.7-DRAFT 处于 草稿 状态。如需获取最新的稳定版文档,请参阅 v3.6。
将 etcd 从 3.1 升级到 3.2
在一般情况下,从 etcd 3.1 升级到 3.2 可以实现零停机、滚动升级:
- 逐个停止 etcd v3.1 进程,并将其替换为 etcd v3.2 进程
- 在所有 v3.2 进程运行后,v3.2 中的新功能即可在集群中使用
在开始升级之前,请通读本指南其余部分以做好准备。
升级检查清单
注意: 当从 v2 迁移且没有 v3 数据时,如果 etcd 从现有快照恢复但不存在 v3 的 ETCD_DATA_DIR/member/snap/db 文件,etcd 服务器 v3.2+ 将会 panic。这种情况发生在服务器已从 v2 迁移但之前没有 v3 数据时。这也防止了意外的 v3 数据丢失(例如 db 文件可能已被移动)。etcd 要求只有在存在 v3 数据的情况下才能进行后续的 v3 版本迁移。在 v3.0 服务器包含 v3 数据之前,请勿升级到更新的 v3 版本。
3.2 版本中的重点破坏性变更。
更改了默认的 snapshot-count 值
较高的 --snapshot-count 会在内存中保留更多的 Raft 条目,直到快照,从而导致反复更高的内存使用量。由于领导者会保留最新的 Raft 条目更长时间,慢速跟随者有更多时间赶上领导者的快照。--snapshot-count 是在较高内存使用量和更好的慢速跟随者可用性之间的权衡。
从 v3.2 开始,--snapshot-count 的默认值已从 10,000 更改为 100,000。
更新了 gRPC 依赖版本(>=3.2.10)
3.2.10 或更高版本现在需要 grpc/grpc-go v1.7.5(<=3.2.9 需要 v1.2.1)。
弃用 grpclog.Logger
grpclog.Logger 已被 grpclog.LoggerV2 取代。clientv3.Logger 现在是 grpclog.LoggerV2。
之前
import "github.com/coreos/etcd/clientv3"
clientv3.SetLogger(log.New(os.Stderr, "grpc: ", 0))
之后
import "github.com/coreos/etcd/clientv3"
import "google.golang.org/grpc/grpclog"
clientv3.SetLogger(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
// log.New above cannot be used (not implement grpclog.LoggerV2 interface)
弃用 grpc.ErrClientConnTimeout
以前,在客户端拨号超时时会返回 grpc.ErrClientConnTimeout 错误。3.2 版本改为返回 context.DeadlineExceeded(参见 #8504)。
之前
// expect dial time-out on ipv4 blackhole
_, err := clientv3.New(clientv3.Config{
Endpoints: []string{"http://254.0.0.1:12345"},
DialTimeout: 2 * time.Second
})
if err == grpc.ErrClientConnTimeout {
// handle errors
}
之后
_, err := clientv3.New(clientv3.Config{
Endpoints: []string{"http://254.0.0.1:12345"},
DialTimeout: 2 * time.Second
})
if err == context.DeadlineExceeded {
// handle errors
}
更改了最大请求大小限制(>=3.2.10)
3.2.10 和 3.2.11 允许服务器端自定义请求大小限制。>=3.2.12 允许同时在服务器端和客户端设置自定义请求大小限制。在早期版本(v3.2.10、v3.2.11)中,客户端响应大小仅限于 4 MiB。
服务器端请求限制可通过 --max-request-bytes 参数进行配置:
# limits request size to 1.5 KiB
etcd --max-request-bytes 1536
# client writes exceeding 1.5 KiB will be rejected
etcdctl put foo [LARGE VALUE...]
# etcdserver: request is too large
或者配置 embed.Config.MaxRequestBytes 字段:
import "github.com/coreos/etcd/embed"
import "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
// limit requests to 5 MiB
cfg := embed.NewConfig()
cfg.MaxRequestBytes = 5 * 1024 * 1024
// client writes exceeding 5 MiB will be rejected
_, err := cli.Put(ctx, "foo", [LARGE VALUE...])
err == rpctypes.ErrRequestTooLarge
若未指定,服务器端限制默认为 1.5 MiB。
客户端请求限制必须基于服务器端限制进行配置。
# limits request size to 1 MiB
etcd --max-request-bytes 1048576
import "github.com/coreos/etcd/clientv3"
cli, _ := clientv3.New(clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
MaxCallSendMsgSize: 2 * 1024 * 1024,
MaxCallRecvMsgSize: 3 * 1024 * 1024,
})
// client writes exceeding "--max-request-bytes" will be rejected from etcd server
_, err := cli.Put(ctx, "foo", strings.Repeat("a", 1*1024*1024+5))
err == rpctypes.ErrRequestTooLarge
// client writes exceeding "MaxCallSendMsgSize" will be rejected from client-side
_, err = cli.Put(ctx, "foo", strings.Repeat("a", 5*1024*1024))
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max (5242890 vs. 2097152)"
// some writes under limits
for i := range []int{0,1,2,3,4} {
_, err = cli.Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("a", 1*1024*1024-500))
if err != nil {
panic(err)
}
}
// client reads exceeding "MaxCallRecvMsgSize" will be rejected from client-side
_, err = cli.Get(ctx, "foo", clientv3.WithPrefix())
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: received message larger than max (5240509 vs. 3145728)"
若未指定,客户端发送限制默认为 2 MiB(1.5 MiB + gRPC 开销字节),接收限制默认为 math.MaxInt32。更多详情请参见 clientv3 godoc。
更改了原始 gRPC 客户端包装器
3.2.12 及以后版本更改了 clientv3 gRPC 客户端包装器的函数签名。此更改是为了支持在消息大小限制上使用自定义 grpc.CallOption。
更改前后对比
-func NewKVFromKVClient(remote pb.KVClient) KV {
+func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
-func NewClusterFromClusterClient(remote pb.ClusterClient) Cluster {
+func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
-func NewLeaseFromLeaseClient(remote pb.LeaseClient, keepAliveTimeout time.Duration) Lease {
+func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {
-func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient) Maintenance {
+func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
-func NewWatchFromWatchClient(wc pb.WatchClient) Watcher {
+func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
更改了 clientv3.Lease.TimeToLive API
以前,当租约 ID 不存在时,clientv3.Lease.TimeToLive API 会返回 lease.ErrLeaseNotFound。3.2 版本改为在响应中返回 TTL=-1 且不返回错误(参见 #7305)。
之前
// when leaseID does not exist
resp, err := TimeToLive(ctx, leaseID)
resp == nil
err == lease.ErrLeaseNotFound
之后
// when leaseID does not exist
resp, err := TimeToLive(ctx, leaseID)
resp.TTL == -1
err == nil
将 clientv3.NewFromConfigFile 移动到 clientv3.yaml.NewConfig
clientv3.NewFromConfigFile 已移至 yaml.NewConfig。
之前
import "github.com/coreos/etcd/clientv3"
clientv3.NewFromConfigFile
之后
import clientv3yaml "github.com/coreos/etcd/clientv3/yaml"
clientv3yaml.NewConfig
对 --listen-peer-urls 和 --listen-client-urls 的更改
3.2 版本现在拒绝在 --listen-peer-urls 和 --listen-client-urls 中使用域名(3.1 版本仅打印警告),因为域名不能用于网络接口绑定。请确保这些 URL 正确格式化为 scheme://IP:port。
更多上下文信息,请参见 issue #6336。
服务器升级检查清单
升级要求
要将现有的 etcd 部署升级到 3.2,运行中的集群必须是 3.1 或更高版本。如果低于 3.1,请先升级到 3.1,然后再升级到 3.2。
此外,为确保平滑的滚动升级,运行中的集群必须处于健康状态。在继续操作前,请使用 etcdctl endpoint health 命令检查集群健康状况。
准备工作
在升级 etcd 之前,务必先在预演环境中测试依赖 etcd 的服务,然后再将升级部署到生产环境。
开始之前,请先备份 etcd 数据。如果升级过程中出现问题,可以使用此备份回滚到现有的 etcd 版本。请注意,snapshot 命令仅备份 v3 数据。对于 v2 数据,请参见 v2 数据存储的备份方法。
混合版本
升级过程中,etcd 集群支持混合版本的 etcd 成员,并以最低公共版本的协议运行。只有当所有成员都升级到 3.2 后,集群才被视为完成升级。内部机制上,etcd 成员之间相互协商以确定整体集群版本,该版本控制报告的版本号和支持的功能。
限制
注意:如果集群仅有 v3 数据而无 v2 数据,则不受此限制影响。
如果集群提供的 v2 数据集大于 50MB,则每个新升级的成员可能需要最多两分钟才能追上现有集群的进度。可通过检查最近快照的大小来估算总数据量。换句话说, safest 的做法是在每次升级成员之间等待 2 分钟。
对于更大的总数据量(100MB 或以上),这个一次性过程可能需要更长时间。对于如此大规模的大型 etcd 集群的管理员,可以在升级前联系etcd 团队,我们将很乐意提供升级流程建议。
Downgrade
如果所有成员均已升级到 v3.2,集群将升级至 v3.2,此时无法降级。然而,只要有任何一个成员仍处于 v3.1,集群及其操作就仍保持为“v3.1”状态,此时可以从这种混合状态回退到所有成员都使用 v3.1 etcd 二进制文件。
请备份所有 etcd 成员的数据目录,以便即使集群已完成升级后仍能进行降级。
升级流程
本示例演示如何在本地机器上将一个由三个成员组成的 v3.1 etcd 集群升级。
1. 检查升级要求
集群是否健康并运行在 v3.1.x?
$ ETCDCTL_API=3 etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:2379 is healthy: successfully committed proposal: took = 6.600684ms
localhost:22379 is healthy: successfully committed proposal: took = 8.540064ms
localhost:32379 is healthy: successfully committed proposal: took = 8.763432ms
$ curl http://localhost:2379/version
{"etcdserver":"3.1.7","etcdcluster":"3.1.0"}
2. 停止现有的 etcd 进程
每当一个 etcd 进程停止时,集群中的其他成员会记录预期的错误日志。这是正常现象,因为集群成员之间的连接已(临时)中断。
2017-04-27 14:13:31.491746 I | raft: c89feb932daef420 [term 3] received MsgTimeoutNow from 6d4f535bae3ab960 and starts an election to get leadership.
2017-04-27 14:13:31.491769 I | raft: c89feb932daef420 became candidate at term 4
2017-04-27 14:13:31.491788 I | raft: c89feb932daef420 received MsgVoteResp from c89feb932daef420 at term 4
2017-04-27 14:13:31.491797 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 6d4f535bae3ab960 at term 4
2017-04-27 14:13:31.491805 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 9eda174c7df8a033 at term 4
2017-04-27 14:13:31.491815 I | raft: raft.node: c89feb932daef420 lost leader 6d4f535bae3ab960 at term 4
2017-04-27 14:13:31.524084 I | raft: c89feb932daef420 received MsgVoteResp from 6d4f535bae3ab960 at term 4
2017-04-27 14:13:31.524108 I | raft: c89feb932daef420 [quorum:2] has received 2 MsgVoteResp votes and 0 vote rejections
2017-04-27 14:13:31.524123 I | raft: c89feb932daef420 became leader at term 4
2017-04-27 14:13:31.524136 I | raft: raft.node: c89feb932daef420 elected leader c89feb932daef420 at term 4
2017-04-27 14:13:31.592650 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream MsgApp v2 reader)
2017-04-27 14:13:31.592825 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message reader)
2017-04-27 14:13:31.693275 E | rafthttp: failed to dial 6d4f535bae3ab960 on stream Message (dial tcp [::1]:2380: getsockopt: connection refused)
2017-04-27 14:13:31.693289 I | rafthttp: peer 6d4f535bae3ab960 became inactive
2017-04-27 14:13:31.936678 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message writer)
此时最好备份 etcd 数据,以便在出现问题时提供降级路径:
$ etcdctl snapshot save backup.db
3. 替换为 etcd v3.2 二进制文件并启动新的 etcd 进程
新的 v3.2 etcd 将向集群发布其信息:
2017-04-27 14:14:25.363225 I | etcdserver: published {Name:s1 ClientURLs:[http://localhost:2379]} to cluster a9ededbffcb1b1f1
验证每个成员以及整个集群是否都使用新的 v3.2 etcd 二进制文件恢复健康状态:
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:22379 is healthy: successfully committed proposal: took = 5.540129ms
localhost:32379 is healthy: successfully committed proposal: took = 7.321771ms
localhost:2379 is healthy: successfully committed proposal: took = 10.629901ms
在整个集群完成升级之前,已升级的成员将记录如下警告。这是正常现象,在所有 etcd 集群成员都升级到 v3.2 后,警告将停止:
2017-04-27 14:15:17.071804 W | etcdserver: member c89feb932daef420 has a higher version 3.2.0
2017-04-27 14:15:21.073110 W | etcdserver: the local etcd version 3.1.7 is not up-to-date
2017-04-27 14:15:21.073142 W | etcdserver: member 6d4f535bae3ab960 has a higher version 3.2.0
2017-04-27 14:15:21.073157 W | etcdserver: the local etcd version 3.1.7 is not up-to-date
2017-04-27 14:15:21.073164 W | etcdserver: member c89feb932daef420 has a higher version 3.2.0
4. 对所有其他成员重复步骤 2 到步骤 3
5. 完成
当所有成员都升级完成后,集群将报告成功升级到 3.2:
2017-04-27 14:15:54.536901 N | etcdserver/membership: updated the cluster version from 3.1 to 3.2
2017-04-27 14:15:54.537035 I | etcdserver/api: enabled capabilities for version 3.2
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:2379 is healthy: successfully committed proposal: took = 2.312897ms
localhost:22379 is healthy: successfully committed proposal: took = 2.553476ms
localhost:32379 is healthy: successfully committed proposal: took = 2.517902ms