快乐成长


每天进步一点点



把学习变为一种快乐,快乐长能长久,才能坚持,才能降低学习成本,带给您学习乐趣。 每天进步点点, 一直是我们的愿望,欢迎您光临!!

为了给您增加学习乐趣,并且学到有意思,简单高效的,有用的学习,为零基础的朋友们录制了趣味编程视频,欢迎您浏览,谢谢!

月度归档:2018年07月

Flannel是如何工作的 

概述

最近我们的TaaS平台遇到很多的网络问题,事实证明“contiv + ovs + vlan”的方案并不适合TaaS这种大规模高并发的场景,填不完的坑,当然DevOps场景下是没什么问题的。时间紧迫,只能使用“Flannel + host-gw”这个简单、稳定的网络方案搭建一个小规模的集群来作为紧急备选方案。趁这个机会,也学习一下前两年因性能差,广为诟病而一直不敢碰的Flannel如今是怎么个样子。经过春节半个月的稳定测试、压力测试证明确实很稳定。当然,calico(bgp)才是我们后续的主要网络方案。

Flannel支持多种Backend协议,但是不支持运行时修改Backend。官方推荐使用以下Backend:

  • VXLAN,性能损耗大概在20~30%;
  • host-gw, 性能损耗大概10%,要求Host之间二层直连,因此只适用于小集群;
  • UDP, 建议只用于debug,因为性能烂到家了,如果网卡支持 enable udp offload,直接由网卡进行拆包解包,性能还是很棒的。
  • AliVPC。

实验性的Backend,不建议上生产:

  • Alloc
  • AWS VPC
  • GCE
  • IPIP
  • IPSec

Flannel的配置

Flannel在官方配置可以在https://github.com/coreos/flannel/blob/master/Documentation/configuration.md找到,但是注意文档中的配置不是最新的,是不完整的。

通过命令行配置

目前最新版的Flannel v0.10.0的命令行配置及说明如下:

Usage: /opt/bin/flanneld [OPTION]...
  -etcd-cafile string
    	SSL Certificate Authority file used to secure etcd communication
  -etcd-certfile string
    	SSL certification file used to secure etcd communication
  -etcd-endpoints string
    	a comma-delimited list of etcd endpoints (default "http://127.0.0.1:4001,http://127.0.0.1:2379")
  -etcd-keyfile string
    	SSL key file used to secure etcd communication
  -etcd-password string
    	password for BasicAuth to etcd
  -etcd-prefix string
    	etcd prefix (default "/coreos.com/network")
  -etcd-username string
    	username for BasicAuth to etcd
  -healthz-ip string
    	the IP address for healthz server to listen (default "0.0.0.0")
  -healthz-port int
    	the port for healthz server to listen(0 to disable)
  -iface value
    	interface to use (IP or name) for inter-host communication. Can be specified multiple times to check each option in order. Returns the first match found.
  -iface-regex value
    	regex expression to match the first interface to use (IP or name) for inter-host communication. Can be specified multiple times to check each regex in order. Returns the first match found. Regexes are checked after specific interfaces specified by the iface option have already been checked.
  -ip-masq
    	setup IP masquerade rule for traffic destined outside of overlay network
  -kube-api-url string
    	Kubernetes API server URL. Does not need to be specified if flannel is running in a pod.
  -kube-subnet-mgr
    	contact the Kubernetes API for subnet assignment instead of etcd.
  -kubeconfig-file string
    	kubeconfig file location. Does not need to be specified if flannel is running in a pod.
  -log_backtrace_at value
    	when logging hits line file:N, emit a stack trace
  -public-ip string
    	IP accessible by other nodes for inter-host communication
  -subnet-file string
    	filename where env variables (subnet, MTU, ... ) will be written to (default "/run/flannel/subnet.env")
  -subnet-lease-renew-margin int
    	subnet lease renewal margin, in minutes, ranging from 1 to 1439 (default 60)
  -v value
    	log level for V logs
  -version
    	print version and exit
  -vmodule value
    	comma-separated list of pattern=N settings for file-filtered logging

需要说明如下:

  • 我们是通过-kube-subnet-mgr配置Flannel从Kubernetes APIServer中读取对应的ConfigMap来获取配置的。-kubeconfig-file, -kube-api-url我们也没有配置,因为我们是使用DaemonSet通过Pod来部署的Flannel,所以Flannel与Kubernetes APIServer是通过ServiceAccount来认证通信的。
  • 另外一种方式是直接从etcd中读取Flannel配置,需要配置对应的-etcd开头的Flag。
  • -subnet-file默认为/run/flannel/subnet.env,一般无需改动。Flannel会将本机的subnet信息对应的环境变量注入到该文件中,Flannel真正是从这里获取subnet信息的,比如:
    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.26.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    
  • -subnet-lease-renew-margin表示etcd租约到期前多少时间就可以重新自动续约,默认是1h。因为ttl时间是24h,所以这项配置自然不允许超过24h,即[1, 1439] min.

通过环境变量配置

上面的命令行配置项,都可以通过改成大写,下划线变中划线,再加上FLANNELD_前缀转成对应的环境变量的形式来设置。

比如--etcd-endpoints=http://10.0.0.2:2379对应的环境变量为FLANNELD_ETCD_ENDPOINTS=http://10.0.0.2:2379

部署Flannel

