k8s实践(12)--K8s service服务详解_kubernetes 中的service支持什么协议(2)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

externalName: my.database.example.com


当查询主机 my-service.prod.svc.CLUSTER时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type。



### 2.4、service的类型


Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。  
 Type 的取值以及行为如下:


1)ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。  
2)NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。  
3)LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。  
4) ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。


**2)通过设置nodePort映射到物理机,同时设置Service的类型为NodePort:**



> 
> kind: Service  
>  apiVersion: v1  
>  metadata:  
>    name: my-service  
>  spec:  
>    type:nodePort  
>    selector:  
>      app: MyApp  
>    ports:  
>      - protocol: TCP  
>        port: 80  
>        targetPort: 9376  
>        nodePort:30376
> 
> 
> 


使用nodePort的缺点:


1. 每个端口只能是一种服务
2. 端口范围只能是 30000-32767
3. 和节点node的 IP 地址紧密耦合。


**(2)通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址。**


这种用法仅用于在公有云服务提供商的云平台上设置Service的场景。在下面的例子中, status.loadBalancer.ingress.ip设置的146.148.47.155为云服务商提供的负载均衡器的IP地址。对该Service的访问请求将会通过LoadBalancer转发到后端Pod上,负载分发的实现方式则依赖于云服务商提供的LoadBalancer的实现机制。



> 
> 
> ```
> kind: Service
> apiVersion: v1
> metadata:
>   name: my-service
> spec:
>   selector:
>     app: MyApp
>   ports:
>     - protocol: TCP
>       port: 80
>       targetPort: 9376
>      nodePort: 19376
>      clusterIP: 10.0.171.239
>      loadBalancerIP: 78.11.24.19
>      type: LoadBalancer
>   status:
>     LoadBalancer:
>      ingress:
>      -ip: 146.148.47.155
> 
> ```
> 
> 



**k8s中有3种IP地址:**


    Node IP: Node节点的IP地址,这是集群中每个节点的物理网卡的IP地址;  
     Pod IP: Pod的IP地址,这是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络;  
     Cluster IP:Service 的IP地址,这也是一个虚拟的IP,但它更像是一个“伪造”的IP地址,因为它没有一个实体网络对象,所以无法响应ping命令。它只能结合Service Port组成一个具体的通信服务端口,单独的Cluster IP不具备TCP/IP通信的基础。在k8s集群之内,Node IP网、Pod IP网与Cluster IP网之间的通信采用的是k8s自己设计的一种编程实现的特殊的路由规则,不同于常见的IP路由实现。


此模式会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的Pod之间通信使用。


**headless** service 需要将 spec.clusterIP 设置成 None。


因为没有ClusterIP,kube-proxy 并不处理此类服务,因为没有load balancing或 proxy 代理设置,在访问服务的时候回返回后端的全部的Pods IP地址,主要用于开发者自己根据pods进行负载均衡器的开发(设置了selector)。



## 三、Service 服务的VIP 和 Service 网络代理




---


### 1、Service 服务的VIP


在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,它会负责把对Service的请求转发到后端的某个Pod实例上并在内部实现服务的负载均衡与会话保持机制。


Service不是共用一个负载均衡器的IP,而是被分配了一个全局唯一的虚拟IP地址,称为Cluster IP。在Service的整个生命周期内,它的Cluster IP不会改变


kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。


在 Kubernetes v1.0 版本,代理完全在 userspace。  
 在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。  
 从 Kubernetes v1.2 起,默认就是 iptables 代理。  
 在 Kubernetes v1.0 版本,Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用来表示 “7层”(HTTP)服务。


每当我们在k8s cluster中创建一个service,**k8s cluster就会在–service-cluster-ip-range的范围内为service分配一个cluster-ip**,比如:


