文章目录
Service
k8s 中的流量负载组件: Service (四层路由的负载)和Ingress(七层路由的负载)
Service 介绍
在K8s 中,pod 是应用程序的载体,我们可以通过pod 的ip 来访问应用程序,但是pod 的ip 地址不是固定的,这也就意味着不方便直接采用pod 的ip 对服务进行访问。
为了解决这个问题,K8s 中提供了Service 资源,Service 对提供同一个服务的多个pod 进行聚合,并且提供了一个统一的入口地址,通过访问Service 的入口地址就能访问后面的pod 服务。
Service 在很多情况下只是一个概念,真正起作用的是kube-proxy 服务进程,每个Node 节点上都运行着一个kube-proxy 服务进程。当创建Service 的时候会通过api-server 向etcd 写入创建的service 的信息,而kube-proxy 会基于监听的机制发现这种Service 的变动,然后它就会将最新的Service 信息转化成对应的访问规则。
service 提供访问入库的ip地址, kube-proxy 会基于轮询 rr(轮询)的策略,将一个请求转发到其中的一个pod 上。实际工作是这个访问规则。
注意这套访问规则,会同时在集群内的所有节点上生成。任何节点都可以查看到相应的规则。
kube-proxy 支持的三种工作模式
1 userspace 模式
userspace 模式下,kube-proxy 会为每一个Service 创建一个监听的端口,发向Cluster Ip 的请求被iptables 规则重定向到kube-proxy 监听的端口上,kube-proxy 根据LB(负载均衡)算法选择一个提供服务的pod 并和其建立连接,以将请求转发到pod 上。
该模式下,kube-proxy 充当了一个四层负载均衡器的角色,由于kube-proxy 运行在userspace 中,在进行转发处理时会增加内核和用户空间的数据拷贝,虽然比较稳定但是效率比较低。
2 iptables 模式
iptables 模式下,kube-proxy 为Service 后端的每一个pod 创建对应的iptables 规则,直接将发向Cluster ip 的请求重定向到一个pod ip。
该模式下kube-proxy 不承担四层负载均衡器的角色,只负责创建iptables 规则,该模式相对于userspace模式效率更高,但不能提供灵活的LB(负载均衡) 策略,当后端Pod不可用时,也无法进行重试。
3 ipvs 模式
ipvs 模式与iptables 模式相似,kube-proxy 监控pod的变换并创建相应的ipvs 规则,ipvs 相对与iptables 的转发效率更高,除此之外,ipvs支持更多的LB 算法。
查看当前主机是否开启ipvs 命令
ipvsadm -Ln
若没有使用 apt get ipvsadm 进行安装
里面没有相应对应的规则,说明ipvs 并没有生效。
1开启ipvs命令
kubectl edit cm kube-proxy -n kube-system
/mode 搜索mode 关键字 并修改为ipvs
2 删除一些当前kube-proxy 的pod ,使其重新的去创建。
kubectl delete pod -l k8s-app=kube-proxy -n kube-system
ipvsadm -Ln
命令进行查看。
使用TCP 模式发一个请求到172.17.0.9:30000 地址,就会基于rr(轮询) 模式转发到172.18.0.2:9090 地址
service 资源清单
Service 资源清单
K8s 中有四种类型的Service:
- ClusterIP: 默认值,它是由K8s 自动分配的虚拟IP,只能在集群内部访问。
- NodePort : 将Service 通过指定的Node 上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
- LoadBalancer: 使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持。
- ExternalName : 将集群外部的服务引入集群内部,直接使用。
sessionAffinity: 通过配置Session 亲和性,来实现对指定ip 轮转到相同的pod 上。
Service 使用
实验准备
使用Service 之前,先利用Deployment 创建出3个pod,注意pod 使用app=nginx-pod
标签
创建deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
strategy: # 策略
type: RollingUpdate # 滚动重建更新策略
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
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
使用命令
kubectl create ns dev
kubectl create -f deployment.yaml
kubectl get pods -n dev -o wide
使用
curl 172.18.0.9:80
访问nginx 服务
为了方便测试进入三台nginx 的index.html 页面(三台修改的ip 地址不一致), 进入容器修改index 页面
kubectl exec -it pc-deployment-5ffc5bf56c-r4n7x -n dev /bin/sh
echo "172.18.0.9" >/usr/share/nginx/html/index.html
插入其节点的ip地址到index.html 文件中
echo “172.18.0.7” >/usr/share/nginx/html/index.html
echo “172.18.0.8” >/usr/share/nginx/html/index.html
curl 172.18.0.7:80
ClusterIP 类型Service
创建service-cluster.yaml 文件
apiVersion: v1 #固定的,可以查看
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
selector:
app: nginx-pod # 选择标签为 app:nginx-pod 的pod
clusterIP: # service的ip 地址,如果不写会默认生成一个
type: ClusterIP
ports:
- port: 80 # Service 端口 Service 端口可以随便设置
targetPort: 80 # pod 端口
因为nginx 服务运行在80 端口上,所以 targetPort 必须设置为80。
kubectl create -f service-cluster.yaml
kubectl get service -n dev 查看service 创建出的情况
kubectl describe svc service-clusterip -n dev 查看service 详细情况
可以看到在Endpoints 中有选择的pod 对应的ip地址,Endpoint 是K8s 中对应的一个资源对象,存储在etcd 中,用来记录一个Service 对应的所有pod的访问地址。它是根据service 配置文件中的selector 描述产生的。
Endpoints 是实现实际服务的端点集合
使用下列命令查看endpoints
kubectl get endpoints -n dev -o wide
测试
1 查看ipvs 的映射规则
2 循环访问测试
while true; do curl 10.102.208.75:80;sleep 5; done;
轮询策略
负载分发策略
对Service 的访问被分发到了后端的pod 上去,目前K8s 提供了两种负载均衡的策略:
- 如果不定义,默认使用kube-proxy 策略,比如随机,轮询
- 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个pod 上,此模式通过在spec 中添加
sessionAffinity:ClientIP
选项
apiVersion: v1 #固定的,可以查看
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
sessionAffinity: ClientIP
selector:
app: nginx-pod
clusterIP: # service的ip 地址,如果不写会默认生成一个
type: ClusterIP
ports:
- port: 80 # Service 端口
targetPort: 80 # pod 端口
ClusterIP 类型的service 只能在其集群内部使用
HeadLiness 类型
在某些场景下,开发人员可能不想使用Service 提供的负载均衡功能。而是希望自己来控制负载均衡策略,针对这种情况,K8s 提供了HeadLiness Service, 这类Service 不会分配Cluster ip,如果要访问 service, 只能通过service 的域名进行查询。
创建service-headliness.yaml
apiVersion: v1 #固定的,可以查看
kind: Service
metadata:
name: service-headlines
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None #将clusterIP 设置为None,即可创建 headliness Service
type: ClusterIP
ports:
- port: 80 # Service 端口
targetPort: 80 # pod 端口
创建了只能通过service 的域名进行访问
进入pod
kubectl exec -it pc-deployment-66667x8s8- gdbxj -n dev /bin/bash
查看 /etc/resolv.conf
默认的域名解析服务器 为: 10.96.0.10
NodePort 类型的Service
NodePort 类型的工作原理就是将service 的端口映射到Node (节点)的一个端口上,然后可以通过 NodeIp:NodePort
来访问service。
创建service-nodeport.yaml
apiVersion: v1 #固定的,可以查看
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort #service 类型
ports:
- port: 80 # Service 端口
nodePort: 30002 # 指定绑定node 的端口(默认取值范围是30000-32767),如果不指定就会默认分配
targetPort: 80 # pod 端口
创建并查看
kubectl create -f service-nodeport.yaml
kubectl get svc service-nodeport -n dev
访问节点ip:30002 会转发到 Cluster-IP:80 端口
ip 地址为172.17.0.85
LoadBalance 类型的Service
LoadBalance 类型的service 和NodePort 很类似,目的都是向外暴露一个端口,区别在与LoadBalancer 会在集群的外部再来做一个负载均衡设备,这个设备需要外部环境支持,外部服务器发送到这个设备上的请求,会被设备负载之后转发到集群中。
ExternalName 类型的Service
ExternalName 类型的service 用于引入集群外部的服务,它通过 externalName 属性指定外部一个服务的地址。然后在集群内部访问该service 就可以访问到外部的服务。与NodePort 是反方向的
Ingress 介绍
前面介绍,service 对集群之外暴露服务的主要方式有两种: NotePort 和LoadBalancer ,但是这两种方式都存在一定的缺点。
- NodePort 方式的缺点在于会占用很多集群机器的端口,当集群变多的时候,这个缺点也就更加明显。
- LB 这种方式的缺点是每一个service 需要一个LB设备,浪费,麻烦,并且需要K8s 之外的设备的支持。
基于这种现状,K8s 提供了Ingress 资源对象,Ingress 只需要一个NodePort 或者一个LB 就可以暴露多个Service 需求。工作机制大致如下图所示:
Ingress 相当于一个七层的负载均衡器,是K8s 对反向代理的一个抽象。 它的工作原理类似与nginx。,可以理解成ingress 里建立诸如多映射规则。Ingress Controller 通过监听这些配置规则并转化为nginx 的反向代理配置,然后对外部提供服务。 两大概念:
- ingress: K8s 中的一个对象,作用是定义请求如何转发到service 的规则。
- ingress controller: 具体实现反向代理和负载均衡的程序。对ingress 定义的规则进行解析,根据配置的规则来实现请求转发。实现方式有很多: nginx,Contour, Haproxy 等
ingress(以Nginx 为例)的工作原理:
ingress 实例
1 ingress 环境准备
本次选用基于nginx 的 ingress controller
创建ingress controller 文件夹
mkdir ingress-controller
cd ingress-controller
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml
kubectl apply -f deploy.yaml
可以看到有一个nginx-controller
kubectl get svc -n ingress-nginx
Service 也产生了并且是NodePort类型,外部可以访问。
2 搭建 三个nginx pod 和三个 tomcat pod 以及对应的service
创建tomcat-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev
spec:
strategy: # 策略
type: RollingUpdate # 滚动重建更新策略
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
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:
strategy: # 策略
type: RollingUpdate # 滚动重建更新策略
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
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 #service 类型
ports:
- port: 80 # Service 端口
targetPort: 80 # pod 端口
---
apiVersion: v1 #固定的,可以查看
kind: Service
metadata:
name: tomcat-service
namespace: dev
spec:
selector:
app: tomcat-pod
clusterIP: None
type: ClusterIP #service 类型
ports:
- port: 8080 # Service 端口
targetPort: 8080 # pod 端口
http 代理
创建 ingress-http.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-http
namespace: dev
spec:
rules: # 策略
- host: nginx.itheima.com
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
host 主机域名
定义相应的规则
kubectl get ing ingress-http -n dev
访问 nginx.itheima.com/ 会转到 后面
因为该域名不能正常解析,只能做模拟。 更改本机的host 目录 : 在 C:\Windows\System32\drivers\etc 目录下。
添加
172.18.0.1 nginx.itheima.com 172.18.0.1 是master 节点的地址
172.18.0.1 tomcat.itheima.com
注意172.18.0.1 是google 内部的内网所以ping 不同,在自己的环境下应该可以ping 通。
查看 kubectl get svc -n ingress-nginx
如果本地或网址为公网访问地址http://nginx.itheima.com:32696/会出现nginx 页面
https 代理
1 生成证书
openssl req -newkey rsa:4096 -nodes -sha256 -keyout tls.key -x509 -out tls.crt -subj /C=CN/ST=BJ/L=BJ/O=DEVOPS/CN=wangzy -days 3650
req 产生证书签发申请命令
-newkey 生成新私钥
rsa:4096 生成秘钥位数
-nodes 表示私钥不加密
-sha256 使用SHA-2哈希算法
-keyout 将新创建的私钥写入的文件名
-x509 签发X.509格式证书命令。X.509是最通用的一种签名证书格式。
-out 指定要写入的输出文件名
-subj 指定用户信息
-days 有效期
2 创建密钥
kubectl create secret tls tls-secret --key=tls.key --cert tls.crt
创建 ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-https
namespace: dev
spec:
tls:
- hosts:
- nginx.itheima.com
- tomcat.itheima.com
secretName: tls-secret #指定密钥
rules: # 策略
- host: nginx.itheima.com
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080