go如何编写一个类似docker的linux的虚拟容器?
网友回复
p-run-hostnet.go (完整代码)
package main
import (
"archive/tar"
"compress/gzip"
"flag"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"syscall"
"github.comcom/google/uuid"
)
// --- 配置和常量 ---
const (
alpineURL = "https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/alpine-minirootfs-3.18.3-x86_64.tar.gz"
alpineTarName = "alpine-minirootfs-3.18.3-x86_64.tar.gz"
)
// 定义终端颜色
const (
ColorRed = "\033[0;31m"
ColorGreen = "\033[0;32m"
ColorBlue = "\033[0;34m"
ColorNone = "\033[0m"
)
// --- 辅助函数 (日志) ---
// Info 打印蓝色信息日志
func Info(format string, a ...interface{}) {
fmt.Printf("%sINFO:%s %s\n", ColorBlue, ColorNone, fmt.Sprintf(format, a...))
}
// Error 打印红色错误日志并退出程序
func Error(format string, a ...interface{}) {
fmt.Fprintf(os.Stderr, "%sERROR:%s %s\n", ColorRed, ColorNone, fmt.Sprintf(format, a...))
os.Exit(1)
}
// --- 容器核心逻辑 ---
// prepareBaseImage 检查基础镜像是否存在,如果不存在则下载并解压
func prepareBaseImage(baseDir, imagesDir, imageName string) error {
imagePath := filepath.Join(imagesDir, imageName)
if _, err := os.Stat(filepath.Join(imagePath, "bin")); err == nil {
return nil // 镜像已存在
}
Info("未找到镜像 '%s',正在准备...", imageName)
if err := os.MkdirAll(imagePath, 0755); err != nil {
return fmt.Errorf("创建镜像目录失败: %w", err)
}
tarPath := filepath.Join(baseDir, alpineTarName)
if _, err := os.Stat(tarPath); os.IsNotExist(err) {
Info("正在下载 Alpine Mini RootFS...")
if err := downloadFile(tarPath, alpineURL); err != nil {
return fmt.Errorf("下载 Alpine 失败: %w", err)
}
}
Info("正在解压 rootfs 到 %s...", imagePath)
if err := untar(tarPath, imagePath); err != nil {
return fmt.Errorf("解压 rootfs 失败: %w", err)
}
return nil
}
// createContainerInstance 创建一个新的容器文件系统实例
func createContainerInstance(imagesDir, containersDir, imageName string) (string, string, error) {
containerID := uuid.New().String()[:8]
containerRootfs := filepath.Join(containersDir, containerID)
Info("正在创建容器实例: %s", containerID)
if err := os.MkdirAll(containerRootfs, 0755); err != nil {
return "", "", fmt.Errorf("创建容器根目录失败: %w", err)
}
imagePath := filepath.Join(imagesDir, imageName)
// 使用 cp -a 命令来保留所有文件属性,比 Go 原生实现更简单可靠
cmd := exec.Command("cp", "-a", filepath.Join(imagePath, "/."), containerRootfs)
if err := cmd.Run(); err != nil {
return "", "", fmt.Errorf("复制镜像文件失败: %w", err)
}
return containerID, containerRootfs, nil
}
// cleanupContainer 在程序退出时清理容器资源
func cleanupContainer(containerRootfs string, autoRemove bool) {
if !autoRemove {
return
}
Info("正在移除容器文件系统: %s", containerRootfs)
// 递归卸载挂载点
exec.Command("umount", "-R", containerRootfs).Run()
if err := os.RemoveAll(containerRootfs); err != nil {
fmt.Fprintf(os.Stderr, "警告: 移除 %s 失败: %v\n", containerRootfs, err)
}
}
// buildMountCommands 为数据卷构建挂载命令字符串
func buildMountCommands(volumes []string, containerRootfs string) (string, error) {
var mountCommands []string
for _, vol := range volumes {
parts := strings.SplitN(vol, ":", 2)
if len(parts) != 2 {
return "", fmt.Errorf("无效的卷格式: '%s',请使用 'host:container'", vol)
}
hostPath, containerPath := parts[0], parts[1]
absHostPath, err := filepath.Abs(hostPath)
if err != nil {
return "", fmt.Errorf("获取绝对路径失败 '%s': %w", hostPath, err)
}
if _, err := os.Stat(absHostPath); os.IsNotExist(err) {
return "", fmt.Errorf("主机路径不...点击查看剩余70%


