sandbox: make event monitor in CRI independent

Signed-off-by: Abel Feng <fshb1988@gmail.com>
This commit is contained in:
Abel Feng
2023-10-23 17:16:09 +08:00
parent 9a2b85561a
commit d0da3d1caf
12 changed files with 486 additions and 409 deletions

View File

@@ -31,6 +31,7 @@ import (
"github.com/containerd/containerd/v2/core/sandbox"
criconfig "github.com/containerd/containerd/v2/internal/cri/config"
"github.com/containerd/containerd/v2/internal/cri/constants"
"github.com/containerd/containerd/v2/internal/cri/server/events"
"github.com/containerd/containerd/v2/internal/cri/server/podsandbox/types"
imagestore "github.com/containerd/containerd/v2/internal/cri/store/image"
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
@@ -85,18 +86,19 @@ func init() {
imageService: criImagePlugin.(ImageService),
store: NewStore(),
}
eventMonitor := events.NewEventMonitor(&podSandboxEventHandler{
controller: &c,
})
eventMonitor.Subscribe(client, []string{`topic="/tasks/exit"`})
eventMonitor.Start()
c.eventMonitor = eventMonitor
return &c, nil
},
})
}
// CRIService interface contains things required by controller, but not yet refactored from criService.
// TODO: this will be removed in subsequent iterations.
type CRIService interface {
// TODO: we should implement Event backoff in Controller.
BackOffEvent(id string, event interface{})
}
// RuntimeService specifies dependencies to CRI runtime service.
type RuntimeService interface {
Config() criconfig.Config
@@ -123,18 +125,13 @@ type Controller struct {
imageService ImageService
// os is an interface for all required os operations.
os osinterface.OS
// cri is CRI service that provides missing gaps needed by controller.
cri CRIService
// eventMonitor is the event monitor for podsandbox controller to handle sandbox task exit event
// actually we only use it's backoff mechanism to make sure pause container is cleaned up.
eventMonitor *events.EventMonitor
store *Store
}
func (c *Controller) Init(
cri CRIService,
) {
c.cri = cri
}
var _ sandbox.Controller = (*Controller)(nil)
func (c *Controller) Platform(_ctx context.Context, _sandboxID string) (platforms.Platform, error) {
@@ -172,11 +169,7 @@ func (c *Controller) waitSandboxExit(ctx context.Context, p *types.PodSandbox, e
defer dcancel()
event := &eventtypes.TaskExit{ExitStatus: exitStatus, ExitedAt: protobuf.ToTimestamp(exitedAt)}
if err := handleSandboxTaskExit(dctx, p, event); err != nil {
// TODO will backoff the event to the controller's own EventMonitor, but not cri's,
// because we should call handleSandboxTaskExit again the next time
// eventMonitor handle this event. but now it goes into cri's EventMonitor,
// the handleSandboxTaskExit will not be called anymore
c.cri.BackOffEvent(p.ID, e)
c.eventMonitor.Backoff(p.ID, event)
}
return nil
case <-ctx.Done():

View File

@@ -0,0 +1,61 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podsandbox
import (
"context"
"fmt"
"time"
"github.com/containerd/log"
eventtypes "github.com/containerd/containerd/v2/api/events"
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
)
const (
// handleEventTimeout is the timeout for handling 1 event. Event monitor
// handles events in serial, if one event blocks the event monitor, no
// other events can be handled.
// Add a timeout for each event handling, events that timeout will be requeued and
// handled again in the future.
handleEventTimeout = 10 * time.Second
)
type podSandboxEventHandler struct {
controller *Controller
}
func (p *podSandboxEventHandler) HandleEvent(any interface{}) error {
switch e := any.(type) {
case *eventtypes.TaskExit:
log.L.Infof("TaskExit event in podsandbox handler %+v", e)
// Use ID instead of ContainerID to rule out TaskExit event for exec.
sb := p.controller.store.Get(e.ID)
if sb == nil {
return nil
}
ctx := ctrdutil.NamespacedContext()
ctx, cancel := context.WithTimeout(ctx, handleEventTimeout)
defer cancel()
if err := handleSandboxTaskExit(ctx, sb, e); err != nil {
return fmt.Errorf("failed to handle container TaskExit event: %w", err)
}
return nil
}
return nil
}

View File

@@ -21,7 +21,6 @@ import (
"fmt"
"path"
"path/filepath"
"time"
"github.com/containerd/log"
"github.com/containerd/typeurl/v2"
@@ -54,10 +53,6 @@ const (
unknownExitCode = 255
)
const (
handleEventTimeout = 10 * time.Second
)
// getSandboxRootDir returns the root directory for managing sandbox files,
// e.g. hosts files.
func (c *Controller) getSandboxRootDir(id string) string {

View File

@@ -95,8 +95,8 @@ func (c *Controller) stopSandboxContainer(ctx context.Context, podSandbox *types
go func() {
defer close(stopCh)
err := c.waitSandboxExit(exitCtx, podSandbox, exitCh)
if err != nil {
log.G(ctx).WithError(err).Errorf("Failed to wait pod sandbox exit %+v", err)
if err != nil && err != context.Canceled && err != context.DeadlineExceeded {
log.G(ctx).WithError(err).Errorf("Failed to wait sandbox exit %+v", err)
}
}()
defer func() {