在 kubernetes,可以从集群外部和内部两种方式访问 kubernetes API,在集群外直接访问 apiserver 提供的 API,在集群内即 pod 中可以通过访问 service 为 kubernetes 的 ClusterIP。kubernetes 集群在初始化完成后就会创建一个 kubernetes service,该 service 是 kube-apiserver 创建并进行维护的,如下所示:
1 | $ kubectl get service |
内置的 kubernetes service 无法删除,其 ClusterIP 为通过 --service-cluster-ip-range
参数指定的 ip 段中的首个 ip,kubernetes endpoints 中的 ip 以及 port 可以通过 --advertise-address
和 --secure-port
启动参数来指定。
kubernetes service 是由 kube-apiserver 中的 bootstrap controller 进行控制的,其主要以下几个功能:
- 创建 kubernetes service;
- 创建 default、kube-system 和 kube-public 命名空间,如果启用了
NodeLease
特性还会创建 kube-node-lease 命名空间; - 提供基于 Service ClusterIP 的修复及检查功能;
- 提供基于 Service NodePort 的修复及检查功能;
kubernetes service 默认使用 ClusterIP 对外暴露服务,若要使用 nodePort 的方式可在 kube-apiserver 启动时通过 --kubernetes-service-node-port
参数指定对应的端口。
bootstrap controller 源码分析
kubernetes 版本:v1.16
bootstrap controller 的初始化以及启动是在 CreateKubeAPIServer
调用链的 InstallLegacyAPI
方法中完成的,bootstrap controller 的启停是由 apiserver 的 PostStartHook
和 ShutdownHook
进行控制的。
k8s.io/kubernetes/pkg/master/master.go:406
1 | func (m *Master) InstallLegacyAPI(......) error { |
postStartHooks 会在 kube-apiserver 的启动方法 prepared.Run
中调用 RunPostStartHooks
启动所有 Hook。
NewBootstrapController
bootstrap controller 在初始化时需要设定多个参数,主要有 PublicIP、ServiceCIDR、PublicServicePort 等。PublicIP 是通过命令行参数 --advertise-address
指定的,如果没有指定,系统会自动选出一个 global IP。PublicServicePort 通过 --secure-port
启动参数来指定(默认为 6443),ServiceCIDR 通过 --service-cluster-ip-range
参数指定(默认为 10.0.0.0/24)。
k8s.io/kubernetes/pkg/master/controller.go:89
1 | func (c *completedConfig) NewBootstrapController(......) *Controller { |
自动选出 global IP 的代码如下所示:
k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/interface.go:323
1 | func ChooseHostInterface() (net.IP, error) { |
bootstrapController.Start
上文已经提到了 bootstrap controller 主要的四个功能:修复 ClusterIP、修复 NodePort、更新 kubernetes service、创建系统所需要的名字空间(default、kube-system、kube-public)。bootstrap controller 在启动后首先会完成一次 ClusterIP、NodePort 和 Kubernets 服务的处理,然后异步循环运行上面的4个工作。以下是其 start
方法:
k8s.io/kubernetes/pkg/master/controller.go:146
1 | func (c *Controller) Start() { |
c.RunKubernetesNamespaces
c.RunKubernetesNamespaces
主要功能是创建 kube-system 和 kube-public 命名空间,如果启用了 NodeLease
特性功能还会创建 kube-node-lease 命名空间,之后每隔一分钟检查一次。
k8s.io/kubernetes/pkg/master/controller.go:199
1 | func (c *Controller) RunKubernetesNamespaces(ch chan struct{}) { |
c.RunKubernetesService
c.RunKubernetesService
主要是检查 kubernetes service 是否处于正常状态,并定期执行同步操作。首先调用 /healthz
接口检查 apiserver 当前是否处于 ready 状态,若处于 ready 状态然后调用 c.UpdateKubernetesService
服务更新 kubernetes service 状态。
k8s.io/kubernetes/pkg/master/controller.go:210
1 | func (c *Controller) RunKubernetesService(ch chan struct{}) { |
c.UpdateKubernetesService
c.UpdateKubernetesService
的主要逻辑为:
- 1、调用
createNamespaceIfNeeded
创建 default namespace; - 2、调用
c.CreateOrUpdateMasterServiceIfNeeded
为 master 创建 kubernetes service; - 3、调用
c.EndpointReconciler.ReconcileEndpoints
更新 master 的 endpoint;
k8s.io/kubernetes/pkg/master/controller.go:230
1 | func (c *Controller) UpdateKubernetesService(reconcile bool) error { |
c.EndpointReconciler.ReconcileEndpoints
EndpointReconciler 的具体实现由 EndpointReconcilerType
决定,EndpointReconcilerType
是 --endpoint-reconciler-type
参数指定的,可选的参数有 master-count, lease, none
,每种类型对应不同的 EndpointReconciler 实例,在 v1.16 中默认为 lease,此处仅分析 lease 对应的 EndpointReconciler 的实现。
一个集群中可能会有多个 apiserver 实例,因此需要统一管理 apiserver service 的 endpoints,c.EndpointReconciler.ReconcileEndpoints
就是用来管理 apiserver endpoints 的。一个集群中 apiserver 的所有实例会在 etcd 中的对应目录下创建 key,并定期更新这个 key 来上报自己的心跳信息,ReconcileEndpoints 会从 etcd 中获取 apiserver 的实例信息并更新 endpoint。
k8s.io/kubernetes/pkg/master/reconcilers/lease.go:144
1 | func (r *leaseEndpointReconciler) ReconcileEndpoints(......) error { |
repairClusterIPs.RunUntil
repairClusterIP 主要解决的问题有:
- 保证集群中所有的 ClusterIP 都是唯一分配的;
- 保证分配的 ClusterIP 不会超出指定范围;
- 确保已经分配给 service 但是因为 crash 等其他原因没有正确创建 ClusterIP;
- 自动将旧版本的 Kubernetes services 迁移到 ipallocator 原子性模型;
repairClusterIPs.RunUntil
其实是调用 repairClusterIPs.runOnce
来处理的,其代码中的主要逻辑如下所示:
k8s.io/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go:134
1 | func (c *Repair) runOnce() error { |
repairNodePorts.RunUnti
repairNodePorts 主要是用来纠正 service 中 nodePort 的信息,保证所有的 ports 都基于 cluster 创建的,当没有与 cluster 同步时会触发告警,其最终是调用 repairNodePorts.runOnce
进行处理的,主要逻辑与 ClusterIP 的处理逻辑类似。
k8s.io/kubernetes/pkg/registry/core/service/portallocator/controller/repair.go:84
1 | func (c *Repair) runOnce() error { |
以上就是 bootstrap controller 的主要实现。
总结
本文主要分析了 kube-apiserver 中 apiserver service 的实现,apiserver service 是通过 bootstrap controller 控制的,bootstrap controller 会保证 apiserver service 以及其 endpoint 处于正常状态,需要注意的是,apiserver service 的 endpoint 根据启动时指定的参数分为三种控制方式,本文仅分析了 lease 的实现方式,如果使用 master-count 方式,需要将每个 master 实例的 port、apiserver-count 等配置参数改为一致。