golang中的单向channel和nil值channel

在阅读client-go代码时发现源码对channel的部分使用方式不太理解,一个是单项channel的使用,一个是nil值channel的使用,于是搜索总结一下。 在golang中可以定义channel为双向的,也可以将channel定义为只能接收或只能发送类型的,如下面所示: // You can edit this code! // Click here and start typing. package main func main() { // 普通双向的 var normalCh chan interface{} // 只能发送 var sendOnlyCh chan<- interface{} // 只能接收 var receiveOnlyCh <-chan interface{} <-sendOnlyCh receiveOnlyCh <- 1 } 编译上述代码,将会得到下列错误: ./prog.go:12:2: invalid operation: <-sendOnlyCh (receive from send-only type chan<- interface {}) ./prog.go:13:16: invalid operation: receiveOnlyCh <- 1 (send to receive-only type <-chan interface {}) Go build failed.…

haproxy+keepalived配置示例

在非云环境中,要实现服务的高可用和负载功能一般会使用haproxy+keepalived方案,通过使用vrrp管理vip地址的的自动漂移,以及通过haproxy实现服务的负载功能。除了vrrp协议,还需要确保我们的主机网络中支持无偿arp,以保证我们的路由器或其他主机能够正确的将vip地址解析到正确的mac地址。在下图中,采用两个服务器安装keepalived+haproxy进行主备部署,后面有三台独立的RS服务器,当然keepalived本身也可以和RS服务部署在同一台服务器上。 下面是针对haproxy的配置,我们要配置haproxy直接绑定的虚拟vip地址的话,需要开启ip_nonlocal_bind这个配置项,如果监听的是0.0.0.0地址的话就无所谓: [root@master3 ~]# echo net.ipv4.ip_nonlocal_bind=1 /etc/sysctl.d/haproxy-keepalived.conf [root@master3 ~]# sysctl -p /etc/sysctl.d/haproxy-keepalived.conf [root@master3 ~]# cat /etc/haproxy/haproxy.cfg #--------------------------------------------------------------------- # Example configuration for a possible web application. See the # full configuration options online. # # http://haproxy.1wt.eu/download/1.4/doc/configuration.txt # #--------------------------------------------------------------------- #--------------------------------------------------------------------- # Global settings #--------------------------------------------------------------------- global # to have these messages end up in /var/log/haproxy.log you will # need to: # # 1) configure syslog to accept network log events. This is done…

bpg协议简要介绍

