自己动手写 Docker 系列 -- 6.5 启动时给容器配置网络

本文档介绍了如何自己动手在 Docker 启动时配置网络,包括新增命令选项以指定网桥和端口映射,详细阐述了启动时配置网络的步骤:创建网桥、配置宿主机 veth、端口映射路由以及容器内 veth 的设置。通过代码实现和测试运行,展示了网络配置的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

在上几篇文章中,我们使用自己程序创建的网桥和手动配置,让容器的网络功能正常,本篇就利用之前的知识,自动配置容器网络

源码说明

同时放到了 Gitee 和 Github 上,都可进行获取

本章节对应的版本标签是:6.3,防止后面代码过多,不好查看,可切换到标签版本进行查看

代码实现

书中原来的实现基本上是顺序是合理的,但在博主机器上在网络启动部分有些问题,做一些调整,在后面的代码部分会讲到

整体的思路是根据我们上两篇文章:

整体的思路如下:

  • 使用 network 命令创建网桥
  • 程序启动的时候,制定网桥
  • 设置宿主机段的 veth:挂载到网桥,设置启动
  • 设置端口映射,如果有-p 选项
  • 设置容器内的 veth:先进入容器,设置 lo 和 veth,分配 IP,设置启动

在书中是先设置容器内的 veth,在进入容器网络命名空间,在博主机器上有点问题,就做了调整

新增命令选项

在 run 命令中增加-net 和-p 选项,用于制定网桥和端口映射

var RunCommand = cli.Command{
   
   
	Name:  "run",
	Usage: `Create a container with namespace and cgroups limit mydocker run -ti [command]`,
	Flags: []cli.Flag{
   
   
		......
		cli.StringFlag{
   
   
			Name:  "net",
			Usage: "container network",
		},
		cli.StringSliceFlag{
   
   
			Name:  "p",
			Usage: "port mapping",
		},
	},

	Action: func(context *cli.Context) error {
   
   
		......
		network := context.String("net")
		portMapping := context.StringSlice("p")
		run.Run(tty, detach, cmdArray, resConfig, volume, containerName, envSlice, network, portMapping)
		return nil
	},
}

启动是配置网络

在 Run 函数中,如果有 net 选项,就为容器配置网络

func Run(tty, detach bool, cmdArray []string, config *subsystem.ResourceConfig, volume, containerName string,
	......
	if nw != "" {
   
   
		// 定义网络
		_ = network.Init()
		containerInfo := &container.ContainerInfo{
   
   
			ID:          id,
			Pid:         strconv.Itoa(parent.Process.Pid),
			Name:        containerName,
			PortMapping: portMapping,
		}
		if err := network.Connect(nw, containerInfo); err != nil {
   
   
			log.Errorf("connnect network err: %v", err)
			return
		}
	}
	......
}

配置网络就分为三部分了:配置宿主机 veth、配置宿主机端口映射路由、配置容器 veth

func Connect(networkName string, cinfo *container.ContainerInfo) error {
   
   
	network, ok := networks[networkName]
	if !ok {
   
   
		return fmt.Errorf("no such network: %s", networkName)
	}

	// 分配容器IP地址
	_, ipNet, _ := net.ParseCIDR(network.Subnet)
	ip, err := ipAllocator.Allocate(ipNet)
	if err != nil {
   
   
		return err
	}
	log.Infof("分配的容器ip地址为: %s", ip)

	// 创建网络端点
	ep := &Endpoint{
   
   
		ID:          fmt.Sprintf("%s-%s", cinfo.ID, networkName),
		IpAddress:   ip,
		Network:     network,
		PortMapping: cinfo.PortMapping,
	}

	// 调用网络驱动挂载和配置网络端点
	if err = drivers[network.Driver].Connect(network, ep); err != nil {
   
   
		log.Errorf("connet err: %v", err)
		return err
	}

	// 配置端口映射
	if err = configPortMapping(ep, cinfo); err != nil {
   
   
		log.Errorf("config port mapping: %v", err)
		return err
	}

	// 到容器的namespace配置容器的网络设备IP地址
	if err = configEndpointIpAddressAndRoute(ep, cinfo); err != nil {
   
   
		log.Errorf("configEndpointIpAddressAndRoute err: %v", err)
		return err
	}

	return nil
}

配置宿主机 veth

这部分是调用原来的驱动的 Connect 函数,这里就不再重复说明了

配置宿主机端口映射路由

设置端口映射就是我们上两篇文字中的配置 iptables 的 DNAT 规则,这里实现也是直接使用的命令

// 配置端口映射
func configPortMapping(ep *Endpoint, cinfo *container.ContainerInfo) error {
   
   
	log.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值