K8S之Service详解

K8S之Service详解#

简介#

在K8S中,Pod是容器运行的载体,我们可以访问Pod内部的容器来访问相对应的服务,但是Pod的IP地址不是固定的,因此这也就意味我们不方便直接采用IP地址对Pod进行访问管理。

在K8S中提供我们 service 服务来解决上述问题, service 对一组pod进行聚合,并且提供一个统一的外部接口地址,我们通过访问该接口地址就可以访问其所对应的pod服务。

本质上来说其实 service 只是一个概念,真正起到转发数据的是 kube-proxy 服务进程,每个节点之上都运行一个 kube-proxy 服务进程,当创建 service 的时候 API Service 会监听 service 然后向 etcd 写入 service 的信息, kube-proxy 会监听 etcdservice 的信息并且将 service 信息转发成对应的访问规则。

s

kube-proxy工作模式#

userspace#

在该模式下 kube-proxy 会为每一个 service 创建一个监听端口,发送给 Cluseter IP 请求会被 iptable 重定向给 kube-proxy 监听的端口上,其中 kube-proxy 会根据 LB 算法将请求转发到相应的pod之上。

该模式下,kube-proxy充当了一个四层负载均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理的时候会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率非常低下。

user

iptables#

iptables模式下 kube-proxy 为每一个pod创建相对应的 iptables 规则,发送给 ClusterIP 的请求会被直接发送给后端pod之上

在该模式下 kube-proxy 不承担负载均衡器的角色,其只会负责创建相应的转发策略,该模式的优点在于较userspace模式效率更高,但是不能提供灵活的LB策略,当后端Pod不可用的时候无法进行重试。

iptab

ipvs模式#

ipvs模式与iptable模式类型, kube-proxy 会根据pod的变化创建相应的 ipvs 转发规则,ipvs相对iptable来说转发效率更加高效,同时提供了大量的负责均衡算法。

ipvs

使用ipvs模式必须安装ipvs内核模块,否则会自动降级为iptables

Copy
# 编辑配置文件 搜索43行mode将其修改为ipvs [root@master k8s]# kubectl edit cm kube-proxy -n kube-system # 删除原有的代理 [root@master k8s]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system # 查看 [root@master k8s]# ipvsadm -Ln

image-20211030172547155

service类型#

资源清单#

Copy
apiVersion: v1 # 版本 kind: Service # 类型 metadata: # 元数据 name: # 资源名称 namespace: # 命名空间 spec: selector: # 标签选择器,用于确定当前Service代理那些Pod app: nginx type: # Service的类型,指定Service的访问方式 clusterIP: # 虚拟服务的IP地址 sessionAffinity: # session亲和性,支持ClientIP、None两个选项,默认值为None ports: # 端口信息 - port: 8080 # Service端口 protocol: TCP # 协议 targetPort : # Pod端口 nodePort: # 主机端口
  • ClusterIP:默认类型,处于该模式下K8S会自动为service分配地址,其只能在集群内部访问。
  • NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务。
  • LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境的支持。
  • ExternalName:把集群外部的服务引入集群内部,直接使用。

service应用#

Clusterip service#

Copy
cat > service-clusterip.yaml << EOF apiVersion: v1 kind: Service metadata: name: service-clusterip namespace: dev spec: selector: app: nginx-pod type: ClusterIP # service IP地址 如果不写默认会生成一个 clusterIP: 10.97.97.97 ports: # service端口 - port: 80 # 目标pod端口 targetPort: 80 EOF # 创建service [root@master k8s]# kubectl create -f service-clusterip.yaml # 查看service [root@master k8s]# kubectl get -n dev service service-clusterip -o wide

创建pod#

Copy
cat > deployment.yaml << EOF apiVersion: apps/v1 kind: Deployment metadata: name: pc-deployment namespace: dev spec: replicas: 3 selector: matchLabels: app: nginx-pod template: metadata: labels: app: nginx-pod spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 protocol: TCP EOF # 创建pod [root@master k8s]# kubectl create -f deployment.yaml # 查看pod [root@master k8s]# kubectl get pod -n dev -o wide --show-labels

