Service 简介

 
K8s 中提供微服务的实体是 Pod,Pod 在创建时 docker engine 会为 pod 分配 ip,“外部”流量通过访问该 ip 获取微服务。但是,Pod 的状态是不稳定的,它容易被销毁,重建,一旦重建, Pod 的 ip 将改变,那么继续访问原来 ip 是不现实的。针对这个问题 K8s 引入 services 这一 kind,它提供类似负载均衡的作用。与 Pod 不同 service 在创建时 K8s 会为其分配一固定 ip,叫做 ClusterIP。外部流量访问 ClusterIP 即可实现对 Pods 的访问。
 
进一步的,通过以下示意图说明 service 的工作原理:
 
如图所示,service 定义了微服务的入口地址,它通过标签选择器匹配到需要转发流量的 pod,将外部来的流量这里是 frontend pod 来的流量引入到 Pod 中。
 
创建 service
根据上节分析,这里我们通过配置 yaml 文件来创建 service,首先创建 service 需要“引流”的 Pods:
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd-deployment
spec:
replicas: 3
selector:
matchLabels:
app: web_server
template:
metadata:
labels:
app: web_server
spec:
containers:
- name: httpd-demo
image: httpd
 
分别介绍上面参数:
  • apiVersion: 创建资源的 api 版本,这里是 apps/v1。
  • kind: 创建的资源类型为 Deployment。
  • metadata.name: 创建的 Deployment 名字。
  • replicas: 资源 Deployment 包括三个 Pods 副本。
  • matchLabels: 匹配到对应的 Pod 标签。
  • labels: 副本 Pod 的标签。
  • containers: Pod 内的 container,它是实际提供微服务的单元。
 
上面我们创建了三个副本,且标签为 app:web_server,RC(replicationController) 通过 matchLabels 和创建的三个副本关联。
 
继续创建 service:
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
selector:
app: web_server
ports:
- protocol: TCP
port: 8080
targetPort: 80
 
创建名为 httpd-svc 的 service,标签选择器将 service 的标签 app:web_server 和对应的 pods 关联。service “对外”(对外实际上还是在集群内)开放的端口为 8080,它将映射到 Pods 中的 80 端口。
 
Deployment,service 创建好后,我们构建了如下的测试场景:
## 创建 Deployment
$ kubectl apply -f deployment-test.yaml
deployment.apps/bootcamp-deployment created
$ kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
bootcamp-deployment 3/3 3 3 2m5s
$ kubectl get replicasets.apps
NAME DESIRED CURRENT READY AGE
bootcamp-deployment-f94bcd74c 3 3 3 2m16s
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
bootcamp-deployment-f94bcd74c-k4mrc 1/1 Running 0 3m17s 172.18.0.5 minikube <none> <none>
bootcamp-deployment-f94bcd74c-q2x6c 1/1 Running 0 3m17s 172.18.0.6 minikube <none> <none>
bootcamp-deployment-f94bcd74c-wwcqx 1/1 Running 0 3m17s 172.18.0.4 minikube <none> <none> ## 创建 Service
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-svc ClusterIP 10.108.52.85 <none> 8080/TCP 41s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10m ## frontend pod
$ kubectl run kubernetes-bootcamp --image=docker.io/jocatalin/kubernetes-bootcamp:v1 --port=8080 --labels="app=bootcamp"
deployment.apps/kubernetes-bootcamp created
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
bootcamp-deployment-f94bcd74c-k4mrc 1/1 Running 0 6m53s 172.18.0.5 minikube <none> <none>
bootcamp-deployment-f94bcd74c-q2x6c 1/1 Running 0 6m53s 172.18.0.6 minikube <none> <none>
bootcamp-deployment-f94bcd74c-wwcqx 1/1 Running 0 6m53s 172.18.0.4 minikube <none> <none>
kubernetes-bootcamp-9966c6d5-qpr9w 1/1 Running 0 14s 172.18.0.7 minikube <none> <none>
 
开始访问 Service:
## cluster node 访问
$ curl 172.18.0.5:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-k4mrc | v=1 $ curl 10.108.52.85:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-k4mrc | v=1
$ curl 10.108.52.85:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-q2x6c | v=1
$ curl 10.108.52.85:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-wwcqx | v=1
 
