前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang+shell快速实现容器运行时

golang+shell快速实现容器运行时

作者头像
有点技术
发布2020-07-14 14:54:59
6860
发布2020-07-14 14:54:59
举报
文章被收录于专栏:有点技术

手动挂载镜像

  • 导出镜像为tar包
代码语言:javascript
复制
docker save -o busybox.tar busybox
  • 解压镜像
代码语言:javascript
复制
tar xf busybox.tar
  • 查看文件
代码语言:javascript
复制
ls020584afccce44678ec82676db80f68d50ea5c766b6e9d9601f7b5fc86dfb96d.json  busybox.tar2ec9d8fd000cf6929df9aa7a58b6d872f5588e097d18d91cadc2a08dd563d590       manifest.json79cf3ae80ddd8a3f5e5f09849fa9b8b35ade13b5c9571b534d3651c6d50668e2       repositoriesb534869c81f05ce6fbbdd3a3293e64fd032e059ab4b28a0e0d5b485cf904be4b.json
  • 解压layer
代码语言:javascript
复制
mkdir layer1 layer2tar xf 2ec9d8fd000cf6929df9aa7a58b6d872f5588e097d18d91cadc2a08dd563d590/layer.tar -C layer1tar xf 79cf3ae80ddd8a3f5e5f09849fa9b8b35ade13b5c9571b534d3651c6d50668e2/layer.tar -C layer2
  • 创建文件系统

选用overlay fs

代码语言:javascript
复制
mkdir ./rootfs/{merged,diff,work} -p
mount -t overlay overlay -o lowerdir=./layer1:./layer2,upperdir=./rootfs/diff,workdir=./rootfs/work ./rootfs/merged
overlayfs有以下概念:merged: 挂载点diff 是upperwork 是work

基于golang创建命名空间挂载rootfs

代码语言:javascript
复制
package main
import (    "flag"    "fmt"    "os"    "os/exec"    "syscall"    "path/filepath"
    "github.com/docker/docker/pkg/reexec")
