【转】kubernetes之ingress和Ingress Controller
一、什么是Ingress?
Kubernetes
暴露服务的方式目前只有三种:LoadBlancer Service、NodePort Service、Ingress
;
1、Pod 漂移问题
在 Kubernetes 中,随着 Pod 的创建和销毁,Pod IP 肯定会动态变化;那么如何把这个动态的 Pod IP 暴露出去?可以借助于 Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外只暴露 Service IP 就行了;
NodePort 模式:在每个节点上开起一个端口,然后转发到内部 Pod IP 上,访问方式:http://nodeip:nodeport,如下图所示:
2、端口管理问题
采用 NodePort 方式暴露服务面临的问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会极其庞大,而且难以维护;
这时,我们能否使用一个Nginx直接对内进行转发呢?在集群内,Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间(hostNetwork方式)的,也就是说当Pod共享宿主机的网络名称空间(hostNetwork方式)时,Pod上所监听的就是Node上的端口。那么这又该如何实现呢?简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了,如下图所示:
3、域名分配及动态更新问题
从上面的方法,采用 Nginx-Pod 似乎已经解决了问题,但是其实这里面有一个很大缺陷:当每次有新服务加入又该如何修改 Nginx 配置呢??我们知道使用Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改 nginx.conf 即可实现,那在K8S中又该如何实现这种方式的调度呢???
假设后端的服务初始服务只有ecshop,后面增加了bbs和member服务,那么又该如何将这2个服务加入到Nginx-Pod进行调度呢?总不能每次手动改或者Rolling Update 前端 Nginx Pod 吧!!此时 Ingress 出现了。
Ingress 包含两大组件:Ingress Controller 和 Ingress。
Ingress 简单的理解就是你原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;
Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,并按照规则模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,工作流程如下图:
二、Ingress的负载均衡机制
k8s提供了两种内建的云端负载均衡机制用于发布公共应用,一种是工作于传输层的service资源,它实现的是TCP负载均衡器,另一种是Ingress资源,它实现的是HTTP(S)负载均衡器。
1)TCP负载均衡器
无论是iptables还是ipvs模型的service资源都配置于Linux内核中的netfilter之上进行四层调度,是一种类型更为通用的调度器,支持调度HTTP、MYSQL等应用层服务。不过,也正是由于工作于传输层从而使得它无法做到https及ssl会话支持等,也不支持基于url的请求调度机制,而且,k8s也不支持为此类负载均衡器配置任何类型的健康检查机制。
2)HTTP(S)负载均衡器
HTTP(S)负载均衡器是应用层负载均衡机制的一种,支持根据环境做出更好的调度决策。与传输层调度器相比,它提供了诸如可自定义url映射和tls卸载等功能,并支持多种类型的后端服务器监控状态检测机制。
Ingress 和 Ingress Controller
service资源和pod资源的IP地址仅能用于集群网络内部的通信,所有的网络流量都无法穿透边界路由器以实现集群内外通信。尽管可以为service使用NodePort或LoadBalancer类型通过节点引入外部流量,但它依然是4层流量转发,可用的负载均衡器也为传输层负载均衡机制。
Ingress是k8s的标准资源类型之一,它其实就是一组基于DNS名称或URL路径把请求转发至指定的service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布。然而,Ingress资源自身并不能进行流量穿透,它仅是规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助,如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器。
Ingress控制器可以由任何具有反向代理功能的服务程序实现,如Nginx、Envoy和Traefik等。Ingress控制器自身也是运行于集群中的pod资源对象,它与被代理的运行为pod资源的应用运行于同一网络中。使用Ingress资源进行流量分发时,Ingress控制器可基于某Ingress资源定义的规则将客户端的请求流量直接转发至与service对应的后端pod资源之上,这种转发机制会绕过service资源,从而省去了由kube-proxy实现的端口代理开销。Ingress规则需要由一个service资源对象辅助识别相关的所有pod对象,但Ingress-nginx控制器可经由api.ilinux.io规则的定义直接将请求流量调度至后端pod上,而无须经由service对象api的再次转发。
三、如何创建Ingress资源
Ingress资源时基于HTTP虚拟主机或URL的转发规则,需要强调的是,这是一条转发规则。资源清单中annotation用于识别其所属的Ingress控制器的类型,这一点在集群中部署有多个Ingress控制器时尤为重要。
Ingress 中的spec字段是Ingress资源的核心组成部分,主要包含以下3个字段:
- rules:用于定义当前Ingress资源的转发规则列表;由rules定义规则,或没有匹配到规则时,所有的流量会转发到由backend定义的默认后端。
- backend:默认的后端,用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,必须要定义backend或rules两者之一,该字段用于让负载均衡器指定一个全局默认的后端。
- tls:TLS配置,目前仅支持通过默认端口443提供服务,如果要配置指定的列表成员指向不同的主机,则需要通过SNI TLS扩展机制来支持该功能。
rules
rules对象由一系列的配置的Ingress资源的host规则组成,这些host规则用于将一个主机上的某个URL映射到相关后端Service对象,其定义格式如下:
1 spec: 2 rules: 3 - hosts: <string> 4 http: 5 paths: 6 - path: 7 backend: 8 serviceName: <string> 9 servicePort: <string>
需要注意的是,.spec.rules.host 属性值,目前暂不支持使用IP地址定义,也不支持IP:Port的格式,若该字段留空,代表着通配所有主机名。
backend
backend对象的定义由2个必要的字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标Service资源名称和端口。
tls
tls对象由2个内嵌的字段组成,仅在定义TLS主机的转发规则上使用。
- hosts: 包含 于 使用 的 TLS 证书 之内 的 主机 名称 字符串 列表, 因此, 此处 使用 的 主机 名 必须 匹配 tlsSecret 中的 名称。
- secretName: 用于 引用 SSL 会话 的 secret 对象 名称, 在 基于 SNI 实现 多 主机 路 由 的 场景 中, 此 字段 为 可选。
四、Ingress资源类型
基于http暴露的每个service资源均可发布于一个独立的FQDN主机名之上,如www.ik8s.io;也可发布于某主机的URL路径之上,从而将它们整合到同一个web站点,如www.ik8s.io/frafana。至于是否需要发布为https类型的应用则取决于用户的业务需求。
Ingress的资源类型有以下4种:
- 1、单Service资源型Ingress
- 2、基于URL路径进行流量转发
- 3、基于主机名称的虚拟主机
- 4、TLS类型的Ingress资源
1、单Service资源型Ingress
暴露单个服务的方法有多种,如NodePort、LoadBanlancer等等,当然也可以使用Ingress来进行暴露单个服务,只需要为Ingress指定default backend即可,如下示例:
1 apiVersion: extensions/v1beta1 2 kind: Ingress 3 metadata: 4 name: my-ingress 5 spec: 6 backend: 7 serviceName: my-svc 8 servicePort: 80
Ingress控制器会为其分配一个IP地址接入请求流量,并将其转发至后端my-svc。
2、基于url路径进行流量分发
垂直拆分或微服务架构中,每个小的应用都有其专用的service资源暴露服务,但在对外开放的站点上,它们可能是财经、新闻、电商等一类的独立应用,可通过主域名的url路径分别接入,用于发布集群内名称为API和WAP的service资源。于是,可对应地创建一个如下的Ingress资源:
1 apiVersion: extensions/v1beta1 2 kind: Ingress 3 metadata: 4 name: ingress-url-demo 5 annotations: 6 nginx.ingress.kubernetes.io/rewrite-target: / 7 spec: 8 rules: 9 - host: myapp.syztoo.com 10 http: 11 paths: 12 - path: /v1 13 backend: 14 serviceName: myappv1-svc 15 servicePort: 80 16 - path: /v2 17 backend: 18 serviceName: myappv2-svc 19 servicePort: 80 20 21 --- 22 apiVersion: v1 23 kind: Service 24 metadata: 25 name: myappv1-svc 26 namespace: default 27 spec: 28 selector: 29 app: myappv1 30 release: canary 31 type: ClusterIP 32 ports: 33 - port: 80 34 targetPort: 80 35 36 --- 37 apiVersion: apps/v1 38 kind: Deployment 39 metadata: 40 name: myappv1-depoly 41 namespace: default 42 spec: 43 replicas: 2 44 selector: 45 matchLabels: 46 app: myappv1 47 release: canary 48 template: 49 metadata: 50 labels: 51 app: myappv1 52 release: canary 53 spec: 54 containers: 55 - name: myappv1 56 image: ikubernetes/myapp:v1 57 ports: 58 - name: http 59 containerPort: 80 60 61 --- 62 apiVersion: v1 63 kind: Service 64 metadata: 65 name: myappv2-svc 66 namespace: default 67 spec: 68 selector: 69 app: myappv2 70 release: canary 71 type: ClusterIP 72 ports: 73 - port: 80 74 targetPort: 80 75 76 --- 77 apiVersion: apps/v1 78 kind: Deployment 79 metadata: 80 name: myappv2-deploy 81 namespace: default 82 spec: 83 replicas: 2 84 selector: 85 matchLabels: 86 app: myappv2 87 release: canary 88 template: 89 metadata: 90 labels: 91 app: myappv2 92 release: canary 93 spec: 94 containers: 95 - name: myappv2 96 image: ikubernetes/myapp:v2 97 ports: 98 - name: http 99 containerPort: 80
3、基于主机名称的虚拟主机
1 apiVersion: extensions/v1beta1 2 kind: Ingress 3 metadata: 4 name: test 5 spec: 6 rules: 7 - host: api.ik8s.io 8 http: 9 paths: 10 - backend: 11 serviceName: api 12 servicePort: 80 13 - host: wap.ik8s.io 14 http: 15 paths: 16 - backend: 17 serviceName: wap 18 servicePort: 80
4、TLS类型的Ingress资源
这种类型用于以https发布service资源,基于一个含有私钥和证书的secret对象即可配置TLS协议的Ingress资源,目前来说,Ingress资源仅支持单TLS端口,并且还能卸载TLS会话,在Ingress资源中引用此secret即可让Ingress控制器加载并配置为https服务。
1 apiVersion: extensions/v1beta1 2 kind: Ingress 3 metadata: 4 name: no-rules-map 5 spec: 6 tls: 7 - secretName: ikubernetesSecret 8 backend: 9 serviceName: homesite 10 servicePort: 80
五、部署Ingress控制器(Nginx)
1、部署Ingress controller
下载 官网 配置清单
[root@master ~]# mkdir ingress-nginx [root@master ~]# cd ingress-nginx/ [root@master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
部署:
[root@master ingress-nginx]# kubectl apply -f mandatory.yaml
ingress-controller 使用的镜像是 quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.1,下载的时间较长,耐心等待即可,也可提前准备好镜像。
[root@master ingress-nginx]# kubectl get pods -n ingress-nginx NAME READY STATUS RESTARTS AGE nginx-ingress-controller-79f6884cf6-8nb6f 1/1 Running 0 35m
2、定义 ingress-controller 的 service 资源
运行为pod资源的Ingress控制器进程接入外部的请求流量常用的有两种解决方案:
1)以deployment控制器管理Ingress控制器的pod资源,并通过NodePort或LoadBalancer类型的service对象为其接入集群外部的请求流量,这就意味着,定义一个Ingress控制器时,必须在其前端定义一个专用的service资源
2)借助于DaemonSet控制器,将Ingress控制器的pod资源各自以单一实例的方式运行于集群的所有或部分工作节点之上,并配置这类pod对象以hostPort或hostNetwork的方式在当前节点接入外部流量。
这里使用nodePort方式,下载官网nodePort文件:
[root@master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
1 [root@master ingress-nginx]# vim service-nodeport.yaml 2 apiVersion: v1 3 kind: Service 4 metadata: 5 name: ingress-nginx 6 namespace: ingress-nginx 7 labels: 8 app.kubernetes.io/name: ingress-nginx 9 app.kubernetes.io/part-of: ingress-nginx 10 spec: 11 type: NodePort 12 ports: 13 - name: http 14 port: 80 15 targetPort: 80 16 protocol: TCP 17 nodePort: 30080 18 - name: https 19 port: 443 20 targetPort: 443 21 protocol: TCP 22 nodePort: 30443 23 selector: 24 app.kubernetes.io/name: ingress-nginx 25 app.kubernetes.io/part-of: ingress-nginx 26 27 ---
上面的文件中,使用了宿主机的固定端口(30080,30443),需保证宿主机的这两个端口未占用。
[root@master ingress-nginx]# kubectl apply -f service-nodeport.yaml service/ingress-nginx created [root@master ingress-nginx]# kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.106.97.57 <none> 80:30080/TCP,443:30443/TCP 13s
3、定义Ingress
[root@master resourcelist]# vim ingress-demo.yaml
1 apiVersion: extensions/v1beta1 # api版本 2 kind: Ingress # 清单类型 3 metadata: # 元数据 4 name: ingress-myapp # ingress的名称 5 namespace: default # 所属名称空间 6 annotations: # 注解信息 7 kubernetes.io/ingress.class: "nginx" 8 spec: # 规格 9 rules: # 定义后端转发的规则 10 - host: myapp.magedu.com # 通过域名进行转发 11 http: 12 paths: 13 - path: # 配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/" 14 backend: # 配置后端服务 15 serviceName: myapp 16 servicePort: 80 17 18 --- 19 apiVersion: v1 20 kind: Service 21 metadata: 22 name: myapp 23 namespace: default 24 spec: 25 selector: 26 app: myapp 27 release: canary 28 type: ClusterIP 29 ports: 30 - port: 80 31 targetPort: 80 32 33 --- 34 apiVersion: apps/v1 35 kind: Deployment 36 metadata: 37 name: myapp-deploy 38 namespace: default 39 spec: 40 replicas: 5 41 selector: 42 matchLabels: 43 app: myapp 44 release: canary 45 template: 46 metadata: 47 labels: 48 app: myapp 49 release: canary 50 spec: 51 containers: 52 - name: myapp 53 image: ikubernetes/myapp:v2 54 ports: 55 - name: http 56 containerPort: 80
[root@master resourcelist]# kubectl apply -f ingress-demo.yaml ingress.extensions/ingress-myapp created service/myapp created deployment.apps/myapp-deploy created
修改本地hosts文件,进行测试访问:
192.168.56.12 myapp.magedu.com
192.168.56.13 myapp.magedu.com
4、总结
从前面的部署过程中,可以再次进行总结部署的流程如下:
①到官网下载ingress-controller一键部署文件(mandatory.yaml);
②部署Ingress-controller的service,以实现接入集群外部流量;
③部署后端的服务,如myapp,并通过service进行暴露;
④定义Ingress规则,使Ingress-controller和后端服务的Pod组进行关联。
六、构建TLS站点
(1)准备证书
[root@master ingress]# openssl genrsa -out tls.key 2048 Generating RSA private key, 2048 bit long modulus .......+++ .......................+++ e is 65537 (0x10001) [root@master ingress]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=myapp.magedu.com
(2)生成secret
[root@master ingress]# kubectl create secret tls myapp-ingress-secret --cert=tls.crt --key=tls.key secret/tomcat-ingress-secret created [root@master ingress]# kubectl get secret NAME TYPE DATA AGE default-token-9qfpd kubernetes.io/service-account-token 3 6d22h myapp-ingress-secret kubernetes.io/tls 2 21m
(3)创建ingress
cat ingress-tls-demo.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-myapp-tls
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- myapp.magedu.com
secretName: tomcat-ingress-secret
rules:
- host: myapp.magedu.com
http:
paths:
- path:
backend:
serviceName: myapp
servicePort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
selector:
app: myapp
release: canary
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort: 80
[root@master resourcelist]# kubectl apply -f ingress-tls-demo.yaml ingress.extensions/ingress-myapp-tls created service/myapp created deployment.apps/myapp-deploy created [root@master resourcelist]# kubectl get ingress NAME HOSTS ADDRESS PORTS AGE ingress-myapp-tls myapp.magedu.com 80, 443 10s
(4)访问测试:https://myapp.magedu.com:30443
=========================================================================================================
k8s修改ingress默认80端口
有时候80端口被其他服务占用,我们想修改ingress的默认端口
kubectl edit daemonset nginx-ingress-controller -n ingress-nginx
containers: - args: - /nginx-ingress-controller - --default-backend-service=$(POD_NAMESPACE)/default-http-backend - --configmap=$(POD_NAMESPACE)/nginx-configuration - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services - --annotations-prefix=nginx.ingress.kubernetes.io
在后面加上
- --http-port=8080 - --https-port=8443
删除pod,k8s将重新使用新配置,生成一个新的pod
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,在此感谢!