边界网关协议(英语:Border Gateway Protocol,缩写:BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或“前缀”表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。 大多数互联网服务提供商必须使用BGP来与其他ISP创建路由连接(尤其是当它们采取多宿主连接时)。因此,即使大多数互联网用户不直接使用它,但是与7号信令系统——即通过PSTN的跨供应商核心响应设置协议相比,BGP仍然是互联网最重要的协议之一。特大型的私有IP网络也可以使用BGP。例如,当需要将若干个大型的OSPF(开放最短路径优先)网络进行合并,而OSPF本身又无法提供这种可扩展性时。使用BGP的另一个原因是其能为多宿主的单个或多个ISP网络提供更好的冗余。 AS就是一个独立管理的网络。大公司或者组织的网络一般是由一到多个AS组成,小的公司或者个人是通常会接入到ISP(Internet Service Provider)的AS。不管怎么样,如果设备在互联网上,那么必定是属于某一个AS。EBGP是用来连接各个AS,这样互联网上的设备的才能够彼此互连。AS之间的连接协议,目前在用的,有且仅有EBGP一种。 在bgp网络中,具有相同AS号的peer称为IBGP(internal peer),而具有不同AS号的peer称为EBGP(external peer)。IBGP应用在AS内部,作为IGP的一种。一般的IGP,例如OSPF,EIGRP,用来在邻接路由器之间传递路由。而IBGP可以用来在edge router之间同步路由,edge router并不需要邻接。Edge router是指在AS边缘,用来连接其他AS的router,那么edge router肯定是运行了EBGP。同时这个edge router也会有对端AS的路由。通过IBGP,edge router会将学习到的对端AS的路由,传递给其他的edge router。这样,可以实现跨AS的连通。 bgp使用使用tcp 179端口就行通信,消息格式按照TLV形式构成,其中T是消息类型,L是消息长度,V是具体的消息。bgp-4(针对ipv4路由,参考rfc2858查看ipv6和其他协议路由)中一共定义了4个核心消息类型,分别为OPEN,UPDATE,KEEPALIVE,NOTIFICATION。其中OPEN用来在建立peer链接时使用,此处还有一个扩展协议rfc2842定义了基于OPEN消息的扩展属性,给双方提供的协商自身能力的能力;UPDATE用来更新和撤销路由信息,KEEPALIVE用来维护peer链接的心跳,NOTIFICATION用来在出错时进行通知,在rfc2918中还定义了一个ROUTE-REFRESH类型的消息,用来实现路由的全量刷新。 UPDATE用来在peer之间进行路由的更新和撤销操作,其中最重要的是UPDATE消息,其消息的格式参考如下: UPDATE消息用来向peer端公布(advertise)可行的具有相同路径属性的路由,或者撤销不再 可行的路由。单条UPDATE消息既可以公布路由,也可撤销之前公布的路由。 +-----------------------------------------------------+ | 撤销的路由信息长度(2字节) | +-----------------------------------------------------+ | 撤销的路由信息(变长) | +-----------------------------------------------------+ | 路径属性信息长度(2字节) | +-----------------------------------------------------+ | 路径属性(变长) | +-----------------------------------------------------+ | 网络层可达性信息(NLRI)(变长) | +-----------------------------------------------------+ 撤销的路由信息长度: 两字节无符号整形,标识撤销的路由信息的数据总长度。通过该值可以定位到NLRI 在数据流中的位置。 0值意味着没有路由信息被撤销,那么撤销的路由信息不包含在这条消息中。 撤销的路由信息: 可变长度的字段,包含了正在被撤回的IP地址前缀列表。每个IP地址前缀使用二元组 <长度,前缀表示,含义如下: +---------------------------+ | 长度(1字节) | +---------------------------+ | 前缀(变长) | +---------------------------+ 这两个字段的含义如下: a) 长度: 表示IP地址前缀的bit位是多少,0值表示匹配所有IP地址。 b) 前缀: IP地址前缀,后面跟随着一些bit位用来进行字节对其,这些用来补齐的bit没有 实际用途,值无所谓。 路径属性信息长度: 这是一个两字节编码的无符号证书表示路径属性信息的数据总长度,通过该值可以定位…

eBPF技术介绍

最近在看cilium和calico关于eBPF功能,发现有许多不理解的地方,比如概念原理等,因此花费了一定的时间进行了学习和整理,翻了不少文档和博客,最后发现还是cilium社区的这篇文章讲解的最通透,希望后来人可以静下心来把这篇文章慢慢读下来并理解。开始方向错了,其实主要两条线,一个是基于llvm的c语言编写eBPF程序,涉及到一些宏,段定义,maps定义,btf等,llvm按照一定规范为我们编译和生成elf格式的文件,另一个是iproute2/bpftool等加载器将eBPF程序加载到内核,加载工具会读取elf中的各种段信息,将其加载到内核中,此处也会涉及段名称的约定,加载工具与编译器对于btf之间的约定。明白这两块之后再看内核提供的相关辅助函数和不同的map就可以做一些特定功能的bpf程序开发了。 简介 BPF(Berkeley Packet Filter ),中文翻译为伯克利包过滤器,是类 Unix 系统上数据链路层的一种原始接口,提供原始链路层封包的收发。1992 年,Steven McCanne 和 Van Jacobson 写了一篇名为《BSD数据包过滤:一种新的用户级包捕获架构》的论文。在文中,作者描述了他们如何在 Unix 内核实现网络数据包过滤,这种新的技术比当时最先进的数据包过滤技术快 20 倍。BPF 在数据包过滤上引入了两大革新: 一个新的虚拟机 (VM) 设计,可以有效地工作在基于寄存器结构的 CPU 之上; 应用程序使用缓存只复制与过滤数据包相关的数据,不会复制数据包的所有信息。这样可以最大程度地减少BPF 处理的数据; 由于这些巨大的改进,所有的 Unix 系统都选择采用 BPF 作为网络数据包过滤技术,直到今天,许多 Unix 内核的派生系统中(包括 Linux 内核)仍使用该实现。 2014 年初,Alexei Starovoitov 实现了 eBPF(extended Berkeley Packet Filter)。经过重新设计,eBPF 演进为一个通用执行引擎,可基于此开发性能分析工具、网络数据包过滤、系统调用过滤,系统观测和分析等诸多场景。eBPF 最早出现在 3.18 内核中,此后原来的 BPF 就被称为经典 BPF,缩写 cBPF(classic BPF),cBPF 现在已经基本废弃。现在,Linux 内核只运行 eBPF,内核会将加载的 cBPF 字节码透明地转换成 eBPF 再执行。 eBPF 新的设计针对现代硬件进行了优化,所以 eBPF 生成的指令集比旧的 BPF 解释器生成的机器码执行得更快。扩展版本也增加了虚拟机中的寄存器数量,将原有的 2 个 32 位寄存器增加到 10…

