⒈引用

  在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(服务)的更多相关文章

  1. Docker Kubernetes Service 网络服务代理模式详解

    Docker Kubernetes  Service 网络服务代理模式详解 Service service是实现kubernetes网络通信的一个服务 主要功能:负载均衡.网络规则分布到具体pod 注 ...

  2. Kubernetes K8S之Service服务详解与示例

    K8S之Service概述与代理说明,并详解所有的service服务类型与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master Cent ...

  3. ASP.NET Core在Azure Kubernetes Service中的部署和管理

    目录 ASP.NET Core在Azure Kubernetes Service中的部署和管理 目标 准备工作 注册 Azure 账户 AKS文档 进入Azure门户(控制台) 安装 Azure Cl ...

  4. Kubernetes之服务发现及负载Services

    Service 概述 kubernetes 中的pod是有生生灭灭的,时刻都有可能被新的pod所代替,而不可复活(pod的生命周期).一旦一个pod生命终止,通过ReplicaSets动态创建和销毁p ...

  5. Docker Kubernetes Service 代理服务创建

    Docker Kubernetes  Service 代理服务创建 创建Service需要提前创建好pod容器.再创建Service时需要指定Pod标签,它会提供一个暴露端口默会分配容器内网访问的唯一 ...

  6. Centos7部署kubernetes API服务(四)

    1.准备软件包 [root@linux-node1 bin]# pwd /usr/local/src/kubernetes/server/bin [root@linux-node1 bin]# cp ...

  7. CoreDNS for kubernetes Service Discovery

    一.CoreDNS简介 Kubernetes包括用于服务发现的DNS服务器Kube-DNS. 该DNS服务器利用SkyDNS的库来为Kubernetes pod和服务提供DNS请求.SkyDNS2的作 ...

  8. 浅谈 kubernetes service 那些事(上篇)

    一.问题 首先,我们思考这样一个问题: 访问k8s集群中的pod, 客户端需要知道pod地址,需要感知pod的状态.那如何获取各个pod的地址?若某一node上的pod故障,客户端如何感知? 二.k8 ...

  9. 浅谈 kubernetes service 那些事 (下篇)

    欢迎访问网易云社区,了解更多网易技术产品运营经验. 五.K8s 1.8 新特性--ipvs ipvs与iptables的性能差异 随着服务的数量增长,IPTables 规则则会成倍增长,这样带来的问题 ...

  10. Istio技术与实践02:源码解析之Istio on Kubernetes 统一服务发现

    前言 文章Istio技术与实践01: 源码解析之Pilot多云平台服务发现机制结合Pilot的代码实现介绍了Istio的抽象服务模型和基于该模型的数据结构定义,了解到Istio上只是定义的服务发现的接 ...

随机推荐

  1. csps模拟73-74

    模拟73: T1:哔-------------------- sb模拟,然而一个小细节打炸了,不想解释(吐嘈大样例没有右移)... #include<iostream> #include& ...

  2. 指针——可能我学的还只是c++的皮毛

    C. 线性链表的插入与删除 单点时限: 2.0 sec 内存限制: 256 MB 实现线性链表的插入与删除操作 只需完成给定函数的定义. NODE* insertLinklist(NODE* head ...

  3. 一、docker安装CentOS7

    一.安装步骤 前提条件 Docker运行在CentOS7上,要求系统64位.系统内核版本为3.10以上. Docker是一个进程,一启动就两个进程,一个服务,一个守护进程.占用资源就非常少,启动速度非 ...

  4. redis几种模式的部署(Windows下实现)

    <参考>http://www.cnblogs.com/ruiati/p/6374152.html 1.   自行下载redis客户端.redis官方不支持Windows系统,所以官网上是下 ...

  5. JMeter压力测试及并发量计算-2

    一个每天1000万PV的网站需要什么样的性能去支撑呢?继续上一篇,下面我们就来计算一下,前面我们已经搞到了一票数据,但是这些数据的意义还没有说.技术是为业务服务的,下面就来说说怎么让些数据变得有意义. ...

  6. 重读APUE(9)-SIG_ERR、SIG_DFL、SIG_IGN定义无参数

    下面这几个函数定义,每次看到都会纠结一阵子,奇怪的是为什么没有参数? #define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 #def ...

  7. zookeeper源码 — 三、集群启动—leader、follower同步

    zookeeper集群启动的时候,首先读取配置,接着开始选举,选举完成以后,每个server根据选举的结果设置自己的角色,角色设置完成后leader需要和所有的follower同步.上面一篇介绍了le ...

  8. Intellij IDEA导入JAVA项目并启动(哈哈哈,天天都有人问)

    最近有很多同学,竟然不知道如何使用Intellij IDEA打开Java项目并启动 现在来讲一下,希望不要忘记了 1.打开IDEA开机页面 Maven项目 2.Maven项目是以pom文件引入各项ja ...

  9. as 什么意思?

    You can denote particular console messages and variable values as having different types using four ...

  10. ubuntu kylin 18.04 安装 Qt Creator 5.11

    首先,去官网(https://download.qt.io/official_releases/qt/ )下载Qt Creator的安装包. 我下载的是5.11.1版本文件:qt-opensource ...