通过Kubernetes DaemonSet部署Flannel,这一点毫无争议。同时创建对应的ClusterRole,ClusterRoleBinding,ServiceAccount,ConfigMap。完整的Yaml描述文件可参考如下:

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: flannel
rules:
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes/status
    verbs:
      - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    k8s-app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "plugins": [
        {
         "type": "flannel",
         "delegate": {
           "hairpinMode": true,
           "isDefaultGateway": true
         }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kube-flannel
  namespace: kube-system
  labels:
    tier: node
    k8s-app: flannel
spec:
  template:
    metadata:
      labels:
        tier: node
        k8s-app: flannel
    spec:
      imagePullSecrets:
      - name: harborsecret
      serviceAccountName: flannel
      containers:
      - name: kube-flannel
        image: registry.vivo.xyz:4443/coreos/flannel:v0.10.0-amd64
        command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr"]
        securityContext:
          privileged: true
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        volumeMounts:
        - name: run
          mountPath: /run
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      - name: install-cni
        image: registry.vivo.xyz:4443/coreos/flannel-cni:v0.3.0
        command: ["/install-cni.sh"]
        #command: ["sleep","10000"]
        env:
        # The CNI network config to install on each node.
        - name: CNI_NETWORK_CONFIG
          valueFrom:
            configMapKeyRef:
              name: kube-flannel-cfg
              key: cni-conf.json
        volumeMounts:
        #- name: cni
        #  mountPath: /etc/cni/net.d
        - name: cni
          mountPath: /host/etc/cni/net.d
        - name: host-cni-bin
          mountPath: /host/opt/cni/bin/
      hostNetwork: true
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      volumes:
        - name: run
          hostPath:
            path: /run
        #- name: cni
        #  hostPath:
        #    path: /etc/kubernetes/cni/net.d
        - name: cni
          hostPath:
            path: /etc/cni/net.d
        - name: flannel-cfg
          configMap:
            name: kube-flannel-cfg
        - name: host-cni-bin
          hostPath:
            path: /etc/cni/net.d
  updateStrategy:
    rollingUpdate:
      maxUnavailable: 1
    type: RollingUpdate

工作原理

很容易混淆几个东西。我们通常说的Flannel(coreos/flannel),其实说的是flanneld。大家都知道Kubernetes是通过CNI标准对接网络插件的,但是当你去看Flannel(coreos/flannel)的代码时,并没有发现它实现了CNI的接口。如果你玩过其他CNI插件,你会知道还有一个二进制文件用来供kubele调用,并且会调用后端的网络插件。对于Flannel(coreos/flannel)来说,这个二进制文件是什么呢?git repo在哪里呢?

这个二进制文件就对应宿主机的/etc/cni/net.d/flannel,它的代码地址是https://github.com/containernetworking/plugins,最可恨的它的名字就叫做flannel,为啥不类似contiv netplugin对应的contivk8s一样,取名flannelk8s之类的。

上面的Flannel Pod中还有一个容器叫做install-cni,它对应的脚本在https://github.com/coreos/flannel-cni。

  • /opt/bin/flanneld --> https://github.com/coreos/flannel
  • /etc/cni/net.d/flannel --> https://github.com/containernetworking/plugins
  • /install-cni.sh --> https://github.com/coreos/flannel-cni

kube-flannel容器

在kube-flannel容器里面运行的是我们的主角flanneld,我们需要关注的这个容器里面的目录/文件:

  • /etc/kube-flannel/cni-conf.json
  • /etc/kube-flannel/net-conf.json
  • /run/flannel/subnet.env
  • /opt/bin/flanneld

下面是我的环境对应的内容:

/run/flannel # ls /etc/kube-flannel/
cni-conf.json  net-conf.json
/run/flannel # cat /etc/kube-flannel/cni-conf.json 
{
  "name": "cbr0",
  "plugins": [
    {
     "type": "flannel",
     "delegate": {
       "hairpinMode": true,
       "isDefaultGateway": true
     }
    }
  ]
}
/run/flannel # cat /etc/kube-flannel/net-conf.json 
{
  "Network": "10.244.0.0/16",
  "Backend": {
    "Type": "host-gw"
  }
}

/run/flannel # cat  /run/flannel/subnet.env 
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.26.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=true

/run/flannel # ls /opt/bin/
flanneld           mk-docker-opts.sh
/run/flannel # cat /opt/bin/mk-docker-opts.sh 
#!/bin/sh

usage() {
	echo "$0 [-f FLANNEL-ENV-FILE] [-d DOCKER-ENV-FILE] [-i] [-c] [-m] [-k COMBINED-KEY]

Generate Docker daemon options based on flannel env file
OPTIONS:
	-f	Path to flannel env file. Defaults to /run/flannel/subnet.env
	-d	Path to Docker env file to write to. Defaults to /run/docker_opts.env
	-i	Output each Docker option as individual var. e.g. DOCKER_OPT_MTU=1500
	-c	Output combined Docker options into DOCKER_OPTS var
	-k	Set the combined options key to this value (default DOCKER_OPTS=)
	-m	Do not output --ip-masq (useful for older Docker version)
" >&2

	exit 1
}

flannel_env="/run/flannel/subnet.env"
docker_env="/run/docker_opts.env"
combined_opts_key="DOCKER_OPTS"
indiv_opts=false
combined_opts=false
ipmasq=true

while getopts "f:d:icmk:?h" opt; do
	case $opt in
		f)
			flannel_env=$OPTARG
			;;
		d)
			docker_env=$OPTARG
			;;
		i)
			indiv_opts=true
			;;
		c)
			combined_opts=true
			;;
		m)
			ipmasq=false
			;;
		k)
			combined_opts_key=$OPTARG
			;;
		[\?h])
			usage
			;;
	esac
done

if [ $indiv_opts = false ] && [ $combined_opts = false ]; then
	indiv_opts=true
	combined_opts=true
fi

if [ -f "$flannel_env" ]; then
	. $flannel_env
fi

if [ -n "$FLANNEL_SUBNET" ]; then
	DOCKER_OPT_BIP="--bip=$FLANNEL_SUBNET"
fi

if [ -n "$FLANNEL_MTU" ]; then
	DOCKER_OPT_MTU="--mtu=$FLANNEL_MTU"
fi

if [ -n "$FLANNEL_IPMASQ" ] && [ $ipmasq = true ] ; then
	if [ "$FLANNEL_IPMASQ" = true ] ; then
		DOCKER_OPT_IPMASQ="--ip-masq=false"
	elif [ "$FLANNEL_IPMASQ" = false ] ; then
		DOCKER_OPT_IPMASQ="--ip-masq=true"
	else
		echo "Invalid value of FLANNEL_IPMASQ: $FLANNEL_IPMASQ" >&2
		exit 1
	fi
fi

eval docker_opts="\$${combined_opts_key}"

if [ "$docker_opts" ]; then
	docker_opts="$docker_opts ";
fi

echo -n "" >$docker_env

for opt in $(set | grep "DOCKER_OPT_"); do

	OPT_NAME=$(echo $opt | awk -F "=" '{print $1;}');
	OPT_VALUE=$(eval echo "\$$OPT_NAME");

	if [ "$indiv_opts" = true ]; then
		echo "$OPT_NAME=\"$OPT_VALUE\"" >>$docker_env;
	fi

	docker_opts="$docker_opts $OPT_VALUE";

done

if [ "$combined_opts" = true ]; then
	echo "${combined_opts_key}=\"${docker_opts}\"" >>$docker_env
fi

install-cni容器

install-cni容器顾名思义就是负责安装cni插件的,把镜像里的flannel等二进制文件复制到宿主机的/etc/cni/net.d,注意这个目录要匹配kubelet对应的cni配置项,如果你没改kubelet默认配置,那么kubelet默认也是配置的这个cni目录。我们需要关注install-cni容器内的目录/文件:

  • /host/etc/cni/net.d/
  • /host/opt/cni/bin/
  • /host/etc/cni/net.d/10-flannel.conflist

下面是我的环境对应的内容:


/host/etc/cni/net.d # pwd
/host/etc/cni/net.d
/host/etc/cni/net.d # ls
10-flannel.conflist  dhcp                 ipvlan               noop                 tuning
bridge               flannel              loopback             portmap              vlan
cnitool              host-local           macvlan              ptp


/host/etc/cni/net.d # cd /host/opt/cni/bin/
/host/opt/cni/bin # ls
10-flannel.conflist  dhcp                 ipvlan               noop                 tuning
bridge               flannel              loopback             portmap              vlan
cnitool              host-local           macvlan              ptp


/opt/cni/bin # ls
bridge      dhcp        host-local  loopback    noop        ptp         vlan
cnitool     flannel     ipvlan      macvlan     portmap     tuning

/opt/cni/bin # cat /host/etc/cni/net.d/10-flannel.conflist 
{
  "name": "cbr0",
  "plugins": [
    {
     "type": "flannel",
     "delegate": {
       "hairpinMode": true,
       "isDefaultGateway": true
     }
    }
  ]
}

Flannel工作原理图

画一个图,应该就很清晰了。注意带颜色的部分是Volume对应的信息,可重点关注。

创建容器网络的流程就是:kubelet ——> flannel ——> flanneld。如果宿主机上并发创建Pod,则你会看到有多个flannel进程在后台,不过正常几秒钟就会结束,而flanneld是常驻进程。

输入图片说明

Flannel host-gw Data Flow

Openshift默认也是使用Flannel host-gw容器网络方案,其官网也清晰的画出了host-gw的data flow diagram:

输入图片说明

  • Node 1中对应的ip routes:
    default via 192.168.0.100 dev eth0 proto static metric 100
    10.1.15.0/24 dev docker0 proto kernel scope link src 10.1.15.1
    10.1.20.0/24 via 192.168.0.200 dev eth0
    
  • Node 2中对应的ip routes:
    default via 192.168.0.200 dev eth0 proto static metric 100
    10.1.20.0/24 dev docker0 proto kernel scope link src 10.1.20.1
    10.1.15.0/24 via 192.168.0.100 dev eth0
    