## frontend pod 访问
$ kubectl exec -it kubernetes-bootcamp-9966c6d5-qpr9w /bin/bash
root@kubernetes-bootcamp-9966c6d5-qpr9w:/# curl 172.18.0.5:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-k4mrc | v=1 root@kubernetes-bootcamp-9966c6d5-qpr9w:/# curl 10.108.52.85:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-k4mrc | v=1
root@kubernetes-bootcamp-9966c6d5-qpr9w:/# curl 10.108.52.85:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-q2x6c | v=1
root@kubernetes-bootcamp-9966c6d5-qpr9w:/# curl 10.108.52.85:8080
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-wwcqx | v=1
 
可以看出,访问 service 即是访问与 service 关联的 pod。这里未指定 service 的负载分发策略,它有两种策略 RoundRobin 和 SessionAffinity。默认策略为 roundRobin 轮询,即轮询将请求转发到后端各个 Pod。service 的 spec.sessionAffinity 字段可修改访问策略,当值为 ClientIP(默认为空) 即表示将同一个客户端的访问请求转发到同一个后端 Pod。
 
集群外部访问 Service
service 是 K8s 中的概念,它分配的 ip 是逻辑的,没有实体的 ip。所以在 K8s 集群外无法访问 service 的 ip,K8s 提供了 NodePort 和 LoadBalancer 两种方式实现集群外访问 Service。这里因实验环境限制只介绍 NodePort 方式。
 
创建类型为 NodePort 的 service:
## 命令行创建 service, 也可通过 yaml 文件创建
$ kubectl expose deployment/bootcamp-deployment --type="NodePort" --port 8080
service/bootcamp-deployment exposed $ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
bootcamp-deployment NodePort 10.107.172.165 <none> 8080:32150/TCP 5s
httpd-svc ClusterIP 10.108.52.85 <none> 8080/TCP 36m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46m $ kubectl describe service bootcamp-deployment
Name: bootcamp-deployment
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=web_server
Type: NodePort
IP: 10.107.172.165
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
NodePort: <unset> 32150/TCP
Endpoints: 172.18.0.4:8080,172.18.0.5:8080,172.18.0.6:8080
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
 
可以看出,K8s 将 Service 的 8080 端口和 node 上的 32150 端口关联,并且 node 上的 32150 端口被 (kube-proxy) 监听:
$ netstat -antp | grep 32150
tcp6 0 0 :::32150 :::* LISTEN 4651/kube-proxy $ ps aux | grep 4651 | grep -v grep
root 4651 0.0 1.2 140108 31048 ? Ssl 07:15 0:01 /usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=minikube
 
外部访问 NodePort Service 的 ClusterIP + Port,即可访问到对应的 Pod:
$ curl 172.17.0.72:32150
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-q2x6c | v=1
$ curl 172.17.0.72:32150
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-k4mrc | v=1
 
Service 的底层实现
探究 service 的底层实现就不得不提到 K8s 的核心组件 kube-proxy,它是一个位于 kube-system namespace 的 Pod,其核心功能是将到 service 的访问请求转发到后端 Pods:
$ kubectl get pods --namespace=kube-system | grep kube-proxy
kube-proxy-gk8zm 1/1 Running 0 37s
 
kube-proxy 的工作流程大致为,查询和监听 API server 的 services 和 Endpoints 变化,如果有变化则修改本机的 iptables。
kube-proxy 在 iptables 中自定义了 KUBE-SERVICES, KUBE-NODEPORTS,KUBE-POSTROUTING,KUBE-MARK-MASQ 和 KUBE-MARK-DROP 五个链,其中 KUBE-SERVICES 链用来添加流量路由规则。每个链的作用分别为:
  • KUBE-SERVICES:操作跳转规则的主要链。
  • KUBE-NODEPORTS:通过 nodeport 访问的流量经过的链。
  • KUBE-POSTROUTING:post 路由经过的链。
  • KUBE-MARK-MASQ:对符合条件的 package set MARK0x4000,有此标记的数据包会在 KUBE-POSTROUTING 链中做 MASQUERADE。
  • KUBE-MARK-DROP:对未能匹配到跳转规则的package set mark 0x8000,有该标记的包会在 filter 表中被 drop 掉。
 
