手写实现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
问题1: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("{}")。
问题2:cni假成功
kubelet不通过cni请求来获取pod ip,而是自己查询容器网卡ip。
如果cni成功后没有给容器网卡设置ip,那么kubelet会重新发起cni请求。
问题3:invalid character '{' after top-level value
cni bin不要输出json日志。
问题4:cni add失败后是否会先delete再继续add
是的。