Kubernetes集群中使用Flannel的注意事项

在我的集群中是使用kube-subnet-mgr来管理subnet的,而不是直接通过etcd v2来管理的。

  • flanneld启动时,需要对应Node上已经配置好PodCIDR,可通过get node信息查看.spec.PodCIDR字段是否有值。
  • 配置Node的CIDR可有两种方式:
    • 手动配置每个Node上kubelet的--pod-cidr
    • 配置kube-controller-manager的--allocate-node-cidrs=true --cluster-cidr=xx.xx.xx.xx/yy,由CIDR Controller自动给每个节点配置PodCIDR。
  • 另外,你还会发现每个Node都被打上了很多flannel开头的Annotation,这些Annotation会在每次flanneld启动时RegisterNetwork的时候进行更新。这些Annotation主要用于Node Lease。
    • flannel.alpha.coreos.com/backend-data: "null"
    • flannel.alpha.coreos.com/backend-type: host-gw
    • flannel.alpha.coreos.com/kube-subnet-manager: "true"
    • flannel.alpha.coreos.com/public-ip: xx.xx.xx.xx
    • flannel.alpha.coreos.com/public-ip-overwrite:yy.yy.yy.yy (ps:optional)

下面是我的环境中某个节点的信息:

# kubectl get no 10.21.36.79 -o yaml
apiVersion: v1
kind: Node
metadata:
  annotations:
    flannel.alpha.coreos.com/backend-data: "null"
    flannel.alpha.coreos.com/backend-type: host-gw
    flannel.alpha.coreos.com/kube-subnet-manager: "true"
    flannel.alpha.coreos.com/public-ip: 10.21.36.79
    node.alpha.kubernetes.io/ttl: "0"
    volumes.kubernetes.io/controller-managed-attach-detach: "true"
  creationTimestamp: 2018-02-09T07:18:06Z
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/hostname: 10.21.36.79
  name: 10.21.36.79
  resourceVersion: "45074326"
  selfLink: /api/v1/nodes/10.21.36.79
  uid: 5f91765e-0d69-11e8-88cb-f403434bff24
spec:
  externalID: 10.21.36.79
  podCIDR: 10.244.29.0/24
status:
  addresses:
  - address: 10.21.36.79
    type: InternalIP
  - address: 10.21.36.79
    type: Hostname
  allocatable:
    alpha.kubernetes.io/nvidia-gpu: "0"
    cpu: "34"
    memory: 362301176Ki
    pods: "200"
  capacity:
    alpha.kubernetes.io/nvidia-gpu: "0"
    cpu: "40"
    memory: 395958008Ki
    pods: "200"
  conditions:
  - lastHeartbeatTime: 2018-02-27T14:07:30Z
    lastTransitionTime: 2018-02-13T13:05:57Z
    message: kubelet has sufficient disk space available
    reason: KubeletHasSufficientDisk
    status: "False"
    type: OutOfDisk
  - lastHeartbeatTime: 2018-02-27T14:07:30Z
    lastTransitionTime: 2018-02-13T13:05:57Z
    message: kubelet has sufficient memory available
    reason: KubeletHasSufficientMemory
    status: "False"
    type: MemoryPressure
  - lastHeartbeatTime: 2018-02-27T14:07:30Z
    lastTransitionTime: 2018-02-13T13:05:57Z
    message: kubelet has no disk pressure
    reason: KubeletHasNoDiskPressure
    status: "False"
    type: DiskPressure
  - lastHeartbeatTime: 2018-02-27T14:07:30Z
    lastTransitionTime: 2018-02-13T13:05:57Z
    message: kubelet is posting ready status
    reason: KubeletReady
    status: "True"
    type: Ready
  daemonEndpoints:
    kubeletEndpoint:
      Port: 10250
  images:
  - names:
    - registry.vivo.xyz:4443/bigdata_release/tensorflow1.5.0@sha256:6d61595c8e85d3724ec42298f8f97cdc782c5d83dd8f651c2eb037c25f525071
    - registry.vivo.xyz:4443/bigdata_release/tensorflow1.5.0:v2.0
    sizeBytes: 3217838862
  - names:
    - registry.vivo.xyz:4443/bigdata_release/tensorflow1.3.0@sha256:d14b7776578e3e844bab203b17ae504a0696038c7106469504440841ce17e85f
    - registry.vivo.xyz:4443/bigdata_release/tensorflow1.3.0:v1.9
    sizeBytes: 2504726638
  - names:
    - registry.vivo.xyz:4443/coreos/flannel-cni@sha256:dc5b5b370700645efcacb1984ae1e48ec9e297acbb536251689a239f13d08850
    - registry.vivo.xyz:4443/coreos/flannel-cni:v0.3.0
    sizeBytes: 49786179
  - names:
    - registry.vivo.xyz:4443/coreos/flannel@sha256:2a1361c414acc80e00514bc7abdbe0cd3dc9b65a181e5ac7393363bcc8621f39
    - registry.vivo.xyz:4443/coreos/flannel:v0.10.0-amd64
    sizeBytes: 44577768
  - names:
    - registry.vivo.xyz:4443/google_containers/pause-amd64@sha256:3b3a29e3c90ae7762bdf587d19302e62485b6bef46e114b741f7d75dba023bd3
    - registry.vivo.xyz:4443/google_containers/pause-amd64:3.0
    sizeBytes: 746888
  nodeInfo:
    architecture: amd64
    bootID: bc7a36a4-2d9b-4caa-b852-445a5fb1b0b9
    containerRuntimeVersion: docker://1.12.6
    kernelVersion: 3.10.0-514.el7.x86_64
    kubeProxyVersion: v1.7.4+793658f2d7ca7
    kubeletVersion: v1.7.4+793658f2d7ca7
    machineID: edaf7dacea45404b9b3cfe053181d317
    operatingSystem: linux
    osImage: CentOS Linux 7 (Core)
    systemUUID: 30393137-3136-4336-5537-3335444C4C30

© 著作权归作者所有

 

来源: https://my.oschina.net/jxcdwangtao/blog/1624486?nocache=1519707925759

flannel原理简析及安装

flannel是CoreOS提供用于解决Dokcer集群跨主机通讯的覆盖网络工具。它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,底层通过UDP/VxLAN等进行报文的封装和转发

flannel项目地址:https://github.com/coreos/flannel

flannel架构介绍

flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口。

那么一条网络报文是怎么从一个容器发送到另外一个容器的呢?

  1. 容器直接使用目标容器的ip访问,默认通过容器内部的eth0发送出去。
  2. 报文通过veth pair被发送到vethXXX
  3. vethXXX是直接连接到虚拟交换机docker0的,报文通过虚拟bridge docker0发送出去。
  4. 查找路由表,外部容器ip的报文都会转发到flannel0虚拟网卡,这是一个P2P的虚拟网卡,然后报文就被转发到监听在另一端的flanneld
  5. flanneld通过etcd维护了各个节点之间的路由表,把原来的报文UDP封装一层,通过配置的iface发送出去。
  6. 报文通过主机之间的网络找到目标主机。
  7. 报文继续往上,到传输层,交给监听在8285端口的flanneld程序处理。
  8. 数据被解包,然后发送给flannel0虚拟网卡。
  9. 查找路由表,发现对应容器的报文要交给docker0
  10. docker0找到连到自己的容器,把报文发送过去。

flannel安装配置

环境准备

一共三台机器:一个etcd集群,三台机器安装flannel和Docker。

