手写实现cni插件

k8s v1.19.0

mycni配置文件

cat >> /etc/cni/net.d/mycni.json << EOF
{
  "cniVersion": "0.1.0",
  "name": "mycni",
  "type": "mycni"
}
EOF

type对应/opt/cni/bin目录下二进制文件。

mycni代码并编译

mkdir /run/netns
# go.mod
module mycni

go 1.19

require (
	github.com/containernetworking/cni v1.0.1
	github.com/containernetworking/plugins v1.1.1
	k8s.io/klog/v2 v2.100.1
)

require (
	github.com/fsnotify/fsnotify v1.5.1 // indirect
	github.com/go-logr/logr v1.2.0 // indirect
	github.com/onsi/ginkgo v1.16.5 // indirect
	github.com/onsi/gomega v1.17.0 // indirect
	golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
	golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect
	golang.org/x/text v0.3.7 // indirect
)

# main.go
package main

import (
	"flag"
	"fmt"
	"os/exec"
    "errors"

	"github.com/containernetworking/cni/pkg/skel"
	"github.com/containernetworking/cni/pkg/version"
	"github.com/containernetworking/plugins/pkg/utils/buildversion"
	"k8s.io/klog/v2"
)

func cmdAdd(args *skel.CmdArgs) error {
	klog.InitFlags(nil)
	flag.Set("logtostderr", "false")
	flag.Set("log_file", "/var/log/mycni.log")
	flag.Parse()
	defer klog.Flush()

	klog.Infof("cmd add args is %v", *args)

	cmds := []string{
		"ip link add veth00 type veth peer name veth01",
		"ip link set dev veth01 up",
        // /run/netns/ns1软链接指向/proc/进程号/ns/net
		fmt.Sprintf("ln -s %s /run/netns/ns1", args.Netns),
		"ip link set veth00 netns ns1",
		"ip netns exec ns1 ip link set veth00 name eth0",
		"ip netns exec ns1 ip addr add 10.16.0.10/24 dev eth0",
		"ip netns exec ns1 ip link set dev eth0 up",
		"ip netns exec ns1 ip route add default dev eth0",
	}
	for i := 0; i < 8; i++ {
		if out, err := exec.Command("bash", "-c", cmds[i]).CombinedOutput(); err != nil {
			klog.Errorf("exec %s failed, out is %s", cmds[i], string(out))
			return errors.New(string(out))
		}
	}

	fmt.Println("{}")
	klog.Infof("cmd add success")
	return nil
}

func cmdDel(args *skel.CmdArgs) error {
	klog.InitFlags(nil)
	flag.Set("logtostderr", "false")
	flag.Set("log_file", "/var/log/mycni.log")
	flag.Parse()
	defer klog.Flush()

	cmd := "ip link delete veth01 type veth"
	exec.Command("bash", "-c", cmd).CombinedOutput()
	cmd = "rm -f /run/netns/ns1"
	exec.Command("bash", "-c", cmd).CombinedOutput()

	klog.Infof("cmd del end")
	return nil
}

// 确认cni是否配置完成
func cmdCheck(args *skel.CmdArgs) error {
	return nil
}

func main() {
	skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, buildversion.BuildString("mycni"))
}

ip route add 10.16.0.10/32 dev veth01

增加路由后容器网卡可以ping通同节点主机IP

问题:unexpected end of JSON input

CNI规定ADD操作结果以JSON格式打印到标准输出Stdout,否则ADD失败。

如果cni二进制执行后没有输出结果到Stdout,那么json.Unmarshal长度是0的[]byte时报错unexpected end of JSON input。

解决方法是,只在cni add成功时fmt.Println("{}")。

问题:cni假成功

kubelet不通过cni请求来获取pod ip,而是自己查询容器网卡ip。
如果cni成功后没有给容器网卡设置ip,那么kubelet会重新发起cni请求。

posted on 2024-02-08 22:06  王景迁  阅读(41)  评论(0编辑  收藏  举报

导航