k8s apiserver之启动执行流程总览二
启动执行流程总览二
- k8s apiserver之启动执行流程总览二
- 启动执行流程总览二
- server.go
启动执行流程总览二
本文主要分析kubernetes在启动kube-apiserver的执行流程,本文不作深入分析,后续会展开
server.go
- 函数
// 构建授权器和授权规则解析器
func BuildAuthorizer(s *options.ServerRunOptions, EgressSelector *egressselector.EgressSelector, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) {
// 将 BuiltInAuthorizationOptions类型的对象 转换为 authorizer.Config类型对象
authorizationConfig := s.Authorization.ToAuthorizationConfig(versionedInformers)
// 如果配置有EgressSelector -- 用来分发访问入口选择器
if EgressSelector != nil {
// 这里直接指定是ControlPlane
egressDialer, err := EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext())
if err != nil {
return nil, nil, err
}
authorizationConfig.CustomDial = egressDialer
}
// 返回多个 authorizer.Authorizer 对象的授权链
return authorizationConfig.New()
}
// 构建 API 优先级和公平性过滤器的核心
func BuildPriorityAndFairness(s *options.ServerRunOptions, extclient clientgoclientset.Interface, versionedInformer clientgoinformers.SharedInformerFactory) (utilflowcontrol.Interface, error) {
// 限制变异请求的最大数量MaxMutatingRequestsInFlight和限制非变异请求的最大数量MaxRequestsInFlight之和
// 若小于等于0,则return err
if s.GenericServerRunOptions.MaxRequestsInFlight+s.GenericServerRunOptions.MaxMutatingRequestsInFlight <= 0 {
return nil, fmt.Errorf("invalid configuration: MaxRequestsInFlight=%d and MaxMutatingRequestsInFlight=%d; they must add up to something positive", s.GenericServerRunOptions.MaxRequestsInFlight, s.GenericServerRunOptions.MaxMutatingRequestsInFlight)
}
return utilflowcontrol.New(
versionedInformer,
extclient.FlowcontrolV1beta2(),
s.GenericServerRunOptions.MaxRequestsInFlight+s.GenericServerRunOptions.MaxMutatingRequestsInFlight,
// 这里注意,flag RequestTimeout的四分之一,设置为RequestWaitLimit(请求等待时长)
s.GenericServerRunOptions.RequestTimeout/4,
), nil
}
// 完成默认为初始化的配置。该方法在解析flags后必须被调用
func Complete(s *options.ServerRunOptions) (completedServerRunOptions, error) {
var options completedServerRunOptions
// 设置AdvertiseAddress字段 用于生成k8s.io中apiserver/pkg/server/config.go中Config结构体的ExternalAddress字段
// 提供给集群外部访问
if err := s.GenericServerRunOptions.DefaultAdvertiseAddress(s.SecureServing.SecureServingOptions); err != nil {
return options, err
}
// 解析启动参数service-cluster-ip-range,获取serverServiceIP 首选serviceIPRange和次要serviceIPRange
apiServerServiceIP, primaryServiceIPRange, secondaryServiceIPRange, err := getServiceIPAndRanges(s.ServiceClusterIPRanges)
if err != nil {
return options, err
}
s.PrimaryServiceClusterIPRange = primaryServiceIPRange
s.SecondaryServiceClusterIPRange = secondaryServiceIPRange
s.APIServerServiceIP = apiServerServiceIP
// 可能需要生成自签名证书 - 注意是可能 如果ServerCert.CertKey中指定了 就不需要生成
if err := s.SecureServing.MaybeDefaultWithSelfSignedCerts(s.GenericServerRunOptions.AdvertiseAddress.String(), []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"}, []net.IP{apiServerServiceIP}); err != nil {
return options, fmt.Errorf("error creating self-signed certificates: %v", err)
}
// 如果为指定ExternalHost
if len(s.GenericServerRunOptions.ExternalHost) == 0 {
// 如果AdvertiseAddress不为空,则会根据其生成ExternalHost
if len(s.GenericServerRunOptions.AdvertiseAddress) > 0 {
s.GenericServerRunOptions.ExternalHost = s.GenericServerRunOptions.AdvertiseAddress.String()
} else {
// 如果AdvertiseAddress为空,则会获取系统hostname,生成ExternalHost
if hostname, err := os.Hostname(); err == nil {
s.GenericServerRunOptions.ExternalHost = hostname
} else {
return options, fmt.Errorf("error finding host name: %v", err)
}
}
klog.Infof("external host was not specified, using %v", s.GenericServerRunOptions.ExternalHost)
}
// 验证Authentication和Authorization设置是否有冲突
s.Authentication.ApplyAuthorization(s.Authorization)
// 该参数在启动kube-apiserver时指定,是一个证书来颁发用户令牌,提供用户访问k8s
if s.ServiceAccountSigningKeyFile == "" {
// 如果ServiceAccounts的私钥没有指定并且ServerCert的私钥指定
if len(s.Authentication.ServiceAccounts.KeyFiles) == 0 && s.SecureServing.ServerCert.CertKey.KeyFile != "" {
// 读取对应路径的证书,并验证是否符合pem公钥(SecureServing.ServerCert.CertKey.KeyFile为私钥,使用该方法可以返回true么?)
if kubeauthenticator.IsValidServiceAccountKeyFile(s.SecureServing.ServerCert.CertKey.KeyFile) {
s.Authentication.ServiceAccounts.KeyFiles = []string{s.SecureServing.ServerCert.CertKey.KeyFile}
} else {
klog.Warning("No TLS key provided, service account token authentication disabled")
}
}
}
// ServiceAccountSigningKeyFile和Authentication.ServiceAccounts.Issuer不为空
if s.ServiceAccountSigningKeyFile != "" && len(s.Authentication.ServiceAccounts.Issuers) != 0 && s.Authentication.ServiceAccounts.Issuers[0] != "" {
// 读取对应路径的证书,并验证是否符合pem私钥
sk, err := keyutil.PrivateKeyFromFile(s.ServiceAccountSigningKeyFile)
if err != nil {
return options, fmt.Errorf("failed to parse service-account-issuer-key-file: %v", err)
}
// 如果Authentication.ServiceAccounts.MaxExpiration不为0,即有过期失效
if s.Authentication.ServiceAccounts.MaxExpiration != 0 {
lowBound := time.Hour
upBound := time.Duration(1<<32) * time.Second
// 验证过期时间在1 hour and 2^32 seconds
if s.Authentication.ServiceAccounts.MaxExpiration < lowBound ||
s.Authentication.ServiceAccounts.MaxExpiration > upBound {
return options, fmt.Errorf("the service-account-max-token-expiration must be between 1 hour and 2^32 seconds")
}
if s.Authentication.ServiceAccounts.ExtendExpiration {
if s.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.WarnOnlyBoundTokenExpirationSeconds*time.Second {
klog.Warningf("service-account-extend-token-expiration is true, in order to correctly trigger safe transition logic, service-account-max-token-expiration must be set longer than %d seconds (currently %s)", serviceaccount.WarnOnlyBoundTokenExpirationSeconds, s.Authentication.ServiceAccounts.MaxExpiration)
}
if s.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.ExpirationExtensionSeconds*time.Second {
klog.Warningf("service-account-extend-token-expiration is true, enabling tokens valid up to %d seconds, which is longer than service-account-max-token-expiration set to %s seconds", serviceaccount.ExpirationExtensionSeconds, s.Authentication.ServiceAccounts.MaxExpiration)
}
}
}
// 获取一个TokenGenerator,用来生成jwt
s.ServiceAccountIssuer, err = serviceaccount.JWTTokenGenerator(s.Authentication.ServiceAccounts.Issuers[0], sk)
if err != nil {
return options, fmt.Errorf("failed to build token generator: %v", err)
}
s.ServiceAccountTokenMaxExpiration = s.Authentication.ServiceAccounts.MaxExpiration
}
// 默认是为true
if s.Etcd.EnableWatchCache {
// DefaultWatchCacheSizes 定义应禁用 watchcache 的默认资源。
sizes := kubeapiserver.DefaultWatchCacheSizes()
// Ensure that overrides parse correctly.
// s.Etcd.WatchCacheSizes默认是空的
userSpecified, err := serveroptions.ParseWatchCacheSizes(s.Etcd.WatchCacheSizes)
if err != nil {
return options, err
}
// 覆盖sizes对应key的value
for resource, size := range userSpecified {
sizes[resource] = size
}
// map变为 group.source#num,group.source#num,覆盖Etcd.WatchCacheSizes
s.Etcd.WatchCacheSizes, err = serveroptions.WriteWatchCacheSizes(sizes)
if err != nil {
return options, err
}
}
// 用于定义启用和禁用 那些gv(有啥用呢? 就是我们可以把alpha 版本的resource禁用),控制是否会生成对应gvr的etcd rest
for key, value := range s.APIEnablement.RuntimeConfig {
// 这里会校验是否有以下几种情况,会从PIEnablement.RuntimeConfig中删除,然后在PIEnablement.RuntimeConfig设置key="/v1",value为原始值
// 为什么这么做呢?单纯猜测:这几种表示方法和s.APIEnablement.RuntimeConfig["/v1"] = value是一样的 只是整理一下
if key == "v1" || strings.HasPrefix(key, "v1/") ||
key == "api/v1" || strings.HasPrefix(key, "api/v1/") {
delete(s.APIEnablement.RuntimeConfig, key)
s.APIEnablement.RuntimeConfig["/v1"] = value
}
// 如果 gv= "api/legacy",则直接从s.APIEnablement.RuntimeConfig移除,因为这是k8s遗留问题,目前来说必须有的东西
if key == "api/legacy" {
delete(s.APIEnablement.RuntimeConfig, key)
}
}
options.ServerRunOptions = s
return options, nil
}
- 结构体
// 在调用 Run 之前必须强制调用 Complete()。
type completedServerRunOptions struct {
*options.ServerRunOptions
}
- 方法
// 根据enabledAggregatorRouting构建service解析器
func buildServiceResolver(enabledAggregatorRouting bool, hostname string, informer clientgoinformers.SharedInformerFactory) webhook.ServiceResolver {
var serviceResolver webhook.ServiceResolver
// 开启了聚合路由
if enabledAggregatorRouting {
// 这里会解析service/clusterip -- endpoint - pod
serviceResolver = aggregatorapiserver.NewEndpointServiceResolver(
informer.Core().V1().Services().Lister(),
informer.Core().V1().Endpoints().Lister(),
)
} else {
// 解析service/clusterip
serviceResolver = aggregatorapiserver.NewClusterIPServiceResolver(
informer.Core().V1().Services().Lister(),
)
}
// 包装解析本地 kubernetes.default.svc
if localHost, err := url.Parse(hostname); err == nil {
serviceResolver = aggregatorapiserver.NewLoopbackServiceResolver(serviceResolver, localHost)
}
return serviceResolver
}
// 解析启动参数service-cluster-ip-range,获取serverServiceIP 首选serviceIPRange和次要serviceIPRange
func getServiceIPAndRanges(serviceClusterIPRanges string) (net.IP, net.IPNet, net.IPNet, error) {
serviceClusterIPRangeList := []string{}
if serviceClusterIPRanges != "" {
// 以逗号分割
serviceClusterIPRangeList = strings.Split(serviceClusterIPRanges, ",")
}
// 首选ip
var apiServerServiceIP net.IP
// 双栈ip 第一个
var primaryServiceIPRange net.IPNet
// 双栈ip 第二个
var secondaryServiceIPRange net.IPNet
var err error
// 用户未提供任何内容,使用默认范围(仅适用于 Primary)
if len(serviceClusterIPRangeList) == 0 {
var primaryServiceClusterCIDR net.IPNet
// 检查 serviceClusterIPRange 标志是否为 nil,如果是则发出警告,并将服务 ip 范围设置为 options.DefaultServiceIPCIDR 中的默认值。返回服务ip范围,api服务器服务IP
primaryServiceIPRange, apiServerServiceIP, err = controlplane.ServiceIPRange(primaryServiceClusterCIDR)
if err != nil {
return net.IP{}, net.IPNet{}, net.IPNet{}, fmt.Errorf("error determining service IP ranges: %v", err)
}
return apiServerServiceIP, primaryServiceIPRange, net.IPNet{}, nil
}
// 将配置的双栈ip1 解析为 CIDR 表示法 IP 地址和长度
_, primaryServiceClusterCIDR, err := netutils.ParseCIDRSloppy(serviceClusterIPRangeList[0])
if err != nil {
return net.IP{}, net.IPNet{}, net.IPNet{}, fmt.Errorf("service-cluster-ip-range[0] is not a valid cidr")
}
// 检查 serviceClusterIPRange 标志是否为 nil,如果是则发出警告,并将服务 ip 范围设置为 options.DefaultServiceIPCIDR 中的默认值。返回服务ip范围,api服务器服务IP
primaryServiceIPRange, apiServerServiceIP, err = controlplane.ServiceIPRange(*primaryServiceClusterCIDR)
if err != nil {
return net.IP{}, net.IPNet{}, net.IPNet{}, fmt.Errorf("error determining service IP ranges for primary service cidr: %v", err)
}
// 用户提供了至少两个条目 注意:验证断言该列表最多包含两个双堆栈条目
if len(serviceClusterIPRangeList) > 1 {
// 将配置的双栈ip2 解析为 CIDR 表示法 IP 地址和长度
_, secondaryServiceClusterCIDR, err := netutils.ParseCIDRSloppy(serviceClusterIPRangeList[1])
if err != nil {
return net.IP{}, net.IPNet{}, net.IPNet{}, fmt.Errorf("service-cluster-ip-range[1] is not an ip net")
}
secondaryServiceIPRange = *secondaryServiceClusterCIDR
}
return apiServerServiceIP, primaryServiceIPRange, secondaryServiceIPRange, nil
}