节点名称IP地址软件环境
etcd1192.168.2.210etcd、flannel、docker
etcd2192.168.2.211etcd、flannel、docker
etcd3192.168.2.212etcd、flannel、docker

安装etcd

关于etcd的安装使用已经在「etcd使用入门」和「通过静态发现方式部署etcd集群」中做了比较详细的讲解,如果你还不会安装etcd可先阅读下这两篇文章。这里就不再重复讲解了。

安装flannel

三个节点都需安装配置flannel,这里以etcd1节点为例。

flannel和etcd一样,直接从官方下载二进制执行文件就可以用了。当然,你也可以自己编译。

1
2
3
$ curl -L https://github.com/coreos/flannel/releases/download/v0.7.0/flannel-v0.7.0-linux-amd64.tar.gz -o flannel.tar.gz
$ mkdir -p /opt/flannel
$ tar xzf flannel.tar.gz -C /opt/flannel

解压后主要有flanneldmk-docker-opts.sh这两个文件,其中flanneld为主要的执行文件,sh脚本用于生成Docker启动参数。

配置flannel

由于flannel需要依赖etcd来保证集群IP分配不冲突的问题,所以首先要在etcd中设置 flannel节点所使用的IP段。

1
2
3
4
$ etcdctl --endpoints "http://etcd1.hi-linux.com:2379" \
set /coreos.com/network/config '{"NetWork":"10.0.0.0/16", "SubnetMin": "10.0.1.0", "SubnetMax": "10.0.20.0"}'

{"NetWork":"10.0.0.0/16", "SubnetMin": "10.0.1.0", "SubnetMax": "10.0.20.0"}

flannel预设的backend type是udp,如果想要使用vxlan作为backend,可以加上backend参数:

1
2
$ etcdctl --endpoints "http://etcd1.hi-linux.com:2379" \
set /coreos.com/network/config '{"NetWork":"10.0.0.0/16", "Backend": {"Type": "vxlan"}}'

flannel backend为vxlan比起预设的udp性能相对好一些。

启动flannel

  • 命令行方式运行
1
$ /opt/flannel/flanneld --etcd-endpoints="http://etcd1.hi-linux.com:2379" --ip-masq=true >> /var/log/flanneld.log 2>&1 &
  • 后台服务方式运行

给flannel创建一个systemd服务,方便以后管理。创建flannel配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ cat <<EOF | sudo tee /etc/systemd/system/flanneld.service
[Unit]
Description=Flanneld
Documentation=https://github.com/coreos/flannel
After=network.target
Before=docker.service

[Service]
User=root
ExecStart=/opt/flannel/flanneld \
--etcd-endpoints="http://etcd1.hi-linux.com:2379,http://etcd2.hi-linux.com:2379,http://etcd3.hi-linux.com:2379" \
--iface=192.168.2.210 \
--ip-masq
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

注意:--iface参数为要绑定的网卡的IP地址,请根据实际情况修改。

启动flannel服务

1
$ systemctl start flanneld

flannel启动过程解析

flannel服务需要先于Docker启动。flannel服务启动时主要做了以下几步的工作:

  • 从etcd中获取network的配置信息。
  • 划分subnet,并在etcd中进行注册。
  • 将子网信息记录到/run/flannel/subnet.env中。

验证flannel网络

在etcd1节点上看etcd中的内容

1
2
3
$ etcdctl  --endpoints "http://etcd1.hi-linux.com:2379" ls /coreos.com/network/subnets

/coreos.com/network/subnets/10.0.2.0-24

查看flannel0的网络情况:

1
2
3
4
5
6
7
8
$ ifconfig flannel0
flannel0  Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:10.0.2.0  P-t-P:10.0.2.0  Mask:255.255.0.0
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1472  Metric:1
          RX packets:85 errors:0 dropped:0 overruns:0 frame:0
          TX packets:75 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:7140 (7.1 KB)  TX bytes:6300 (6.3 KB)

可以看到flannel0网卡的地址和etcd中存储的地址一样,这样flannel网络配置完成。

配置Docker

在各个节点安装好以后最后要更改Docker的启动参数,使其能够使用flannel进行IP分配,以及网络通讯。

flannel运行后会生成一个环境变量文件,包含了当前主机要使用flannel通讯的相关参数。

  • 查看flannel分配的网络参数
1
2
3
4
5
6
$ cat /run/flannel/subnet.env

FLANNEL_NETWORK=10.0.0.0/16
FLANNEL_SUBNET=10.0.2.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true
  • 创建Docker运行参数

使用flannel提供的脚本将subnet.env转写成Docker启动参数,创建好的启动参数位于/run/docker_opts.env文件中。

1
2
3
4
$ /opt/flannel/mk-docker-opts.sh -d /run/docker_opts.env -c

$ cat /run/docker_opts.env
DOCKER_OPTS=" --bip=10.0.2.1/24 --ip-masq=false --mtu=1472"
  • 修改Docker启动参数

修改docker的启动参数,并使其启动后使用由flannel生成的配置参数,修改如下:

1
2
3
4
5
6
# 编辑 systemd service 配置文件
$ vim /lib/systemd/system/docker.service
# 在启动时增加flannel提供的启动参数
ExecStart=/usr/bin/dockerd $DOCKER_OPTS
# 指定这些启动参数所在的文件位置(这个配置是新增的,同样放在Service标签下)
EnvironmentFile=/run/docker_opts.env

然后重新加载systemd配置,并重启Docker即可

1
2
$ systemctl daemon-reload
$ systemctl restart docker

此时可以看到docker0的网卡ip地址已经处于flannel网卡网段之内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ifconfig flannel0
flannel0  Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:10.0.2.0  P-t-P:10.0.2.0  Mask:255.255.0.0
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1472  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

$ ifconfig docker0
docker0   Link encap:Ethernet  HWaddr 02:42:cf:87:3c:f7
          inet addr:10.0.2.1  Bcast:0.0.0.0  Mask:255.255.255.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

到此节点etcd1的flannel安装配置完成了,其它两节点按以上方法配置完成就行了。

测试flannel

三台机器都配置好了之后,我们在三台机器上分别开启一个docker容器,测试它们的网络是否可相互联通的。

etcd1

1
2
3
4
5
$ docker run -it  busybox sh

# 查看容器IP
$ cat /etc/hosts
10.0.2.2	9de86bfde6cc

etcd2

1
2
3
4
5
$ docker run -it  busybox sh

# 查看容器IP
$ cat /etc/hosts
10.0.5.2	9ddd4a4e455b

etcd3

1
2
3
4
5
$ docker run -it  busybox sh

# 查看容器IP
$ cat /etc/hosts
10.0.6.2	cbb0d891f353
  • 从不同宿主机容器到三台宿主机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/ # ping -c3 192.168.2.210
PING 192.168.2.210 (192.168.2.210): 56 data bytes
64 bytes from 192.168.2.210: seq=0 ttl=64 time=0.089 ms
64 bytes from 192.168.2.210: seq=1 ttl=64 time=0.065 ms

/ # ping -c5 192.168.2.211
PING 192.168.2.211 (192.168.2.211): 56 data bytes
64 bytes from 192.168.2.211: seq=0 ttl=63 time=1.712 ms
64 bytes from 192.168.2.211: seq=1 ttl=63 time=0.356 ms
64 bytes from 192.168.2.211: seq=2 ttl=63 time=2.201 ms

/ # ping -c3 192.168.2.212
PING 192.168.2.212 (192.168.2.212): 56 data bytes
64 bytes from 192.168.2.212: seq=0 ttl=63 time=0.467 ms
64 bytes from 192.168.2.212: seq=1 ttl=63 time=0.477 ms
64 bytes from 192.168.2.212: seq=2 ttl=63 time=0.532 ms
  • 从容器到到跨宿主机容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/ # ping -c3  10.0.5.2
