+----------------------------------------------------------+
| Master |
| +-------------------------+ |
| +------->| API Server |<--------+ |
| | | | | |
| v +-------------------------+ v |
| +----------------+ ^ +--------------------+ |
| | | | | | |
| | Scheduler | | | Controller Manager | |
| | | | | | |
| +----------------+ v +--------------------+ |
| +------------------------------------------------------+ |
| | | |
| | Cluster state store | |
| | | |
| +------------------------------------------------------+ |
+----------------------------------------------------------+
在第 3 节《宏观认识:整体架构》 中,我们也认识到了 etcd
的存在,知道了 Master 是
K8S 是集群的大脑,而 etcd
则是大脑的核心。为什么这么说?本节我们一同来看看
etcd
为何如此重要。
etcd
是什么先摘录官方文档的一句说明:
etcd is a consistent distributed key-value store. Mainly used as a separate coordination service, in distributed systems. And designed to hold small amounts of data that can fit entirely in memory.
etcd
是由 CoreOS 团队发起的一个分布式,强一致的键值存储。它用 Go 语言编写,使用
Raft
协议作为一致性算法。多数情况下会用于分布式系统中的服务注册发现,或是用于存储系统的关键数据。
etcd
有什么作用etcd
在 K8S 中,最主要的作用便是其高可用,强一致的键值存储以及监听机制。
在 kube-apiserver
收到对应请求经过一系列的处理后,最终如果是集群所需要存储的数据,便会存储至
etcd
中。主部分主要是集群状态信息和元信息。
我们来实际操作 K8S 集群中的 etcd
看下真实情况。
etcd
Pod# 默认集群中的 etcd 会放到 kube-system Namespace 中
master $ kubectl -n kube-system get pods | grep etcd
etcd-master 1/1 Running 0 1h
etcd
集群的 member
master $ kubectl -n kube-system exec -it etcd-master sh
/ # ETCDCTL_API=3 etcdctl --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --cacert=/etc/kubernetes/pki/etcd/ca.crt member list
a874c87fd42044f, started, master, https://127.0.0.1:2380, https://127.0.0.1:2379
这里由于在 K8S 1.11.3 中默认使用的是 etcd
3.2 版本,所以需要加入 ETCDCTL_API=3
的环境变量,且 etcd
从 2 到 3 很明显的一个变化也是使用上的变化,在 2 中是
HTTP 接口的。
我们通过传递证书等相关参数进去,完成校验,查看 member
。
etcd
中存储的 K8S 集群元信息基本都是 /registry
下,我们可通过下面的方式进行查看
/ # ETCDCTL_API=3 etcdctl --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --cacert=/etc/kubernetes/pki/etcd/ca.crt get /registry --prefix --keys-only
/registry/apiregistration.k8s.io/apiservices/v1.
/registry/clusterroles/system:aggregate-to-admin
/registry/configmaps/kube-public/cluster-info
/registry/masterleases/172.17.0.53
/registry/namespaces/default
/registry/namespaces/kube-public
/registry/namespaces/kube-system
/registry/pods/kube-system/etcd-master
/registry/pods/kube-system/kube-apiserver-master
/registry/ranges/serviceips
/registry/ranges/servicenodeports
...
# 篇幅原因,删掉了很多输出
可以看到有各种类型的资源。我们直接以 Namespaces 为例。
/ # ETCDCTL_API=3 etcdctl --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --cacert=/etc/kubernetes/pki/etcd/ca.crt get /registry/namespaces --prefix --keys-only
/registry/namespaces/default
/registry/namespaces/kube-public
/registry/namespaces/kube-system
kubectl
创建名为 moelove
的 Namespacesmaster $ kubectl create ns moelove
namespace/moelove created
/ # ETCDCTL_API=3 etcdctl --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --cacert=/etc/kubernetes/pki/etcd/ca.crt get /registry/namespaces --prefix --keys-only
/registry/namespaces/default
/registry/namespaces/kube-public
/registry/namespaces/kube-system
/registry/namespaces/moelove
发现刚创建的 moelove
的 Namespaces 已经在 etcd
中了。
etcd
是如何被使用的首先,在 staging/src/k8s.io/apiserver/pkg/server/options/etcd.go
中,存在一处声明:
var storageTypes = sets.NewString(
storagebackend.StorageTypeUnset,
storagebackend.StorageTypeETCD2,
storagebackend.StorageTypeETCD3,
)
在 1.13 发布时,etcd 2 的相关代码已经移除,其中就包含上面声明中的 storagebackend.StorageTypeETCD2
这里是在过渡期间为了同时兼容 etcd
2 和 3 而增加的。
我们来看下实际对各类资源的操作,还是以对 Namespace
的操作为例:代码在 pkg/registry/core/namespace/storage/storage.go
中,
比如,Get
:
func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
而 REST
则是这样定义的:
type REST struct {
store *genericregistry.Store
status *genericregistry.Store
}
通过 REST
实现了一个 RESTStorage
,实际使用时,也是调用了 staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go
对接口的封装。
func (e *Store) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
obj := e.NewFunc()
key, err := e.KeyFunc(ctx, name)
if err != nil {
return nil, err
}
if err := e.Storage.Get(ctx, key, options.ResourceVersion, obj, false); err != nil {
return nil, storeerr.InterpretGetError(err, e.qualifiedResourceFromContext(ctx), name)
}
if e.Decorator != nil {
if err := e.Decorator(obj); err != nil {
return nil, err
}
}
return obj, nil
}
在该处实现了对各类基本方法的封装,各种资源类型都会统一去调用。而更上层则是对 storagebackend
的统一封装,最终会调用 etcd
客户端的实现完成想对应的操作,这里就不再多展开了。
在本节中,我们介绍了 etcd
以及它在 K8S 中的作用以及如何使用。我们还介绍了如何通过
etcdctl
工具去操作 etcd
。
在某些极端情况下,也许你需要通过直接操作 etcd
集群去变更数据,这里没有介绍所有的操作命令,感兴趣的可以自行通过下方的链接看官方文档进行学习。
但通常情况下,不建议直接操作 etcd
,除非你已经明确自己在做什么。
另外,由于 etcd
集群使用 Raft
一致性算法,通常情况下
etcd
集群需要部署奇数个节点,如 3,5,7 等。etcd
集群维护也相对容易,很容易可以做成高可用集群。(这也是保障 K8S 集群高可用的重要一环)
学习了 etcd
之后,下节我们来学习同样很重要的一个组件 Controller
Manager
,学习它是如何保障集群符合预期状态的。