前言:在本章的内容中,注意一些域名的访问,要在自己的win中hosts文件中做映射欧
一、Service 的概念
Kubernetes Service 定义了这样一种抽象:一个 Pod
的逻辑分组,一种可以访问它们的策略 – 通常称为微服务。 这一组 Pod
能够被 Service
访问到,通常是通过 Label Selector
因为这三个pod都有 标签app=webapp
和role-frontend
,所以都爱莫能助最上面的那个 svc
,所以我们可以通过svc
提供的ip和端口来访问内部的三个pod,后续如果pod的ip发生改变,或者增加减少pod,都会在svc
中记录,我们不需要关系,目前svc
只有一个轮询的算法。
Service能够提供负载均衡的能力,但是在使用上有以下限制
- 只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的。(后续可以使用 igneress模块来满足七层)
二、Service 的类型
Service 在 K8s 中有以下四种类型
项目 | Value |
---|---|
ClusterIp | 默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP,即other-pod也在当前节点,可以通过当前节点的svc来访问nginx-deployment的三个pod; |
NodePort | 在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 : NodePort 来访问该服务;但是这个nginx这个地方就需要我们自己做了,对于下面一个类型LoadBalancer 可以花钱让云厂商提供,这也是这两者的区别了。 |
LoadBalancer | 在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort; |
ExternalName | 把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持; |
三、Service 的实现
VIP 和 Service 代理
在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一种VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在
userspace
。在Kubernetes v1.1 版本,新增了iptables
代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是iptables
代理。 在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理
在 Kubernetes 1.14 版本开始默认使用ipvs
代理在 Kubernetes v1.0 版本, Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务
即三种方式
-
userspace
-
iptables 即通过防火墙的方式实现。
-
ipvs 最好的实现
这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
rr :轮询调度
lc :最小连接数
dh :目标哈希
sh :源哈希
sed :最短期望延迟
nq : 不排队调度
四、Service 类型实验
注意以下的实验的svc的实现,是根据你的svc的具体实现来处理的,比如下面的文章都是说使用iptables,如果你使用的是 ipvs ,那么把文章中对应的 iptables 的字段换成 ipvs,看文档即可
4.1、ClusterIP
clusterIP 主要在每个 node 节点使用 iptables(如这里换成ipvs),将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口
为了实现图上的功能,主要需要以下几个组件的协同工作:
- apiserver
用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
- kube-proxy
kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables (如这里换成ipvs)规则中
- iptables
使用NAT等技术将virtualIP的流量转至endpoint中
先创建deployment
vim svc-deployment.yaml
内容如下:
apiVersion: apps/v1
kind: Deployment # 类型
metadata:name: myapp-deploy # 名称namespace: default
spec:replicas: 3 # 副本数selector:matchLabels:app: myapp # 当前 deployment 只管理 标签为 app=myapp 和 release=stabel的podrelease: stabeltemplate:metadata:labels:app: myapp # 为当前pod 设置 标签,不然无法被当前deployment 管理,以及后面的svc管理release: stabelenv: testspec:containers:- name: myappimage: wangyanglinux/myapp:v2imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80
创建svc,注意svc的标签选择,一定要匹配到对应的pod
vim svc.yaml
内容如下
apiVersion: v1
kind: Service
metadata:name: myapp # 这个svc的名称namespace: default
spec:type: ClusterIP # 类型,这个类型只能集群内部访问selector:app: myapp # 注意这里的标签选择,要能选择到前面的那些pod,不然这个svc就创建了个寂寞,没有可以映射的podrelease: stabelports:- name: httpport: 80targetPort: 80
查看svc
ipvsadm -Ln
而 2.70
、2.69
、1.66
就是那三个对应的pod地址
4.2、headless(一种特殊的 clusterIp)
创建,至于deployment 采用4.1中的的即可
vim svc-none.yaml
具体内容如下:
piVersion: v1
kind: Service
metadata:name: myapp-headlessnamespace: default
spec:selector:app: myappclusterIP: "None" # 只有这里和 clusterIP 类型不同ports: - port: 80targetPort: 80
我们每创建一个svc,都会在k8s的coredns中记录
kubectl ge pod -n kube-system -o wide
# 对 svcname.namespece.svc.cluster.local. 使用 10.224.0.7(选择10.244.0.6也行看上面截图,二个ip选一个)来解析
dig -t A myapp-headless.default.svc.cluster.local. @10.224.0.7
解析结果如下,意思就是我们也可以进行域名访问(em…感觉不是那么的靠谱,好像不是这个意思,哈哈哈哈)
4.3、node port
一个pod可以对应多个 svc,即你可以通过一号svc访问这个pod,也可以根据二号svc来访问它,M-N(多对多)的关系。
至于deployment 采用4.1中的的即可
vim nodeport.yaml
内容如下
apiVersion: v1
kind: Service
metadata:name: myappnamespace: default
spec:type: NodePortselector:app: myapp # 当然这个标签的意义还是需要选择的对应的pod,不然这个svc下面是空的,没啥意义release: stabelports:- name: httpport: 80targetPort: 80
外网访问,并且是所有的节点都开放了这个端口,都可以访问
通过查看svc 的ipvs(这里使用的实现是ipvs)的路由规则也是能看出来的
ipvsadm -Ln
4.4、LoadBalancer
loadBalancer 和 nodePort 其实是同一种方式。区别在于 loadBalancer 比 nodePort 多了一步,就是可以调用 cloud provider 去创建 LB 来向节点导流,所以需要云供应商来提供,花钱。
4.4、ExternalName
这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.atguigu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。这样一旦你在k8s集群内部访问xxx就会访问到外部的 hub.atguigu.com(可以参考前面的几种类型的图示说明),意义就是把外部的流量引入到k8s内部,且当外部地址改变时,不需要在k8s内部应用的地方一个一个的去改,而内部只需要引用这个svc的名称,通过这个名称来访问,发生改变时修改这个svc的配置文件即可,其它都不需要动
创建
vim ex.yaml
具体内容
kind: Service
apiVersion: v1
metadata:name: my-service-1 # 我们直 svc 一旦创建,集群内部就可以根据 svc的名称访问namespace: default
spec:type: ExternalNameexternalName: hub.atguigu.com # 外部服务的域名,当然你也可以换成ip
当查询主机 my-service-1.defalut.svc.cluster.local (SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的DNS 服务将返回一个值 my.database.example.com 的 CNAME 记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发
# 对 svcname.namespece.svc.cluster.local. 使用 10.224.0.7(选择10.244.0.6也行看上面截图,二个ip选一个)来解析
dig -t A my-service-1.default.svc.cluster.local. @10.224.0.7
五、Service 增强之七层代理 Ingress
Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方网站:https://kubernetes.github.io/ingress-nginx/
大致来说就是,我们写配置文件,它帮我们在nginx的conf文件中补充。
5.1、前置环境部署
如果有疑问,也可以看看网上其它的文章 https://blog.csdn.net/zhangjunli/article/details/107545984 这个还不错。
ingress-nginx的部署方式有多种,本文介绍nodeport方式(还有DaemonSet等等方式,这个上网搜索一下这个部署的文章就有很详细的说了,这里就不展开了),下面都是可以选择的方案
总的来说我们前置的条件需要部署2个,一个官方的 ingress-nginx,另一个是官方的 svc(方式有很多,这里采用nodePort方式),这个两个官方都提供的模板yaml文件。
1、下载mandatory.yaml文件:
wget https://raw.githubusercontent.com/kubernetes/ingressnginx/master/deploy/static/mandatory.yaml
然后修改文件中的镜像:registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:0.25.1
改为阿里云的,这样下载会快很多,而且可能国外的你下载不了。
#部署
kubectl apply -f mandatory.yaml
查看,注意,它创建在 ingress-nginx的名称空间中,如果想更改也可以,修改那个yaml文件,修改就行。
2、编辑service-nodeport.yaml(nodeport方式需要额外使用这个文件)
创建 ingress的node port 模式的svc ,下载,部署
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.3/deploy/static/provider/baremetal/deploy.yamlkubectl apply -f deploy.yaml
查看,注意它也创建在 ingress-nginx的名称空间中,后面这个端口31345就是后面我们访问的端口(域名加上端口),而对于https来说就是后面的31802
5.2、自己的服务(一个ingress对应一个我们的svc)
上面前置准备已经做好,那么接下来我们就可以部署自己的了,先部署一套如下
1、创建自己的 deployment 和 svc
vim ingress.http.yaml
内容如下,注意一个yaml文件里面可以有多个类型的控制器pod,或者非控制器pod,只要以---
分割即可,这样可以让其他开发人员知道这两个组件是一起的,后续删除的时候,也可以根据文件删除。
apiVersion: extensions/v1beta1
kind: Deployment # 先创建一个deployment
metadata:name: nginx-dm
spec:replicas: 2template:metadata:labels:name: nginx # 标签意义就不再重复说了spec:containers:- name: nginximage: wangyanglinux/myapp:v1imagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:name: nginx-svc
spec:ports:- port: 80targetPort: 80protocol: TCPselector:name: nginx # 管理上面的pod
2、配置后端程序的ingress
这个ingress就是会管理我们自己的svc和外部域名的关联的
vim ingress1.yaml
内容如下, 注意这里的kind是 ingress 的类型欧
apiVersion: extensions/v1beta1
kind: Ingress # 注意这里是 ingress 的类型欧
metadata:name: nginx-test
spec:rules:- host: www1.atguigu.comhttp:paths:- path: /backend:serviceName: nginx-svc # 说明外部访问 www1.atguigu.com 就是访问内部我们上面定义的那个 nginx-svc(前面定义的我们自己那个svc的名称)servicePort: 80
3、访问
- 注意这里的域名是我们前面配置的,而且不要忘记防护墙以及win的host域名解析欧
- 域名访问的时候需要加上端口,这个端口是我们前置准备里面的那个nodePort暴露的端口
5.3、自己的服务(一个ingress对应多个我们的svc)
这中模式,就是对上面的5.2的一个加强而已,其实k8s也就是会在nginx里面多做一个域名的分流而已。
1、创建我们的一套服务(deployment1 和 svc1)
vim deploment1.yaml
内容如下,注意欧这里的标签名称不能等下和后面的第二套管理混淆了,不能一样,这个不用解释了把。
apiVersion: extensions/v1beta1
kind: Deployment # 先创建一个deployment
metadata:name: deployment1 # 名称为 deployment1
spec:replicas: 2template:metadata:labels:name: nginx1 # 标签意义就不再重复说了spec:containers:- name: nginximage: wangyanglinux/myapp:v1imagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:name: svc-1
spec:ports:- port: 80targetPort: 80protocol: TCPselector:name: nginx1 # 管理上面的pod
创建
2、创建我们的二套服务(deployment2 和 svc2)
vim deploment2.yaml
内容如下,注意欧这里的标签名称不能等下和后面的第一套管理混淆了,不能一样
apiVersion: extensions/v1beta1
kind: Deployment # 先创建一个deployment
metadata:name: deployment2 # 名称为 deployment2
spec:replicas: 2template:metadata:labels:name: nginx2 # 标签意义就不再重复说了spec:containers:- name: nginximage: wangyanglinux/myapp:v2imagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:name: svc-2
spec:ports:- port: 80targetPort: 80protocol: TCPselector:name: nginx2 # 管理上面的pod
第二个svc以及下面关联的 deployment
3、创建ingress来管理上面的两套
这个ingress就是会管理我们自己的svc和外部域名的关联的
vim ingress-two.yaml
内容如下, 注意这里的kind是 ingress 的类型欧
apiVersion: extensions/v1beta1
kind: Ingress # 注意这里是 ingress 的类型欧
metadata:name: nginx-test
spec:rules:- host: www1.atguigu.comhttp:paths:- path: /backend:serviceName: svc-1 # 说明外部访问 www1.atguigu.com 就是访问内部我们上面定义的那个 svc-1(前面定义的第一套我们自己那个svc的名称)servicePort: 80- host: www2.atguigu.comhttp:paths:- path: /backend:serviceName: svc-2 # 说明外部访问 www2.atguigu.com 就是访问内部我们上面定义的那个 svc-2(前面定义的第二套我们自己那个svc的名称)servicePort: 80
查看
其实这个ingress 就是帮忙我们在nginx增加映射分流关系。
5.4、配置https
1、创建一套我们的体系(即deployment 和 svc),这里以3为代号,前面的是1和2
apiVersion: extensions/v1beta1
kind: Deployment # 先创建一个deployment
metadata:name: deployment3 # 名称为 deployment3
spec:replicas: 2template:metadata:labels:name: nginx3 # 标签意义就不再重复说了spec:containers:- name: nginximage: wangyanglinux/myapp:v3imagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:name: svc-3
spec:ports:- port: 80targetPort: 80protocol: TCPselector:name: nginx3 # 管理上面的pod
查看我们的体系
2、创建 ingress
创建证书,以及 cert 存储方式
# 创建证书 openssl req -x509 -sha256 -nodes -days 有效天数 -newkey rsa:2048 -keyout 输出文件 -out 输出证书 -subj "/CN=nginxsvc/O=nginxsvc"
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
# 生成 kubectl create secret tls 类型(这个很重要,后面我们配置ingress的时候,类型指定要和这个一样) --key 上面生成的key --cert 上面生成的crt
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
ingress的内容
apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: nginx-test
spec:tls:- hosts:- www3.aiguigu.comsecretName: tls-secret # 注意这个类型跟上面 kubectl 的那个说的类型必须一致rules:- host: www3.aiguigu.comhttp:paths:- path: /backend:serviceName: svc3servicePort: 80
查看,注意这里是https即443,所以我们访问的端口就是后面的31802
3、结果
5.5、http的验证
其实这里的http验证,就是nginx的验证,我们配置的就是nginx,也是因为我们选择的ingress的ingx的实现。
1、生成用户和密码凭证
# 下载nginx的基础验证的模块 httpd
yum -y install httpd
# 使用htpasswd生成证书 htpasswd -c 证书文件 用户名(后面会让你输入密码)
htpasswd -c auth foo
# 使用这个证书文件 kubectl create secret generic basic-auth --from-file=上面生成的证书文件
kubectl create secret generic basic-auth --from-file=auth
2、创建ingress
这里就不再创建一套我们的体系了,我们可以用前面的 svc1的体系,注意svc1的体系是80即http的是没有https的欧
apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: ingress-with-auth # 这个ingress的名称annotations:nginx.ingress.kubernetes.io/auth-type: basicnginx.ingress.kubernetes.io/auth-secret: basic-authnginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:rules:- host: auth.atguigu.com # 访问的域名http:paths:- path: /backend:serviceName: svc-1 # 使用svc-1的体系servicePort: 80
查看
5.6、Nginx 进行重写
意思我们可以把nginx的一些配置规则,放到yaml文件里面,k8s的ingress会帮我们写入它创建的Nginx里面
名称 | 描述 | 值 |
---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | 必须重定向流量的目标URI | 字符串 |
nginx.ingress.kubernetes.io/ssl-redirect | 指示位置部分是否仅可访问SSL(当Ingress包含证书时默认为True) | 布尔 |
nginx.ingress.kubernetes.io/force-ssl-redirect | 即使Ingress未启用TLS,也强制重定向到HTTPS | 布尔 |
nginx.ingress.kubernetes.io/app-root | 定义Controller必须重定向的应用程序根,如果它在’/'上下文中 | 字符串 |
nginx.ingress.kubernetes.io/use-regex | 指示Ingress上定义的路径是否使用正则表达式 | 布尔 |
示例,当访问 www4.atguigu.com 时 跳转到 http://www.baidu.com
apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: nginx-testannotations:nginx.ingress.kubernetes.io/rewrite-target: http://www.baidu.com
spec:rules:- host: www4.atguigu.com http:paths:- path: /backend:serviceName: ngin