K8S 源码探秘 之 nginx-ingress 工作原理分析_nginx与ingress

复制代码
...
for {
	select {
	...
	case event := <-n.updateCh.Out():
		if n.isShuttingDown {
			break
		}
		if evt, ok := event.(store.Event); ok {
			if evt.Type == store.ConfigurationEvent {
				n.syncQueue.EnqueueTask(task.GetDummyObject("configmap-change"))
				continue
			}
			n.syncQueue.EnqueueSkippableTask(evt.Obj)
		} 
	...
}

}

复制代码
       可以看到,NginxController 首先启动了 Store 协程,然后启动了 syncQueue 协程,最后监听 updateCh,当收到事件后,经过简单判断就向 syncQueue 写入了一个 task。


       再来看 Store 协程,跟踪到 internal/ingress/controller/store/store.go#Run()

func (s k8sStore) Run(stopCh chan struct{}) {

s.informers.Run(stopCh)

...

}

复制代码
       可以看到,继续调用了 informer 的 Run 方法,继续跟踪,还在这个文件,移步到 148 行左右

// Run initiates the synchronization of the informers against the API server.

func (i *Informer) Run(stopCh chan struct{}) {

go i.Endpoint.Run(stopCh)

go i.Service.Run(stopCh)

go i.Secret.Run(stopCh)

go i.ConfigMap.Run(stopCh)

...

go i.Ingress.Run(stopCh)

...

}

复制代码
       我们不难发现,informer 的 Run 方法,会起更多的协程,去监听不同资源的变化,包括 Endpoint、Service、Secret、ConfigMap、Ingress。我们以 Ingress 为例,跟踪到其定义处,仍在这个文件,找到 New() 方法

// New creates a new object store to be used in the ingress controller

func New(... updateCh *channels.RingChannel ...) Storer {

...

store.informers.Ingress = infFactory.Extensions().V1beta1().Ingresses().Informer()

...

ingEventHandler := cache.ResourceEventHandlerFuncs{

AddFunc: func(obj interface{}) {

...

updateCh.In() <- Event{

Type: CreateEvent,

Obj: obj,

}

},

DeleteFunc: func(obj interface{}) {

...

updateCh.In() <- Event{

Type: DeleteEvent,

Obj: obj,

}

},

UpdateFunc: func(old, cur interface{}) {

...

updateCh.In() <- Event{

Type: UpdateEvent,

Obj: cur,

}

},

}

...

store.informers.Ingress.AddEventHandler(ingEventHandler)

...

}

复制代码
        可以看出,Ingress 协程定义了监听 ingress 信息的 informer 对象,并注册了相关事件的回调方法,在回调方法内向之前提到的 updateCh 写入了事件,进而也就达到了当资源变化时通知 Controller 主程向同步队列写入task的目的。


        反过头来,看一下 syncQueue ,首先找到其定义,跟踪到 internal/ingress/controller/nginx.go#NewNGINXController()

// NewNGINXController creates a new NGINX Ingress controller.

func NewNGINXController(config *Configuration, mc metric.Collector, fs file.Filesystem) *NGINXController {

...

n.syncQueue = task.NewTaskQueue(n.syncIngress)

...

}

复制代码
       不难发现,队列的创建是通过 task.NewTaskQueue() 完成的,而且传入了关键的处理函数 n.syncIngress


       继续跟踪到 internal/task/queue.go#NewTaskQueue()

// NewTaskQueue creates a new task queue with the given sync function.

// The sync function is called for every element inserted into the queue.

func NewTaskQueue(syncFn func(interface{}) error) *Queue {

return NewCustomTaskQueue(syncFn, nil)

}

// NewCustomTaskQueue ...

func NewCustomTaskQueue(syncFn func(interface{}) error, fn func(interface{}) (interface{}, error)) *Queue {

q := &Queue{

queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),

sync: syncFn,

workerDone: make(chan bool),

fn: fn,

}

...

return q

}

复制代码
       可以看出,传入的处理函数 n.syncIngress 被赋值给 Queue 的 sync 属性了。实际上,syncQueue 的执行就是在反复执行该方法以消费队列里的元素。Queue 的 Run 定义可以在本文件中找到:

// Run starts processing elements in the queue