PING 10.0.5.2 (10.0.5.2): 56 data bytes
64 bytes from 10.0.5.2: seq=0 ttl=60 time=0.692 ms
64 bytes from 10.0.5.2: seq=1 ttl=60 time=0.565 ms
64 bytes from 10.0.5.2: seq=2 ttl=60 time=1.135 ms

/ # ping -c3  10.0.6.2
PING 10.0.6.2 (10.0.6.2): 56 data bytes
64 bytes from 10.0.6.2: seq=0 ttl=60 time=0.678 ms
64 bytes from 10.0.6.2: seq=1 ttl=60 time=0.907 ms
64 bytes from 10.0.6.2: seq=2 ttl=60 time=1.272 ms

/ # ping -c3 10.0.2.2
PING 10.0.2.2 (10.0.2.2): 56 data bytes
64 bytes from 10.0.2.2: seq=0 ttl=60 time=0.644 ms
64 bytes from 10.0.2.2: seq=1 ttl=60 time=0.915 ms
64 bytes from 10.0.2.2: seq=2 ttl=60 time=1.032 ms

测试容器到到跨宿主机容器遇到一个坑,开始怎么都不通,后找到原因是宿主机iptables给阻挡掉了。附:Ubuntu一键清除iptables规则脚本

1
2
3
4
5
6
7
8
9
10
$ cat clear_iptables_rule.sh

#!/bin/bash

iptables -F
iptables -X
iptables -Z
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

参考文档

http://www.google.com

http://t.cn/RcnGQ02

http://t.cn/RXVHGpI

http://t.cn/RXfavPG

http://t.cn/RXfEThA

http://t.cn/RXfEmS8

http://t.cn/R5Xgfnx

 

来源:https://www.hi-linux.com/posts/30481.html

深入理解flannel

根据官网的描述,flannel是一个专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。

1.概况


首先,flannel利用Kubernetes API或者etcd用于存储整个集群的网络配置,其中最主要的内容为设置集群的网络地址空间。例如,设定整个集群内所有容器的IP都取自网段“10.1.0.0/16”。

接着,flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。

然后,flanneld再将本主机获取的subnet以及用于主机间通信的Public IP,同样通过kubernetes API或者etcd存储起来。

最后,flannel利用各种backend mechanism,例如udp,vxlan等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。

 
1.1容器间的跨主机通信如何运行

如下图所示,集群范围内的网络地址空间为10.1.0.0/16,Machine A获取的subnet为10.1.15.0/24,且其中的两个容器IP分别为10.1.15.2/24和10.1.15.3/24,两者都在10.1.15.0/24这一子网范围内,对于下方的Machine B同理。如果上方Machine A中IP地址为10.1.15.2/24的容器要与下方Machine B中IP地址为10.1.16.2/24的容器进行通信,封包是如何进行转发的。从上文可知,每个主机的flanneld会将自己与所获取subnet的关联信息存入etcd中,例如,subnet 10.1.15.0/24所在主机可通过IP 192.168.0.100访问,subnet 10.1.16.0/24可通过IP 192.168.0.200访问。反之,每台主机上的flanneld通过监听etcd,也能够知道其他的subnet与哪些主机相关联。如上图,Machine A上的flanneld通过监听etcd已经知道subnet 10.1.16.0/24所在的主机可以通过Public 192.168.0.200访问,而且熟悉docker桥接模式的同学肯定知道,目的地址为10.1.16.2/24的封包一旦到达Machine B,就能通过cni0网桥转发到相应的pod,从而达到跨宿主机通信的目的。

因此,flanneld只要想办法将封包从Machine A转发到Machine B就OK了,而上文中的backend就是用于完成这一任务。不过,达到这个目的的方法是多种多样的,所以我们也就有了很多种backend. 在这里我们举例介绍的是最简单的一种方式hostgw : 因为Machine A和Machine B处于同一个子网内,它们原本就能直接互相访问。因此最简单的方法是:在Machine A中的容器要访问Machine B的容器时,我们可以将Machine B看成是网关,当有封包的目的地址在subnet 10.1.16.0/24范围内时,就将其直接转发至B即可。而这通过图中那条红色标记的路由就能完成,对于Machine B同理可得。由此,在满足仍有subnet可以分配的条件下,我们可以将上述方法扩展到任意数目位于同一子网内的主机。而任意主机如果想要访问主机X中subnet为S的容器,只要在本主机上添加一条目的地址为R,网关为X的路由即可。

下面,我们以问题驱动的方式来详细分析flannel是如何运作的。

 
2.节点初始化


首先,我们最感兴趣的是,当一个新的节点加入集群时,它是如何初始化的。对此,我们可能会有以下几个疑问:

若主机有多张网卡和多个IP,如何选择其中的一张网卡和一个IP用于集群主机间的通信?主机如何获取属于自己的subnet并维护?

我们如何在集群中有新的节点加入时,获取对应的subnet和Public IP,并通过配置backend进行访问?

 
2.1网卡及对外IP选择

对于第一个问题,事实上我们可以在flanneld的启动参数中通过”–iface”或者”–iface-regex”进行指定。其中”–iface”的内容可以是完整的网卡名或IP地址,而”–iface-regex”则是用正则表达式表示的网卡名或IP地址,并且两个参数都能指定多个实例。flannel将以如下的优先级顺序来选取:

1) 如果”–iface”和”—-iface-regex”都未指定时,则直接选取默认路由所使用的输出网卡

2) 如果”–iface”参数不为空,则依次遍历其中的各个实例,直到找到和该网卡名或IP匹配的实例为止

3) 如果”–iface-regex”参数不为空,操作方式和2)相同,唯一不同的是使用正则表达式去匹配

最后,对于集群间交互的Public IP,我们同样可以通过启动参数”–public-ip”进行指定。否则,将使用上文中获取的网卡的IP作为Public IP。

 
2.2获取subnet

在获取subnet之前,我们首先要创建一个SubnetManager,它在具体的代码实现中,表现为一个接口,如下所示:

从接口中各个函数的名字,我们大概就能猜出SubnetManager的作用是什么了。但是,为什么获取subnet的函数叫AcquireLease,而不叫AcquireSubnet呢?实际上,每台主机都是租借了一个subnet,如果到了一定时间不进行更新,那么该subnet就会过期从而重新分配给其他的主机,即主机和subnet的关联信息会从etcd中消失(在本文中我们将默认选择etcd作为SubnetManager的后端存储)。因此,lease就是一条subnet和所属主机的关联信息,并且具有时效性,需要定期更新。

下面我们来看看,每台主机都是如何获取lease的:

1) 首先,我们调用GetNetworkConfig(),它会访问etcd获取集群网络配置并封装在结构Config中返回,Config结构如下所示。其中的Network字段对应的集群网络地址空间是在flannel启动前,必须写入etcd中的,例如”10.1.0.0/16″。

对于其他字段的含义及默认值如下:

①SubnetLen表示每个主机分配的subnet大小,我们可以在初始化时对其指定,否则使用默认配置。在默认配置的情况下,如果集群的网络地址空间大于/24,则SubnetLen配置为24,否则它比集群网络地址空间小1,例如集群的大小为/25,则SubnetLen的大小为/26

②SubnetMin是集群网络地址空间中最小的可分配的subnet,可以手动指定,否则默认配置为集群网络地址空间中第一个可分配的subnet。例如对于”10.1.0.0/16″,当SubnetLen为24时,第一个可分配的subnet为”10.1.1.0/24″。