image-20211031172624022

Copy
# 任意节点发起请求 [root@master k8s]# curl 10.244.2.64

image-20211031173316240

Copy
# 由于pod节点都是nginx默认界面都是一样的为了方便测试修改默认界面 # 进入容器 [root@master k8s]# kubectl exec -it -n dev pc-deployment-7d7dd5499b-j5hnp /bin/sh # 三个节点写入数据 echo "Current Request is 10.244.1.64" > /usr/share/nginx/html/index.html echo "Current Request is 10.244.1.63" > /usr/share/nginx/html/index.html echo "Current Request is 10.244.1.62" > /usr/share/nginx/html/index.html # 发起请求 [root@master k8s]# curl 10.244.2.64 [root@master k8s]# curl 10.244.2.63 [root@master k8s]# curl 10.244.2.62

image-20211031173932500

创建services#

Copy
cat > service-clusterip.yaml << EOF apiVersion: v1 kind: Service metadata: name: service-clusterip namespace: dev spec: selector: app: nginx-pod type: ClusterIP # service IP地址 如果不写默认会生成一个 clusterIP: 10.97.97.97 ports: # service端口 - port: 80 # 目标pod端口 targetPort: 80 EOF # 创建service [root@master k8s]# kubectl create -f service-clusterip.yaml # 查看service [root@master k8s]# kubectl get -n dev service service-clusterip -o wide

image-20211031174525393

Copy
# 查看详情 [root@master k8s]# kubectl describe svc service-clusterip -n dev

image-20211031174702153

Copy
# 多次访问后端节点 [root@master k8s]# curl 10.97.97.97

image-20211031174928125

Endpoint#

Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有Pod的访问地址,它是根据service配置文件中的selector描述产生的。

一个service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换言之,service和Pod之间的联系是通过Endpoints实现的。

end

Copy
# 查看endpoints [root@master k8s]# kubectl get endpoints -n dev

image-20211031222101664

负载分发策略#

对Service的访问被分发到了后端的Pod上去,目前kubernetes提供了两种负载分发策略:

  • 如果不定义,默认使用kube-proxy的策略,比如随机、轮询等。
  • 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上,这对于传统基于Session的认证项目来说很友好,此模式可以在spec中添加sessionAffinity: ClusterIP选项。
Copy
# 修改分发策略 apiVersion: v1 kind: Service metadata: name: service-clusterip namespace: dev spec: selector: app: nginx-pod clusterIP: 10.97.97.97 # service的IP地址,如果不写,默认会生成一个 type: ClusterIP sessionAffinity: ClientIP # 修改分发策略为基于客户端地址的会话保持模式 ports: - port: 80 # Service的端口 targetPort: 80 # Pod的端口 [root@master k8s]# kubectl create -f service-clusterip.yaml # 循环测试 [root@master k8s]# while true;do curl 10.97.97.97:80; sleep 5; done;

image-20211031222807592

HeadLiness services#

在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLinesss Service,这类Service不会分配Cluster IP,如果想要访问Service,只能通过Service的域名进行查询。

创建service#

Copy
cat > service-headliness.yaml << EOF apiVersion: v1 kind: Service metadata: name: service-headliness namespace: dev spec: selector: app: nginx-pod clusterIP: None # 将clusterIP设置为None,即可创建headliness Service type: ClusterIP ports: - port: 80 # Service的端口 targetPort: 80 # Pod的端口 EOF [root@master k8s]# kubectl create -f service-headliness.yaml # 查看service [root@master k8s]# kubectl get svc service-headliness -n dev -o wide

image-20211031233312112

Copy
# 查看详情 [root@master k8s]# kubectl describe svc service-headliness -n dev

image-20211031233406900

Copy
# 查看pod [root@master k8s]# kubectl get pod -n dev