kubernetes基础 – 使用kubeadm部署kubernetes集群

环境信息 ​ 主机名 内网ip 公网ip 负载均衡器 master1 192.168.3.29 x.x.x.x 192.168.3.33:6443 *:6443 master2 192.168.3.30 n/a master3 192.168.3.31 n/a node1 192.168.3.27 n/a n/a node2 192.168.3.32 n/a n/a node3 192.168.3.28 n/a n/a 主机操作系统版本为CentOS 7.9。 配置基础环境 cat /etc/hosts ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ​ 192.168.3.29 master1 192.168.3.30 master2 192.168.3.31 master3 192.168.3.27 node1 192.168.3.32 node2 192.168.3.28 node3 # 生成known_hosts ssh-keyscan -t rsa -f /etc/hosts >> ~/.ssh/known_hosts 配置master1节点到其他节点的免密登录 ssh-keygen yum install -y…

kubernetes网络 – linux网络基础设施

在介绍之前,先来熟悉一下linux给我们提供的一些基础设备。回顾一下之前cni与pod网络中的测试用例。 [root@localhost ~]# ip link add name testbr type bridge [root@localhost ~]# ip link add name veth1 type veth peer name br-veth1 [root@localhost ~]# ip link add name veth2 type veth peer name br-veth2 [root@localhost ~]# ip netns add test1 [root@localhost ~]# ip netns add test2 [root@localhost ~]# ip link set netns test1 dev veth1 [root@localhost ~]# ip link set netns test2 dev veth2 [root@localhost ~]# ip link set master…

linux的proxy_arp和arp_filter参数解释

proxy_arp和arp_filter用来控制是否对arp请求作出响应,在正常情况下,如果收到arp请求,且请求的ip地址为网卡自身的ip地址时都会回复。那么当ip地址不是网卡自身的ip地址时是否进行回复则取决于这两个参数的配置,使用网卡自身的mac地址回复。两个参数的官方文档解释如下: arp_filter 1 – 允许主机上拥有同一个子网的多个网卡,对于每个接口上收到的arp请求是否进行回复取决于当本机向发出arp请求的那个客户端的源ip发数据包时,这个数据包是否会从当前接收到请求的接口发出。如果是则回复,否则不回复。 0 – 本机有那个ip地址的话则会回复(其实是看路由,网卡配置ip地址后再local表里自动创建一条路由)。 proxy_arp 是否开启arp代理,开启arp代理的话则会以自己的mac地址回复arp请求,0为不开启,1则开启。 在开启了proxy_arp的情况下,如果请求中的ip地址不是本机网卡接口的地址:但是有该地址的路由,则会以自己的mac地址进行回复;如果没有该地址的路由,不回复。 如果开启了arp_filter,请求中的ip地址是本机接口的地址:如果且当我们向发起arp请求的客户端回复的包也通过当前网卡出去的话则进行回复;如果回包时不走当前网卡则不会进行回复。 下面通过一小段命令来测试,首先准备一个veth-pair和netns。 ip netns add test1 ip link add name veth0 type veth peer name host-veth0 ip link set host-veth0 up ip link set veth0 netns test1 ip netns exec test1 ip link set veth0 up ip netns exec test1 ip addr add 10.0.0.1 dev veth0 ip netns exec test1 ip route add default dev veth0 ip link add…

