ingress简单来讲,就是一个利用负载均衡的玩意,其主要用来解决使用NodePort暴露Service的端口时Node IP会漂移的问题。同时,若大量使用NodePort暴露主机端口,管理会非常混乱。
好的解决方案就是让外界通过域名去访问Service,而无需关心其Node IP及Port。那为什么不直接使用Nginx?这是因为在K8S集群中,如果每加入一个服务,我们都在Nginx中添加一个配置,其实是一个重复性的体力活,只要是重复性的体力活,我们都应该通过技术将它干掉
Pod与Ingress的关系
通过label-selector相关联
通过Ingress Controller实现Pod的负载均衡
支持TCP/UDP 4层和HTTP 7层
Kubernetes提供了两种负载均衡机制,一种是工作与传输层的Service资源,实现“TCP负载均衡器”,另一种是Ingress资源,实现“HTTP(S)负载均衡器”
在Kubernetes中,服务和Pod的IP地址仅可以在集群网络内部使用,对于集群外的应用是不可见的。
为了使外部的应用能够访问集群内的服务,Kubernetes目前提供了以下几种方案:
NodePort: 先访问Service,Service可以直接通过集群内部负载均衡至pod中的应用,而外部访问集群中的Service可以通过在Service中定义NodePort实现;这种方式在集群中的每台主机上开放一个随机的指定的端口,且每个端口只能提供一个服务,它是通过端口不同来区分不同应用,而不是通过域名,缺点是管理不便,不适合在大规模集群中部署。
LoadBalancer:通过LoadBlancer Service访问Service,这个需要接入云服务,每个服务都会由云服务提供一个IP作为入口,转发相应的流量,但每个LoadBlancer Service都会产生费用,成本比较高。
Ingress:可以作为k8s集群的前端调度器,可以基于域名的方式将请求反向给集群中的service去处理。
ingress controller
将新加入的Ingress转化成Nginx的配置文件并使之生效
ingress服务
将Nginx的配置抽象成一个Ingress对象,每添加一个新的服务只需写一个新的Ingress的yaml文件即可
1.ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化,
2.然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置,
3.再写到nginx-ingress-control的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,
4.然后reload一下使配置生效。以此达到域名分配置和动态更新的问题
1.动态配置服务
如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指向我们新的k8s服务. 而如果用了Ingress, 只需要配置好这个服务, 当服务启动时, 会自动注册到Ingress的中, 不需要而外的操作.
2.减少不必要的端口暴露
配置过k8s的都清楚, 第一步是要关闭防火墙的, 主要原因是k8s的很多服务会以NodePort方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅. 而Ingress可以避免这个问题, 除了Ingress自身服务可能需要映射出去, 其他服务都不要用NodePort方式
下载nginx-ingress-controller配置文件
(如果下载的该版本不适用于你的k8s版本,可以下载其他的版本,要理解配置文件的各项参数配置才能更好的学习)
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.21.0/deploy/mandatory.yaml
修改镜像路径
#替换镜像路径
#image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
image: willdockerhub/nginx-ingress-controller:0.21.0
执行yaml文件部署
kubectl apply -f mandatory.yaml
nodeport方式对外提供服务:
通过ingress-controller对外提供服务,现在还需要手动给ingress-controller建立一个servcie,接收集群外部流量。这里要理解为什么要用nodeport把ingress-controller代理到外部,因为我们需要把ingress-controller服务的默认端口映射到宿主机的端口上,外部才能访问到ingress,然后ingress通过域名转发到自己的各种服务上,如同nginx,你需要先把nginx的80端口映射到公网80上,外部通过公网80端口找到nginx的80端口,才能通过nginx定义的规则找到你的其他服务
service-nodeport配置文件:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
kubectl apply -f service-nodeport.yaml
查看ingress-nginx组件状态
kubectl get pod svc -n ingress-nginx
[centos@k8s-master ~]$ kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-6bdcbbdfdc-wd2bn 1/1 Running 0 24s
[centos@k8s-master ~]$ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.104.138.113 <none> 80:30737/TCP,443:31952/TCP 13s
可以看到,ingress中的80端口何443端口分别映射到了主机的30737和31952端口,提供给一会的服务使用
如此ingress组件就创建好了,我们可以开始学习使用它
Ingress资源的类型
(你可以看作是nginx配置文件中的转发规则,这样好理解)
1)单service资源型ingress
暴露单个服务的方法可以使用service的NodePort、LoadBalancer类型;也可使用Ingress,使用Ingress时,只需指定ingress的default backend即可。
# 定义一个单Service资源型的ingres
[root@master01 test03]# cat test05.yaml
apiVersion: extensions/v1beat1
kind: Ingress
metadata:
name: test-ingress02
spec:
backend:
serviceName: nginx-svc
serviceName: 80
2)基于URL路径进行流量分发的ingress
基于URL路径转发是根据客户端请求不同的URL路径转发到不同的后端服务中。
# 定义一个多路径的ingress资源
[root@master01 test03]# cat test06.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress05
annotations:
kubernetes.io/ingress: “nginx test path”
spec:
rules:
- host: www.dayi123.com
http:
paths:
- path: /nginx
backend:
serviceName: nginx-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: test-svc
servicePort: 80
3)基于主机名称的ingress虚拟主机
基于主机名的ingress虚拟主机是将每个应用分别以独立的FQDN主机名进行输出。
# 定义一个基于主机名的service
[root@master01 test03]# cat test07.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress06
spec:
rules:
- host: blog.dayi123.com
http:
paths:
- backend:
serviceName: nginx-svc
servicePort: 80
- host: doc.dayi123.com
http:
paths:
- backend:
serviceName: test-svc
servicePort: 80
4)TLS类型的Ingress资源
TLS类型的Ingress资源用于以HTTPS发布Service资源,基于一个含有私钥和证书的Secret对象即可配置TLS协议的Ingress资源。Ingress资源目前仅支持单TLS端口,并且还会卸载TLS会话
做个实验实战讲解:
使用ingress发布Tomcat
前提:已经部署好ingress服务,没有部署的照着上面的部署即可。
部署一个tomcat后端服务
(1)创建名称空间
# 定义名称空间的资源配置清单
[root@master01 ingress-tomcat]# cat ingress-tomcat.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test-ingress
labels:
env: test-ingress
[root@master01 ingress-tomcat]# kubectl apply -f ingress-tomcat.yaml
namespace/test-ingress created
(2)部署tomcat实例
定义基于deployment的tomcat资源配置清单
[root@master01 ingress-tomcat]# cat tomcat-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: test-ingress
spec:
replicas: 2
selector:
matchLabels:
app: ingress-tomcat
template:
metadata:
labels:
app: ingress-tomcat
spec:
containers:
- name: tomcat
image: tomcat
ports:
- name: httport
containerPort: 8080
- name: ajpport
containerPort: 8009
# 创建tomcat的pod控制器
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-deployment.yaml
deployment.apps/tomcat-deployment created
# 查看创建的tomcat pod
[root@master01 ingress-tomcat]# kubectl get pods -n test-ingress
NAME READY STATUS RESTARTS AGE
tomcat-deployment-67f5dcbdb4-7mmhz 1/1 Running 0 96s
tomcat-deployment-67f5dcbdb4-km5q8 1/1 Running 0 96s
(3)创建Service资源
Ingress资源仅能通过Service资源识别相应的Pod资源,获取其IP地址和端口,然后Ingress控制器即可直接使用各Pod对象的IP地址与Pod内的服务直接进行通信,不经过Service的代理和调度,因此Service资源的ClusterIP对Ingress控制器来说存不存在无所谓。
# 定义service的资源配置清单文件
[root@master01 ingress-tomcat]# cat tomcat-service.yaml
apiVersion: v1
kind: Service
metadata:
name: tomcat-svc
namespace: test-ingress
labels:
app: tomcat-svc
spec:
selector:
app: ingress-tomcat
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
# 创建并查看service
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-service.yaml
service/tomcat-svc created
[root@master01 ingress-tomcat]# kubectl get svc -n test-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
tomcat-svc ClusterIP 10.106.153.248 <none> 80/TCP 15s
(4)创建Ingress资源
# 定义ingress资源配置清单
[root@master01 ingress-tomcat]# cat tomcat-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tomcat
namespace: test-ingress
annotations:
kubernetes.io/ingress: "nginx"
spec:
rules:
- host: tomcat.dayi123.com
http:
paths:
- path:
backend:
serviceName: tomcat-svc
servicePort: 80
# 创建并查看Ingress
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-ingress.yaml
ingress.extensions/tomcat created
[root@master01 ingress-tomcat]# kubectl get ingress -n test-ingress
NAME HOSTS ADDRESS PORTS AGE
tomcat tomcat.dayi123.com 80 14s
上面的配置完成后,在宿主机的/etc/hosts中添加tomcat.dayi123.com对应node节点的解析后,就可在本地通过http://tomcat.dayi123.com:30737去访问tomcat服务。这个node节点的30737端口就是在前面为nginx类型的ingress控制器创建的service中定义的
(5)配置TLS Ingress资源
互联网中的服务基本都是以https的方式提供服务的,如果希望ingress控制器接受客户端的请求时又希望它能够提供https服务,就应该配置tls类型的ingress资源
# 生成用于测试的私钥和自签证书
[root@master01 ingress-tomcat]# openssl genrsa -out tls.key 2048
[root@master01 ingress-tomcat]# openssl req -new -x509 -key tomcat.key -out tomcat.crt -subj /C=CN/ST=Shanghai/L=Shanghai/O/dev/CN=tomcat.dayi123.com -days 736
在ingress控制器上配置HTTPS主机时,是不能直接使用私钥和证书文件的,而是要使用Secret资源对象来传递相关的数据。
# 创建一个TLS类型名为tomcat-ingress-secret的secret资源
[root@master01 ingress-tomcat]# kubectl create secret tls tomcat-ingress-secret --cert=tomcat.crt --key=tomcat.key -n test-ingress
secret/tomcat-ingress-secret created
# 查看创建的secret资源
[root@master01 ingress-tomcat]# kubectl get secrets tomcat-ingress-secret -n test-ingress
NAME TYPE DATA AGE
tomcat-ingress-secret kubernetes.io/tls 2 109s
secret资源创建完成后,就可以将创建的secret应用到ingress资源的配置清单中。
# 定义TLS类型的Ingress资源的配置清单
[root@master01 ingress-tomcat]# cat tomcat-ingress-tls.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tomcat-ingress-tls
namespace: test-ingress
annotations:
kubernetes.io/ingress: "nginx"
spec:
tls:
- hosts:
- tomcat.dayi123.com
secretName: tomcat-ingress-secret
rules:
- host: tomcat.dayi123.com
http:
paths:
- path:
backend:
serviceName: tomcat-svc
servicePort: 80
# 创建并查看tls类型的ingress资源
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-ingress-tls.yaml
ingress.extensions/tomcat-ingress-tls created
[root@master01 ingress-tomcat]# kubectl get ingress tomcat-ingress-tls -n test-ingress
NAME HOSTS ADDRESS PORTS AGE
tomcat-ingress-tls tomcat.dayi123.com 80, 443 3m35s
(6)测试
在前面为基于nginx的ingress控制器创建的service中已将443端口映射到了node节点的31952端口,在本地对域名tomcat.dayi123.com做了解析就可以通过https://tomcat.dayi123.com:31952进行测试。
# 查看ingress控制器中映射到本地的端口
[root@master01 ingress-tomcat]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller NodePort 10.105.134.97 <none> 80:30737/TCP,443:31952/TCP 167m
# 在linux客户端进行测试(测试前要做hosts解析)
[root@master01 ingress-tomcat]# curl -k -v https://tomcat.dayi123.com:31952
还记得上面的实验中我们是如何把内网server通过ingress映射到主机外网的吗,我们是通过nodeport,其实实现的方法还有其他几种,我们学习一下
Deployment + LoadBalancer
如果要把 ingress 部署在公有云,那用这种方式比较合适。用 Deployment 部署 ingress-controller,创建一个 type 为 LoadBalancer 的 service 关联这组 pod。大部分公有云,都会为 LoadBalancer 的 service 自动创建一个负载均衡器,通常还绑定了公网地址。只要把域名解析指向该地址,就实现了集群服务的对外暴露。
Deployment + NodePort (刚才实验所用的方法)
同样用 deployment 模式部署 ingress-controller,并创建对应的服务,但是 type 为 NodePort。这样,ingress 就会暴露在集群节点 ip 的特定端口上。由于 nodeport 暴露的端口是随机端口,一般会在前面再搭建一套负载均衡器来转发请求。该方式一般用于宿主机是相对固定的环境 ip 地址不变的场景。NodePort 方式暴露 ingress 虽然简单方便,但是 NodePort 多了一层 NAT,在请求量级很大时可能对性能会有一定影响。
DaemonSet + HostNetwork + nodeSelector(原理就是直接使用宿主机的80端口和443端口,不再进行端口映射)
用 DaemonSet 结合 nodeselector 来部署 ingress-controller 到特定的 node 上,然后使用 HostNetwork 直接把该 pod 与宿主机 node 的网络打通,直接使用宿主机的 80/433 端口就能访问服务。这时,ingress-controller 所在的 node 机器就很类似传统架构的边缘节点,比如机房入口的 nginx 服务器。该方式整个请求链路最简单,性能相对 NodePort 模式更好。缺点是由于直接利用宿主机节点的网络和端口,一个 node 只能部署一个 ingress-controller pod。比较适合大并发的生产环境使用。
两种部署方式的比较
相比较起来,nodePort部署模式中需要部署的ingress-controller容器较少。一个集群可以部署几个就可以了。而hostNetwork模式需要在每个节点部署一个ingress-controller容器,因此总起来消耗资源较多。另外一个比较直观的区别,nodePort模式主要占用的是svc的nodePort端口。而hostNetwork则需要占用物理机的80和443端口。
从网络流转来说,通过nodePort访问时,该node节点不一定部署了ingress-controller容器。因此还需要iptables将其转发到部署有ingress-controller的节点上去,多了一层流转。
另外,通过nodePort访问时,nginx接收到的http请求中的source ip将会被转换为接受该请求的node节点的ip,而非真正的client端ip。
而使用hostNetwork的方式,ingress-controller将会使用的是物理机的DNS域名解析(即物理机的/etc/resolv.conf)。而无法使用内部的比如coredns的域名解析。
因此具体使用哪种部署方式,需要根据实际情况和需求进行选择
现在我们来部署hostNetwork的ingress
为需要部署为边缘节点的 node 打上 labe:
$ kubectl label node k8sn91 isIngress="true"
在官方 yaml 配置文件中,相关资源的创建已经包含在内,但是我们需要使用 DaemonSet 的方式部署,就需要修改一小部分内容。下载官方 yaml:
$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
修改 Deployment 部分的配置:
……
apiVersion: apps/v1
#kind: Deployment
#修改成 把Deployment改成DaemonSet,DaemonSet 的不同之处在于:每个 Node 上最多只能运行一个副本
kind: DaemonSet
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
#注释掉 replicas
#replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
# wait up to five minutes for the drain of connections
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
# 选择打上 isIngress 标签的 node
nodeSelector:
isIngress: "true"
# 暴露服务
hostNetwork: true
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1
……
部署 nginx-ingress-controller:
$ kubectl apply -f mandatory.yaml
查看:
$ kubectl get ds -n ingress-nginx 查看deamonset
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
nginx-ingress-controller 1 1 1 1 1 isIngress=true 10m
$ kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-prqv5 1/1 Running 0 10m
部署测试 web 服务
为了展示 ingress 路由转发功能,创建两个 web 服务,然后通过 ingress 编写规则来转发相关请求。
apple.yaml
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
spec:
selector:
app: apple
ports:
- port: 5678 # Default port for image
banana.yaml
kind: Pod
apiVersion: v1
metadata:
name: banana-app
labels:
app: banana
spec:
containers:
- name: banana-app
image: hashicorp/http-echo
args:
- "-text=banana"
---
kind: Service
apiVersion: v1
metadata:
name: banana-service
spec:
selector:
app: banana
ports:
- port: 5678 # Default port for image
部署 pod
$ kubectl apply -f apple.yaml
$ kubectl apply -f banana.yaml
查看 service、pod:
$ kubectl get svc,po | grep -E "apple|banana"
service/apple-service ClusterIP 10.106.133.148 <none> 5678/TCP 10m
service/banana-service ClusterIP 10.108.239.61 <none> 5678/TCP 10m
pod/apple-app 1/1 Running 0 10m
pod/banana-app 1/1 Running 0 10m
$ curl 10.106.133.148:5678
apple
$ curl 10.108.239.61:5678
banana
3、部署 Ingress
编写 yaml 文件,指定路由规则
$ vim ingress-nginx.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: nginx.tempdomain.com
http:
paths:
- path: /apple
backend:
serviceName: apple-service
servicePort: 5678
- path: /banana
backend:
serviceName: banana-service
servicePort: 5678
部署 ingress:
$ kubectl apply -f ingress-nginx.yaml
查看 ingress:
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
nginx-ingress nginx.tempdomain.com 80 10m
$ kubectl describe ingress nginx-ingress
Name: nginx-ingress
Namespace: default
Address:
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
nginx.tempdomain.com
/apple apple-service:5678 (100.93.23.211:5678)
/banana banana-service:5678 (100.107.55.15:5678)
4、在宿主机上检查可用性
$ curl nginx.tempdomain.com/apple
apple
$ curl nginx.tempdomain.com/banana
banana
这样ingress的基础功能就算实现了,其他的今后慢慢深入学习