AWS VPC CNI路由分析

amazon-vpc-cni-k8s是AWS基于VPC CNI的k8s网络插件,有着高性能及高度灵活的优点。项目地址:
https://github.com/aws/amazon-vpc-cni-k8s

下面我们通过分析其源码,查看实际上相关的CNI网络是怎么实现的。

1. AWS VPC CNI Agent node部分

Node上的Agent程序与路由相关的,主要做2块工作,一个是配置主网卡(SetupHostNetwork),一个是配置辅助网卡(setupENINetwork)。

1.1 SetupHostNetwork

针对host网络和主网卡Primary ENI做一些配置,Primary ENI: 主机的默认网卡,默认一般为eth0。

  1. 如果启用ipv4和支持node port,设置RP Filter:

  1. 设置Primary eni网卡的mtu:

  1. 先删除main表里优先级为1024的rule

  1. 如果启用node port支持,添加基于fwmark的路由策略:

  1. 如果启用ipv4且启用pod eni,则:

  1. 创建snat的iptables规则,这里省略

1.2 setupENINetwork

setupENINetwork为非Primary ENI的弹性网卡配置网络,每块弹性网卡都有一个主IP+N个辅助IP

eniName: 主机的默认网卡,默认一般为eth0+x,X为网卡序号,如eth1

  1. 设置eni网卡的mtu

  1. 让eni网卡up

  1. 删除eni网卡上所有已经存在的ip地址

  1. 设置eni网卡的ip

  1. 在deviceNumber + 1路由表里,删除已经存在默认路由,添加默认路由指向eni子网的网关

2. AWS CNI二进制文件

CNI二进制文件主要是实现CNI标准规则的几个接口:

2.1 cmdAdd

  1. grpc请求”/rpc.CNIBackend/AddNetwork”调用ipam分配ip

  2. 构建hostVethName

  1. 创建veth和容器内网卡,绑定容器网卡ip地址,容器内设置路由和其他内核参数

4.以下步骤在POD network namespce内执行

  1. 启动host网卡

  1. 添加用于host上访问pod的路由

  1. 添加用于访问pod流量的路由策略

3. 参考文件

  • AWS 弹性网卡参考

https://aws.amazon.com/cn/premiumsupport/knowledge-center/ec2-ubuntu-secondary-network-interface/

  • cni文档

https://github.com/containernetworking/cni/blob/spec-v1.0.0/SPEC.md
https://www.cni.dev/docs/

  • libcni库

https://github.com/containernetworking/cni/tree/main/scripts
https://www.cni.dev/docs/spec-upgrades/#specific-guidance-for-plugins-written-in-go

  • cni调用原理

http://www.noobyard.com/article/p-mjmyxamv-ob.html
https://blog.csdn.net/shida_csdn/article/details/79752411
https://segmentfault.com/a/1190000019956620
https://morningspace.github.io/tech/k8s-net-cni/

k8s apiserver 自签名证书过期导致集群故障

1. 故障处理过程

今天接到同事反馈发现有一套k8s apiserver集群出现如下报错:

随后去api server节点上查询api server日志,发现也有大量报错:

让人奇怪的是,并不是所有请求都报证书错误,通过日志发现,大量的请求到api server都是200的,出现500的为少数。

同时我们通过上面的报错发现,报错的是10.13.96.11:36528,这个IP是api server自己。

最后我们将错误定位到以下范围:

api server自己访问自己报证书过期的错误,而其它组件访问都是正常的。

同时我们检查了服务器上的所有k8s 集群使用的证书,并没有过期。

在无计可施时,怀着试试看的想法,我们将api server服务重启了,发现重启后恢复正常。果然是重启能解决99%的问题。

2. 故障原因排查

故障恢复后,我们一直在排查原因,排查原因时我们发现下面3点:

  • api server服务正好启动了一年没有重启
  • api server自己访问自己报证书过期的错误
  • api server访问自己使用的是IP地址,然我们api server对其它组件及外部都是使用域名的

下面就只能看源码了,通过api server的源码,我们发现api server启动时会自动生成一个自签名证书:

k8s.io/apiserver/pkg/server/options/serving_with_loopback.go:

而且这个自签名证书的时间为一年。

k8s.io/client-go/util/cert/cert.go:

3. 故障结论

故障原因很明显:api server启动时会自动生成一个自签名证书,而且这个自签名证书的时间为一年。

故障处理方法:重启api server(建议在一年内重启)

github上有人提过issue,官方以k8s本来就应该一年内升级且会重启为由,不认为这是一个问题:https://github.com/kubernetes/kubernetes/issues/86552

通过coredns跨k8s集群解析DNS

最近在弄AlertManager,发现其集群模式需要知道各个AlertManager节点的IP地址,而我们现在需要在2个K8s集群同时部署AlertManager上来创建一套AlertManager,以确保在某一个K8s集群挂了后报警还能发出来,同时确保当2个K8s集群都正常的时候,一个告警只发一次出来。

