开发环境下的 Kubernetes 容器网络演进之路
马蜂窝技术原创文章,更多干货请搜索公众号:mfwtech
使用 Docker+Kubernetes 来简化开发人员的工作流,使应用更加快速地迭代,缩短发布周期,在很多研发团队中已经是常见的做法。
如果说 Docker 提供的是应用级的主机抽象,那么 Kubernetes 的作用就是应用级的集群抽象,提供容器集群运行所需的基础设施,旨在解决容器化应用的资源调度、部署运行、服务发现、扩容缩容等问题。
一直以来,容器网络设计都被认为是非常重要,但相对复杂的部分。 本文要介绍的 Kubernetes 网络,目前主要为马蜂窝旅游网大多数 Java 业务提供开发环境的底层基础设施。随着团队的调整及业务需求升级,整个系统对网络架构的要求也越来越严苛。基于这样的背景,Kubernetes 网络过去一年多经历了两次比较重要的改造,以期不断降低运维调试成本,提高研发效率。
借由本文分享我们的实践经验,与君共勉。
Part.1 Kubernetes 网络原理及挑战
1. Kubernetes Pod 设计
Pod 是 Kubernetes 的基本调度单元,我们可以将 Pod 认为是容器的一种延伸扩展,一个 Pod 也是一个隔离体,而 Pod 内部又包含一组共享的容器。
每个 Pod 中的容器由一个特殊的 Pause 容器,及一个或多个紧密相关的业务容器组成。Pause 容器是 Pod 的根容器,对应的镜像属于 Kubernetes 平台的一部分,以它的状态代表整个容器组的状态。同一个 Pod 里的容器之间仅需通过 localhost 就能互相通信。
每个 Pod 会被 Kubernetes 网络组件分配一个唯一的(在集群内的 IP 地址,称为 Pod IP,这样就允许不同 Pod 中的服务可以使用同一端口 (同一个 Pod 中端口只能被一个服务占用),避免了发生端口冲突的问题。
2. 挑战
Pod 的 IP 是在 Kubernetes 集群内的,是虚拟且局域的。这就带来一个问题,Pod IP 在集群内部访问没有问题,但在实际项目开发中,难免会有真实网络环境下的应用需要访问 Kubernetes 集群里的容器,这就需要我们做一些额外的工作。
本文将结合我们开发环境下基于 K8S 容器网络演进的过程,介绍在 Pod IP 为虚拟或真实 IP 情况下的几种直接访问 Pod IP 的解决方案。
Part.2 基于 Kubernetes 的容器网络演进
第一阶段:Kubernetes + Flannel
最早,我们的网络设计方案只服务于大交通业务。初期大交通的 Java 应用是部署在物理机上的,团队内部容器相关的底层基础设施几乎为 0。为了更加平稳地实现容器化的落地,中间我们没有直接把服务全部迁移到 K8S 中去,而是经历了一段混跑。
这个过程中必然会有物理机上的 Java 应用访问 K8S 里 Java 容器的情况 (一个注册中心)。当时我们选用的网络架构是 Flannel VXLAN + Kube-proxy,主要是考虑到 Flannel 的简洁性。
Flannel 是为 K8S 设计的一个非常简洁的多节点三层网络方案,主要用于解决容器的跨主机通信问题,是一个比较大一统的方案。它的设计目的是为集群中的所有节点重新规划 IP 地址的使用规则,从而使得不同节点上的容器能够获得「同属一个内网」且「不重复的」IP 地址,并让属于不同节点上的容器能够直接通过内网 IP 通信。
Flannel 的原理是为每个 host 分配一个 subnet,容器从此 subnet 中分配 IP,这些 IP 可以在 host 间路由,容器间无需 NAT 和 port mapping 就可以跨主机通信。每个 subnet 都是从一个更大的 IP 池中划分的,Flannel 会在每个 host 上面运行一个守护进程 flanneld,其职责就是从大池子中分配 subnet,为了各个主机间共享信息。Flannel 用 ETCD 存放网络配置、已分配的 subnet、host 的 IP 等信息。
Flannel 的节点间有三种通信方式:
VXLAN:默认配置,利用内核级别的 VXLAN 来封装 host 之间传送的包
Host-gw:二层网络配置,不支持云环境,通过在 host 的路由表中直接创建到其他主机 subnet 的路由条目
UDP:通常用于 debug
我们在所有的业务物理机上都部署了 Flannel,并且启动 Flanneld 服务,使之加入 K8S 虚拟网络,这样物理机上的服务与 K8S 里的容器服务在互相调用时,就可以通过 Flannel+Kube-proxy 的虚拟网络。整体结构图如下:
流量 1 是部署在物理机和 K8S 容器里的应用注册服务的线路
流量 2 是两个物理机节点互相调用时的线路
流量 3 是物理机节点调用 K8S 容器应用时的线路,反之 app3 调用 app1 时就会直接访问 app1 所在的物理机 IP
第二阶段:Kubernetes + Flannel+ VPN-server
为了加速大交通业务微服务和容器化的落地,我们在团队内部完成了开发环境容器化的改造,并将所有流量全部迁移到 K8S 编排系统上。
大交通整体的微服务改造基于 Dubbo。了解的朋友都知道,Dubbo 服务中 Consumer 和 Provider 的通讯很常见,对应到我们的场景就有了本地 Dubbo 服务 (Consumer) 消费开发环境微服务 (Provider),以及本地远程 debug 开发环境的情况。但是 Dubbo consumer 从注册中心拿到的都是容器的虚拟网络 IP,在集群外的真实网络环境里根本访问不通。
为了解决这个问题,提高开发与联调效率,我们开始了第一次 K8S 容器网络方案的改造。
我们设想,既然一个公司的员工可以通过拨通 VPN,从公网进入到公司的内网,那如果在 K8S 集群内部署一个 OpenVPN 的 Server,是不是也可以从集群外进入到集群的内网之中?实现思路如下图所示:
我们在集群的 middleware 空间下以 nodeport 的方式部署了 VPN Server,并给客户端分配了 10.140 的网段
当客户端通过 172.18.12.21:30030 拨通 VPN 时,客户端与 VPN Server 间的网络被打通
因为 VPN Server 本身处于集群虚拟网络环境中,所以其他容器的 IP 对于 vpn server 是可见的,因此拨通 VPN 后,VPN 客户端就可以直接对集群内的 Pod 进行访问
改造后开发环境与机房 K8S 集群之间的网络架构图如下所示:
通过在 K8S 集群里架设 OpenVPN,我们暂时解决了办公区对机房 K8S 集群的 RPC 通讯问题。该方案的优缺点如下:
优点:
快速实现
工程量小
网络隔离,证书验证更安全
不足:
操作略繁琐,如使用时需要申请证书,安装客户端软件;每次使用前需要先打开 OpenVPN
是一种中间方案,没有从本质上解决问题
第三阶段:基于 MAC-VLAN 的 Kubernetes CNI
为了更好地支持 Java 业务的微服务改造,避免重复造轮子,我们构建了统一的 Java 基础平台,之前的网络方案也面向更多的部门提供服务。
随着服务的业务和人员越来越多,由人工操作带来的不便和问题越来越明显,我们决定对 K8S 网络进行再一次改造。从之前的经验中我们感到,如果 K8S 的虚拟网络不去除,容器服务的 IP 是不可能直接由集群外的真实网络到达的。
为了快速满足不同业务场景下对于 K8S 网络功能的需求,我们开始深入研究 CNI。
关于 CNI
CNI (Conteinre Network Interface) 旨在为容器平台提供统一的网络标准,由 Google 和 CoreOS 主导制定。它本身并不是一个完整的解决方案或者程序代码,而是综合考虑了灵活性、扩展性、IP 分配、多网卡等因素后,在 RKT 的基础上发展起来的一种容器网络接口协议。
CNI 的网络分类和主流的实现工具主要包括:
第⼀类:与宿主机平⾏⽹络(2 层⽹络或 3 层⽹络),⽹络插件主要包括 Bridge、MAC-VLAN 、IP-VLAN、Calico BGP、Flannel host-GW 等
第⼆类:利⽤ SDN 技术的虚拟⽹络,⽹络插件主要有:Flannel vxlan、Calico ipip、Weave 等
MAC-VLAN 及其带来的问题
通过对比测试,考虑到当下需求,我们最终决定基于网络改造、运维成本和复杂度都较低的 MAC-VLAN 方案:
MAC-VLAN 具有 Linux Kernal 的特性,用于给一个物理网络接口(parent)配置虚拟化接口。虚拟化接口与 parent 网络接口拥有不同的 mac 地址,但 parent 接口上收到发给其对应的虚拟化接口的 mac 包时,会分发给对应的虚拟化接口,有点像将虚拟化接口和 parent 接口进行了「桥接」,使虚拟化网络接口在配置了 IP 和路由后就能互相访问。
在 MAC-VLAN 场景下,K8S 容器访问可能会遇到一些问题,比如配置 MAC-VLAN 后,容器不能访问 parent 接口的 IP。
通过调研,发现有以下两种解决方案:
1. 虚拟网卡:打开网卡混杂模式,通过在宿主机上虚拟出一个虚拟网卡,将虚拟网卡与宿主机真实网卡聚合绑定
2. PTP 方案:类似 Bridge 的简化版,但是网络配置更复杂,并且有一些配置在自测过程中发现并没有太大用处。与 Bridge 主要的不同是 PTP 不使用网桥,而是直接使用 vethpair+路由配置。
通过对比两种方案的可实施性,最终我们选择了第一种方案,但是第一种方案在虚拟机环境中经过测试发现偶尔会有宿主机与本机容器不通的现象,建议采用第一种方案的同学尽量不要使用虚拟机环境(怀疑还是网卡混杂设置的问题)。
「分区而治」的网络改造
K8S 的核心组件 KubeDNS 在启动时默认会访问 ClusterIP 段的第一个 IP;如果继续使用原有的 Nginx-ingress,那么 Nginx-ingress 的容器在启动时也是使用 ClusterIP 去连接 KubeDNS。而使用 MAC-VLAN 给 KubeDNS 和 Nginx-ingress 分配的都是真实网络 IP,他们是无法联通 ClusterIP 的,所以 KubeDNS 和 Nginx-ingress 容器又不能使用 MAC-VLAN 方案,否则容器服务的域名访问能力将丧失。
综合考虑之下,最终我们采取了「分区而治」的措施,将不同类别的容器调度到不同的区域,使用不同的网络方案,大致的图解如下:
采用 MAC-VLAN 方案时容器 IP 的分配方案有两种:DHCP 和 host-local。考虑到公司目前没有统一的 DHCP 和 IPAM 服务器为容器分配 IP,开发环境的机器数量不多,我们就采用了人工参与每个节点的网络 IP 段分配。
综上,此次改造后的网络架构图大致如下:
效果
可以看到与第一次网络改造的架构图对比:
宿主机 1 和宿主机 2 上已经抛弃了 Kube-proxy 和 Flannel 这些虚拟网络的组件
宿主机 1 和宿主机 2 这些业务容器节点直接使用了公司公共 DNS 设施
办公区本地 consumer 服务在注册中心拿到 provider 的 IP 后,可以直接连接消费,反之亦可
K8S 集群分为了虚拟网络区 (运行 K8S 集群管理组件) 和真实网络区 (运行业务容器)
此次改造的优势和不足总结为:
优点:
远程 debug
办公网与集群内服务间的 RPC,TCP 通讯在二层网络中打通
网络延迟大大降低
支持多机房容灾部署
缺点:
工程量大
需要耗费大量真实 IP 地址
集群规模很大时,存在 ARP 广播风暴和交换机 MAC 表超限风险
Part.3 近期优化方向
每一次挑战都是进步的基石。K8S 网络系统自上线以来,极大提高了 Java 业务容器化的运维效率,降低了运维成本,同时提供了更灵活、更稳定的服务运行环境。
在使用和改造 K8S 网络的过程中, 我们也遇到了很多问题,比如服务的优雅上下线、容器 HPA 的设定规则、容器模版的多样化支持等等,近期我们将重点针对以下几方面近一步优化和改进:
1. 生产环境逐步进行网络改造,并实现集群多机房部署,提高容灾能力
2. 对第二次网络改造中的虚拟网络区中的 Nginx-ingress 二次开发,使其支持使用公司公共 DNS,并且废弃 SVC,彻底抛弃虚拟网络的使用
本文作者:张小伍,马蜂窝旅游网交酒平台研发工程师。
开发环境下的 Kubernetes 容器网络演进之路的更多相关文章
- iOS系统提供开发环境下命令行编译工具:xcodebuild
iOS系统提供开发环境下命令行编译工具:xcodebuild[3] xcodebuild 在介绍xcodebuild之前,需要先弄清楚一些在XCode环境下的一些概念[4]: Workspace:简单 ...
- 利用Redis实现集群或开发环境下SnowFlake自动配置机器号
前言: SnowFlake 雪花ID 算法是推特公司推出的著名分布式ID生成算法.利用预先分配好的机器ID,工作区ID,机器时间可以生成全局唯一的随时间趋势递增的Long类型ID.长度在17-19位. ...
- 海纳百川无所不容,Win10环境下使用Docker容器式部署前后端分离项目Django+Vue.js
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_179 随着现代化产品研发的不断推进,我们会发现,几乎每个产品线都会包含功能各异的服务,而且服务与服务之间存在也会存在着错综复杂的依 ...
- spark最新源码下载并导入到开发环境下助推高质量代码(Scala IDEA for Eclipse和IntelliJ IDEA皆适用)(以spark2.2.0源码包为例)(图文详解)
不多说,直接上干货! 前言 其实啊,无论你是初学者还是具备了有一定spark编程经验,都需要对spark源码足够重视起来. 本人,肺腑之己见,想要成为大数据的大牛和顶尖专家,多结合源码和操练编程. ...
- angular 4 开发环境下打包文件过大
angular 4本地开发环境下,ng server -- port 8080 -o 之后在在浏览器中查看数据请求,其中vendor.bundle.js有8.3mb,而整个传输数据大小为16.3mb ...
- centos7.2环境下安装smokeping对网络状态进行监控
centos7.2环境下安装smokeping对网络状态进行监控 安装smokeping建议用centos7,用centos6.5一直卡在smokeping那里,下载不了perl的扩展插件,可能是因为 ...
- 【应用笔记】【AN005】Qt开发环境下基于RS485的4-20mA电流采集
简介 4-20mA电流环具有广泛的应用前景,在许多行业中都发挥着重要作用.本文主要介绍在Qt开发环境下基于RS485实现4-20mA电流采集,实现WINDOWS平台对数据的采集.分析及显示. 系统组成 ...
- jdk1.8源码包下载并导入到开发环境下助推高质量代码(Eclipse、MyEclipse和Scala IDEA for Eclipse皆适用)(图文详解)
不多说,直接上干货! jdk1.8 源码, Linux的同学可以用的上. 由于源码JDK是前版本的超集, 所以1.4, 1.5, 1.6, 1.7都可以用的上. 其实大家安装的jdk路径下,这 ...
- asp.net core 2.1开发环境下配置IIS
asp.net core 2.1是可以在开发环境下配置到IIS中的,这样就可以在vs中按F5直接运行了 在项目上右键点击属性,然后在Debug中配置如下参数 首先点击New按钮,输入IIS,点击确定 ...
随机推荐
- Codevs 3002 石子归并 3(DP四边形不等式优化)
3002 石子归并 3 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次 ...
- LOJ6071. 「2017 山东一轮集训 Day5」字符串 [SAM]
LOJ 思路 这种计数题显然是要先把每一个合法的串用唯一的方法表示出来.(我连这都没想到真是无可救药了) 如何唯一?容易想到把前缀尽可能多地在第一个串填掉,然后填第二个,第三个-- 如何做到这样?可以 ...
- 数组思维 -- join的一些用法感悟
组合字符串的时候, 组合 sql 的时候, 使用join 会非常有用, join and 记得前端时间去看面试题的时候, 总会出一个小的性能题目, 就是 如果有大量的字符串处理的时候, 怎么 ...
- 数据结构Java版之深度优先-图(十二)
这里用深度优先遍历存在矩阵里面的图. 深度优先利用的是栈的FIFO特性.为此遍历到底后,可以找到最相邻的节点继续遍历.实现深度优先,还需要在节点加上一个访问标识,来确定该节点是否已经被访问过了. 源码 ...
- JAVA的日期类DATE
好记性不如烂笔头. 1:常见场景 字符串转时间格式,日期转换字符串(在前后端交互 json) 导入包(好像我的IDEA 不知道装了什么插件 会自动补齐提示) import java.text.Par ...
- fmex挂单挖矿
最近fmex上线挂单挖矿,针对挂单写了个程序,"跟随盘口,避免成交",0成本薅羊毛. 代码在 https://github.com/xiaoxiaoleo/fmexminer 使用 ...
- google chrome 浏览器插件
如果感觉浏览器(chrome)的背景是白色太亮太刺眼,可以先在 设置->外观->主题背景 里选择 oceanic,将浏览器头部颜色设置为海蓝色.然后再安装插件 “眼睛护航”,改变所有网页的 ...
- LITEIDE配置
LITEIDE配置 工具--管理GOPATH 工具--编辑当前环境
- ubuntu取消自动登录
/etc/lightdm/lightdm.conf.d/50-nvidia.conf 注释 autologin-user=<YOUR USER>
- PHP7 MongoDB 使用方法
原文链接: http://www.zhaokeli.com/article/8574.html MongoDb原生操作 Mongodb连接 PHP7 连接 MongoDB 语法如下: 复制代码 $ma ...