③ SubnetMax表示最大可分配的subnet,对于”10.1.0.0/16″,当subnetLen为24时,SubnetMax为”10.1.255.0/24″

④BackendType为使用的backend的类型,如未指定,则默认为“udp”

⑤ Backend中会包含backend的附加信息,例如backend为vxlan时,其中会存储vtep设备的mac地址

2) 在获取了集群的网络配置之后,接下来我们就调用SubnetManager中的AcquireLease()获取本主机的subnet。其中的参数类型LeaseAttrs如下所示:

显然,其中最重要的字段就是Public IP,它实质上是标识了一台主机。在获取subnet之前,我们先要从etcd中获取当前所有已经存在的lease信息—-leases,以备后用。下面我们将对不同情况下lease的获取进行讨论:

① 事实上,这可能并不是我们第一次在这台机器上启动flannel,因此,很有可能,在此之前,这台机器已经获取了lease。已知一台主机其实是由它的Public IP标识的,所以我们可以用Public IP作为关键字匹配leases中所有lease的Public IP。若匹配成功,则检查相应的lease是否和当前的集群网络配置兼容:检查的内容包括IP是否落在SubnetMin和SubnetMax内,以及subnet大小是否和SubnetLen相等。若兼容,则用新的LeaseAttrs和ttl更新该lease,表示成功获取本机的lease,否则只能将该lease删除。

② 当初始化SubnetManager时,会先试图解析之前flannel获取了lease后留下的配置文件(该文件的创建,会在下文描述),从中读取出之前获取的subnet。如果读取到的subnet不为空,则同样利用该subnet去匹配leases中所有lease的subnet。若匹配成功,则同样检查lease是否和当前的集群网络配置兼容。若兼容则更新lease,表示成功获取本机的lease,否则将其删除。如果该subnet并不能从leases中找到,但是它和当前的集群网络配置兼容的话,可以直接将它和LeaseAttrs封装为lease,写入etcd。

③ 若此时还未获取到lease,那么我们有必要自己创建一个新的了。创建的方法很简单,从SubnetMin遍历到SubnetMax,将其中和leases中已有的subnet都不重合者加入一个集合中。再从该集合随机选择一个,作为本主机的subnet即可。最后,将subnet和LeaseAttrs封装为一个lease写入etcd。由此,该主机获取了自己的subnet。

最后,我们将有关的集群网络和subnet的配置信息写入文件/run/flannel/subnet.env(可通过命令行参数”–subnet-file”手动指定)中,写入的信息如下所示,包括:集群网络地址空间FLANNEL_NETWORK,获取的子网信息FLANNEL_SUBNET等等

 

2.3维护subnet

当SubnetManager的后端存储使用的是etcd时,各个主机还需要对自己的lease进行维护,在租期即将到期时,需要对etcd中的lease进行更新,调用SubnetManager中的RenewLease()方法,防止它到期后被自动删除。另外,我们可以在flanneld的命令行启动参数中用”–subnet-lease-renew-margin”指定在租期到期前多久进行更新。默认值为1小时,即每23小时更新一次lease,重新获取一次24小时的租期。

 
2.4发现新节点

现在,初始化已经完成了,我们需要面对如下两个问题:

1、当本主机的flanneld启动时,如果集群中已经存在了其他主机,我们如何通过backend进行配置,使得封包能够到达它们

2、如果之后集群中又添加了新的主机,我们如何获取这一事件,并通过backend对配置进行调整,对于删除主机这类事件同理

当然上述两个问题,都是通过etcd解决的。backend会一边通过上文中的WatchLeases()方法对etcd进行监听,从中获取各类事件,另一边会启动一个事件处理引擎,不断地对监听到的事件进行处理。

对于问题1,我们首先要从etcd中获取当前所有的lease信息,并将其转化为一系列的event,将它交于事件处理引擎进行处理,从而让封包能够到达这些主机。

对于问题2,直接对etcd中的事件进行监听,将获取的事件转换为事件处理引擎能够处理的形式,并进行处理即可。

事件的类型也很简单,总共就只有EventAdded和EventRemoved两种,分别表示新增了lease以及一个lease过期。因为不同backend的配置方式是完全不同的,下面我们就对各种backend的基本原理进行解析,并说明它们如何处理EventAdded和EventRemoved这两类事件。

 
3. backend原理解析


在本节中,我们将对hostgw,udp和vxlan三种backend进行解析。

 
3.1 hostgw

hostgw是最简单的backend,它的原理非常简单,直接添加路由,将目的主机当做网关,直接路由原始封包。

例如,我们从etcd中监听到一个EventAdded事件subnet为10.1.15.0/24被分配给主机Public IP 192.168.0.100,hostgw要做的工作就是在本主机上添加一条目的地址为10.1.15.0/24,网关地址为192.168.0.100,输出设备为上文中选择的集群间交互的网卡即可。对于EventRemoved事件,只需删除对应的路由。

 
3.2 udp

我们知道当backend为hostgw时,主机之间传输的就是原始的容器网络封包,封包中的源IP地址和目的IP地址都为容器所有。这种方法有一定的限制,就是要求所有的主机都在一个子网内,即二层可达,否则就无法将目的主机当做网关,直接路由。

而udp类型backend的基本思想是:既然主机之间是可以相互通信的(并不要求主机在一个子网中),那么我们为什么不能将容器的网络封包作为负载数据在集群的主机之间进行传输呢?这就是所谓的overlay。具体过程如下所示:

当容器10.1.15.2/24要和容器10.1.20.2/24通信时,因为该封包的目的地不在本主机subnet内,因此封包会首先通过网桥转发到主机中。最终在主机上经过路由匹配,进入网卡flannel0。需要注意的是flannel0是一个tun设备,它是一种工作在三层的虚拟网络设备,而flanneld是一个proxy,它会监听flannel0并转发流量。当封包进入flannel0时,flanneld就可以从flannel0中将封包读出,由于flannel0是三层设备,所以读出的封包仅仅包含IP层的报头及其负载。最后flanneld会将获取的封包作为负载数据,通过udp socket发往目的主机。同时,在目的主机的flanneld会监听Public IP所在的设备,从中读取udp封包的负载,并将其放入flannel0设备内。由此,容器网络封包到达目的主机,之后就可以通过网桥转发到目的容器了。

最后和hostgw不同的是,udp backend并不会将从etcd中监听到的事件里所包含的lease信息作为路由写入主机中。每当收到一个EventAdded事件,flanneld都会将其中的subnet和Public IP保存在一个数组中,用于转发封包时进行查询,找到目的主机的Public IP作为udp封包的目的地址。

 
3.3 vxlan

首先,我们对vxlan的基本原理进行简单的叙述。从下图所示的封包结构来看,vxlan和上文提到的udp backend的封包结构是非常类似的,不同之处是多了一个vxlan header,以及原始报文中多了个二层的报头。

下面让我们来看看,当有一个EventAdded到来时,flanneld如何进行配置,以及封包是如何在flannel网络中流动的。

如上图所示,当主机B加入flannel网络时,和其他所有backend一样,它会将自己的subnet 10.1.16.0/24和Public IP 192.168.0.101写入etcd中,和其他backend不一样的是,它还会将vtep设备flannel.1的mac地址也写入etcd中。

之后,主机A会得到EventAdded事件,并从中获取上文中B添加至etcd的各种信息。这个时候,它会在本机上添加三条信息:

1) 路由信息:所有通往目的地址10.1.16.0/24的封包都通过vtep设备flannel.1设备发出,发往的网关地址为10.1.16.0,即主机B中的flannel.1设备。

