mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
improvement(mcp): make CNI binary & data path configurable (#55)
This commit is contained in:
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user