【编者按】openshift底层是通过kubelet来管理pod,kubelet通过CNI插件来配置pod网络.openshift node节点在启动的时会在一个goroutine中启动kubelet, 由kubelet来负责pod的管理工作。
本文主要从源码的角度入手,简单分析在openshift环境下kubelet是如何通过调用openshift sdn插件来配置pod网络。
上一节分析了openshift-sdn插件是如何配置Pod网络的,本节分析openshift-sdn插件获取Pod IP时cniServer的处理流程。
CNIServer流程
在上面的分析中我们知道,openshift-sdn插件是通过方法doCNIServerAdd向cniserver来请求IP的,那cniserver是如何处理请求的呢?我们先来看cniServer的逻辑。
cniServer的定义位于openshit代码库的pkg/network/node/cniserver/cniserver.go文件,定义如下:
1type CNIServer struct {
2 http.Server
3 requestFunc cniRequestFunc
4 rundir string
5 config *Config
6}
它包括了一个http server,以及一个处理请求的handler cniRequestFunc, 还有一些配置相关的字段。
cniSever的构造器方法位于pkg/network/node/cniserver/cniserver.go#L120, 内容如下:
1// Create and return a new CNIServer object which will listen on a socket in the given path
2func NewCNIServer(rundir string, config *Config) *CNIServer {
3 router := mux.NewRouter()
4
5 s := &CNIServer{
6 Server: http.Server{
7 Handler: router,
8 },
9 rundir: rundir,
10 config: config,
11 }
12 router.NotFoundHandler = http.HandlerFunc(http.NotFound)
13 router.HandleFunc("/", s.handleCNIRequest).Methods("POST")
14 return s
15}
从上面第13行的代码可以看出,该server只处理一条POST方法的路由,处理请求的handler是handleCNIRequest这个方法,该方法的定义位于 pkg/network/node/cniserver/cniserver.go#L277,内容如下:
1// Dispatch a pod request to the request handler and return the result to the
2// CNI server client
3func (s *CNIServer) handleCNIRequest(w http.ResponseWriter, r *http.Request) {
4 req, err := cniRequestToPodRequest(r)
5 if err != nil {
6 http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
7 return
8 }
9
10 glog.V(5).Infof("Waiting for %s result for pod %s/%s", req.Command, req.PodNamespace, req.PodName)
11 result, err := s.requestFunc(req)
12 if err != nil {
13 http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
14 } else {
15 // Empty response JSON means success with no body
16 w.Header().Set("Content-Type", "application/json")
17 if _, err := w.Write(result); err != nil {
18 glog.Warningf("Error writing %s HTTP response: %v", req.Command, err)
19 }
20 }
21}
从第11行可以看出,该方法又是调用requestFunc这个方法来处理请求,请求结束后通过w.Write或者是http.Error返回调用者的response。requestFunc是在cniserver的Start的方法中传入的,传入的实际上是podManager的handleCNIRequest方法,该方法位于文件pkg/network/node/pod.go#L25,内容如下:
1// Enqueue incoming pod requests from the CNI server, wait on the result,
2// and return that result to the CNI client
3func (m *podManager) handleCNIRequest(request *cniserver.PodRequest) ([]byte, error) {
4 glog.V(5).Infof("Dispatching pod network request %v", request)
5 m.addRequest(request)
6 result := m.waitRequest(request)
7 glog.V(5).Infof("Returning pod network request %v, result %s err %v", request, string(result.Response), result.Err)
8 return result.Response, result.Err
9}
在第5行该方法先通过addRequest方法把请求放到一个队列里面,然后调用第6行的waitRequest等待请求执行完成。
addRequest定义位于pkg/network/node/pod.go#L240, 内容如下:
1// Add a request to the podManager CNI request queue
2func (m *podManager) addRequest(request *cniserver.PodRequest) {
3 m.requests <- request
4}
可以看出请求被放到了m.requests这个channel里面,也就是在这里用channel做的队列。
waitRequest是从一个channel里取出结果,定义位于pkg/network/