查看路由表,以类型为 ClusterIP 的 service 为例:
-A KUBE-SERVICES -d 10.108.52.85/32 -p tcp -m comment --comment "default/httpd-svc: cluster IP" -m tcp --dport 8080 -j KUBE-SVC-RL3JAE4GN7VOGDGP
 
链 KUBE-SERVICES 的规则为访问目的地址 10.108.52.85,端口为 8080 的数据包都被转发到规则 KUBE-SVC-RL3JAE4GN7VOGDGP:
-A KUBE-SVC-RL3JAE4GN7VOGDGP -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-NGIJJXQTL6LQACUG
-A KUBE-SVC-RL3JAE4GN7VOGDGP -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-KSASEUT37GIWYDZK
-A KUBE-SVC-RL3JAE4GN7VOGDGP -j KUBE-SEP-TTP6SZ4CLZYRFEZJ
 
规则 RL3JAE4GN7VOGDGP 有三条,三条规则分别是数据包随机 1/3 的概率发到规则 KUBE-SEP-NGIJJXQTL6LQACUG,KSASEUT37GIWYDZK 和 KUBE-SEP-TTP6SZ4CLZYRFEZJ。再看这三条定义的是什么规则:
-A KUBE-SEP-NGIJJXQTL6LQACUG -s 172.18.0.4/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-NGIJJXQTL6LQACUG -p tcp -m tcp -j DNAT --to-destination172.18.0.4:8080
 
三条规则都是类似的,以规则 NGIJJXQTL6LQACUG 为例,其定义了两条规则。第一条,如果发往 ClusterIP 的源 ip 地址是 172.18.0.4 则进入到链 KUBE-MARK-MASQ。第二条,如果是“外部”协议为 tcp 的数据包进入该规则,则做目的 NAT 转换,将目的地址转换为 Pod 的地址 172.18.0.4:8080。
 
继续,查看类型为 NodePort 的 service:
-A KUBE-SERVICES -d 10.107.172.165/32 -p tcp -m comment --comment "default/bootcamp-deployment: cluster IP" -m tcp --dport 8080 -j KUBE-SVC-7BU2JDGBFZPRVB5H
 
路由表中定义,集群外部访问 10.107.172.165 端口为 8080 的地址的流量将跳转到规则 KUBE-SVC-7BU2JDGBFZPRVB5H:
-A KUBE-SVC-7BU2JDGBFZPRVB5H -m statistic --mode random --probability0.33333333349 -j KUBE-SEP-JRC46L5OEHUJ3JOG
-A KUBE-SVC-7BU2JDGBFZPRVB5H -m statistic --mode random --probability0.50000000000 -j KUBE-SEP-RVACF472KUDH2WI5
-A KUBE-SVC-7BU2JDGBFZPRVB5H -j KUBE-SEP-NHLSGKWGDK2BACCW
 
类似的,规则 KUBE-SVC-7BU2JDGBFZPRVB5H 分别是数据包随机 1/3 的概率发到规则 KUBE-SEP-JRC46L5OEHUJ3JOG,KUBE-SEP-RVACF472KUDH2WI5 和 KUBE-SEP-NHLSGKWGDK2BACCW。查看规则 KUBE-SEP-JRC46L5OEHUJ3JOG 定义:
-A KUBE-SEP-JRC46L5OEHUJ3JOG -s 172.18.0.4/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-JRC46L5OEHUJ3JOG -p tcp -m tcp -j DNAT --to-destination 172.18.0.4:8080
 
它有两条规则,重点是第二条,当外部协议为 tcp 的数据包到达该规则,则做目的 NAT 将数据包直接发到目的地址 172.18.0.4:8080。
 
