4. apiserver (一、启动流程)
4.1 启动文件
基本所有的k8s组件main方法都是一样的:
func main() {
command := app.NewAPIServerCommand()
code := cli.Run(command)
os.Exit(code)
}
先用Cobra组成一个conmmand,再用cli.Run跑起来。
4.2 生成command
主要就是使用cobra创建command,然后利用options设置所有的flagSet,以下是源码:
// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
s := options.NewServerRunOptions()
cmd := &cobra.Command{
Use: "kube-apiserver",
...
}
fs := cmd.Flags()
namedFlagSets := s.Flags()
verflag.AddFlags(namedFlagSets.FlagSet("global"))
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name(), logs.SkipLoggingConfigurationFlags())
options.AddCustomGlobalFlags(namedFlagSets.FlagSet("generic"))
for _, f := range namedFlagSets.FlagSets {
fs.AddFlagSet(f)
}
...
return cmd
}
为更直观的看清楚逻辑,重构一下:
// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
cmdName := "kube-apiserver"
s := options.NewServerRunOptions()
// 添加额外的flagSet
addAdditionalFlags(s)
cmd := &cobra.Command{
Use: cmdName,
...
}
// 将s的flagSet复制到cmd
copyFlags(s, cmd)
return cmd
}
4.3 启动command
一步步追踪下去,比较重要的逻辑为:
- 读取启动参数
// ExecuteC executes the command.
func (c *Command) ExecuteC() (cmd *Command, err error) {
...
args := c.args
// Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155
if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" {
// 1. 读取启动参数
args = os.Args[1:]
}
...
// 由args解析成flags,不必关注如何解析的,正常情况下可以flags和args的值是一样的
err = cmd.execute(flags)
...
return cmd, err
}
- 解析参数、运行RunE
func (c *Command) execute(a []string) (err error) {
...
// 解析参数,非法参数会报错
err = c.ParseFlags(a)
// 开放自定义前置逻辑
c.preRun()
// 开放自定义后置逻辑
defer c.postRun()
...
if c.PreRunE != nil {
if err := c.PreRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PreRun != nil {
c.PreRun(c, argWoFlags)
}
...
if c.RunE != nil {
// apiserver对应的是这部分
if err := c.RunE(c, argWoFlags); err != nil {
return err
}
} else {
c.Run(c, argWoFlags)
}
if c.PostRunE != nil {
if err := c.PostRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PostRun != nil {
c.PostRun(c, argWoFlags)
}
...
return nil
}
- 执行RunE
RunE: func(cmd *cobra.Command, args []string) error {
...
// set default options
// 这里的 s 就是 4.2 中的 s(ServerRunOptions),设置默认值
completedOptions, err := s.Complete()
if err != nil {
return err
}
// validate options
if errs := completedOptions.Validate(); len(errs) != 0 {
return utilerrors.NewAggregate(errs)
}
// add feature enablement metrics
utilfeature.DefaultMutableFeatureGate.AddMetrics()
return Run(completedOptions, genericapiserver.SetupSignalHandler())
}
options在cli.Run(cmd)之前和之后有什么区别呢?这里需要注意的就是在复制的参数使用的是引用类型,所以在cli.Run中解析的参数也在 options 中了。
- Run 方法
// Run runs the specified APIServer. This should never exit.
func Run(opts options.CompletedOptions, stopCh <-chan struct{}) error {
...
// 1. 生成 apiserver、aggregator、apiExtensions 的 config
config, err := NewConfig(opts)
if err != nil {
return err
}
// 2. 填充默认值
completed, err := config.Complete()
if err != nil {
return err
}
// 3. 创建执行链
server, err := CreateServerChain(completed)
if err != nil {
return err
}
prepared, err := server.PrepareRun()
if err != nil {
return err
}
return prepared.Run(stopCh)
}