![](https://img-blog.csdnimg.cn/20190710190121397.png)


通过 kubectl get ep可以看到对应Endpoints信息,即代理的pod。


![](https://img-blog.csdnimg.cn/20190710190143487.png)


另外,也可以将已有的服务以 Service 的形式加入到 Kubernetes 集群中来,只需要在创建 Service 的时候不指定 Label selector,而是在 Service 创建好后手动为其添加 endpoint。


### 2、service网络代理模式:


拥有三种代理模式:userspace、iptables和ipvs。



> 
> 现在默认使用iptables,在1.8版本之后增加了ipvs功能。
> 
> 
> 


#### 1)早期 userspace 代理模式



> 
> client先请求serviceip,经由iptables转发到kube-proxy上之后再转发到pod上去。这种方式效率比较低。
> 
> 
> 


这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的backend Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个 backend Pod,是基于 Service 的 SessionAffinity 来确定的。 最后,它安装 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod。


网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。


默认的策略是,通过 round-robin 算法来选择 backend Pod。 实现基于客户端 IP 的会话亲和性,可以通过设置 service.spec.sessionAffinity 的值为 "ClientIP" (默认值为 "None")。


![](https://img-blog.csdnimg.cn/20190624142109648.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ndWlzdS5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70)



#### 2) 当前iptables 代理模式



> 
> client请求serviceip后会直接转发到pod上。这种模式性能会高很多。kube-proxy就会负责将pod地址生成在node节点iptables规则中。
> 
> 
> 


这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod。


默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。


和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖 [readiness probes]( )。


![](https://img-blog.csdnimg.cn/20190624142200377.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ndWlzdS5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70)


#### 3)、ipvs代理方式



> 
> 这种方式是通过内核模块ipvs实现转发,这种效率更高。
> 
> 
> 


![](https://img-blog.csdnimg.cn/20210108094335461.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hndWlzdQ==,size_16,color_FFFFFF,t_70)


### 3、多端口 Service


很多 Service 需要暴露多个端口。对于这种情况,Kubernetes 支持在 Service 对象中定义多个端口。 当使用多个端口时,必须给出所有的端口的名称,这样 Endpoint 就不会产生歧义,例如:



kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377


### 4、 选择自己的 IP 地址


在 Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。 用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range CIDR 范围内,这对 API Server 来说是通过一个标识来指定的。 如果 IP 地址不合法,API Server 会返回 HTTP 状态码 422,表示值不合法。


#### service为何使用vip而不是不使用 round-robin DNS?


一个不时出现的问题是,为什么我们都使用 VIP 的方式,而不使用标准的 round-robin DNS,有如下几个原因:


* 长久以来,DNS 库都没能认真对待 DNS TTL、缓存域名查询结果
* 很多应用只查询一次 DNS 并缓存了结果.
* 就算应用和库能够正确查询解析,每个客户端反复重解析造成的负载也是非常难以管理的


我们尽力阻止用户做那些对他们没有好处的事情,如果很多人都来问这个问题,我们可能会选择实现它。




## 四、服务发现和DNS




---


Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。


### 1、环境变量


当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。 它同时支持 [Docker links兼容]( ) 变量(查看 [makeLinkVariables]( ))、简单的 {SVCNAME}\_SERVICE\_HOST 和 {SVCNAME}\_SERVICE\_PORT 变量,这里 Service 的名称需大写,横线被转换成下划线。


举个例子,一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:



REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11


这意味着需要有顺序的要求 —— Pod 想要访问的任何 Service 必须在 Pod 自己之前被创建,否则这些环境变量就不会被赋值。DNS 并没有这个限制。


### 2、DNS


一个可选(尽管强烈推荐)[集群插件]( ) 是 DNS 服务器。 DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一个 Service 创建一组 DNS 记录。 如果整个集群的 DNS 一直被启用,那么所有的 Pod 应该能够自动对 Service 进行名称解析。


例如:


Service为:webapp,Namespace 为: "my-ns"。 它在 Kubernetes 集群为 "webapp.my-ns" 创建了一条 DNS 记录。


1)在同一个集群(名称为 "my-ns" 的 Namespace 中)内的 Pod 应该能够简单地通过名称查询找到 "webapp"。


2)在另一个 Namespace 中的 Pod 必须限定名称为 "webapp.my-ns"。 这些名称查询的结果是 Cluster IP。


Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 如果名称为 "webapp.my-ns" 的 Service 有一个名为 "http" 的 TCP 端口,可以对 "\_http.\_tcp.webapp.my-ns" 执行 DNS SRV 查询,得到 "http" 的端口号。


Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。 更多信息可以查看[https://guisu.blog.csdn.net/article/details/93501650]( )



## 五、集群外部访问服务




---


k8s集群外如何访问集群内的服务,主要方式有:hostPort或hostNetwork、NodePort、Ingress


### 1、hostPort或hostNetwork


 hostPort和hostNetwork 放在首位是因为大家很容易忽略它们,它们也可让集群外访问集群内应用,


#### hostNetwork 用法:



apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector: # node节点选择器
role: master # node节点标签(Label)
hostNetwork: true # 使用node节点网络
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 8080


重点在和containers平级的**hostNetwork: true**,表示pod使用宿主机网络,配合nodeSelector,把pod实例化在固定节点,如上,我给mater节点加上标签role: master,通过nodeSelector,nginx就会实例化在master节点,这样就可以通过master节点的ip和8080端口访问这个nginx了。


#### **hostPort用法:**



apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector: # node节点选择器
role: master # node节点标签(Label)
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 8080
hostPort: 80 #重点


和**hostNetwork**相比多了映射能力,可以把容器端口映射为node节点不同端口,**hostPort,**当然也需要**nodeSelector**来固定节点,不然每次创建,节点不同,ip也会改变


访问方式:nodeSelector所选节点ip:hostPort, 如上:role=Master标签节点Ip:80


### 2、NodePort


NodePort是最常见的提供集群外访问的方式之一,该方式使用Service提供集群外访问:


**通过设置nodePort映射到物理机,同时设置Service的类型为NodePort:**



> 
> kind: Service  
>  apiVersion: v1  
>  metadata:  
>    name: my-service  
>  spec:  
>    type:nodePort  
>    selector:  
>      app: MyApp  
>    ports:  
>      - protocol: TCP  
>        port: 80  
>        targetPort: 9376  
>        nodePort:30376
> 
> 
> 


访问方式:集群内任意节点ip加nodePort所配端口号,如上:集群内任一节点ip:30376,即可访问服务了。


使用nodePort的缺点:


1. **每个端口只能是一种服务**
2. **端口范围只能是 30000-32767**
3. **和节点node的 IP 地址紧密耦合。**



> 
> 在本机可以访问nodePort, 其他服务器无法问题,例如node 172.16.1.23上可以访问curl 172.16.1.23:30018,但是在服务器172.16.1.21上无法访问:
> 
> 
> ![](https://img-blog.csdnimg.cn/20210219191243637.png)
> 
> 
> ![](https://img-blog.csdnimg.cn/20210219191358958.png)  
>  解决的办法是:  
> cat > /etc/sysctl.d//etc/sysctl.conf <<EOF  
>  net.ipv4.ip\_forward=1  
>  EOF  
>  sysctl -p
> 
> 
> 具体原因:[vendor libnetwork @1861587 by sanimej · Pull Request #28257 · moby/moby · GitHub]( )
> 
> 
> 如果net.ipv4.ip\_forward=1参数是由Docker所设置,则iptables的FORWARD会被设置为DORP策略。如果是由用户设置net.ipv4.ip\_forward=1参数,则用户可能会进行某些意图需要FORWARD为ACCEPT策略,这时候Docker就不会去修改FORWARD策略。
> 
> 
> 



### **3、LoadBalancer**


**通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址。**


这种用法仅用于在公有云服务提供商的云平台上设置Service的场景。在下面的例子中, status.loadBalancer.ingress.ip设置的146.148.47.155为云服务商提供的负载均衡器的IP地址。对该Service的访问请求将会通过LoadBalancer转发到后端Pod上,负载分发的实现方式则依赖于云服务商提供的LoadBalancer的实现机制。



> 
> 
> ```
> kind: Service
> apiVersion: v1
> metadata:
>   name: my-service
> spec:
>   selector:
>     app: MyApp
>   ports:
>     - protocol: TCP
>       port: 80
>       targetPort: 9376
>      nodePort: 19376
>      clusterIP: 10.0.171.239
>      loadBalancerIP: 78.11.24.19
>      type: LoadBalancer
>   status:
>     LoadBalancer:
>      ingress:
>      -ip: 146.148.47.155
> 
> ```
> 
> 



### 4、Ingress


可以简单理解为部署了一个nginx服务,该服务使用hostNetwork或hostPort方式提供集群外访问,再根据配置的路由规则,路由的集群内部各个service。


根据前面对Service的使用说明,我们知道Service的表现形式为IP:Port,即工作在TCP/IP层,而对于基于HTTP的服务来说,不同的URL地址经常对应到不同的后端服务或者虚拟服务器,这些应用层的转发机制仅通过kubernetes的Service机制是无法实现的。kubernetes V1.1版本中新增的Ingress将不同URL的访问请求转发到后端不同的Service,实现HTTP层的业务路由机制。在kubernetes集群中,Ingress的实现需要通过Ingress的定义与Ingress Controller的定义结合起来,才能形成完整的HTTP负载分发功能。


#### 1)、创建Ingress Controller


      使用Nginx来实现一个Ingress Controller,需要实现的基本逻辑如下:  
      a)监听apiserver,获取全部ingress的定义  
     b)基于ingress的定义,生成Nginx所需的配置文件/etc/nginx/nginx.conf  
     c)执行nginx -s relaod命令,重新加载nginx.conf文件,写个脚本。


通过直接下载谷歌提供的nginx-ingress镜像来创建Ingress Controller:  
 文件nginx-ingress-rc.yaml  
 apiVersion: v1  
 kind: ReplicationController  
 matadata:  
   name: nginx-ingress  
   labels:  
     app: nginx-ingress  
 spec:  
   replicas: 1  
   selector:  
     app: nginx-ingress  
   template:  
     metadata:  
       labels:  
         app: nginx-ingress  
     spec:  
       containers:  
       - image: gcr.io/google\_containers/nginx-ingress:0.1  
         name: nginx  
         ports:  
         - containerPort: 80  
           hostPort: 80  
 这里,Nginx应用配置设置了hostPort,即它将容器应用监听的80端口号映射到物理机,以使得客户端应用可以通过URL地址“http://物理机IP:80”来访问该Ingress Controller  
 #kubectl create -f nginx-ingress-rc.yaml  
 #kubectl get pods


#### 2)、定义Ingress


为mywebsite.com定义Ingress,设置到后端Service的转发规则:  
 apiVersion: extensions/vlbeta1  
 kind: Ingress  
 metadata:  
   name: mywebsite-ingress  
 spec:  
   rules:  
   - host: mywebsite.com  
     http:  
       paths:  
       - path: /web  
         backend:  
           serviceName: webapp  
           servicePort: 80


这个Ingress的定义说明对目标http://mywebsite.com/web的访问将被转发到kubernetes的一个Service上 webapp:80


创建该Ingress  
 #kubectl create -f Ingress.yaml


#kubectl get ingress  
 NAME |Hosts |Address |Ports |Age  
 mywebsite-ingress |mywebsite.com |80 |17s


创建后登陆nginx-ingress Pod,查看自动生成的nginx.conf内容


3)访问http://mywebsite.com/web  
 我们可以通过其他的物理机对其进行访问。通过curl --resolve进行指定  
 #curl --resolve mywebsite.com:80:192.169.18.3 mywebsite.com/web


#### 3)、使用Ingress 场景


Ingress 可能是暴露服务的最强大方式,但同时也是最复杂的。Ingress 控制器有各种类型,包括 [Google Cloud Load Balancer]( ), [Nginx]( ),[Contour]( ),[Istio]( ),等等。它还有各种插件,比如 [cert-manager]( ),它可以为你的服务自动提供 SSL 证书。  
 如果你想要使用同一个 IP 暴露多个服务,这些服务都是使用相同的七层协议(典型如 HTTP),那么Ingress 就是最有用的。如果你使用本地的 GCP 集成,你只需要为一个负载均衡器付费,且由于 Ingress是“智能”的,你还可以获取各种开箱即用的特性(比如 SSL,认证,路由,等等)。



### 5、总结各方式利弊


**hostPort**和**hostNetwork**直接使用节点网络,部署时节点需固定,访问ip也固定(也可以用host),端口为正常端口


**nodeport**方式部署时不要求固定节点,可通过集群内任一ip进行访问,就是端口为30000以上,很多时候由于公司安全策略导致不能访问。


**LoadBalancer**依赖于云服务商提供的LoadBalancer的实现机制。


**ingress**需要额外安装ingress模块,配置路由规则,且仅能通过所配置域名访问,配置好域名后,可以直接对外提供服务,和传统的nginx作用类似



## 六、Headless Service




---


**1、定义:**有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定Cluster IP(spec.clusterIP)的值为“None”来创建Headless Service。


**2、和普通`Service`相比:**


     对这类Headless Service并不会分配Cluster IP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。


     它会给一个集群内部的每个成员提供一个唯一的`DNS域名`来作为每个成员的网络标识,集群内部成员之间使用域名通信。


**3、无头服务管理的域名是如下的格式:**`$(service_name).$(k8s_namespace).svc.cluster.local`。其中的`"cluster.local"`是集群的域名,除非做了配置,否则集群域名默认就是`cluster.local`。


    因此选项spec.clusterIP允许开发人员自由的寻找他们自己的方式,从而降低与Kubernetes系统的耦合性。应用仍然可以使用一种自注册的模式和适配器,对其他需要发现机制的系统能够很容易的基于这个API来构建。


     因为没有load balancing或 proxy 代理设置,在访问服务的时候回返回后端的全部的Pods IP地址,主要用于开发者自己根据pods进行负载均衡器的开发(设置了selector)。


     DNS如何实现自动配置,依赖于Service时候定义了selector。  
  


**(1)编写headless service配置清单**  
 # vim myapp-svc-headless.yaml  
 apiVersion: v1  
 kind: Service  
 metadata:  
   name: myapp-headless  
   namespace: default  
 spec:  
   selector:  
     app: myapp  
     release: canary  
   clusterIP: "None" #headless的clusterIP值为None  
   ports:  
   - port: 80  
     targetPort: 80


**(2)创建headless service**  
 # kubectl apply -f myapp-svc-headless.yaml  
 [root@k8s-master mainfests]# kubectl get svc  
 NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE  
 kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP        36d  
 myapp            NodePort    10.101.245.119   <none>        80:30080/TCP   1h  
 myapp-headless   ClusterIP   None             <none>        80/TCP         5s  
 redis            ClusterIP   10.107.238.182   <none>        6379/TCP       2h


**(3)使用coredns进行解析验证**  
 [root@k8s-master mainfests]# dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10


; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp-headless.default.svc.cluster.local. @10.96.0.10  
 ;; global options: +cmd  
 ;; Got answer:  
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62028  
 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1


;; OPT PSEUDOSECTION:  
 ; EDNS: version: 0, flags:; udp: 4096  
 ;; QUESTION SECTION:  
 ;myapp-headless.default.svc.cluster.local. IN A


;; ANSWER SECTION:  
 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.1.18  
 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.1.19  
 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.15  
 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.16  
 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.17


;; Query time: 4 msec  
 ;; SERVER: 10.96.0.10#53(10.96.0.10)  
 ;; WHEN: Thu Sep 27 04:27:15 EDT 2018  
 ;; MSG SIZE  rcvd: 349


[root@k8s-master mainfests]# kubectl get svc -n kube-system  
 NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE  
 kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP   36d


[root@k8s-master mainfests]# kubectl get pods -o wide -l app=myapp  
 NAME                            READY     STATUS    RESTARTS   AGE       IP            NODE  
 myapp-deploy-69b47bc96d-4hxxw   1/1       Running   0          1h        10.244.1.18   k8s-node01  
 myapp-deploy-69b47bc96d-95bc4   1/1       Running   0          1h        10.244.2.16   k8s-node02  
 myapp-deploy-69b47bc96d-hwbzt   1/1       Running   0          1h        10.244.1.19   k8s-node01  
 myapp-deploy-69b47bc96d-pjv74   1/1       Running   0          1h        10.244.2.15   k8s-node02  
 myapp-deploy-69b47bc96d-rf7bs   1/1       Running   0          1h        10.244.2.17   k8s-node02


**(4)对比含有ClusterIP的service解析**  
 [root@k8s-master mainfests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10


; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp.default.svc.cluster.local. @10.96.0.10  
 ;; global options: +cmd  
 ;; Got answer:  
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50445  
 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1


;; OPT PSEUDOSECTION:  
 ; EDNS: version: 0, flags:; udp: 4096  
 ;; QUESTION SECTION:  
 ;myapp.default.svc.cluster.local. IN    A


;; ANSWER SECTION:  
 myapp.default.svc.cluster.local. 5 IN    A    10.101.245.119


;; Query time: 1 msec  
 ;; SERVER: 10.96.0.10#53(10.96.0.10)  
 ;; WHEN: Thu Sep 27 04:31:16 EDT 2018  
 ;; MSG SIZE  rcvd: 107


**# kubectl get svc**  
 NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE  
 kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP        36d  
 myapp            NodePort    10.101.245.119   <none>        80:30080/TCP   1h  
 myapp-headless   ClusterIP   None             <none>        80/TCP         11m  
 redis            ClusterIP   10.107.238.182   <none>        6379/TCP       2h  
  
 从以上的演示可以看到对比普通的service和headless service,headless service做dns解析是直接解析到pod的,而servcie是解析到ClusterIP的,那么headless有什么用呢?这将在statefulset中应用到,这里暂时仅仅做了解什么是headless service和创建方法。  
  



## 七、VIP注意事项




---


对很多想使用 Service 的人来说,前面的信息应该足够了。 然而,有很多内部原理性的内容,还是值去理解的。


#### 避免冲突


Kubernetes 最主要的哲学之一,是用户不应该暴露那些能够导致他们操作失败、但又不是他们的过错的场景。 这种场景下,让我们来看一下网络端口 —— 用户不应该必须选择一个端口号,而且该端口还有可能与其他用户的冲突。 这就是说,在彼此隔离状态下仍然会出现失败。


为了使用户能够为他们的 Service 选择一个端口号,我们必须确保不能有2个 Service 发生冲突。 我们可以通过为每个 Service 分配它们自己的 IP 地址来实现。


为了保证每个 Service 被分配到一个唯一的 IP,需要一个内部的分配器能够原子地更新 etcd 中的一个全局分配映射表,这个更新操作要先于创建每一个 Service。 为了使 Service能够获取到 IP,这个映射表对象必须在注册中心存在,否则创建 Service 将会失败,指示一个 IP 不能被分配。 一个后台 Controller 的职责是创建映射表(从 Kubernetes 的旧版本迁移过来,旧版本中是通过在内存中加锁的方式实现),并检查由于管理员干预和清除任意 IP 造成的不合理分配,这些 IP 被分配了但当前没有 Service 使用它们。


#### IP 和 VIP


不像 Pod 的 IP 地址,它实际路由到一个固定的目的地,Service 的 IP 实际上不能通过单个主机来进行应答。 相反,我们使用 iptables(Linux 中的数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以根据需要透明地进行重定向。 当客户端连接到 VIP 时,它们的流量会自动地传输到一个合适的 Endpoint。 环境变量和 DNS,实际上会根据 Service 的 VIP 和端口来进行填充。


Userspace


作为一个例子,考虑前面提到的图片处理应用程序。 当创建 backend Service 时,Kubernetes master 会给它指派一个虚拟 IP 地址,比如 10.0.0.1。 假设 Service 的端口是 1234,该 Service 会被集群中所有的 kube-proxy 实例观察到。 当代理看到一个新的 Service, 它会打开一个新的端口,建立一个从该 VIP 重定向到新端口的 iptables,并开始接收请求连接。


当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 Service代理 的端口。 Service代理 选择一个 backend,并将客户端的流量代理到 backend 上。


这意味着 Service 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。 客户端可以简单地连接到一个 IP 和端口,而不需要知道实际访问了哪些 Pod。


Iptables



### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/b2004d55b881455b4dd6681f02503e8b.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/cc45093f518a0bdae0618e111a0c35ef.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

比如 10.0.0.1。 假设 Service 的端口是 1234,该 Service 会被集群中所有的 kube-proxy 实例观察到。 当代理看到一个新的 Service, 它会打开一个新的端口,建立一个从该 VIP 重定向到新端口的 iptables,并开始接收请求连接。


当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 Service代理 的端口。 Service代理 选择一个 backend,并将客户端的流量代理到 backend 上。


这意味着 Service 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。 客户端可以简单地连接到一个 IP 和端口,而不需要知道实际访问了哪些 Pod。


Iptables



### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

[外链图片转存中...(img-6w8PM40a-1715738506616)]

给大家整理的电子书资料:

  

[外链图片转存中...(img-KCIiXLMc-1715738506616)]



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值