kubernetes网络 – cni与pod网络

pod网络 kubernetes中每个pod都拥有自己的ip地址,pod内的所有容器实例都共享同一个网络命名空间,因此容器实例之间可以通过localhost地址进行通信,并且他们都可以看到同样的ip地址和mac地址。 kubernetes对于pod的网络定制了下列三个要求,所有网络插件支持这几个要求即可: 所有节点上所有的pod可以互相通信,并且不依赖NAT 节点上的进程可以和该节点上的所有pod通信 运行在主机网络空间下的pod可以和所有节点上的所有pod互相通信,并且不依赖NAT 网络插件 cni代表容器网络接口,是CNCF社区下的一个项目,定义了一系列的接口格式,来规范容器的网络接口的配置。 kubernetes支持两种网络插件配置模式,分别为 cni kubenet kubenet 其中kubenet是一个非常简单的网络插件,并且只支持linux。本身不提供跨节点的网络通信以及网络策略功能。通常是要和云控制器结合起来去使用的。kubenet模式下,kubelet会创建一个叫做cbr0的网桥设备,并未每个pod创建一个veth-pair(虚拟接口对),一端在容器内,一端在容器外,在容器外的接口加入到这个网桥(二层)中。节点上所有的pod都通过主机上的接口连接到网桥,因此针对单个节点上的pod是可以互相通信的。 可以通过以下示例进行简单测试验证: echo 1 > /proc/sys/net/ipv4/conf/all/accept_local echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter echo 1 > /proc/sys/net/ipv4/conf/default/accept_local echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables ip link add testbr type bridge ip link set testbr promisc on ip link set testbr up ip netns add test1 ip netns add test2 ip link add veth1 type veth peer br-veth1 ip link add…

client-go代码分析之informer和sharedInformer分析