image-20211031233453549

Copy
# 进入Pod [root@master k8s]# kubectl exec -it -n dev pc-deployment-7d7dd5499b-jc84t /bin/bash # 查看域名 root@pc-deployment-7d7dd5499b-jc84t:/# cat /etc/resolv.conf

image-20211031233631713

Copy
# 通过Service的域名进行查询: # 默认访问规则service名称.名称空间.svc.cluster.local # 安装dig [root@master k8s]# yum -y install bind-utils # 访问 [root@master k8s]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local

NodePort service#

在上述案例之中 service 暴露的IP 只能供集群内部访问,但是我们创建资源即暴露给用户使用,因此 K8S 为我们提供了 NodePort service

其会将service 的端口与 node 的端口进行映射,当我们访问 node 的 IP + Port 即为访问 service 所对应的资源

nodeport

创建 service#

Copy
# 创建 service cat > cat service-nodeport.yaml << EOF apiVersion: v1 kind: Service metadata: name: service-nodeport namespace: dev spec: selector: app: nginx-pod type: NodePort # Service类型为NodePort ports: - port: 80 # Service的端口 targetPort: 80 # Pod的端口 nodePort: 30002 # 指定绑定的node的端口(默认取值范围是30000~32767),如果不指定,会默认分配 EOF # 创建pod [root@master k8s]# kubectl create -f service-nodeport.yaml # 查看详情 [root@master k8s]# kubectl get svc -n dev -o wide

image-20211108102608132

Copy
# 访问测试 由于更换机器此时 master 的地址由 10.1.1.2 更改为 172.16.137.128 curl 172.16.137.128:30002

image-20211108103002569

LoadBalancer service#

通过上图可以看到使用 NodePort 模式中对负载均衡能力不是很友好,external 类型与 nodeport 类似都是对于外部暴露一个访问端口

区别在于使用该模式会在集群外部添加一个负载均衡设备,外部访问负载均衡设置,由负载均衡设备在根据一定的算法转发到后端服务节点

load

ExternalName service#

ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定一个服务的地址,然后在集群内部访问此Service就可以访问到外部的服务了。

exte

创建 service#

Copy
cat > service-externalname.yaml << EOF apiVersion: v1 kind: Service metadata: name: service-externalname namespace: dev spec: type: ExternalName # Service类型为ExternalName externalName: www.baidu.com # 改成IP地址也可以 EOF [root@master k8s]# kubectl create -f service-externalname.yaml # 查看 service [root@master k8s]# kubectl get svc service-externalname -n dev

image-20211108140945724

Copy
# 安装域名解析 [root@master k8s]# yum install bind-utils # 域名解析 [root@master k8s]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local

Ingress#

简介#

在上述中 service 对外提供负载均衡主要有 nodeport 与 loadblancer 两种方式,但是这两种方式各自都有一定的缺点,在 nodeport 方式中 service 会与 node 节点进行映射,这样会占用大量的端口,当 service 过多的时候可能会导致节点端口不够,在 loadblancer 中每一个service 都需要一个 LB,并且需要外部的负载均衡设备进行支持

基于上述问题,在 K8S 中提出了 ingress 资源对象,该资源对象只需要一个 nodeport 或者一个 LB 就可以满足暴露多个 service 需求

ingress
实际上 Ingress 类似于一个七层的负载均衡器,是由 K8S 对反向代理的抽象,其工作原理类似于 Nginx 可以理解为Ingress里面建立了诸多映射规则,Ingress Controller通过监听这些配置规则并转化为Nginx的反向代理配置,然后对外提供服务。

  • Ingress:kubernetes中的一个对象,作用是定义请求如何转发到Service的规则。

  • Ingress Controller:具体实现反向代理及负载均衡的程序,对Ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现的方式有很多,比如Nginx,Contour,Haproxy等。

  • 其工作原理如下

    • 用户编写 Ingress 规则说明那个域名对应那个 service
    • Ingress Contoller 动态感知 ingress 编写的规则,然后生成对应的反向代理规则
    • ingress 控制器会根据生成代理规则写入到代理服务中
    • 客户端请求代理服务,由代理服务转发到后端 pod 节点