func (t *Queue) Run(period time.Duration, stopCh <-chan struct{}) {

wait.Until(t.worker, period, stopCh)

}

// worker processes work in the queue through sync.

func (t *Queue) worker() {

for {

key, quit := t.queue.Get()

...

if err := t.sync(key); err != nil {

t.queue.AddRateLimited(Element{

Key: item.Key,

Timestamp: time.Now().UnixNano(),

})

} else {

t.queue.Forget(key)

t.lastSync = ts

}

复制代码
	t.queue.Done(key)
}

}

复制代码
        同步队列协程的主要工作就是定期取出队列里的元素,并利用传入的 n.syncIngress (即 t.sync(key))方法处理队列里的元素。


        n.syncIngress 方法的定义在 internal/ingress/controller/controller.go#syncIngress()

// syncIngress collects all the pieces required to assemble the NGINX

// configuration file and passes the resulting data structures to the backend

// (OnUpdate) when a reload is deemed necessary.

func (n *NGINXController) syncIngress(interface{}) error {

复制代码
// 获取最新配置信息
....
// 构造 nginx 配置
pcfg := &ingress.Configuration{
	Backends:              upstreams,
	Servers:               servers,
	PassthroughBackends:   passUpstreams,
	BackendConfigChecksum: n.store.GetBackendConfiguration().Checksum,
}
...
// 不能避免 reload,就执行 reload 更新配置
if !n.IsDynamicConfigurationEnough(pcfg) {
	...
	err := n.OnUpdate(*pcfg)
	...
}
...
// 动态更新配置
err := wait.ExponentialBackoff(retry, func() (bool, error) {
	err := configureDynamically(pcfg, n.cfg.ListenPorts.Status, n.cfg.DynamicCertificatesEnabled)
	...
})
...

}

复制代码
       reload 配置的函数定义位于 internal/ingress/controller/nginx.go#OnUpdate(),具体就不解释了,读者可自行查阅。


       这里主要看下动态更新是怎么完成的,函数定义位于 internal/ingress/controller/nginx.go#configureDynamically()

// configureDynamically encodes new Backends in JSON format and POSTs the

// payload to an internal HTTP endpoint handled by Lua.

func configureDynamically(pcfg *ingress.Configuration, port int, isDynamicCertificatesEnabled bool) error {

backends := make([]*ingress.Backend, len(pcfg.Backends))

复制代码
for i, backend := range pcfg.Backends {
	var service *apiv1.Service
	if backend.Service != nil {
		service = &apiv1.Service{Spec: backend.Service.Spec}
	}
	luaBackend := &ingress.Backend{
		Name:                 backend.Name,
		Port:                 backend.Port,
		SSLPassthrough:       backend.SSLPassthrough,
		SessionAffinity:      backend.SessionAffinity,
		UpstreamHashBy:       backend.UpstreamHashBy,



既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0

25636469319)]

外链图片转存中...(img-IRNnslMq-1725636469320)

外链图片转存中...(img-61IZCJBY-1725636469320)

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0

相关推荐
皮皮冰燃5 小时前
docker-18-WSL中安装docker并部署flask服务
docker·容器·flask
techdashen10 小时前
Cloudflare 为何抛弃 NGINX,用 Rust 自研了一个代理
运维·nginx·rust
木雷坞12 小时前
K8s GPU 推理服务 ImagePullBackOff 排查与预热
云原生·容器·kubernetes·gpu算力
人生匆匆12 小时前
通过nginx解决跨域问题
运维·nginx
吴爃12 小时前
Spring Boot 项目在 K8S 中的打包、部署与运维发布实践
运维·spring boot·kubernetes
人工智能培训14 小时前
工程科研中的AI应用:结构力学分析技巧
人工智能·深度学习·机器学习·docker·容器
子木HAPPY阳VIP15 小时前
信创UOS,Docker 完整操作部署(Dockerfile部署方式)&排错整合
linux·运维·redis·nginx·docker·容器·tomcat
The Straggling Crow15 小时前
Monitoring 2026-04-30
kubernetes
AOwhisky15 小时前
Kubernetes调度与服务暴露:从“定时任务”到“服务发现”的完全指南
linux·运维·云原生·容器·kubernetes·服务发现
Cyber4K15 小时前
【Kubernetes专项】温故而知新,重温技术原理(6)
云原生·容器·kubernetes