service 与 Endpoints
细心的读者会发现在 service 的 describe 内容中有个 Endpoints 参数,它描述的是 service 所映射的后端 pod 的地址,如下所示:
$ kubectl describe service httpd-svc
Name: httpd-svc
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"httpd-svc","namespace":"default"},"spec":{"ports":[{"port":8080,"...
Selector: app=web_server
Type: ClusterIP
IP: 10.108.52.85
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
Endpoints: 172.18.0.4:8080,172.18.0.5:8080,172.18.0.6:8080
Session Affinity: None
Events: <none>
 
selector 在匹配到对应的后端 pod 后,service 会更新 Endpoints 为后端 pod 的地址。这里我们可以构造这样一种场景,service 不通过 selector 选择后端 pod,而是直接将它与 Endpoints 做关联:
 
创建 Endpoints:
apiVersion: v1
kind: Endpoints
metadata:
name: httpd-svc-endpoints
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 80
 
创建 service:
apiVersion: v1
kind: Service
metadata:
name: httpd-svc-endpoints
spec:
ports:
- protocol: TCP
port: 8080
targetPort: 80 [centos@k8s-master-node-1 hxia]$ kubectl describe services httpd-svc-endpoints
Name: httpd-svc-endpoints
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP: 10.102.65.186
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 1.2.3.4:80
Session Affinity: None
Events: <none>
 
创建完毕,可以看到 service 的 Endpoints 更新为提前创建好的 Endpoints,且 selector 为 none。相应的,kube-proxy 会在路由表中建立 service 到 Endpoints 的规则。
 
除了不指定 selector,创建 service 时也可以不指定 ClusterIP (ClusterIP: None),不指定 ClusterIP 的 service 称为 Headless service,使用该 service 即是去中心化,外部流量直接获取到后端 pod 的 Endpoints,然后自行选择该访问哪个 pod。
 
service 与 DNS
除了直接访问 service ClusterIP 访问后端 pod 外,还可以通过 service 名,对于 NodePort 类型的 service 也可通过集群 node 名访问 pod。K8s 中实现名称解析和 ip 对应的核心组件是 coredns: 
$ curl minikube:32150
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-q2x6c | v=1
$ curl minikube:32150
Hello Kubernetes bootcamp! | Running on: bootcamp-deployment-f94bcd74c-wwcqx | v=1 $ kubectl get pods --namespace=kube-system -o wide | grep dns
coredns-6955765f44-rb828 1/1 Running 0 24m 172.18.0.3 minikube <none> <none>
coredns-6955765f44-rm4pv 1/1 Running 0 24m 172.18.0.2 minikube <none> <none>
 
集群内部访问 pod 的流量路径大致为流量访问 service,通过 coredns 解析该 service 对应的 ClusterIP,继续访问 ClusterIP,当请求到达宿主机网络后 kube-proxy 会对请求做拦截,根据路由表规则将请求转发到后端 pod 实现服务发现和流量转发。集群外部访问 pod 的流量路径大致类似。
 
 
 
关于 Kubernetes 服务发现 service 就介绍到这里,想继续深入了解,推荐看这篇博文
 

一文看懂 Kubernetes 服务发现: Service的更多相关文章

  1. 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    我们知道,不同肤色的人外貌差别很大,而双胞胎的辨识很难.有意思的是Web服务器/Web容器/Web应用程序服务器/反向代理有点像四胞胎,在网络上经常一起出现.本文将带读者对这四个相似概念如何区分. 1 ...

  2. Docker Kubernetes 服务发现原理详解

    Docker Kubernetes  服务发现原理详解 服务发现支持Service环境变量和DNS两种模式: 一.环境变量 (默认) 当一个Pod运行到Node,kubelet会为每个容器添加一组环境 ...

  3. [转帖]一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    一文看懂web服务器.应用服务器.web容器.反向代理服务器区别与联系 https://www.cnblogs.com/vipyoumay/p/7455431.html 我们知道,不同肤色的人外貌差别 ...

  4. [转帖] 一文看懂:"边缘计算"究竟是什么?为何潜力无限?

    一文看懂:"边缘计算"究竟是什么?为何潜力无限? 转载cnbeta   云计算 雾计算 边缘计算...   知名创投调研机构CB Insights撰文详述了边缘计算的发展和应用前景 ...

  5. Nature 为引,一文看懂个体化肿瘤疫苗前世今生

    进入2017年,当红辣子鸡PD-1疗法,一路横扫多个适应症.而CAR-T治疗的“小车”在获得FDA专委会推荐后也已经走上高速路,成为免疫治疗又一里程碑事件.PD-1.CAR-T之后,下一个免疫治疗产品 ...

  6. 一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了

    一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了 转载: 大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的.你可以把它 ...

  7. 转载来自朱小厮博客的 一文看懂Kafka消息格式的演变

    转载来自朱小厮博客的 一文看懂Kafka消息格式的演变     ✎摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在 ...

  8. Kubernetes服务发现入门:如何高效管理服务?

    愈发复杂的应用程序正在依靠微服务来保持可扩展性和提升效率.Kubernetes为微服务提供了完美的环境,并能够让其与Kubernetes的工具组件和功能兼容.当应用程序的每个部分放置在一个容器中,整个 ...

  9. 【转帖】一文看懂docker容器技术架构及其中的各个模块

    一文看懂docker容器技术架构及其中的各个模块 原创 波波说运维 2019-09-29 00:01:00 https://www.toutiao.com/a6740234030798602763/ ...

随机推荐

  1. gcc入门(下)

    一 头文件与库文件(模块化,可重用,好维护)在使用C语言和其他语言进行程序设计的时候,我们需要头文件来提供对常数的定义和对系统以及库函数调用的声明库文件是一些预先编译好的函数的集合,那些函数都是按照可 ...

  2. wireguard使用

    1.编译与安装 sudo apt-get install libmnl-dev libelf-dev linux-headers-$(uname -r) build-essential pkg-con ...

  3. Abbott的复仇(Abbott's Revenge)

    题目:有一个最多包含9*9个交叉点的迷宫.输入起点.离开起点时的朝向和终点,求一条最短路(多解时任意输出一个即可). 这个迷宫的特殊之处在于:进入一个交叉点的方向(用NEWS这4个字母分别表示北东西南 ...

  4. 推荐一个学习SQL Server基本语法及其他编程的网站

    菜鸟们的练习场 1.领先的 Web 技术教程 在 w3school,你可以找到你所需要的所有的网站建设教程. 从基础的 HTML 到 CSS,乃至进阶的XML.SQL.JS.PHP 和 ASP.NET ...

  5. 微信公众号获取openid(php实例)

    微信公众号获取openid 公众号获取openid的方法跟小程序获取openid其实是一样的,只是code获取的方式不一样 小程序获取code: 用户授权登录时调用wx.login即可获取到code ...

  6. MathType总结编辑括号的类型(上)

    括号的种类有很多,我们用得也很多,可以说无处不见,不只是在数学物理这些自然科学的公式中来断地出现括号,即使是在人文艺术类的领域也会有括号的出现.下面就和小编一起来看看公式编辑器编辑括号的类型吧! Ma ...

  7. Camtasia中对录制视频进行编辑——音效

    市场上有很多的视频处理软件,形形色色的软件往往会使人眼花缭乱,而对于那些短视频的制作者来说,拥有一款好的视频处理软件会让自己的视频收获更多的点赞.那么今天我便给大家推荐一款同时具有录屏和编辑视频功能的 ...

  8. 带你体验Folx的智能化下载功能

    BT下载器,也就是种子下载器,其优点是下载的人越多,下载的速度就越快,可以更快速地下载热门资源.这是因为,每个下载种子的用户,在下载的同时,也会上传资源,从而加速其他用户的下载速度. Folx软件,作 ...

  9. LeetCode周赛#203 题解

    1561. 你可以获得的最大硬币数目 #贪心 题目链接 题意 有 3n 堆数目不一的硬币,你和你的朋友们打算按以下方式分硬币: 每一轮中,你将会选出 任意 3 堆硬币(不一定连续). Alice 将会 ...

  10. 【mq读书笔记】消息消费队列和索引文件的更新

    ConsumeQueue,IndexFile需要及时更新,否则无法及时被消费,根据消息属性查找消息也会出现较大延迟. mq通过开启一个线程ReputMessageService来准时转发commitL ...