2) fdb信息:MAC地址为MAC B的封包,都将通过vxlan首先发往目的地址192.168.0.101,即主机B

3)arp信息:网关地址10.1.16.0的地址为MAC B

现在有一个容器网络封包要从A发往容器B,和其他backend中的场景一样,封包首先通过网桥转发到主机A中。此时通过,查找路由表,该封包应当通过设备flannel.1发往网关10.1.16.0。通过进一步查找arp表,我们知道目的地址10.1.16.0的mac地址为MAC B。到现在为止,vxlan负载部分的数据已经封装完成。由于flannel.1是vtep设备,会对通过它发出的数据进行vxlan封装(这一步是由内核完成的,相当于udp backend中的proxy),那么该vxlan封包外层的目的地址IP地址该如何获取呢?事实上,对于目的mac地址为MAC B的封包,通过查询fdb,我们就能知道目的主机的IP地址为192.168.0.101。

最后,封包到达主机B的eth0,通过内核的vxlan模块解包,容器数据封包将到达vxlan设备flannel.1,封包的目的以太网地址和flannel.1的以太网地址相等,三层封包最终将进入主机B并通过路由转发达到目的容器。

事实上,flannel只使用了vxlan的部分功能,由于VNI被固定为1,本质上工作方式和udp backend是类似的,区别无非是将udp的proxy换成了内核中的vxlan处理模块。而原始负载由三层扩展到了二层,但是这对三层网络方案flannel是没有意义的,这么做也仅仅只是为了适配vxlan的模型。vxlan详细的原理参见文后的参考文献,其中的分析更为具体,也更易理解。

 
4. 总结


总的来说,flannel更像是经典的桥接模式的扩展。我们知道,在桥接模式中,每台主机的容器都将使用一个默认的网段,容器与容器之间,主机与容器之间都能互相通信。要是,我们能手动配置每台主机的网段,使它们互不冲突。接着再想点办法,将目的地址为非本机容器的流量送到相应主机:如果集群的主机都在一个子网内,就搞一条路由转发过去;若是不在一个子网内,就搞一条隧道转发过去。这样以来,容器的跨网络通信问题就解决了。而flannel做的,其实就是将这些工作自动化了而已。

来源: http://blog.51cto.com/lookingdream/2093101

overlay 网络模式

Overlay是在传统网络上虚拟出一个虚拟网络来,传统网络不需要在做任何适配,这样物理层网络只对应物理层的计算(物理机、虚拟化层管理网),虚拟的网络只对应虚拟计算。

Overlay的技术路线,其实是从架构上对数据中心的建设模式进行了颠覆,对物理设备的要求降至最低,业务完全定义在层叠网络上。那么,这是否意味着将来数据中心使用Overlay网络不需要硬件支持而只需要软件定义就足够了呢?答案无疑是否定的。

Overlay 在网络技术领域,指的是一种网络架构上叠加的虚拟化技术模式,其大体框架是对基础网络不进行大规模修改的条件下,实现应用在网络上的承载,并能与其它网络业务分离,并且以基于IP的基础网络技术为主。Overlay 技术是在现有的物理网络之上构建一个虚拟网络,上层应用只与虚拟网络相关。一个Overlay网络主要由三部分组成:

边缘设备:是指与虚拟机直接相连的设备
控制平面:主要负责虚拟隧道的建立维护以及主机可达性信息的通告
转发平面:承载 Overlay 报文的物理网络

overlay network

overlay1.png

容器在两个跨主机进行通信的时候,是使用overlay network这个网络模式进行通信,如果使用host也可以实现跨主机进行通信,直接使用这个物理的ip地址就可以进行通信。overlay它会虚拟出一个网络比如10.0.9.3这个ip地址,在这个overlay网络模式里面,有一个类似于服务网关的地址,然后把这个包转发到物理服务器这个地址,最终通过路由和交换,到达另一个服务器的ip地址。

在docker容器里面overlay 是怎么实现的呢?

我们会有一个服务发现,比如说是consul,会定义一个ip地址池,比如10.0.9.0/24之类的,上面会有容器,容器的ip地址会从上面去获取,获取完了后,会通过eth1进行通信,贼这实现跨主机的东西。

overlay2.png

需要创建一个consul的服务容器

docker run -d -p 8400:8400 -p 8500:8500 -p 8600:53/udp -h consul progrium/consul -server -bootstrap -ui-dir /ui

修改它的启动参数

ExecStart=/usr/bin/docker daemon -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --cluster-store=consul://192.168.59.100:8500 --cluster-advertise=enp0s8:2376 --insecure-registry=0.0.0.0/0

hostA和hostB都需要修改

示例

两台主机

overlay3.png

overlay4.png

通过这个网卡出去

overlay5.png

修改它的启动参数,这里的ip等要修改成自己的。

overlay6.png

第二个服务器也一样

overlay7.png

修改完后,需要重启。

overlay8.png

查看重启后是否启动成功

overlay9.png

这边也一样

overlay10.png

在第一台主机上创建一个consul

overlay11.png

查看启动是否成功

overlay12.png

创建完后通过浏览器访问一下,可以看到这两台会自动注册上来,这样的话这两个主机之间就会进行通信。

overlay13.png

 

overlay网络模式实战


Create overlay network

docker network create -d overlay --subnet=10.0.9.0/24 edu-net

-d确定我们这个overlay 使用的是哪个驱动

Subnet:是子网定义一个网段

edu-net:网络名称

示例

在第一台机器上查看有哪些网络,可以看到没有overlay网络。

overlay1.png

在第二台机器上查看有哪些网络,可以看到也没有overlay网络。

overlay2.png

在第一台主机上创建一下

overlay3.png

这边自动回进行通步,因为使用的是同一个服务器发件。

overlay4.png


containers use overlay network

hostA

docker run -d --name app1 --net=edu-net reg.maiziedu.com:5000/nginx:1.11.1

创建一个容器

overlay5.png

创建成功

overlay6.png

登录进去,ip地址为9.2。

overlay7.png

它也具备一个nat网络模式。

overlay8.png


containers use overlay network

hostB

docker run -d --name app2 --net=edu-net reg.maiziedu.com:5000/nginx:1.11.1

示例

在hostB主机上也创建一个容器,叫app2,单也使用edu-net这个网络。

overlay9.png

登录进去,可以看到这台容器的ip是9.3。

overlay10.png

怎么去验证这两条容器是不是通的呢?

test overlay network:

overlay11.png

在A主机上的容器可以直接通,也可以ping域名。

overlay12.png

在B主机上的容器可以直接通,我们这里实现了跨主机通信,是通过overlay network这种网络模式进行通信的。

overlay13.png

 

来源: http://www.maiziedu.com/wiki/cloud/overlayact/

Overlay网络与物理网络的关系

编者按:无论是云计算还是SDN都把注意力集中在数据中心网络的建设上,各种解决方案层出不穷,其中以VMware为代表的软件厂商提出Overlay网络方案后,为数据中心网络的发展提出了新的思路。那么Overlay是如何与物理网络相互依存的?

在以往IT建设中,硬件服务器上运行的是虚拟层的计算,物理网络为了与虚拟服务器对接,需要网络自己进行调整,以便和新的计算层对接(如图1所示)。

Overlay网络与物理网络的关系

Overlay是在传统网络上虚拟出一个虚拟网络来,传统网络不需要在做任何适配,这样物理层网络只对应物理层的计算(物理机、虚拟化层管理网),虚拟的网络只对应虚拟计算(虚拟机的业务IP),如图2所示。

Overlay网络与物理网络的关系

