从外部访问Kubernetes集群中的应用
https://zhaohuabing.com/2017/11/28/access-application-from-outside/
前言
我们知道,kubernetes的Cluster Network属于私有网络,只能在cluster Network内部才能访问部署的应用,那如何才能将Kubernetes集群中的应用暴露到外部网络,为外部用户提供服务呢?本文探讨了从外部网络访问kubernetes cluster中应用的几种实现方式。
本文尽量试着写得比较容易理解,但要做到“深入浅出”,把复杂的事情用通俗易懂的语言描述出来是非常需要功力的,个人自认尚未达到此境界,唯有不断努力。此外,kubernetes本身是一个比较复杂的系统,无法在本文中详细解释涉及的所有相关概念,否则就可能脱离了文章的主题,因此假设阅读此文之前读者对kubernetes的基本概念如docker,container,pod已有所了解。
另外此文中的一些内容是自己的理解,由于个人的知识范围有限,可能有误,如果读者对文章中的内容有疑问或者勘误,欢迎大家指证。
Pod和Service
我们首先来了解一下Kubernetes中的Pod和Service的概念。
Pod(容器组),英文中Pod是豆荚的意思,从名字的含义可以看出,Pod是一组有依赖关系的容器,Pod包含的容器都会运行在同一个host节点上,共享相同的volumes和network namespace空间。Kubernetes以Pod为基本操作单元,可以同时启动多个相同的pod用于failover或者load balance。
Pod的生命周期是短暂的,Kubernetes根据应用的配置,会对Pod进行创建,销毁,根据监控指标进行缩扩容。kubernetes在创建Pod时可以选择集群中的任何一台空闲的Host,因此其网络地址是不固定的。由于Pod的这一特点,一般不建议直接通过Pod的地址去访问应用。
为了解决访问Pod不方便直接访问的问题,Kubernetes采用了Service的概念,Service是对后端提供服务的一组Pod的抽象,Service会绑定到一个固定的虚拟IP上,该虚拟IP只在Kubernetes Cluster中可见,但其实该IP并不对应一个虚拟或者物理设备,而只是IPtable中的规则,然后再通过IPtable将服务请求路由到后端的Pod中。通过这种方式,可以确保服务消费者可以稳定地访问Pod提供的服务,而不用关心Pod的创建、删除、迁移等变化以及如何用一组Pod来进行负载均衡。
Service的机制如下图所示,Kube-proxy监听kubernetes master增加和删除Service以及Endpoint的消息,对于每一个Service,kube proxy创建相应的iptables规则,将发送到Service Cluster IP的流量转发到Service后端提供服务的Pod的相应端口上。
备注:虽然可以通过Service的Cluster IP和服务端口访问到后端Pod提供的服务,但该Cluster IP是Ping不通的,原因是Cluster IP只是iptable中的规则,并不对应到一个网络设备。
Service的类型
Service的类型(ServiceType)决定了Service如何对外提供服务,根据类型不同,服务可以只在Kubernetes cluster中可见,也可以暴露到Cluster外部。Service有三种类型,ClusterIP,NodePort和LoadBalancer。其中ClusterIP是Service的缺省类型,这种类型的服务会提供一个只能在Cluster内才能访问的虚拟IP,其实现机制如上面一节所述。
通过NodePort提供外部访问入口
通过将Service的类型设置为NodePort,可以在Cluster中的主机上通过一个指定端口暴露服务。注意通过Cluster中每台主机上的该指定端口都可以访问到该服务,发送到该主机端口的请求会被kubernetes路由到提供服务的Pod上。采用这种服务类型,可以在kubernetes cluster网络外通过主机IP:端口的方式访问到服务。
注意:官方文档中说明了Kubernetes clusterIp的流量转发到后端Pod有Iptable和kube proxy两种方式。但对Nodeport如何转发流量却语焉不详。该图来自网络,从图来看是通过kube proxy转发的,我没有去研究过源码。欢迎了解的同学跟帖说明。
下面是通过NodePort向外暴露服务的一个例子,注意可以指定一个nodePort,也可以不指定。在不指定的情况下,kubernetes会从可用的端口范围内自动分配一个随机端口。
kind: Service
apiVersion: v1
metadata:name: influxdb
spec:type: NodePortports:- port: 8086nodePort: 30000selector:name: influxdb
通过NodePort从外部访问有下面的一些问题,自己玩玩或者进行测试时可以使用该方案,但不适宜用于生产环境。
Kubernetes cluster host的IP必须是一个well-known IP,即客户端必须知道该IP。但Cluster中的host是被作为资源池看待的,可以增加删除,每个host的IP一般也是动态分配的,因此并不能认为host IP对客户端而言是well-known IP。
客户端访问某一个固定的host IP存在单点故障。假如一台host宕机了,kubernetes cluster会把应用 reload到另一节点上,但客户端就无法通过该host的nodeport访问应用了。
该方案假设客户端可以访问Kubernetes host所在网络。在生产环境中,客户端和Kubernetes host网络可能是隔离的。例如客户端可能是公网中的一个手机APP,是无法直接访问host所在的私有网络的。
因此,需要通过一个网关来将外部客户端的请求导入到Cluster中的应用中,在kubernetes中,这个网关是一个4层的load balancer。
通过Load Balancer提供外部访问入口
通过将Service的类型设置为LoadBalancer,可以为Service创建一个外部Load Balancer。Kubernetes的文档中声明该Service类型需要云服务提供商的支持,其实这里只是在Kubernetes配置文件中提出了一个要求,即为该Service创建Load Balancer,至于如何创建则是由Google Cloud或Amazon Cloud等云服务商提供的,创建的Load Balancer不在Kubernetes Cluster的管理范围中。kubernetes 1.6版本中,WS, Azure, CloudStack, GCE and OpenStack等云提供商已经可以为Kubernetes提供Load Balancer.下面是一个Load balancer类型的Service例子:
kind: Service
apiVersion: v1
metadata:name: influxdb
spec:type: LoadBalancerports:- port: 8086selector:name: influxdb
部署该Service后,我们来看一下Kubernetes创建的内容
$ kubectl get svc influxdb
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
influxdb 10.97.121.42 10.13.242.236 8086:30051/TCP 39s
Kubernetes首先为influxdb创建了一个集群内部可以访问的ClusterIP 10.97.121.42。由于没有指定nodeport端口,kubernetes选择了一个空闲的30051主机端口将service暴露在主机的网络上,然后通知cloud provider创建了一个load balancer,上面输出中的EEXTERNAL-IP就是load balancer的IP。
测试使用的Cloud Provider是OpenStack,我们通过neutron lb-vip-show可以查看创建的Load Balancer详细信息。
$ neutron lb-vip-show 9bf2a580-2ba4-4494-93fd-9b6969c55ac3
+---------------------+--------------------------------------------------------------+
| Field | Value |
+---------------------+--------------------------------------------------------------+
| address | 10.13.242.236 |
| admin_state_up | True |
| connection_limit | -1 |
| description | Kubernetes external service a6ffa4dadf99711e68ea2fa163e0b082 |
| id | 9bf2a580-2ba4-4494-93fd-9b6969c55ac3 |
| name | a6ffa4dadf99711e68ea2fa163e0b082 |
| pool_id | 392917a6-ed61-4924-acb2-026cd4181755 |
| port_id | e450b80b-6da1-4b31-a008-280abdc6400b |
| protocol | TCP |
| protocol_port | 8086 |
| session_persistence | |
| status | ACTIVE |
| status_description | |
| subnet_id | 73f8eb91-90cf-42f4-85d0-dcff44077313 |
| tenant_id | 4d68886fea6e45b0bc2e05cd302cccb9 |
+---------------------+--------------------------------------------------------------+$ neutron lb-pool-show 392917a6-ed61-4924-acb2-026cd4181755
+------------------------+--------------------------------------+
| Field | Value |
+------------------------+--------------------------------------+
| admin_state_up | True |
| description | |
| health_monitors | |
| health_monitors_status | |
| id | 392917a6-ed61-4924-acb2-026cd4181755 |
| lb_method | ROUND_ROBIN |
| members | d0825cc2-46a3-43bd-af82-e9d8f1f85299 |
| | 3f73d3bb-bc40-478d-8d0e-df05cdfb9734 |
| name | a6ffa4dadf99711e68ea2fa163e0b082 |
| protocol | TCP |
| provider | haproxy |
| status | ACTIVE |
| status_description | |
| subnet_id | 73f8eb91-90cf-42f4-85d0-dcff44077313 |
| tenant_id | 4d68886fea6e45b0bc2e05cd302cccb9 |
| vip_id | 9bf2a580-2ba4-4494-93fd-9b6969c55ac3 |
+------------------------+--------------------------------------+$ neutron lb-member-list
+--------------------------------------+--------------+---------------+--------+----------------+--------+
| id | address | protocol_port | weight | admin_state_up | status |
+--------------------------------------+--------------+---------------+--------+----------------+--------+
| 3f73d3bb-bc40-478d-8d0e-df05cdfb9734 | 10.13.241.89 | 30051 | 1 | True | ACTIVE |
| d0825cc2-46a3-43bd-af82-e9d8f1f85299 | 10.13.241.10 | 30051 | 1 | True | ACTIVE |
+--------------------------------------+--------------+---------------+--------+----------------+--------
可以看到OpenStack使用VIP 10.13.242.236在端口8086创建了一个Load Balancer,Load Balancer对应的Lb pool里面有两个成员10.13.241.89 和 10.13.241.10,正是Kubernetes的host节点,进入Load balancer流量被分发到这两个节点对应的Service Nodeport 30051上。
但是如果客户端不在Openstack Neutron的私有子网上,则还需要在load balancer的VIP上关联一个floating IP,以使外部客户端可以连接到load balancer。
部署Load balancer后,应用的拓扑结构如下图所示(注:本图假设Kubernetes Cluster部署在Openstack私有云上)。
备注:如果kubernetes环境在Public Cloud上,Loadbalancer类型的Service创建出的外部Load Balancer已经带有公网IP地址,是可以直接从外部网络进行访问的,不需要绑定floating IP这个步骤。例如在AWS上创建的Elastic Load Balancing (ELB),有兴趣可以看一下这篇文章:Expose Services on your AWS Quick Start Kubernetes cluster。
如果Kubernetes Cluster是在不支持LoadBalancer特性的cloud provider或者裸机上创建的,可以实现LoadBalancer类型的Service吗?应该也是可以的。Kubernetes本身并不直接支持Loadbalancer,但我们可以通过对Kubernetes进行扩展来实现,可以监听kubernetes Master的service创建消息,并根据消息部署相应的Load Balancer(如Nginx或者HAProxy),来实现Load balancer类型的Service。
通过设置Service类型提供的是四层Load Balancer,当只需要向外暴露一个服务的时候,可以直接采用这种方式。但在一个应用需要对外提供多个服务时,采用该方式会为每一个服务(IP+Port)都创建一个外部load balancer。如下图所示一般来说,同一个应用的多个服务/资源会放在同一个域名下,在这种情况下,创建多个Load balancer是完全没有必要的,反而带来了额外的开销和管理成本。直接将服务暴露给外部用户也会导致了前端和后端的耦合,影响了后端架构的灵活性,如果以后由于业务需求对服务进行调整会直接影响到客户端。可以通过使用Kubernetes Ingress进行L7 load balancing来解决该问题。
采用Ingress作为七层load balancer
首先看一下引入Ingress后的应用拓扑示意图(注:本图假设Kubernetes Cluster部署在Openstack私有云上)。这里Ingress起到了七层负载均衡器和Http方向代理的作用,可以根据不同的url把入口流量分发到不同的后端Service。外部客户端只看到foo.bar.com这个服务器,屏蔽了内部多个Service的实现方式。采用这种方式,简化了客户端的访问,并增加了后端实现和部署的灵活性,可以在不影响客户端的情况下对后端的服务部署进行调整。
下面是Kubernetes Ingress配置文件的示例,在虚拟主机foot.bar.com下面定义了两个Path,其中/foo被分发到后端服务s1,/bar被分发到后端服务s2。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: testannotations:ingress.kubernetes.io/rewrite-target: /
spec:rules:- host: foo.bar.comhttp:paths:- path: /foobackend:serviceName: s1servicePort: 80- path: /barbackend:serviceName: s2servicePort: 80
注意这里Ingress只描述了一个虚拟主机路径分发的要求,可以定义多个Ingress,描述不同的7层分发要求,而这些要求需要由一个Ingress Controller来实现。Ingress Contorller会监听Kubernetes Master得到Ingress的定义,并根据Ingress的定义对一个7层代理进行相应的配置,以实现Ingress定义中要求的虚拟主机和路径分发规则。Ingress Controller有多种实现,Kubernetes提供了一个基于Nginx的Ingress Controller。需要注意的是,在部署Kubernetes集群时并不会缺省部署Ingress Controller,需要我们自行部署。
下面是部署Nginx Ingress Controller的配置文件示例,注意这里为Nginx Ingress Controller定义了一个LoadBalancer类型的Service,以为Ingress Controller提供一个外部可以访问的公网IP。
apiVersion: v1
kind: Service
metadata:name: nginx-ingress
spec:type: LoadBalancerports:- port: 80name: http- port: 443name: httpsselector:k8s-app: nginx-ingress-lb
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:name: nginx-ingress-controller
spec:replicas: 2revisionHistoryLimit: 3template:metadata:labels:k8s-app: nginx-ingress-lbspec:terminationGracePeriodSeconds: 60containers:- name: nginx-ingress-controllerimage: gcr.io/google_containers/nginx-ingress-controller:0.8.3imagePullPolicy: Always//----omitted for brevity----
备注:Google Cloud直接支持Ingress资源,如果应用部署在Google Cloud中,Google Cloud会自动为Ingress资源创建一个7层load balancer,并为之分配一个外部IP,不需要自行部署Ingress Controller。
结论
采用Ingress加上Load balancer的方式可以将Kubernetes Cluster中的应用服务暴露给外部客户端。这种方式比较灵活,基本可以满足大部分应用的需要。但如果需要在入口处提供更强大的功能,如有更高的效率要求,需求进行安全认证,日志记录,或者需要一些应用的定制逻辑,则需要考虑采用微服务架构中的API Gateway模式,采用一个更强大的API Gateway来作为应用的流量入口。
参考
Accessing Kubernetes Pods from Outside of the Cluster
Kubernetes nginx-ingress-controller
Using Kubernetes external load balancer feature
Expose Services on your AWS Quick Start Kubernetes cluster
从外部访问Kubernetes集群中的应用相关推荐
- 在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer(下)
作者 | Addo Zhang 来源 | 云原生指北 在上一篇<在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer(上)>中,我们使用 MetalLB 的 ...
- 想提高运维效率,那就把MySQL数据库部署到Kubernetes 集群中
摘要:Kubernetes 很多看起来比较"繁琐"的设计的主要目的,都是希望为开发者提供更多的"可扩展性",给使用者带来更多的"稳定性"和& ...
- 在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer(下)- BGP
在上一篇<在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer(上)>中,我们使用 MetalLB 的 Layer2 模式作为 LoadBalancer 的 ...
- Kubernetes集群中部署Node节点
Kubernetes集群中的Node节点部署 kubernetes的Node节点包含如下组件: flanneld docker kubelet kube-proxy 环境变量 需要的变量. $ # 替 ...
- 基于CNI保护Kubernetes集群中的主机
原文发表于kubernetes中文社区,为作者原创翻译 ,原文地址 更多kubernetes文章,请多关注kubernetes中文社区 目录 DaemonSet解决方案概述 创建应用程序 创建一个Do ...
- 使用kubectl访问Kubernetes集群时的身份验证和授权
全栈工程师开发手册 (作者:栾鹏) 架构系列文章 kubectl是日常访问和管理Kubernetes集群最为常用的工具. 当我们使用kubeadm成功引导启动(init)一个Kubernetes集群的 ...
- 如何调试Kubernetes集群中的网络延迟问题
本文深入研究和解决了 Kubernetes 平台上的服务零星延迟问题,就在不久前我也遇到了类似的问题,看似是玄学事件,刚开始归结于网络链路抖动,一段时间后依然存在,虽然影响都是 P99.99 以后的数 ...
- 调试Kubernetes集群中的网络停顿问题
调试Kubernetes集群中的网络停顿问题 在过去几年,Kubernetes在GitHub已经成为标准的部署模式.目前在GitHub,我们在Kubernetes上运行着海量的面向内部团队以及面向C端 ...
- K8s——kubernetes集群中ceph集群使用【下】
kubernetes集群中ceph集群使用 一:CephFS 创建和使用 CephFS 允许用户挂载一个兼容posix的共享目录到多个主机,该存储和NFS共享存储以及CIFS共享目录相似 1.file ...
最新文章
- 千呼万唤始出来!OpenCV 4.0正式发布!
- 状态同步与帧同步的简介
- 7x24小时实时互动,网易云信金融行业虚拟客服解决方案来啦!
- 设计模式六大原则 图
- JS里的onclick事件
- Java制作VCARD
- select默认下拉箭头改变、option样式清除
- Element表单验证(2)
- vs 2015 加载该页时出错。 解决方案
- OSPF配置末梢区域
- UNITY开发VR从入门到放弃---VR自学手册
- 车规SoC芯片厂商征战功能安全,谁是最佳助力者?
- 开源看板 wekan docker-compose部署
- 前端做CRM管理系统是做什么_总结一下最近做crm客户管理系统使用到的前端知识点...
- 保存图片到相册并打开微信扫一扫
- android 热更新 方案,与Android热更新方案Amigo的初次接触
- 职场情商“知多少”?
- java根据2个经纬度点,计算这2个经纬度点之间的距离(通过经度纬度得到距离)
- Linux后处理软件,FieldView(CFD后处理软件)
- cad如何打开stp文件_stp文件怎么打开_CAD打开stp文件详细介绍_酷下载
热门文章
- 好的测试用例应能证明软件是正确的.,好的测试用例应能证明软件是正确的。...
- aspx 判断字符串是否为decimal_C++编程技巧: 如何判断输入是否为整型
- 复合选择器-focus选择器(HTML、CSS)
- 如何使用Java进行简单爬虫
- 设置分录行按钮监听事件
- MySQL5.6基于GTID同步复制,与如何实现MySQL负载均衡、读写分离。
- Python报错: RuntimeError: The current Numpy installation (‘D:\\Develop\\anaconda\\lib\\site-packages\\
- 201312-2-ISBN号码
- 【Computer Organization笔记06】浮点数的数据表示,浮点数加减运算
- 无需公式或代码,用生活实例谈谈 AI 自动控制技术“强化学习”算法框架