etcd-operator简单体验

operator

Operator是指一类基于Kubernetes自定义资源对象(CRD)和控制器(Controller)的云原生拓展服务,其中CRD定义了每个operator所创建和管理的自定义资源对象,Controller则包含了管理这些对象所相关的运维逻辑代码。

operator主要是为了管理有状态服务的一种方式,可以理解分为存储和控制两部分:

存储 CRD

operator利用k8s内部的CRD对象来进行数据存储,本质上也就是把数据存储在etcd中,借助etcd分布式存储的特性,有效可靠的在集群中共享需要存储的对象

控制器 Controller

通过etcd的list-watch机制,controller能够第一时间得到最新的CRD状态,根据其中的相应字段执行对应的预设逻辑,以达到期望的状态

比如,我新建一个EtcdCluster对象,如果没有controller那么他就是一个简单的数据对象,controller检测到这个对象以后,就会按照这个对象对应的数据去创建一个真正的etcd集群,逻辑其实就是创建对应的deployment

etcd-operator

etcd-operator是coreos开发的一个开源项目,也是operator理论的最初实践,使用简单的命令行安装,并且使用户能够使用简单的声明式配置来配置和管理 etcd 的复杂性,这些配置将创建,配置和管理 etcd 集群。

功能

  1. 创建和销毁etcd集群
  2. 调整集群大小
  3. 故障自动恢复
  4. 集群滚动升级
  5. 数据备份和还原

CRD

EtcdCluster 集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type EtcdCluster struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ClusterSpec `json:"spec"`
Status ClusterStatus `json:"status"`
}
type ClusterSpec struct {
Size int `json:"size"`
Repository string `json:"repository,omitempty"`
Version string `json:"version,omitempty"`
Paused bool `json:"paused,omitempty"`
Pod *PodPolicy `json:"pod,omitempty"`
TLS *TLSPolicy `json:"TLS,omitempty"`
}

EtcdCluster对象代表一个集群,描述一个集群的节点数量以及版本

参数 含义 默认值
size 集群节点数量,1-7,推荐单数
repository image的镜像地址,在无法直接访问的时候可以尝试更换镜像 quay.io/coreos/etcd
version etcd的版本号,实际上也就是镜像的版本号 3.2.13
pod k8s中pod相关的配置,注意,pod配置修改不会影响运行中的容器
1
2
3
4
5
6
7
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdCluster"
metadata:
name: "example-etcd-cluster"
spec:
size: 5
version: "3.2.13"
创建集群
1
kubectl apply -f cluster.yaml
调整集群大小

修改size来控制集群节点数目,推荐奇数个,然后apply

删除集群
1
kubectl delete -f cluster.yaml
设置集群node选择器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
spec:
size: 3
pod:
nodeSelector:
diskType: ssd
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: etcd_cluster
operator: In
values: ["$cluster_name"]
topologyKey: kubernetes.io/hostname
自定义annotations
1
2
3
4
5
6
spec:
size: 3
pod:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "2379"

这里配置prometheus采集所需要使用的annotations

EtcdBackup 备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type EtcdBackup struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec BackupSpec `json:"spec"`
Status BackupStatus `json:"status,omitempty"`
}

