Kubernetes-Service(服务)
⒈引用
在Kubernetes中,pod通常需要对来自集群内部的其他pod或来自集群外部的客户端的HTTP请求做出响应。pod需要一种寻找其他pod的方法来使用其他pod提供的服务,不像在没有Kubernetes的世界,系统管理员需要在配置文件中明确指出服务的精确的IP地址或者主机名,这种方式在Kubernetes中并不适用,因为
·pod是短暂的一—它们随时会启动或者关闭,无论是为了给其他pod提供空间而从节点中被移除,或者是减少了pod的数量,又或者是因为节点异常。
·Kubernetes在pod启动前会给已经调度到节点上的pod分配IP地址一—因此客户端不能提前知道提供服务pod的IP地址。
·水平伸缩意味着多个pod可能会提供相同的服务一—每个pod都有自己的IP地址,客户端无须关心后端提供服务pod的数量,以及各自对应的IP地址。它们无须记录每个pod的IP地址。相反,所有的pod可以通过一个单一的IP地址进行访问。
为了解决上述问题,Kubernetes提供了一种资源类型——服务(service)。
⒉介绍
Kubenetes服务是一种为一组功能相同的pod提供单一不变的接入点的资源。当服务存在时,它的IP地址和端口不会改变。客户端通过IP地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个pod上。通过这种方式,客户端不需要知道每个单独的提供服务的pod的地址,这样这些pod就可以在集群中随时被创建或移除。
⒊创建服务
1.通过kubectl expose创建服务
创建服务的最简单的方法是通过kubectl expose,利用expose命令和pod选择器来创建服务资源,从而通过单个的IP和端口来访问所有的pod。
2.通过YAML描述文件来创建服务
apiVersion: v1
kind: Service
metadata:
name: coreqi
spec:
ports:
- port: 80 #该服务将在80端口接收请求
targetPort: 8080 #服务将把请求转发到pod的8080端口
selector:
app: coreqi #具有app=coreqi标签的pod都属于该服务
创建以上描述文件后使用kubectl命令创建服务
kubectl create -f coreqi-svc.yaml
⒋获取所有服务(当面命名空间下)
kubectl get svc
⒌测试服务
从内部集群测试服务可以通过以下几种方法向服务发送请求:
·显而易见的方法是创建一个pod,它将请求发送到服务的集群IP并记录响应。可以通过查看pod日志检查服务的响应。
·使用ssh远程登录到其中一个Kubernetes节点上,然后使用curl命令。
·可以通过kubectl exec命令在一个已经存在的pod中执行curl命令。
以第三种方式为例,可以使用kubectl exec命令远程地在一个已经存在的pod容器上执行任何命令。这样就可以很方便地了解pod的内容、状态及环境。用kubectl get pod命令列出所有的pod,并且选择其中一个作为exec命令的执行目标。
kubectl exec {podName} --curl -s http://10.111.249.153
⒍服务的会话亲和性
如果向一个服务多次执行同样的命令,每次调用都应该在不同的pod上执行。因为服务代理通常将每个连接随机指向选中的后端pod中的一个,即使连接来自于同一个客户端。
如果希望特定客户端产生的所有请求每次都指向同一个pod,可以设置服务的 sessionAffinity属性为 ClientIP(而不是none,none是默认值)。这种方式将会使服务代理将来自同一个 客户端 IP的所有请求转发至同一个pod上。
Kubernetes仅仅支持两种形式的会话亲和性服务:None和 ClientIP,你或许惊讶竟然不支持基于 cookie的会话亲和性的选项,但是你要了解 Kubernetes服务不是在HTTP层面上工作。服务处理TP和UDP包,并不关心其中的载荷内容。因为 cookie HTTP是HTTP协议中的一部分,服务并不知道它们,这就解释了为什么会话亲和性不能基于 cookie。
apiVersion: v1
kind: Service
metadata:
name: coreqi
spec:
ports:
- port: 80 #该服务将在80端口接收请求
targetPort: 8080 #服务将把请求转发到pod的8080端口
selector:
app: coreqi #具有app=coreqi标签的pod都属于该服务
sessionAffinity: ClientIP #会话亲和性,默认值值为None【随机指向pod】,设置为ClientIP则特定客户端所产生的所有请求每次都会指向同一个pod,这种方式将会使服务代理将来自同一个client IP的所有请求转发至同一个pod上。
⒎单个服务暴露多个端口
创建的服务可以暴露一个端口,也可以暴露多个端口。比如,你的pod监听两个端口,比如HTTP监听8080端口、 ,HTTP HTTPS,监听8443端口,可以使用一个服务从端口80和443转发至pod端口8080和8443。在这种情况下,无须创建两个不同的服务。通过一个集群IP,使用一个服务就可以将多个端口全部暴露出来。
注意:在创建一个有多个端口的服务的时候必须给每个端口指定名字。
apiVersion: v1
kind: Service
metadata:
name: coreqi
spec:
ports:
- name: http
port: 80 #该服务将在80端口接收请求
targetPort: 8080 #服务将把请求转发到pod的8080端口
- name: https
port: 443
targetPort: 8443
selector:
app: coreqi #具有app=coreqi标签的pod都属于该服务
sessionAffinity: ClientIP #会话亲和性,默认值值为None【随机指向pod】,设置为ClientIP则特定客户端所产生的所有请求每次都会指向同一个pod,这种方式将会使服务代理将来自同一个client IP的所有请求转发至同一个pod上。
注意:服务的标签选择器应用于整个服务,不能对每个端口做单独的配置。如果不同的pod有不同的端口映射关系,需要创建两个服务。
⒏为端口号命名(起别名)
#在pod的定义中指定port名称
kind: Pod
spec:
containers:
- name: coreqi
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
#在Service的定义中引用
apiVersion: v1
kind: Service
spec:
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
采用命名端口最大的好处就是即使更换端口号也无须更改服务 spec,你的pod现在对http服务用的是8080,但是假设过段时间你决定将端口更换为80呢?
如果你采用了命名的端口,仅仅需要做的就是改变pod中spec描述的端口号(当然你的端口号的名称没有改变)。在你的pod向新端口更新时,根据pod收到的连接(8080端口在旧的pod上、80端口在新的pod上),用户连接将会转发到对应的端口号上。
⒐服务发现
通过创建服务,现在就可以通过一个单一稳定的IP地址访问到pod。在服务整个生命周期内这个地址保持不变。在服务后面的pod可能删除重建,它们的IP地址可能改变,数量也会增减,但是始终可以通过服务的单一不变的IP地址访问到这些pod。
但客户端pod如何知道服务的IP和端口?是否需要先创建服务,然后手动查找其IP地址并将IP传递给客户端pod的配置选项?当然不是。Kubernetes为客户端提供了发现服务的IP和端口的方式。
1.通过环境变量发现服务
在pod开始运行的时候,Kubernetes会初始化一系列的环境变量指向现在存在的服务。如果你创建的服务早于客户端pod的创建,则哭护短pod上的进程可以根据环境变量获得服务的IP地址和端口号。但是如果服务的创建晚于哭护短pod的创建,那么关于这个服务的环境变量并没有设置,这个问题也需要解决。
查看客户端pod的环境变量
kubectl exec {podName} env
列表中将显示集群中所有服务的环境变量(在客户端pod创建之前所有服务的环境变量),其中环境变量使用HOST保存了该服务的IP地址,使用PORT保存了服务的端口。
注意:环境变量保存的服务名称中的横杠被转换为下画线,并且当服务名称用作环境变量名称中的前缀时,所有的字母都是大写的。
2.通过DNS发现服务
默认情况下每一个命名空间下都有一个称作kube-dns的pod,就像名字的暗示,这个pod运行DNS服务,集群中的其他pod都被配置为使用其作为dns(Kubernetes通过修改每个容器的/etc/resolv.conf文件实现)。集群中的其他pod所运行的DNS查询都会被该DNS服务器响应,该服务器知道系统中运行的所有服务。
注意:集群中的pod是否使用内部的DNS服务器是根据pod中spec的dnsPolicy属性来决定的。
每个服务从内部DNS服务器中获得一个DNS条目,客户端的pod在知道服务名称的情况下可以通过全限定域名(FQDN)来访问,而不仅仅只是通过环境变量。
通过FQDN连接服务
集群中的其他pod可以通过打开以下FQDN连接来访问服务:
serviceName.default.svc.cluster.local
其中serviceName对应于服务名称,default表示当前服务所在的命名空间,而svc.cluster.local是在所有集群本地服务名称中使用的可配置集群域后缀。
注意:客户端仍然必须知道服务的端口号。如果服务使用标准端口号(例如,HTTP的80端口或Postgres的5432端口),这样是没问题的。如果并不是标准端口,客户端可以从环境变量中获取端口号。
如果其他pod和服务在同一个命名空间下,可以省略svc.cluster.local后缀,甚至命名空间。因此可以使用serviceName来指代服务。这简单到不可思议。
在pod容器中运行shell
可以通过kubectl exec命令在一个pod容器上运行bash(或者其他形式的shell)。通过这种方式,可以随意浏览容器,而无须为每个要运行的命令执行kubectl exec。
注意:shell的二进制可执行文件必须在容器镜像中可用才能使用。
为了正常地使用shell,kubectl exec命令需要添加-it选项
进入容器内部运行bash shell
kubectl exec-it {podName} bash
通过任意一种方式访问服务
1.curl http://serviceName.default.svc.cluster.local
2.curl http://serviceName.default
3.curl http://serviceName
在请求的URL中,可以将服务的名称作为主机名来访问服务。因为每个pod容器在DNS解析器配置中都默认配置了可以将命名空间和svc.cluster.local后缀省略掉。通过以下命令查看一下pod容器中的/etc/resilv.conf文件就明白了。
cat /etc/resolv.conf
无法ping 通服务IP的原因
当我们使用curl这个服务时,这个服务是工作的,但是却ping不通。这是因为服务的集群IP是一个虚拟IP,并且只有在与服务端口结合时才有意义。
⒑连接集群外部的服务
如果我们希望不要让Kubernetes服务将请求重定向到集群中的pod,而是让它重定向到外部IP和端口。这样做的话可以充分利用服务的负载平衡和服务发现。在集群中运行的客户端pod可以像连接到内部服务一样连接到外部服务。
1.介绍服务endpoint
在讲述如何做到这一点之前,先阐述一下服务。服务并不是和pod直接相连的。相反,有一种资源介于两者之间—-它就是Endpoint资源。如果之前在服务上运行过kubectl describe命令。
kubectl describe svc {serviceName}
则展示服务的详细列表内将会出现Selector、Endpoints属性等等,其中Selector描述了服务用于创建endpoint列表的pod标签选择器。而Endpoints则描述了服务的endpoint的pod的IP和端口列表。
Endpoint 资源就是暴露一个服务所指向的的IP地址和端口的列表,Endpoint资源和其他Kubernetes 资源一样,所以可以使用kubectl info来获取它的基本信息。
kubectl get endpoints {serviceName}
尽管在服务中定义了pod选择器,但在将请求转发时不会直接使用它。相反,选择器用于构建IP和端口列表,然后存储在Endpoint资源中。当客户端连接到服务时,服务代理会选择这些IP和端口列表中的一个,并将传入的连接重定向到选中的服务器。
2.手动配置服务的 endpoint
或许你已经意识到一点,只有将服务的endpoint与服务解耦后,才可以分别手动配置和更新它们。只有创建了不包含pod选择器的服务,Kubernetes才不会创建Endpoint资源(毕竟,缺少选择器,服务就不知道应该包括哪些pod)。这样就需要手动创建Endpoint 资源来指定该服务的endpoint列表。
要使用手动的方式配置服务的endpoint,需要分别创建服务和Endpoint资源。
一、首先创建一个没有选择器的服务YAML描述文件。
#在该服务的描述文件中并没有定义pod选择器
apiVersion: v1
kind: Service
metadata:
name: coreqi-service #注意服务的名称必须和Endpoint对象的名称相匹配
spec:
ports:
port: 80
创建以上描述文件后使用kubectl命令创建服务
kubectl create -f coreqi-svc.yaml
二、为没有选择器的服务创建 Endpoint 资源
Endpoint是一个单独的资源并不是服务的一个属性。由于创建的资源中并不包含选择器,相关的Endpoints资源并没有自动创建,所以必须手动创建。
apiVersion: v1
kind: Endpoints
metadata:
name: coreqi-service #Endpoint的名称必须和服务的名称相匹配
subsets:
- addresses: #服务将请求重定向到endpoint的ip地址
- ip: 11.11.11.11
- ip: 22.22.22.22
ports: #endpoint的目标端口
- port: 80
创建以上描述文件后使用kubectl命令创建
Endpoint对象需要与服务具有相同的名称,并包含该服务的目标IP地址和端口列表。服务和Endpoint资源都发布到服务器后服务就可以像具有pod 选择器那样的服务正常使用。在服务创建之后所创建的容器将包含该服务的环境变量,并且与其IP:port对的所有连接都将在服务端点之间进行负载均衡。
如果稍后决定将外部服务迁移到Kubernetes中运行的pod,可以为服务添加选择器,从而对Endpoint进行自动管理。反过来也是一样的一—将选择器从服务中移除,Kubernetes将停止更新Endpoints。这意味着服务的IP地址可以保持不变,同时服务的实际实现却发生了改变。
3.为外部服务创建别名
除了手动配置服务的Endpoint来代替公开外部服务方法,有一种更简单的方法,就是通过其完全限定域名(FQDN)访问外部服务
创建 ExternalName类型的服务
要创建一个具有别名的外部服务的服务时,要将创建服务资源的一个type字段设置为ExternalName。例如,设想一下在api.coreqi.cn上有公共可用的API,可以定义一个指向它的服务,如下面的代码清单所示。
apiVersion: v1
kind: Service
metadata:
name: coreqi-service
spec:
type: ExternalName #type被设置成ExternalName
externalName: api.coreqi.cn #实际服务的完全限定域名
ports:
port: 80
服务创建完成后,pod可以通过coreqi-service.default.svc.cluster.local域名(甚至是coreqi-service)连接到外部服务,而不是使用服务的实际FQDN。这隐藏了实际的服务名称及其使用该服务的pod的位置,如果以后要将其指向不同的服务,只需简单地修改服务定义externalName属性,或者将类型重新变回ClusterIP并为服务创建Endpoint--无论是手动创建,还是对服务上指定标签选择器使其自动创建。
ExternalName服务仅在DNS级别实施一—为服务创建了简单的CNAME DNS记录。因此,连接到服务的客户端将直接连接到外部服务,完全绕过服务代理。出于这个原因,这些类型的服务甚至不会获得集群IP。
注意:CNAME记录指向完全限定的域名而不是数字IP地址。
⒒将服务暴露给外部客户端
到目前为止,只讨论了集群内服务如何被pod使用;但是,还需要向外部公开某些服务。例如前端web服务器,以便外部客户端可以访问它们。
有几种方式可以在外部访问服务:
一、将服务的类型设置成NodePort--每个集群节点都会在节点上打开一个端口,对于NodePort服务,每个集群节点在节点本身(因此得名叫NodePort)上打开一个端口,并将在该端口上接收到的流量重定向到基础服务。该服务仅在内部集群IP和端口上才可访问,但也可通过所有节点上的专用端口访问。
二、将服务的类型设置成LoadBalance,NodePort类型的一种扩展--这使得服务可以通过一个专用的负载均衡器来访问,这是由Kubernetes中正在运行的云基础设施提供的。负载均衡器将流量重定向到跨所有节点的节点端口。客户端通过负载均衡器的IP连接到服务。
三、创建一个Ingress资源,这是一个完全不同的机制,通过一个IP地址公开多个服务——它运行在HTTP层(网络协议第7层)上,因此可以提供比工作在第4层的服务更多的功能。
1.使用NodePort类型的服务
将一组pod公开给外部客户端的第一种方法是创建一个服务并将其类型设置为NodePort。通过创建NodePort服务,可以让Kubernetes在其所有节点上保留一个端口(所有节点上都使用相同的端口号),并将传入的连接转发给作为服务部分的pod。
这与常规服务类似(它们的实际类型是ClusterIP),但是不仅可以通过服务的内部集群IP访问NodePort服务,还可以通过任何节点的IP和预留节点端口访问NodePort服务。
当尝试与NodePort服务交互时,意义更加重大。
创建NodePort 类型的服务
现在将创建一个NodePort服务,以查看如何使用它。下面的代码清单显示了服务的YAML。
apiVersion: v1
kind: Service
metadata:
name: coreqi-service
spec:
type: NodePort #type被设置成NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30123 #通过集群节点的30123端口可以访问该服务
selector:
app: coreqi
将类型设置为NodePort并指定该服务应该绑定到的所有集群节点的节点端口。指定端口不是强制性的。如果忽略它,Kubernetes将选择一个随机端口。
注意当在GKE中创建服务时,kubectl将会打印出一个关于必须配置防火墙规则的警告。
查看NodePort类型的服务
查看该服务的基础信息:
Kubernetes-Service(服务)的更多相关文章
- Docker Kubernetes Service 网络服务代理模式详解
Docker Kubernetes Service 网络服务代理模式详解 Service service是实现kubernetes网络通信的一个服务 主要功能:负载均衡.网络规则分布到具体pod 注 ...
- Kubernetes K8S之Service服务详解与示例
K8S之Service概述与代理说明,并详解所有的service服务类型与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master Cent ...
- ASP.NET Core在Azure Kubernetes Service中的部署和管理
目录 ASP.NET Core在Azure Kubernetes Service中的部署和管理 目标 准备工作 注册 Azure 账户 AKS文档 进入Azure门户(控制台) 安装 Azure Cl ...
- Kubernetes之服务发现及负载Services
Service 概述 kubernetes 中的pod是有生生灭灭的,时刻都有可能被新的pod所代替,而不可复活(pod的生命周期).一旦一个pod生命终止,通过ReplicaSets动态创建和销毁p ...
- Docker Kubernetes Service 代理服务创建
Docker Kubernetes Service 代理服务创建 创建Service需要提前创建好pod容器.再创建Service时需要指定Pod标签,它会提供一个暴露端口默会分配容器内网访问的唯一 ...
- Centos7部署kubernetes API服务(四)
1.准备软件包 [root@linux-node1 bin]# pwd /usr/local/src/kubernetes/server/bin [root@linux-node1 bin]# cp ...
- CoreDNS for kubernetes Service Discovery
一.CoreDNS简介 Kubernetes包括用于服务发现的DNS服务器Kube-DNS. 该DNS服务器利用SkyDNS的库来为Kubernetes pod和服务提供DNS请求.SkyDNS2的作 ...
- 浅谈 kubernetes service 那些事(上篇)
一.问题 首先,我们思考这样一个问题: 访问k8s集群中的pod, 客户端需要知道pod地址,需要感知pod的状态.那如何获取各个pod的地址?若某一node上的pod故障,客户端如何感知? 二.k8 ...
- 浅谈 kubernetes service 那些事 (下篇)
欢迎访问网易云社区,了解更多网易技术产品运营经验. 五.K8s 1.8 新特性--ipvs ipvs与iptables的性能差异 随着服务的数量增长,IPTables 规则则会成倍增长,这样带来的问题 ...
- Istio技术与实践02:源码解析之Istio on Kubernetes 统一服务发现
前言 文章Istio技术与实践01: 源码解析之Pilot多云平台服务发现机制结合Pilot的代码实现介绍了Istio的抽象服务模型和基于该模型的数据结构定义,了解到Istio上只是定义的服务发现的接 ...
随机推荐
- 18.4.1 考试解题报告 P71
题目:https://files.cnblogs.com/files/lovewhy/problem.pdf 偷偷摘来dalao题面. P71竞赛时间:???? 年?? 月?? 日??:??-??:? ...
- LOJ166 拉格朗日插值 2【卷积,NTT】
题目链接:LOJ 题目描述:输入多项式的次数$n$,一个整数$m$和$f(0),f(1),f(2),\ldots,f(n)$,输出$f(m),f(m+1),f(m+2),\ldots,f(m+n)$ ...
- 顺序表应用7:最大子段和之分治递归法(SDUT 3664)
#include <bits/stdc++.h> using namespace std; const int maxn = 50005; int num = 0; struct node ...
- Java面向对象4(P~U)
P 3-1 Point类的构造函数 (SDUT 2670) import java.util.Arrays; import java.util.Scanner; public class Mai ...
- 7.27T2
不可做题 sol:首先有个很显然的性质就是答案一定是在叶子上最优,然后画画图发现就是从最底层看,如果一条链就看做一个点,向上的第一颗非链的节点,它的儿子数-1就会对答案贡献,所有这样的累加起来就是答案 ...
- JAVA基础知识|类设计技巧
1.一定要保证数据私有 2.一定要对数据初始化 3.不要再类中使用过多的基本类型 4.不是所有的域都需要独立的域访问器和域更改器 5.将职责过多的类进行分解 6.类名和方法名要能够体现它们的职责 7. ...
- Linux设备驱动程序 之 延迟执行
长延迟 有些驱动程序需要延迟比较长的时间,即长于一个时钟滴答: 忙等待 如果想把执行延迟若干个时钟滴答,或者对延迟的精度要求不高,最简单的实现方法就是一个监视jiffies计数器的循环:这种忙等待的实 ...
- sass/scss 和 less对比
一. Sass/Scss.Less是什么? Sass (Syntactically Awesome Stylesheets)是一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量 ...
- APP界面架构设计
作为PM,信息架构和页面流的设计想必烂熟于心,当确定好产品战略层和范围层即为何种目标用户提供何种服务后,就要着手搭建功能架构,将目标功能通过良好的用户体验传递给用户,目的是高效解决用户痛点,从而实现价 ...
- php中strlen()和mb_strlen()函数
php中strlen()和mb_strlen()函数 一.总结 一句话总结: mb_strlen()函数 的作用是 通过不同的编码计算字符串的长度: 比如 echo mb_strlen('中文a字1符 ...