func init() {    // 使用reexec注册函数,reexec提供了执行自身的方法,从而对默认的初始命名空间信息进行修改    reexec.Register("docker-shim", nsInitialisation)    if reexec.Init() {        os.Exit(0)    }}
func nsInitialisation() {    newrootPath := os.Args[1]
    // 挂载proc    if err := mountProc(newrootPath); err != nil {        fmt.Printf("Error mounting /proc - %s\n", err)        os.Exit(1)    }
    // 执行pivot_root    if err := pivotRoot(newrootPath); err != nil {        fmt.Printf("Error running pivot_root - %s\n", err)        os.Exit(1)    }
    // 配置hostname    if err := syscall.Sethostname([]byte("my-docker")); err != nil {        fmt.Printf("Error setting hostname - %s\n", err)        os.Exit(1)    }    nsRun()}
func nsRun() {    cmd := exec.Command("/bin/sh")
    cmd.Stdin = os.Stdin    cmd.Stdout = os.Stdout    cmd.Stderr = os.Stderr
    if err := cmd.Run(); err != nil {        fmt.Printf("Error running the /bin/sh command - %s\n", err)        os.Exit(1)    }}
func main() {    var rootfsPath string    flag.StringVar(&rootfsPath, "rootfs", "./rootfs/merged", "Path to the root filesystem to use")    flag.Parse()
    cmd := reexec.Command("docker-shim", rootfsPath)
    cmd.Stdin = os.Stdin    cmd.Stdout = os.Stdout    cmd.Stderr = os.Stderr
    // 创建命名空间    cmd.SysProcAttr = &syscall.SysProcAttr{        Cloneflags: syscall.CLONE_NEWNS |            syscall.CLONE_NEWUTS |            syscall.CLONE_NEWIPC |            syscall.CLONE_NEWPID |            syscall.CLONE_NEWNET |            syscall.CLONE_NEWUSER,        // UID映射        UidMappings: []syscall.SysProcIDMap{            {                ContainerID: s.Getuid(),0,                HostID:      o                Size:        1,            },        },        // GID映射        GidMappings: []syscall.SysProcIDMap{            {                ContainerID: 0,                HostID:      os.Getgid(),                Size:        1,            },        },    }
    if err := cmd.Run(); err != nil {        fmt.Printf("Error running the reexec.Command - %s\n", err)        os.Exit(1)    }}
// 相当于 pivot_root . .pivot_rootfunc pivotRoot(newroot string) error {    putold := filepath.Join(newroot, "/.pivot_root")
    // 挂载newroot到自身,目的是为了确保newroot和putold处于不同目录,    // MS_BIND:执行bind挂载,使文件或者子目录树在文件系统内的另一个点上可视。    // MS_REC:创建递归绑定挂载,递归更改传播类型    if err := syscall.Mount(newroot, newroot, "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {        return err    }
    // 创建put_old目录    if err := os.MkdirAll(putold, 0700); err != nil {        return err    }
    // 调用 pivot_root    if err := syscall.PivotRoot(newroot, putold); err != nil {        return err    }
    // 切换到根目录,不然的话为pivot_root后的当前目录    if err := os.Chdir("/"); err != nil {        return err    }
    // 卸载pivot_root    putold = "/.pivot_root"    if err := syscall.Unmount(putold, syscall.MNT_DETACH); err != nil {        return err    }
    // 删除put_old目录    if err := os.RemoveAll(putold); err != nil {        return err    }
    return nil}
// 挂载func mountProc(newroot string) error {    source := "proc"    target := filepath.Join(newroot, "/proc")    fstype := "proc"    flags := 0    data := ""
    // 创建Proc目录    os.MkdirAll(target, 0755)    // 挂载proc    // mount -t proc proc ./proc    if err := syscall.Mount(source, target, fstype, uintptr(flags), data); err != nil {        return err    }
    return nil}
代码语言:javascript
复制
go build main.go -o docker./docker

创建网络

为进程命名空间添加虚拟设备

代码语言:javascript
复制
# 查找IDps -ef | grep docker-shimroot     32133 32128  0 13:07 pts/1    00:00:00 docker-shim ./rootfs/mergedroot     32149 14085  0 13:08 pts/0    00:00:00 grep --color=auto docker-shim
# 查看PID命名空间nsenter -t 32133 -n ip link showip link add veth0 type veth peer name veth1ip link set veth0 netns 32133
查看命名空间内网卡信息nsenter -t 32133 -n ip link show

在宿主机上执行为veth1配置IP

代码语言:javascript
复制
ip addr add 100.100.1.1/24 dev veth1ip link set veth1 up

在docker内执行为容器配置IP,测试网络

代码语言:javascript
复制
ip link set veth0 name  eth0ip addr add 100.100.1.2/24 dev eth0ip link set eth0 upip link set lo upip route add default via 100.100.1.1ping 100.100.1.2

为容器配置NAT规则

代码语言:javascript
复制
iptables -t nat -A POSTROUTING -s 100.100.1.0/255.255.255.0 -o eth0 -j MASQUERADEiptables -A FORWARD -i eth0 -o veth1 -j ACCEPTiptables -A FORWARD -o eth0 -i veth1 -j ACCEPT

验证

代码语言:javascript
复制
echo "nameserver 8.8.8.8" > /etc/resolv.confping baidu.com

此时容器已经可以正常上网了

总结

本文通过golang创建命名空间,挂载文件系统,通过pivot_root切换文件系统root,通过linux命令创建虚拟设备对并配置网络,通过iptables实现网络的NAT,从而快速实现一个简单的容器。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 有点技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 手动挂载镜像
  • 基于golang创建命名空间挂载rootfs
  • 创建网络
  • 为容器配置NAT规则
  • 验证
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档