ha

Ingress 使用#

环境搭建#

Copy
mkdir ingress-controller && cd ingress-controller # 下载service 与控制器 wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml # 更换网络源 sed -i 's#quay.io/kubernetes-ingress-controller/nginx-ingress-controller#registry.cn-qingdao.aliyuncs.com/kubernetes_xingej/nginx-ingress-controller#g' mandatory.yaml # 使用配置文件 kubectl apply -f ./ # 查看pod [root@master ~]# kubectl get -n ingress-nginx pod # 查看svc [root@master ~]# kubectl get -n ingress-nginx svc

image-20211109224550664

准备Service和pod#

Copy
cat > tomcat-nginx.yaml << EOF apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: dev spec: replicas: 3 selector: matchLabels: app: nginx-pod template: metadata: labels: app: nginx-pod spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: tomcat-deployment namespace: dev spec: replicas: 3 selector: matchLabels: app: tomcat-pod template: metadata: labels: app: tomcat-pod spec: containers: - name: tomcat image: tomcat:8.5-jre10-slim ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: nginx-service namespace: dev spec: selector: app: nginx-pod clusterIP: None type: ClusterIP ports: - port: 80 targetPort: 80 --- apiVersion: v1 kind: Service metadata: name: tomcat-service namespace: dev spec: selector: app: tomcat-pod clusterIP: None type: ClusterIP ports: - port: 8080 targetPort: 8080 EOF # 创建pod [root@master ~]# kubectl create -f tomcat-nginx.yaml # 查看 [root@master ~]# kubectl get svc,pod -n dev

image-20211109225128565

HTTP代理#

Copy
cat > ingress-http.yaml << EOF apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-http namespace: dev spec: # 代理规则 rules: # 绑定域名 - host: nginx.sr.com http: paths: # 绑定路径 - path: / backend: serviceName: nginx-service # service暴露端口 servicePort: 80 - host: tomcat.sr.com http: paths: - path: / backend: serviceName: tomcat-service servicePort: 8080 EOF # 创建 [root@master ~]# kubectl create -f ingress-http.yaml # 查看 [root@master ~]# kubectl get pod -n dev -o wide

image-20211109225900016

Copy
# 查看详情 [root@master ~]# kubectl describe ingress ingress-http -n dev

image-20211109225951385

配置本地host文件将master的IP地址与上述域名绑定

Copy
# 访问nginx curl nginx.sr.com:30771

image-20211109230744863

Copy
# 访问tomcat curl tomcat.sr.com:30378

image-20211109230847610

HTTPS 代理#

Copy
# 生成证书 [root@master ~]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=sr.com" # 生成秘钥 [root@master ~]# kubectl create secret tls tls-secret --key tls.key --cert tls.crt # 生成配置文件 cat > ingress-https.yaml << EOF apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-https namespace: dev spec: tls: - hosts: - nginx.sr.com - tomcat.sr.com secretName: tls-secret # 指定秘钥需要与上述生成的秘钥名称相同 rules: - host: nginx.sr.com http: paths: - path: / backend: serviceName: nginx-service servicePort: 80 - host: tomcat.sr.com http: paths: - path: / backend: serviceName: tomcat-service servicePort: 8080 EOF # 加载配置文件 [root@master k8s]# kubectl create -f ingress-https.yaml # 查看配置文件 [root@master k8s]# kubectl get ingress ingress-https -n dev

image-20211118164057887

Copy
# 查看详情 [root@master k8s]# kubectl describe -n dev ingress ingress-https

image-20211118165502336

Copy
# 查看service [root@master k8s]# kubectl get -n ingress-nginx service

image-20211118165641222

posted @   SR丶  阅读(3144)  评论(1编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示
CONTENTS