improvement(mcp): make CNI binary & data path configurable (#55)

This commit is contained in:
斬風千雪
2026-02-17 17:57:13 +08:00
committed by GitHub
parent 3aea635e44
commit 0bdc31311c
5 changed files with 46 additions and 43 deletions
+2
View File
@@ -28,6 +28,8 @@ image = "docker.io/library/memoh-mcp:dev"
snapshotter = "overlayfs" snapshotter = "overlayfs"
data_root = "data" data_root = "data"
data_mount = "/data" data_mount = "/data"
cni_bin_dir = "/opt/cni/bin"
cni_conf_dir = "/etc/cni/net.d"
## Postgres configuration ## Postgres configuration
[postgres] [postgres]
+13 -7
View File
@@ -15,6 +15,8 @@ const (
DefaultMCPImage = "docker.io/library/memoh-mcp:latest" DefaultMCPImage = "docker.io/library/memoh-mcp:latest"
DefaultDataRoot = "data" DefaultDataRoot = "data"
DefaultDataMount = "/data" DefaultDataMount = "/data"
DefaultCNIBinaryDir = "/opt/cni/bin"
DefaultCNIConfigDir = "/etc/cni/net.d"
DefaultJWTExpiresIn = "24h" DefaultJWTExpiresIn = "24h"
DefaultPGHost = "127.0.0.1" DefaultPGHost = "127.0.0.1"
DefaultPGPort = 5432 DefaultPGPort = 5432
@@ -63,10 +65,12 @@ type ContainerdConfig struct {
} }
type MCPConfig struct { type MCPConfig struct {
Image string `toml:"image"` Image string `toml:"image"`
Snapshotter string `toml:"snapshotter"` Snapshotter string `toml:"snapshotter"`
DataRoot string `toml:"data_root"` DataRoot string `toml:"data_root"`
DataMount string `toml:"data_mount"` DataMount string `toml:"data_mount"`
CNIBinaryDir string `toml:"cni_bin_dir"`
CNIConfigDir string `toml:"cni_conf_dir"`
} }
type PostgresConfig struct { type PostgresConfig struct {
@@ -124,9 +128,11 @@ func Load(path string) (Config, error) {
Namespace: DefaultNamespace, Namespace: DefaultNamespace,
}, },
MCP: MCPConfig{ MCP: MCPConfig{
Image: DefaultMCPImage, Image: DefaultMCPImage,
DataRoot: DefaultDataRoot, DataRoot: DefaultDataRoot,
DataMount: DefaultDataMount, DataMount: DefaultDataMount,
CNIBinaryDir: DefaultCNIBinaryDir,
CNIConfigDir: DefaultCNIConfigDir,
}, },
Postgres: PostgresConfig{ Postgres: PostgresConfig{
Host: DefaultPGHost, Host: DefaultPGHost,
+23 -28
View File
@@ -14,13 +14,8 @@ import (
gocni "github.com/containerd/go-cni" gocni "github.com/containerd/go-cni"
) )
const (
defaultCNIConfDir = "/etc/cni/net.d"
defaultCNIBinDir = "/opt/cni/bin"
)
// SetupNetwork attaches CNI networking to a running task. // SetupNetwork attaches CNI networking to a running task.
func SetupNetwork(ctx context.Context, task client.Task, containerID string) error { func SetupNetwork(ctx context.Context, task client.Task, containerID string, CNIBinDir string, CNIConfDir string) error {
if task == nil { if task == nil {
return ErrInvalidArgument return ErrInvalidArgument
} }
@@ -36,14 +31,14 @@ func SetupNetwork(ctx context.Context, task client.Task, containerID string) err
return fmt.Errorf("task pid not available for %s", containerID) return fmt.Errorf("task pid not available for %s", containerID)
} }
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
return setupNetworkWithCLI(ctx, containerID, pid) return setupNetworkWithCLI(ctx, containerID, pid, CNIBinDir, CNIConfDir)
} }
if _, err := os.Stat(defaultCNIConfDir); err != nil { if _, err := os.Stat(CNIConfDir); err != nil {
return fmt.Errorf("cni config dir missing: %s: %w", defaultCNIConfDir, err) return fmt.Errorf("cni config dir missing: %s: %w", CNIConfDir, err)
} }
if _, err := os.Stat(defaultCNIBinDir); err != nil { if _, err := os.Stat(CNIBinDir); err != nil {
return fmt.Errorf("cni bin dir missing: %s: %w", defaultCNIBinDir, err) return fmt.Errorf("cni bin dir missing: %s: %w", CNIBinDir, err)
} }
netnsPath := filepath.Join("/proc", fmt.Sprint(pid), "ns", "net") netnsPath := filepath.Join("/proc", fmt.Sprint(pid), "ns", "net")
if _, err := os.Stat(netnsPath); err != nil { if _, err := os.Stat(netnsPath); err != nil {
@@ -51,8 +46,8 @@ func SetupNetwork(ctx context.Context, task client.Task, containerID string) err
} }
cni, err := gocni.New( cni, err := gocni.New(
gocni.WithPluginDir([]string{defaultCNIBinDir}), gocni.WithPluginDir([]string{CNIBinDir}),
gocni.WithPluginConfDir(defaultCNIConfDir), gocni.WithPluginConfDir(CNIConfDir),
) )
if err != nil { if err != nil {
return err return err
@@ -74,7 +69,7 @@ func SetupNetwork(ctx context.Context, task client.Task, containerID string) err
return err return err
} }
func setupNetworkWithCLI(ctx context.Context, containerID string, pid uint32) error { func setupNetworkWithCLI(ctx context.Context, containerID string, pid uint32, CNIBinDir string, CNIConfDir string) error {
args := []string{ args := []string{
"shell", "shell",
"--tty=false", "--tty=false",
@@ -86,8 +81,8 @@ func setupNetworkWithCLI(ctx context.Context, containerID string, pid uint32) er
"cni-setup", "cni-setup",
"--id", containerID, "--id", containerID,
"--pid", fmt.Sprint(pid), "--pid", fmt.Sprint(pid),
"--conf-dir", defaultCNIConfDir, "--conf-dir", CNIConfDir,
"--bin-dir", defaultCNIBinDir, "--bin-dir", CNIBinDir,
} }
cmd := exec.CommandContext(ctx, "limactl", args...) cmd := exec.CommandContext(ctx, "limactl", args...)
var stderr bytes.Buffer var stderr bytes.Buffer
@@ -101,7 +96,7 @@ func setupNetworkWithCLI(ctx context.Context, containerID string, pid uint32) er
} else if !isDuplicateAllocationError(err) { } else if !isDuplicateAllocationError(err) {
return err return err
} }
if rmErr := removeNetworkWithCLI(ctx, containerID, pid); rmErr != nil { if rmErr := removeNetworkWithCLI(ctx, containerID, pid, CNIBinDir, CNIConfDir); rmErr != nil {
return rmErr return rmErr
} }
cmd = exec.CommandContext(ctx, "limactl", args...) cmd = exec.CommandContext(ctx, "limactl", args...)
@@ -118,7 +113,7 @@ func setupNetworkWithCLI(ctx context.Context, containerID string, pid uint32) er
} }
// RemoveNetwork detaches CNI networking for a running task. // RemoveNetwork detaches CNI networking for a running task.
func RemoveNetwork(ctx context.Context, task client.Task, containerID string) error { func RemoveNetwork(ctx context.Context, task client.Task, containerID string, CNIBinDir string, CNIConfDir string) error {
if task == nil { if task == nil {
return ErrInvalidArgument return ErrInvalidArgument
} }
@@ -134,14 +129,14 @@ func RemoveNetwork(ctx context.Context, task client.Task, containerID string) er
return fmt.Errorf("task pid not available for %s", containerID) return fmt.Errorf("task pid not available for %s", containerID)
} }
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
return removeNetworkWithCLI(ctx, containerID, pid) return removeNetworkWithCLI(ctx, containerID, pid, CNIBinDir, CNIConfDir)
} }
if _, err := os.Stat(defaultCNIConfDir); err != nil { if _, err := os.Stat(CNIConfDir); err != nil {
return fmt.Errorf("cni config dir missing: %s: %w", defaultCNIConfDir, err) return fmt.Errorf("cni config dir missing: %s: %w", CNIConfDir, err)
} }
if _, err := os.Stat(defaultCNIBinDir); err != nil { if _, err := os.Stat(CNIBinDir); err != nil {
return fmt.Errorf("cni bin dir missing: %s: %w", defaultCNIBinDir, err) return fmt.Errorf("cni bin dir missing: %s: %w", CNIBinDir, err)
} }
netnsPath := filepath.Join("/proc", fmt.Sprint(pid), "ns", "net") netnsPath := filepath.Join("/proc", fmt.Sprint(pid), "ns", "net")
@@ -150,8 +145,8 @@ func RemoveNetwork(ctx context.Context, task client.Task, containerID string) er
} }
cni, err := gocni.New( cni, err := gocni.New(
gocni.WithPluginDir([]string{defaultCNIBinDir}), gocni.WithPluginDir([]string{CNIBinDir}),
gocni.WithPluginConfDir(defaultCNIConfDir), gocni.WithPluginConfDir(CNIConfDir),
) )
if err != nil { if err != nil {
return err return err
@@ -162,7 +157,7 @@ func RemoveNetwork(ctx context.Context, task client.Task, containerID string) er
return cni.Remove(ctx, containerID, netnsPath) return cni.Remove(ctx, containerID, netnsPath)
} }
func removeNetworkWithCLI(ctx context.Context, containerID string, pid uint32) error { func removeNetworkWithCLI(ctx context.Context, containerID string, pid uint32, CNIBinDir string, CNIConfDir string) error {
args := []string{ args := []string{
"shell", "shell",
"--tty=false", "--tty=false",
@@ -174,8 +169,8 @@ func removeNetworkWithCLI(ctx context.Context, containerID string, pid uint32) e
"cni-remove", "cni-remove",
"--id", containerID, "--id", containerID,
"--pid", fmt.Sprint(pid), "--pid", fmt.Sprint(pid),
"--conf-dir", defaultCNIConfDir, "--conf-dir", CNIConfDir,
"--bin-dir", defaultCNIBinDir, "--bin-dir", CNIBinDir,
} }
cmd := exec.CommandContext(ctx, "limactl", args...) cmd := exec.CommandContext(ctx, "limactl", args...)
var stderr bytes.Buffer var stderr bytes.Buffer
+6 -6
View File
@@ -245,7 +245,7 @@ func (h *ContainerdHandler) CreateContainer(c echo.Context) error {
UseStdio: false, UseStdio: false,
}); err == nil { }); err == nil {
started = true started = true
if netErr := ctr.SetupNetwork(ctx, task, containerID); netErr != nil { if netErr := ctr.SetupNetwork(ctx, task, containerID, h.cfg.CNIBinaryDir, h.cfg.CNIConfigDir); netErr != nil {
h.logger.Warn("mcp container network setup failed, task kept running", h.logger.Warn("mcp container network setup failed, task kept running",
slog.String("container_id", containerID), slog.String("container_id", containerID),
slog.Any("error", netErr), slog.Any("error", netErr),
@@ -304,7 +304,7 @@ func (h *ContainerdHandler) ensureContainerAndTask(ctx context.Context, containe
// Task is running but CNI state may be stale (e.g. server container restarted). // Task is running but CNI state may be stale (e.g. server container restarted).
// Re-apply network to ensure connectivity. // Re-apply network to ensure connectivity.
if task, taskErr := h.service.GetTask(ctx, containerID); taskErr == nil { if task, taskErr := h.service.GetTask(ctx, containerID); taskErr == nil {
if netErr := ctr.SetupNetwork(ctx, task, containerID); netErr != nil { if netErr := ctr.SetupNetwork(ctx, task, containerID, h.cfg.CNIBinaryDir, h.cfg.CNIConfigDir); netErr != nil {
h.logger.Warn("network re-setup failed for running task", h.logger.Warn("network re-setup failed for running task",
slog.String("container_id", containerID), slog.Any("error", netErr)) slog.String("container_id", containerID), slog.Any("error", netErr))
} }
@@ -322,7 +322,7 @@ func (h *ContainerdHandler) ensureContainerAndTask(ctx context.Context, containe
if err != nil { if err != nil {
return err return err
} }
if netErr := ctr.SetupNetwork(ctx, task, containerID); netErr != nil { if netErr := ctr.SetupNetwork(ctx, task, containerID, h.cfg.CNIBinaryDir, h.cfg.CNIConfigDir); netErr != nil {
h.logger.Warn("network setup failed, task kept running", h.logger.Warn("network setup failed, task kept running",
slog.String("container_id", containerID), slog.Any("error", netErr)) slog.String("container_id", containerID), slog.Any("error", netErr))
} }
@@ -781,7 +781,7 @@ func (h *ContainerdHandler) SetupBotContainer(ctx context.Context, botID string)
if task, err := h.service.StartTask(ctx, containerID, &ctr.StartTaskOptions{ if task, err := h.service.StartTask(ctx, containerID, &ctr.StartTaskOptions{
UseStdio: false, UseStdio: false,
}); err == nil { }); err == nil {
if netErr := ctr.SetupNetwork(ctx, task, containerID); netErr != nil { if netErr := ctr.SetupNetwork(ctx, task, containerID, h.cfg.CNIBinaryDir, h.cfg.CNIConfigDir); netErr != nil {
h.logger.Warn("setup bot container: network setup failed, task kept running", h.logger.Warn("setup bot container: network setup failed, task kept running",
slog.String("bot_id", botID), slog.String("bot_id", botID),
slog.String("container_id", containerID), slog.String("container_id", containerID),
@@ -833,7 +833,7 @@ func (h *ContainerdHandler) CleanupBotContainer(ctx context.Context, botID strin
if task, taskErr := h.service.GetTask(ctx, containerID); taskErr == nil { if task, taskErr := h.service.GetTask(ctx, containerID); taskErr == nil {
h.logger.Info("CleanupBotContainer: removing network", slog.String("container_id", containerID)) h.logger.Info("CleanupBotContainer: removing network", slog.String("container_id", containerID))
if err := ctr.RemoveNetwork(ctx, task, containerID); err != nil { if err := ctr.RemoveNetwork(ctx, task, containerID, h.cfg.CNIBinaryDir, h.cfg.CNIConfigDir); err != nil {
h.logger.Warn("cleanup: remove network failed", slog.String("container_id", containerID), slog.Any("error", err)) h.logger.Warn("cleanup: remove network failed", slog.String("container_id", containerID), slog.Any("error", err))
} }
} }
@@ -940,7 +940,7 @@ func (h *ContainerdHandler) ReconcileContainers(ctx context.Context) {
// veth endpoints and iptables masquerade rules while the MCP task keeps // veth endpoints and iptables masquerade rules while the MCP task keeps
// running inside containerd. // running inside containerd.
if task, taskErr := h.service.GetTask(ctx, containerID); taskErr == nil { if task, taskErr := h.service.GetTask(ctx, containerID); taskErr == nil {
if netErr := ctr.SetupNetwork(ctx, task, containerID); netErr != nil { if netErr := ctr.SetupNetwork(ctx, task, containerID, h.cfg.CNIBinaryDir, h.cfg.CNIConfigDir); netErr != nil {
h.logger.Warn("reconcile: network re-setup failed for running task", h.logger.Warn("reconcile: network re-setup failed for running task",
slog.String("bot_id", botID), slog.String("bot_id", botID),
slog.String("container_id", containerID), slog.String("container_id", containerID),
+2 -2
View File
@@ -174,7 +174,7 @@ func (m *Manager) Start(ctx context.Context, botID string) error {
if err != nil { if err != nil {
return err return err
} }
if err := ctr.SetupNetwork(ctx, task, m.containerID(botID)); err != nil { if err := ctr.SetupNetwork(ctx, task, m.containerID(botID), m.cfg.CNIBinaryDir, m.cfg.CNIConfigDir); err != nil {
if stopErr := m.service.StopTask(ctx, m.containerID(botID), &ctr.StopTaskOptions{Force: true}); stopErr != nil { if stopErr := m.service.StopTask(ctx, m.containerID(botID), &ctr.StopTaskOptions{Force: true}); stopErr != nil {
m.logger.Warn("cleanup: stop task failed", slog.String("container_id", m.containerID(botID)), slog.Any("error", stopErr)) m.logger.Warn("cleanup: stop task failed", slog.String("container_id", m.containerID(botID)), slog.Any("error", stopErr))
} }
@@ -199,7 +199,7 @@ func (m *Manager) Delete(ctx context.Context, botID string) error {
} }
if task, taskErr := m.service.GetTask(ctx, m.containerID(botID)); taskErr == nil { if task, taskErr := m.service.GetTask(ctx, m.containerID(botID)); taskErr == nil {
if err := ctr.RemoveNetwork(ctx, task, m.containerID(botID)); err != nil { if err := ctr.RemoveNetwork(ctx, task, m.containerID(botID), m.cfg.CNIBinaryDir, m.cfg.CNIConfigDir); err != nil {
m.logger.Warn("cleanup: remove network failed", slog.String("container_id", m.containerID(botID)), slog.Any("error", err)) m.logger.Warn("cleanup: remove network failed", slog.String("container_id", m.containerID(botID)), slog.Any("error", err))
} }
} }