但怎么在A集群上找到B集群上的POD的IP,这是个问题。通过研究,我们发现CoreDns可以很好的解决这个问题。

假设我们现在有2套集群,2套集群使用的DNS后缀都是cluster.local,容器网段及service网段都不一样。

我们现在在A集群上,先看旧的CoreDNS的配置:

继续阅读

通过Jenkins Pipeline在Dockerfile内自动构建包含私有模块的golang项目

以前写的golang项目一直使用的都是公开的module,最近使用私有gitlab仓库的module时,发现go get mod时需要输入用户名密码,开发环境还好解决,手动输一次让他记住就好了。但是项目正式上线,通过公司统一的jenkins Pipeline执行构建时,没有办法去手动输用户名密码。

Dockerfile配置

要解决这个问题,可以通过.netrc文件来处理。Dockerfile如下:

重点在3,4行和15行,3,4行通过传入GIT_USR和GIT_PWD两个参数到docker构建内,第15行生成.netrc配置。

现在我们就可以通过传参的方式来构建了:

docker build -t hub.docker.com/xxx/knative-audit-log:v0.1 --build-arg GIT_USR=**** --build-arg 'GIT_PWD=****' -f ./Dockerfile .

继续阅读

如何删除一个tcp长连接

有时端口被占用导致服务起不来,发现本机端口被另一个程序使用了。这个时候我们可以使用tcpkill命令来删除这个长连接,让其自动重连换一个端口。然后再启动想启动的服务。

安装tcpkill:

查询这个连接:

删除连接:

继续阅读

Traefik 2.5 源码分析1 – 程序启动部分

1. 前言

Traefik版本为2.5.2,从今天开始,计划陆续看完Traefik的核心部分的源码。
本文是第一部分,Traefik的程序启动分析。

2. 主流程

main.go在cmd/traefik/traefik.go内,其主要流程为:

先构造3个Command启动器,分别是traefik,healthcheck,version,Command结构如下:

源码文件:github.com/traefik/paerser@v0.1.4/cli/commands.go

可以看到,每个Command都可以配置名字,资源,Run函数等。

然后通过cli.Execute(cmdTraefik)来启动上面3个服务,也就是调用Command内的Run函数。

ResourceLoader是用于配置的加载,有文件,参数,环境变量3种加载方法,下面会提到。

继续阅读

linux kernel 4.8以上内核NR_KERNEL_STACK的改变

1. NR_KERNEL_STACK是干什么用的?

通过下面的命令,我们可以查看内核栈的数量:

这个值在内核中的宏为:NR_KERNEL_STACK,表示当前内核中有多少个内核栈。

我们使用这个数字来监控K8S节点上使用PID的数量,以避免PID被耗尽。

2. 那么PID与内核栈有什么关系呢?

在linux系统中,每个进程、子进程和线程在运行时都会有一个PID。这些进程或线程在运行时,因为CPU需要进行任务切换,在任务切换时就需要上下文交换,在上下文交换时就需要把当前进程的上下文压到内核栈内去,以便下次再运行时取出继续执行。

所以可以确定:每个进程、子进程和线程都会有一个内核栈。内核栈的数量与PID的数量大致相当。

注:基于linux内核的线程,比如java的线程与linux的线程是一一对应的,nodejs只使用了linux的进程,线程模型是其自己实现的,golang最特别,使用了多进程,每个进程上有多线程(基于内核),线程上还是自己实现的协程或者说goroute(可以理解为自己实现的线程)

继续阅读

PVC在K8S集群迁移或恢复需注意地方

目前在做同城双活,每个机房都有一个k8s集群,其中一些共享卷需要做到双活的2个集群上都可用。

比如我们现在在主机房的集群上有一个PVC:

以及对应的PV:

继续阅读

k8s源码分析:kube-apiserver –admission-control及–enable-admission-plugins –disable-admission-plugins参数差异

kubernetes源码分支:1.18

先说结论,kube-apiserver启动时:

  1. –admission-control参数带的插件将是apiserver启动的插件,不包括默认插件
  2. –admission-control和–enable-admission-plugins –disable-admission-plugins不能同时使用
  3. –enable-admission-plugins参数不需要按加载顺序填写
  4. 不使用–admission-control参数时,api server会同时启动默认插件
  5. –enable-admission-plugins参数启用时,api server会同时启动默认插件,除非使用了–disable-admission-plugins显示的关闭某个插件
  6. –enable-admission-plugins和–disable-admission-plugins如果同时填写了某一个插件,这个插件将会被加载

继续阅读

istio 1.6新特性:istio自身灰度发布功能测试

1. istio 1.6灰度功能测试

istio 1.6新增了istio自身的灰度测试特性,我先测试其灰度功能。

在官网下载并解压istio 1.6,并把istioctl复制到对应目录:

继续阅读