type BackupSpec struct {
EtcdEndpoints []string `json:"etcdEndpoints,omitempty"`
StorageType BackupStorageType `json:"storageType"`
BackupPolicy *BackupPolicy `json:"backupPolicy,omitempty"`
BackupSource `json:",inline"`
ClientTLSSecret string `json:"clientTLSSecret,omitempty"`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdBackup"
metadata:
name: example-etcd-cluster-backup
spec:
etcdEndpoints: [<etcd-cluster-endpoints>]
storageType: S3
s3:
# The format of "path" must be: "<s3-bucket-name>/<path-to-backup-file>"
# e.g: "mybucket/etcd.backup"
path: <full-s3-path>
awsSecret: <aws-secret>

EtcdBackup代表一个备份策略,用来定义备份相关的参数

字段 含义 默认值
etcdEndpoints etcd地址路径
storageType 存储类型比如S3,ABS,GCS,OSS
backupPolicy 备份策略,频率,备份次数
s3等 对应类型的配置

其中backupPolicy包括

字段 含义 默认值
timeoutInSecond 单词备份最长时间,单位秒
backupIntervalInSecond 备份间隔,单位秒
maxBackups 备份最多保留数量,超过会自动删除

EtcdBackup对象中的status字段,不需要人为设置,是有operator根据任务完成结果自动设置方便查看,其中包含以下字段

字段 含义 默认值
succeeded 是否备份成功
Reason 备份失败原因
etcdVersion etcd版本
etcdRevision etcd内部数据的rev号
lastSuccessDate 上次备份时间
创建备份
1
2
3
4
5
sed -e 's|<full-s3-path>|mybucket/etcd.backup|g' \
-e 's|<aws-secret>|aws|g' \
-e 's|<etcd-cluster-endpoints>|"http://example-etcd-cluster-client:2379"|g' \
example/etcd-backup-operator/backup_cr.yaml \
| kubectl create -f -

创建备份,配置对应的存储配置,以及etcd地址信息

查看备份状态

查看所创建的对象的status字段

1
2
3
4
5
6
7
8
$ kubectl get EtcdBackup example-etcd-cluster-backup -o yaml
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdBackup
...
status:
etcdRevision: 1
etcdVersion: 3.2.13
succeeded: true
删除备份

直接删除CR对象既可

1
kubectl delete etcdbackup example-etcd-cluster-backup

这里注意,删除CR对象,只是删除备份策略,实际上存储上的备份不会被删除,需要手动删除

EtcdRestore 恢复

1
2
3
4
5
6
7
8
9
10
11
type EtcdRestore struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec RestoreSpec `json:"spec"`
Status RestoreStatus `json:"status,omitempty"`
}
type RestoreSpec struct {
BackupStorageType BackupStorageType `json:"backupStorageType"`
RestoreSource `json:",inline"`
EtcdCluster EtcdClusterRef `json:"etcdCluster"`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdRestore"
metadata:
# The restore CR name must be the same as spec.etcdCluster.name
name: example-etcd-cluster
spec:
etcdCluster:
# The namespace is the same as this EtcdRestore CR
name: example-etcd-cluster
backupStorageType: S3
s3:
# The format of "path" must be: "<s3-bucket-name>/<path-to-backup-file>"
# e.g: "mybucket/etcd.backup"
path: <full-s3-path>
awsSecret: <aws-secret>

EtcdRestore是一次备份恢复,从之前备份的存储中,恢复数据到指定的集群,其中包含字段

字段 含义 默认值
backupStorageType 指定备份的存储类型
s3等 设置对应存储类型的配置
etcdCluster 指定新的集群名称
创建一次备份恢复

新建EtcdRestore CR

1
2
3
4
sed -e 's|<full-s3-path>|mybucket/etcd.backup|g' \
-e 's|<aws-secret>|aws|g' \
example/etcd-restore-operator/restore_cr.yaml \
| kubectl create -f -
查看备份恢复状态
  1. 检查EtcdRestore的status字段,如果失败的话可以查看原因

    1
    2
    3
    4
    5
    6
    $ kubectl get etcdrestore example-etcd-cluster -o yaml
    apiVersion: etcd.database.coreos.com/v1beta2
    kind: EtcdRestore
    ...
    status:
    succeeded: true
  2. 检查EtcdCluster对象是否创建

    1
    2
    3
    $ kubectl get etcdcluster
    NAME KIND
    example-etcd-cluster EtcdCluster.v1beta2.etcd.database.coreos.com
  3. 检查etcd-operator是否完整的构建了整个集群

    1
    2
    3
    4
    5
    6
    7
    $ kubectl get pods
    NAME READY STATUS RESTARTS AGE
    etcd-operator-2486363115-ltc17 1/1 Running 0 1h
    etcd-restore-operator-4203122180-npn3g 1/1 Running 0 30m
    example-etcd-cluster-795649v9kq 1/1 Running 1 3m
    example-etcd-cluster-jtp447ggnq 1/1 Running 1 4m
    example-etcd-cluster-psw7sf2hhr 1/1 Running 1 4m
清理

如果一个恢复已经完成,那么现在EtcdRestore就没有多用了,这个时候需要清理掉这个CR,这里注意,清理掉EtcdRestore并不会删除他恢复的那个EtcdCluster,可以放心删除

感想

etcd-operator整体逻辑清晰,使用还比较简单,但是个人使用感觉还是有一些问题

  1. 更新缓慢,项目基本停摆,虽然基本功能已经完善,但是很多代码没有适配新版本的k8s,项目PR无人处理
  2. 存储局限,项目中仅仅支持了4种内置的存储类型,虽然够用,这部分扩展需要额外维护,如果能自动识别k8s所支持的存储类型,动态配置就更好了
  3. DNS问题,整体添加节点,完成备份,都是按照规则拼接域名来完成的,之前测试中,dns服务有问题,或者网络有问题的时候,无法完成对应的添加节点逻辑,完全无法工作