github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/monitor/internal/pod/monitor.go (about) 1 // +build linux !windows 2 3 package podmonitor 4 5 import ( 6 "context" 7 "errors" 8 "fmt" 9 "time" 10 11 "go.aporeto.io/trireme-lib/monitor/config" 12 "go.aporeto.io/trireme-lib/monitor/extractors" 13 "go.aporeto.io/trireme-lib/monitor/registerer" 14 15 "k8s.io/client-go/rest" 16 "k8s.io/client-go/tools/clientcmd" 17 18 "sigs.k8s.io/controller-runtime/pkg/client" 19 "sigs.k8s.io/controller-runtime/pkg/event" 20 "sigs.k8s.io/controller-runtime/pkg/manager" 21 22 "go.uber.org/zap" 23 ) 24 25 // PodMonitor implements a monitor that sends pod events upstream 26 // It is implemented as a filter on the standard DockerMonitor. 27 // It gets all the PU events from the DockerMonitor and if the container is the POD container from Kubernetes, 28 // It connects to the Kubernetes API and adds the tags that are coming from Kuberntes that cannot be found 29 type PodMonitor struct { 30 localNode string 31 handlers *config.ProcessorConfig 32 metadataExtractor extractors.PodMetadataExtractor 33 netclsProgrammer extractors.PodNetclsProgrammer 34 pidsSetMaxProcsProgrammer extractors.PodPidsSetMaxProcsProgrammer 35 resetNetcls extractors.ResetNetclsKubepods 36 sandboxExtractor extractors.PodSandboxExtractor 37 enableHostPods bool 38 workers int 39 kubeCfg *rest.Config 40 kubeClient client.Client 41 eventsCh chan event.GenericEvent 42 resyncInfo *ResyncInfoChan 43 } 44 45 // New returns a new kubernetes monitor. 46 func New() *PodMonitor { 47 podMonitor := &PodMonitor{ 48 eventsCh: make(chan event.GenericEvent), 49 resyncInfo: NewResyncInfoChan(), 50 } 51 52 return podMonitor 53 } 54 55 // SetupConfig provides a configuration to implmentations. Every implmentation 56 // can have its own config type. 57 func (m *PodMonitor) SetupConfig(_ registerer.Registerer, cfg interface{}) error { 58 59 defaultConfig := DefaultConfig() 60 61 if cfg == nil { 62 cfg = defaultConfig 63 } 64 65 kubernetesconfig, ok := cfg.(*Config) 66 if !ok { 67 return fmt.Errorf("Invalid configuration specified (type '%T')", cfg) 68 } 69 70 kubernetesconfig = SetupDefaultConfig(kubernetesconfig) 71 72 // build kubernetes config 73 var kubeCfg *rest.Config 74 if len(kubernetesconfig.Kubeconfig) > 0 { 75 var err error 76 kubeCfg, err = clientcmd.BuildConfigFromFlags("", kubernetesconfig.Kubeconfig) 77 if err != nil { 78 return err 79 } 80 } else { 81 var err error 82 kubeCfg, err = rest.InClusterConfig() 83 if err != nil { 84 return err 85 } 86 } 87 88 if kubernetesconfig.MetadataExtractor == nil { 89 return fmt.Errorf("missing metadata extractor") 90 } 91 92 if kubernetesconfig.NetclsProgrammer == nil { 93 return fmt.Errorf("missing net_cls programmer") 94 } 95 96 if kubernetesconfig.ResetNetcls == nil { 97 return fmt.Errorf("missing reset net_cls implementation") 98 } 99 if kubernetesconfig.SandboxExtractor == nil { 100 return fmt.Errorf("missing SandboxExtractor implementation") 101 } 102 if kubernetesconfig.Workers < 1 { 103 return fmt.Errorf("number of Kubernetes monitor workers must be at least 1") 104 } 105 // Setting up Kubernetes 106 m.kubeCfg = kubeCfg 107 m.localNode = kubernetesconfig.Nodename 108 m.enableHostPods = kubernetesconfig.EnableHostPods 109 m.metadataExtractor = kubernetesconfig.MetadataExtractor 110 m.netclsProgrammer = kubernetesconfig.NetclsProgrammer 111 m.pidsSetMaxProcsProgrammer = kubernetesconfig.PidsSetMaxProcsProgrammer 112 m.sandboxExtractor = kubernetesconfig.SandboxExtractor 113 m.resetNetcls = kubernetesconfig.ResetNetcls 114 m.workers = kubernetesconfig.Workers 115 116 return nil 117 } 118 119 // Run starts the monitor. 120 func (m *PodMonitor) Run(ctx context.Context) error { 121 if m.kubeCfg == nil { 122 return errors.New("pod: missing kubeconfig") 123 } 124 125 if err := m.handlers.IsComplete(); err != nil { 126 return fmt.Errorf("pod: handlers are not complete: %s", err.Error()) 127 } 128 129 // ensure to run the reset net_cls 130 // NOTE: we also call this during resync, however, that is not called at startup (we call ResyncWithAllPods instead before we return) 131 if m.resetNetcls == nil { 132 return errors.New("pod: missing net_cls reset implementation") 133 } 134 if err := m.resetNetcls(ctx); err != nil { 135 return fmt.Errorf("pod: failed to reset net_cls cgroups: %s", err.Error()) 136 } 137 138 // starts the manager in the background and will return once it is running 139 // NOTE: This will block until the Kubernetes manager and all controllers are up. All errors are being handled within the function 140 m.startManager(ctx) 141 142 // call ResyncWithAllPods before we return from here 143 // this will block until every pod at this point in time has been seeing at least one `Reconcile` call 144 // we do this so that we build up our internal PU cache in the policy engine, 145 // so that when we remove stale pods on startup, we don't remove them and create them again 146 if err := ResyncWithAllPods(ctx, m.kubeClient, m.resyncInfo, m.eventsCh, m.localNode); err != nil { 147 zap.L().Warn("Pod resync failed", zap.Error(err)) 148 } 149 return nil 150 } 151 152 // SetupHandlers sets up handlers for monitors to invoke for various events such as 153 // processing unit events and synchronization events. This will be called before Start() 154 // by the consumer of the monitor 155 func (m *PodMonitor) SetupHandlers(c *config.ProcessorConfig) { 156 m.handlers = c 157 } 158 159 // Resync requests to the monitor to do a resync. 160 func (m *PodMonitor) Resync(ctx context.Context) error { 161 if m.resetNetcls != nil { 162 if err := m.resetNetcls(ctx); err != nil { 163 return err 164 } 165 } 166 167 if m.kubeClient == nil { 168 return errors.New("pod: client has not been initialized yet") 169 } 170 171 return ResyncWithAllPods(ctx, m.kubeClient, m.resyncInfo, m.eventsCh, m.localNode) 172 } 173 174 const ( 175 startupWarningMessage = "pod: the Kubernetes controller did not start within the last 5s. Waiting..." 176 ) 177 178 var ( 179 retrySleep = time.Second * 3 180 warningMessageSleep = time.Second * 5 181 warningTimeout = time.Second * 5 182 managerNew = manager.New 183 ) 184 185 func (m *PodMonitor) startManager(ctx context.Context) { 186 var mgr manager.Manager 187 188 startTimestamp := time.Now() 189 controllerStarted := make(chan struct{}) 190 191 go func() { 192 // manager.New already contacts the Kubernetes API 193 for { 194 var err error 195 mgr, err = managerNew(m.kubeCfg, manager.Options{}) 196 if err != nil { 197 zap.L().Error("pod: new manager instantiation failed. Retrying in 3s...", zap.Error(err)) 198 time.Sleep(retrySleep) 199 continue 200 } 201 break 202 } 203 204 // Create the delete event controller first 205 dc := NewDeleteController(mgr.GetClient(), m.localNode, m.handlers, m.sandboxExtractor, m.eventsCh) 206 for { 207 if err := mgr.Add(dc); err != nil { 208 zap.L().Error("pod: adding delete controller failed. Retrying in 3s...", zap.Error(err)) 209 time.Sleep(retrySleep) 210 continue 211 } 212 break 213 } 214 215 // Create the main controller for the monitor 216 for { 217 if err := addController( 218 mgr, 219 newReconciler(mgr, m.handlers, m.metadataExtractor, m.netclsProgrammer, m.sandboxExtractor, m.localNode, m.enableHostPods, dc.GetDeleteCh(), dc.GetReconcileCh(), m.resyncInfo), 220 m.workers, 221 m.eventsCh, 222 ); err != nil { 223 zap.L().Error("pod: adding main monitor controller failed. Retrying in 3s...", zap.Error(err)) 224 time.Sleep(retrySleep) 225 continue 226 } 227 break 228 } 229 230 for { 231 if err := mgr.Add(&runnable{ch: controllerStarted}); err != nil { 232 zap.L().Error("pod: adding side controller failed. Retrying in 3s...", zap.Error(err)) 233 time.Sleep(retrySleep) 234 continue 235 } 236 break 237 } 238 239 // starting the manager is a bit awkward: 240 // - it does not use contexts 241 // - we pass in a fake signal handler channel 242 // - we start another go routine which waits for the context to be cancelled 243 // and closes that channel if that is the case 244 245 for { 246 if err := mgr.Start(ctx.Done()); err != nil { 247 zap.L().Error("pod: manager start failed. Retrying in 3s...", zap.Error(err)) 248 time.Sleep(retrySleep) 249 continue 250 } 251 break 252 } 253 }() 254 255 waitLoop: 256 for { 257 select { 258 case <-ctx.Done(): 259 break waitLoop 260 case <-time.After(warningMessageSleep): 261 // we give everything 5 seconds to report back before we issue a warning 262 zap.L().Warn(startupWarningMessage) 263 case <-controllerStarted: 264 m.kubeClient = mgr.GetClient() 265 t := time.Since(startTimestamp) 266 if t > warningTimeout { 267 zap.L().Warn("pod: controller startup finished, but took longer than expected", zap.Duration("duration", t)) 268 } else { 269 zap.L().Debug("pod: controller startup finished", zap.Duration("duration", t)) 270 } 271 break waitLoop 272 } 273 } 274 } 275 276 type runnable struct { 277 ch chan struct{} 278 } 279 280 func (r *runnable) Start(z <-chan struct{}) error { 281 // close the indicator channel which means that the manager has been started successfully 282 close(r.ch) 283 284 // stay up and running, the manager needs that 285 <-z 286 return nil 287 }