在茫茫代码中,很容易迷失。经过一段时间的艰苦探索,终于对client-go有了一定的了解,能够从整体上把相关功能串起来。本文主要是informer相关的内容,因为这是client-go比较核心的功能。后面还会发一篇关于client-go编程使用的文章,会从kubernetes.Interface,dynamic,discovery,workequeue和informer工厂函数等方面介绍,再结合一个operator作为实例进行分析(虽然大部分operator都使用kube-builder等类似的工具构建,但是本着求根问底的精神还是要分析一下)。 相关存储结构 在开始介绍之前,先介绍一下client-go中提供存储相关的接口,这些接口用来缓存api对象。Store是最基础的接口,提供增删改查等,还有一些继承于Store的接口,比Store可能会多实现一些方法,可以针对不同的场景进行使用。 Store ExpirationStore UndeltaStore Queue FIFO DeltaFIFO Indexer cache 不同Store的引用 Store接口的具体定义可以看vendor/k8s.io/client-go/tools/cache/store.go这个文件。在IDE的帮助下,找到了所有实现了这个Store的相关实现。本文主要关心client-go,所以主要跟进client-go下面的相关实现。 delta_fifo.go NewDeltaFIFO() NewDeltaFIFOWithOptions() informer和sharedinformer使用到了 vendor/k8s.io/client-go/tools/cache/controller.go:NewInformer() vendor/k8s.io/client-go/tools/cache/shared_informer.go:sharedIndexInformer.Run() expiration_cache.go NewTTLStore() pkg/kubelet/util/cache/object_cache.go:NewObjectCache() NewExpirationStore() pkg/credentialprovider/aws/aws_credentials.go:newECRProvider() pkg/credentialprovider/azure/azure_credentials.go:NewACRProvider() fifo.go 该文件中定义了Queue接口,继承了Store,并且在该文件中通过FIFO实现了这个queue,另外一个实现是在delta_fifo.go中的DeltaFIFO NewFIFO() 代码中未找到该引用 index.go 这个文件定义了Indexer接口,继承了Store,在store.go中通过cache实现了该接口 store.go NewStore() staging/src/k8s.io/client-go/tools/cache/controller.go:NewInformer() staging/src/k8s.io/client-go/tools/cache/undelta_store.go:NewUndeltaStore() NewIndexer() staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go:newWatchCache() staging/src/k8s.io/client-go/tools/cache/controller.go:NewIndexerInformer() staging/src/k8s.io/client-go/tools/cache/reflector.go:NewNamespaceKeyedIndexerAndReflector() 该函数在IDE中显示未被使用 staging/src/k8s.io/client-go/tools/cache/shared_informer.go:NewSharedIndexInformer() undelata_store.go NewUndeltaStore() pkg/kubelet/config/apiserver.go:newSourceApiserverFromLW() informer简要介绍 client-go中提供了普通informer和sharedInformer两种informer给我们使用。使用informer可以快速的构建各种资源的控制器,来对k8s进行扩展。informer提供了资源变化时执行回调的功能,可以在新增资源,修改资源和是删除资源时执行相应的控制器逻辑。 使用sharedInformer可以对同一个资源注册多个控制器,每个控制器独立执行自己的业务逻辑,互不干扰。多个控制器之间底层公用一个存储和一个到apiserver的连接,可以减少apiserver的压力的和本地内存的占用。informer则不具备这些能力,对于简单控制器可以使用informer,对于较复杂的控制器,会有针对同一个资源多个控制逻辑的推荐使用sharedInformer。 这两个informer在实现的复杂度上有很大差异,sharedInformer相对于普通informer来说要复杂的多。因此本文先从普通informer开始来进行分析,普通informer还分两种,一种是普通的informer,另外一种indexerinfomer。两种informer的区别在于前者使用Store作为对象存储,而后者采用Indexer作为对象存储,通过索引可以加快搜索。 普通informer分析 接下来我们以普通的informer进行分析,关于informer的使用可以参考这里。代码位于k8s.io/client-go/tools/cache/controller.go文件中,该文件中定义了Controller接口,并通过controller进行了实现。 controller分析 先看下接口定义以及controller包含的关键数据结构: // 这两个函数用来创建informer实例 func NewInformer(lw ListerWatcher, objType runtime.Object, resyncPeriod time.Duration, h ResourceEventHandler,) (Store, Controller) {} func NewIndexerInformer(lw ListerWatcher, objType…

flannel源码简要分析

启动运行流程 启动流程 寻找要使用的主机网卡,该网卡会在创建flannel.1设备时作为dev参数使用,表示flannel.1设备绑定的网卡为该网卡,vxlan的出站和入站数据都会经过这个网卡。 接下来是创建网络管理器,网络管理器用来获取网络配置,本机的pod网络等。 创建后端插件,图中实例的是vxlan后端,类型是Backend,有一个RegisterNetwork方法,在vxlan后端中在这个方法中创建vxlan设备以及设置其ip地址,并返回一个backend.Network结构。 设置iptables的伪装和转发规则,其中的-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/16 -j MASQUERADE这条iptables规则导致host到容器网络的访问源地址使用flannel.1的ip。 运行backend.Networ.Run()方法持续不断的监听所有网络变更的事件,根据事件刷新相关的route,arp和fdb表到主机上。 定时对本机的租约进行续租(etcd时才用)。 功能组件 SubnetManager 网络管理器用来管理当前主机和集群主机的网络配置。 接口说明 GetNetworkConfig(ctx context.Context) (*Config, error) 获取当前节点的网络配置信息: type Config struct { Network ip.IP4Net SubnetMin ip.IP4 SubnetMax ip.IP4 SubnetLen uint BackendType string `json:"-"` Backend json.RawMessage `json:",omitempty"` } // 该配置由下列函数生成 func ParseConfig(s string) (*Config, error) { cfg := new(Config) err := json.Unmarshal([]byte(s), cfg) if err != nil { return nil, err } if cfg.SubnetLen…