Overlay的技术路线,其实是从架构上对数据中心的建设模式进行了颠覆,对物理设备的要求降至最低,业务完全定义在层叠网络上。那么,这是否意味着将来数据中心使用Overlay网络不需要硬件支持而只需要软件定义就足够了呢?答案无疑是否定的。

以下讨论Overlay网络与物理网络的依存关系。由于VXLAN(Virtual eXtensible LAN)技术是当前最为主流的Overlay标准,以下VXLAN技术为代表进行具体描述。

1. 报文的封装与解封装

VXLAN的核心在于承载于物理网络上的隧道技术,这就意味着要对报文进行封装和解封装,因此需要硬件来加速处理。

在VXLAN网络中,用于建立VXLAN隧道的端点设备称为VTEP(VXLAN Tunneling End Point,VXLAN隧道终结点),封装和解封装在VTEP节点上进行。

在云数据中心,部分业务是不适合进行虚拟化的(如小机服务器,高性能数据库服务器),这些服务器会直接与物理交换机互联,而他们又必须与对应租户/业务的VXLAN网络互通,此时就必须要求与其互联的硬件交换机也能支持VXLAN协议,以接入VXLAN网络。

Overlay网络与物理网络的关系

考虑到服务器接入的可以是虚拟交换机,也可以是物理交换机,因此存在三种不同的构建模式(如图3所示):其中网络Overlay方案中,所有终端均采用物理交换机作为VTEP节点;主机Overlay方案中,所有终端均采用虚拟交换机作为VTEP节点;混合Overlay方案中,既有物理交换机接入,又有虚拟交换机接入,且软件VTEP和硬件VTEP之间可以基于标准协议互通。

在网络Overlay方案和混合Overlay方案中,都需要有物理交换机设备支持VXLAN协议栈,并能与虚拟交换机构建的VTEP互通。由于在实际组网环境中,服务器种类很多,高吞吐高性能要求的业务一般都采用单独物理服务器甚至小机的硬件环境,而非虚拟化的x86服务器,这就没法使用vSwitch来接入VXLAN网络,只能让支持VXLAN的物理交换机来接入了。

2. 组播协议传播

VXLAN网络的MAC表与隧道终端的绑定关系要用组播协议传播,而大规格组播协议离不开物理网络设备的支持。

按照VXLAN的标准,每一个VTEP都需要了解其接入的终端MAC地址,同时还需要知道整网(该VXLAN实例中)其他VTEP下所有的终端MAC地址。只有这样,在本地的VTEP收到报文后需要转发时,才能根据目的MAC查询到需要送到远端的目的VTEP那里。

按照IETF中对VXLAN网络的定义,负责在网络中传播MAC地址和VTEP对应关系的机制,正是依托于物理网络中的组播协议。VTEP将本地的MAC地址表利用组播协议在整个组播中传播,从而使得整网中所有组播成员,也就是其他VTEP都知道本地的MAC地址表。当VTEP下的终端接入情况有所更改,如新增了MAC地址或者减少了MAC地址,也需要利用组播协议通知同一个实例下的所有VTEP。另外,当本地VTEP找不到目的MAC处于哪一个远端VTEP时,也需要发送组播报文来查找目的MAC主机所属的远端VTEP。

Overlay网络与物理网络的关系

如图4所示,多个VTEP需要在整网中传递VTEP下MAC地址信息,逻辑传递路线如绿色虚线所示。由于需要进行逻辑上的Full-Mesh连接,连接逻辑线路会达到N平方量级,因此实际组网中,VXLAN利用了物理网络的组播组,在建立好的组播组中加入VXLAN中所有VTEP成员,传递VTEP变更信息。在多用户多业务情况下,组播组要求与VXLAN数量息息相关。由于VXLAN网络规模的不断拓展(最大可达到16M个VXLAN网络),所需要的组播条目数会不断增加,这实际上对于物理网络承载组播处理能力和规格提出了要求。

由于标准VXLAN架构下使用组播协议,对物理网络组播数规格要求较大,因此H3C VXLAN解决方案基于SDN架构,通过引入全网的SDN Controller来实现VXLAN的管理和维护,使得VTEP之间的信息可以通过Controller来进行反射(如图5所示)。这样,VTEP的MAC地址表映射关系不再通过组播向全网其他VTEP传达,而是统一上报给控制器,由控制器统一下发给需要接受此消息的其他VTEP,由具体的VTEP执行转发机制。

Overlay网络与物理网络的关系

可见,在SDN架构下,硬件形态的VTEP需要能够支持集中控制器下发的业务控制信息,同时基于Openflow进行流表转发。而传统硬件交换机不能支持上述特性,必须由新硬件设备来执行和完成的。

3. VXLAN网络互通

在传统L2网络中,报文跨VLAN转发,需要借助三层设备来完成不同VLAN之间的互通问题。VXLAN网络与传统网络、以及VXLAN网络的互通,必须有网络设备的支持。

VXLAN网络框架中定义了两种网关单元。

VXLAN三层网关。用于终结VXLAN网络,将VXLAN报文转换成传统三层报文送至IP网络,适用于VXLAN网络内服务器与远端终端之间的三层互访;同时也用作不同VXLAN网络互通。如图6所示,当服务器访问外部网络时,VXLAN三层网关剥离对应VXLAN报文封装,送入IP网络;当外部终端访问VXLAN内的服务器时,VXLAN根据目的IP地址确定所属VXLAN及所属的VTEP,加上对应的VXLAN报文头封装进入VXLAN网络。VXLAN之间的互访流量与此类似,VXLAN网关剥离VXLAN报文头,并基于目的IP地址确定所属VXLAN及所属的VTEP,重新封装后送入另外的VXLAN网络。

Overlay网络与物理网络的关系

VXLAN二层网关。用于终结VXLAN网络,将VXLAN报文转换成对应的传统二层网络送到传统以太网络,适用于VXLAN网络内服务器与远端终端或远端服务器的二层互联。如在不同网络中做虚拟机迁移时,当业务需要传统网络中服务器与VXLAN网络中服务器在同一个二层中,此时需要使用VXLAN二层网关打通VXLAN网络和二层网络。如图7所示,VXLAN 10网络中的服务器要和IP网络中VLAN100的业务二层互通,此时就需要通过VXLAN的二层网关进行互联。VXLAN10的报文进入IP网络的流量,剥掉VXLAN的报文头,根据VXLAN的标签查询对应的VLAN网络(此处对应的是VLAN100),并据此在二层报文中加入VLAN的802.1Q报文送入IP网络;相反VLAN100的业务流量进入VXLAN也需要根据VLAN获知对应的VXLAN网络编号,根据目的MAC获知远端VTEP的IP地址,基于以上信息进行VXLAN封装后送入对应的VXLAN网络。

Overlay网络与物理网络的关系

可见,无论是二层还是三层网关,均涉及到查表转发、VXLAN报文的解封装和封装操作。从转发效率和执行性能来看,都只能在物理网络设备上实现,并且传统设备无法支持,必须通过新的硬件形式来实现。

结束语

Overlay由于其简单、一致的解决问题方法,加上重新定义的网络可以进行软件定义,已经成为数据中心网络最炙手可热的技术方案。然而,它并不是一张完全由软件定义的网络,Overlay网络解决方案必定是一种软硬结合的方案,无论是从接入层VTEP混合组网的组网要求、组播或SDN控制层协议的支持,还是VXLAN网络与传统网络的互通来看,都需要硬件积极的配合和参与,必须构建在坚实和先进的物理网络架构基础上。
转载自:http://www.h3c.com.cn/About_H3C/Company_Publication/IP_Lh/2014/07/Home/Catalog/201501/852551_30008_0.htm