先看服务启动过程
func main() {
// start a new nginx controller
ngx := newNGINXController()
// create a custom Ingress controller using NGINX as backend
ic := controller.NewIngressController(ngx)
go handleSigterm(ic)
// start the controller
ic.Start()
// wait
glog.Infof("shutting down Ingress controller...")
for {
glog.Infof("Handled quit, awaiting pod deletion")
time.Sleep(30 * time.Second)
}
}
这里定义了服务的启动和停止,先创建NewIngressController然后启动ic.Start(),如果停止通过SIGTERM信号关闭,停止的代码很简单,先解决
func handleSigterm(ic *controller.GenericController) {
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGTERM)
<-signalChan
glog.Infof("Received SIGTERM, shutting down")
exitCode := 0
if err := ic.Stop(); err != nil {
glog.Infof("Error during shutdown %v", err)
exitCode = 1
}
glog.Infof("Exiting with %v", exitCode)
os.Exit(exitCode)
}
很简单,就是接受SIGTERM调用Stop方法,Stop方法里面把同步queue关闭。讲完关闭,回到开始创建的地方。
func newNGINXController() ingress.Controller {
ngx := os.Getenv("NGINX_BINARY")
if ngx == "" {
ngx = binary
}
n := &NGINXController{
binary: ngx,
configmap: &api.ConfigMap{},
}
var onChange func()
onChange = func() {
template, err := ngx_template.NewTemplate(tmplPath, onChange)
if err != nil {
// this error is different from the rest because it must be clear why nginx is not working
glog.Errorf(`
-------------------------------------------------------------------------------
Error loading new template : %v
-------------------------------------------------------------------------------
`, err)
return
}
n.t.Close()
n.t = template
glog.Info("new NGINX template loaded")
}
ngxTpl, err := ngx_template.NewTemplate(tmplPath, onChange)
if err != nil {
glog.Fatalf("invalid NGINX template: %v", err)
}
n.t = ngxTpl
go n.Start()
return ingress.Controller(n)
}
创建一个Nginx的controller,里没有有两个重要的参数:nginx的二进制文件和configmap。其实还有第三个字段是t(n.t = ngxTpl),它是一个Template结构体,主要负责修改nginx的配置文件
func NewTemplate(file string, onChange func()) (*Template, error) {
tmpl, err := text_template.New("nginx.tmpl").Funcs(funcMap).ParseFiles(file)
if err != nil {
return nil, err
}
fw, err := watch.NewFileWatcher(file, onChange)
if err != nil {
return nil, err
}
return &Template{
tmpl: tmpl,
fw: fw,
s: defBufferSize,
tmplBuf: bytes.NewBuffer(make([]byte, 0, defBufferSize)),
outCmdBuf: bytes.NewBuffer(make([]byte, 0, defBufferSize)),
}, nil
}
加载的是nginx.tmpl的模板文件。那么模板文件是啥呢?
client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }};
large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }};
http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }};
http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }};
types_hash_max_size 2048;
server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }};
server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }};
map_hash_bucket_size {{ $cfg.MapHashBucketSize }};
是gotemplate模板,里面有很多变量,当组织好结构后就可以通过替换变量,并且写入到nginx.conf里面。从而更新nginx配置。
然后启动nginx, go n.Start()
func (n *NGINXController) Start() {
glog.Info("starting NGINX process...")
done := make(chan error, 1)
cmd := exec.Command(n.binary, "-c", cfgPath)
n.start(cmd, done)
for {
err := <-done
if exitError, ok := err.(*exec.ExitError); ok {
waitStatus := exitError.Sys().(syscall.WaitStatus)
glog.Warningf(`
-------------------------------------------------------------------------------
NGINX master process died (%v): %v
-------------------------------------------------------------------------------
`, waitStatus.ExitStatus(), err)
}
cmd.Process.Release()
cmd = exec.Command(n.binary, "-c", cfgPath)
// we wait until the workers are killed
for {
conn, err := net.DialTimeout("tcp", "127.0.0.1:80", 1*time.Second)
if err != nil {
break
}
conn.Close()
time.Sleep(1 * time.Second)
}
// start a new nginx master process
n.start(cmd, done)
}
}
上面代码创建了nginx controller,ingress设计中nginx只是其中一种负载均衡,所以ic := controller.NewIngressController(ngx) 其中ic为ingress controller,在这个方法里面创建了调用k8s api的客户端,最终启动ingress的controller:ic.Start()
func (ic GenericController) Start() {
glog.Infof("starting Ingress controller")
go ic.ingController.Run(ic.stopCh)
go ic.endpController.Run(ic.stopCh)
go ic.svcController.Run(ic.stopCh)
go ic.nodeController.Run(ic.stopCh)
go ic.secrController.Run(ic.stopCh)
go ic.mapController.Run(ic.stopCh)
go ic.secretQueue.Run(5*time.Second, ic.stopCh)
go ic.syncQueue.Run(5*time.Second, ic.stopCh)
if ic.syncStatus != nil {
go ic.syncStatus.Run(ic.stopCh)
}
<-ic.stopCh
}
这个方法是重点,这里面启动了对ingress、endpoint、service、node、secret、configmap的listwatch。还有两个对列secretQueue和syncQueue。
这样服务就可以启动了。