云原生时代如火如荼,掌握云原生的架构和开发,是Java开发高薪的必备技能。
SVC、Ingress原理和实操,是云原生的基础知识。
这里尼恩给大家 调优,做一下Ingress 的系统化、体系化的梳理。
在面试之前,也可以复习一下,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”。
在这里也一并把这些宝贵内容作为“K8S云原生学习”重要的内容,收入尼恩的《K8S学习圣经》,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从这里获取:码云
先介绍一下svc作用与不足,再介绍Ingress原理和实操
service的作用体现在两个方面:
在Kubernetes中,Pod的IP地址和service的ClusterIP仅可以在集群网络内部做用,对于集群外的应用是不可见的。
为了使外部的应用能够访问集群内的服务,Kubernetes目前提供了以下几种方案:
Ingress:只需一个或者少量的公网IP和LB,即可同时将多个HTTP服务暴露到外网,七层反向代理。
可以简单理解为service的service,它其实就是一组基于域名和URL路径,把用户的请求转发到一个或多个service的规则。
Ingress解决的是新的服务加入后,域名和服务的对应问题,基本上是一个ingress的对象,通过yaml进行创建和更新进行加载。工作机制大致可以用下图表示:
ingress相当于一个7层的负载均衡器,是k8s对反向代理的一个抽象。
大概的工作原理也确实类似于Nginx,可以理解成在 Ingress 里建立一个个映射规则 , ingress Controller 通过监听 Ingress这个api对象里的配置规则并转化成 Nginx 的配置(kubernetes声明式API和控制循环) , 然后对外部提供服务。
Ingress资源对象,用于将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制。
Kubernetes使用一个Ingress策略定义和一个具体的Ingress Controller,两者结合并实现了一个完整的Ingress负载均衡器。
Ingress Controller将基于Ingress规则将客户请求直接转发到Service对应的后端Endpoint上,这样会跳过kube-proxy的转发功能,kube-proxy 不再起作用。
在定义Ingress策略之前,需要先部署Ingress Controller,以实现为所有后端Service提供一个统一的入口。
Ingress Controller需要实现基于不同HTTP URL向后转发的负载分发机制,并可以灵活设置7层的负载分发策略。如果公有云服务商提供该类型的HTTP路由LoadBalancer,则可以设置其为Ingress Controller.
在Kubernetes中,Ingress Controller将以Pod的形式运行,监控apiserver的/ingress端口后的backend services, 如果service发生变化,则Ingress Controller 应用自动更新其转发规则
在定义Ingress策略之前,需要先部署Ingress Controller,为所有后端Service都提供一个统一的入口。
Ingress Controller需要实现基于不同HTTP URL向后转发的负载分发规则,并可以灵活设置7层负载分发策略。
An API object that manages external access to the services in a cluster, typically HTTP. Ingress can provide load balancing, SSL termination and name-based virtual hosting.
引用官方关于ingress的介绍我们可以得知,ingress是一种通过http协议暴露kubernetes内部服务的api对象,即充当Edge Router边界路由器的角色对外基于七层的负载均衡调度机制,
Ingress 能够提供以下几个功能:
在Kubernetes中,Ingress Controller将以Pod的形式运行,监控API Server的/ingress接口后端的backend services,如果Service发生变化,则Ingress Controller应自动更新其转发规则。
Ingress包含的组件有:
从 开发人员的角度讲,ingress包括:ingress controller和ingress resources
ingress resources:这个就是一个类型为Ingress的k8s api对象了,这部分则是面向开发人员。Ingress解决的是新的服务加入后,域名和服务的对应问题,基本上是一个ingress的对象,通过yaml进行创建和更新进行加载。
ingress controller:包括 proxy+ Controller 两个部分,proxy比如nginx, Haproxy, trafik, Istio,proxy一般通过service 接收流量,其中service的类型可以是NodePort或者LoadBalancer。Ingress Controller是将Ingress这种变化生成一段proxy的配置,然后将这个配置通过Kubernetes API写到proxy的Pod中,然后reload.
注意:写入proxy 配置文件如 nginx.conf 的不是backend service的地址,而是backend service 的 pod 的地址,避免在 service 在增加一层负载均衡转发
简而言之,ingress Controller借助service的服务发现机制,实现配置的动态更新以实现Pod的负载均衡机制实现,
由于涉及到proxy 的动态更新,目前社区Ingress Controller大体包含两种类型的控制器:
类型 | 常见类型 | 优点 | 缺点 |
---|---|---|---|
传统负载均衡 | nginx,haproxy | 成熟,稳定,高性能 | 动态更新需reload配置文件 |
微服务负载均衡 | Traefik,Envoy,Istio | 天生为微服务而生,动态更新 | 性能还有待提升 |
如果公有云服务商能够提供该类型的HTTP路由LoadBalancer,则也可设置其为Ingress Controller。
ingress是一个API对象,通过yaml文件来配置,ingress对象的作用是定义请求如何转发到 backend service的规则,可以理解为配置模板。
ingress通过http或https暴露集群backend service,给backend service提供外部URI、负载均衡、SSL/TLS能力以及基于域名的反向代理。ingress要依靠ingress-controiler来具体实现以上功能。
简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。
Ingress 格式示例:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: abc-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- api.abc.com
secretName: abc-tls
rules:
- host: api.abc.com
http:
paths:
- backend:
serviceName: apiserver
servicePort: 80
- host: www.abc.com
http:
paths:
- path: /image/*
backend:
serviceName: fileserver
servicePort: 80
- host: www.abc.com
http:
paths:
- backend:
serviceName: feserver
servicePort: 8080
每个 Ingress 都需要配置 rules,目前 Kubernetes 仅支持 http 规则。上面的示例表示请求/image/* 时转发到服务 fileserver的 80 端口。
与其他k8s对象一样,ingress配置也包含了apiVersion、kind、metadata、spec等关键字段。有几个关注的在spec字段中,tls用于定义https密钥、证书。rule用于指定请求路由规则。
这里值得关注的是metadata.annotations字段。在ingress配置中,annotations很重要。
前面有说ingress-controller有很多不同的实现,而不同的ingress-controller就可以根据 “kubernetes.io/ingress.class:” 来判断要使用哪些ingress配置,同时,不同的ingress-controller也有对应的annotations配置,用于自定义一些参数。
例如上面配置的’nginx.ingress.kubernetes.io/use-regex: “true”',最终是在生成nginx配置中,会采用location ~来表示正则匹配。
ingress-controller是具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。
ingress-controller并不是k8s自带的组件,实际上ingess-controller只是一个统称,用户可以选择不同的ingress-controller实现,
目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,其他还有很多第三方维护的ingress-controller,比如Contour, Haproxy, trafik, Istio,具体可以参考官方文档。
一般来说,ingress-controller的形式都是一个pod,里面跑着daemon程序和反向代理程序。
daemon负责不断监控集群的变化,根据ingress对象生成配置并应用新配置到反向代理,比如ingress-nginx就是动态生成nginx配置,动态更新upstreanm,并在需要的时候reload程序应用新配置。
为了方便,后面的例子都以k8s官方维护的ingress-nginx为例。
Ingress-Nginx github 地址: GitHub - kubernetes/ingress-nginx: Ingress-NGINX Controller for Kubernetes
Ingress-Nginx 官方网站: Welcome - NGINX Ingress Controller
nginx-ingress-controller是一个使用Nginx来实现一个Ingress Controller,需要实现的基本逻辑如下。
Ingress安装很简单,Minikube里面带了Ingress附件。
minikube addons enable ingress
minikube addons disable ingress
尼恩提示:
Minikube使用addon插件的模式安装Ingress很简单,但是,插件式部署是了解不到ingress 的底层原理
要了解 ingress 的底层原理, 还是得手工部署的模式。
官方文档中,部署ingress-nginx 只要简单的执行一个yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.25.0/deploy/static/mandatory.yaml
上面的下载地址可能无法下载,可用国内的Gitee地址
cd /vagrant/chapter28/ingress
wget https://gitee.com/mirrors/ingress-nginx/raw/nginx-0.25.0/deploy/static/mandatory.yaml
wget https://gitee.com/mirrors/ingress-nginx/raw/nginx-0.30.0/deploy/static/mandatory.yaml
wget https://gitee.com/mirrors/ingress-nginx/raw/controller-v1.0.0/deploy/static/provider/cloud/deploy.yaml
下载之后,到了window本地,使用editplus就可编辑了
使用editplus打开mandatory.yaml,
mandatory.yaml文件中包含了很多资源的创建,包括namespace、ConfigMap、role,ServiceAccount等等所有部署ingress-controller需要的资源。
重点看下deployment部分:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
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:
serviceAccountName: nginx-ingress-serviceaccount
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 33
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
可以看到主要使用了“quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0”这个镜像,
并且deployment指定了一些启动参数。
同时开放了80与443两个端口,并在10254端口做了健康检查。
网上的资料一般是基于v0.30.0来安装,但是对于[email protected]来说要安装[email protected]以上版本(目前最新版本是v1.0.4,本文采用v1.0.0),
原因是 [email protected]版本不再支持v1beta1
如果安装[email protected]版本后启动pod有如下问题
Failed to list *v1beta1.Ingress: the server could not find the requested resource
有一个版本的支持情况(https://github.com/kubernetes/ingress-nginx/)
尼恩也是先用的 0.25,然后换成 0.30,再换成1.0.0
躺了很多的坑
早期版本:
RBAC相关资源从1.17版本开始改用rbac.authorization.k8s.io/v1,rbac.authorization.k8s.io/v1beta1在1.22版本即将弃用
查看 kubectl api-versions
版本
1.0.0的版本,不用改
修改完后执行apply,并检查服务
kubectl apply -f deploy.yaml
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
# 检查部署情况
kubectl get daemonset -n ingress-nginx
kubectl get po -n ingress-nginx -o wide
kubectl get svc -n ingress-nginx
kubectl logs -f nginx-ingress-controller-hrd46 -n ingress-nginx
可以看到,nginx-controller的pod已经部署上了,但是没有启动。
换成最新版本,就启动啦
部署完ingress-controller,接下来就按照测试的需求来创建ingress资源。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-test
spec:
ingressClassName: nginx #指定Ingress 类
defaultBackend: #默认后端路由
service:
name: service1
port:
number: 8008
rules:
- host: "foo.bar.com" #主机精确匹配
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 8008
- host: "*.foo.com" #通配符匹配,bar.foo.com匹配,test.bar.foo.com不匹配
http:
paths:
- pathType: Prefix #以 / 分隔的 URL 路径前缀匹配,匹配区分大小写(必填)
path: "/foo"
backend:
service:
name: service2
port:
number: 9008
部署资源
kubectl apply -f app-service.yaml
curl http://192.168.49.2:30808/demo-provider/swagger-ui.html
kubectl apply -f app-service-2.yaml
curl http://192.168.49.2:30909/demo-provider/swagger-ui.html
kubectl apply -f ingresstest.yaml
kubectl delete -f ingresstest.yaml
curl http://10.100.15.33:30909/demo-provider/swagger-ui.html
curl http://10.100.15.33:30909/demo-provider/swagger-ui.html
kubectl get svc -A
curl http://foo.bar.com:32661/bar/
curl http://a.foo.com:32661/foo/
Error from server (InternalError): error when creating "ingress-http.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": x509: certificate signed by unknown authority
[root@k8s-master01 ~]# kubectl get -A ValidatingWebhookConfiguration
NAME WEBHOOKS AGE
ingress-nginx-admission 1 52m
[root@k8s-master01 ~]# kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted
[root@k8s-master01 ~]# kubectl apply -f ingresstest.yaml
再次创建就发现没有问题了
kubectl get svc -A
kubectl get svc -A
部署好以后,做一条本地host来模拟解析 foo.bar.com 到node的ip地址。
echo 192.168.49.2 foo.bar.com >> /etc/hosts
echo 192.168.49.2 a.foo.com >> /etc/hosts
curl http://foo.bar.com/demo-provider/swagger-ui.html
curl http://foo.bar.com:32661/bar/
curl http://a.foo.com:32661/foo/
curl http://foo.bar.com:32661/bar/ ,
请求不同的path已经按照需求请求到不同服务了,这里是 请求到service1
curl http://a.foo.com:32661/foo/
请求不同的path已经按照需求请求到不同服务了,这里是 请求到service2
如果没有路径, 则发生了404
实验完成,可以清理所有服务
kubectl delete all -l app=my-app
kubectl delete all -l app=my-app-2
通过实操可以发现,k8s ingress的两大实操部分:
(1)Ingress Controller 控制器
(2)Ingress 资源对象
要理解ingress,需要区分两个概念,ingress和ingress-controller:
简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。
Ingress Controller实质上可以理解为是个监视器,
Ingress Controller通过不断地跟Kubernetes API Server打交道,实时的感知后端Service、Pod等变化,比如新增和减少Pod,Service增加与减少等;
当得到这些变化信息后,Ingress Controller在结合下文的Ingress生成配置,然后更新反向代理负载均衡器,并刷新其配置,达到服务发现的作用。
Ingress Controller是将Ingress这种变化生成一段Nginx的配置,然后将这个配置通过Kubernetes API写到Nginx的Pod中,然后reload.(注意:写入 nginx.conf 的不是service的地址,而是service backend 的 pod 的地址,避免在 service 在增加一层负载均衡转发)。
ingress-controller并不是k8s自带的组件,实际上ingress-controller只是一个统称,用户可以选择不同的ingress-controller实现,目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,其他还有很多第三方维护的ingress-controller,具体可以参考官方文档。
但是不管哪一种ingress-controller,实现的机制都大同小异,只是在具体配置上有差异。
一般来说,ingress-controller的形式都是一个pod,里面跑着daemon程序和反向代理程序。
daemon负责不断监控集群的变化,根据ingress对象生成配置并应用新配置到反向代理,比如nginx-ingress就是动态生成nginx配置,动态更新upstream,并在需要的时候reload程序应用新配置。为了方便,后面的例子都以k8s官方维护的nginx-ingress为例。
Ingress简单理解就是个路由规则定义,
比如某个域名对应某个Serivce,即当某个域名的请求进来时转发给某个Service;
这个规则将与Ingress Controller结合,然后Ingress Controller将其动态写入到负载均衡器中,从而实现整体的服务发现和负载均衡。
Ingress解决的是新的服务加入后,域名和服务的对应问题,基本上是一个ingress的对象,通过yaml进行创建和更新进行加载。
从上图可以很清晰的看到,实际上请求进行被负载均衡器拦截,比如nginx,然后Ingress Controller通过交互得知某个域名对应哪个Service,再通过跟Kubernetes API交互得知Service地址等信息;
综合以后生成配置文件实时写入负载均衡器,然后负载均衡器reload该规则便可实现服务发现,即动态映射。
从上图中可以很清晰的看到,实际上请求进来还是被负载均衡器拦截,比如 nginx,然后 Ingress Controller 通过跟 Ingress 交互得知某个域名对应哪个 service,再通过跟 kubernetes API 交互得知 service 地址等信息;综合以后生成配置文件,实时写入负载均衡器,然后负载均衡器 reload 该规则便可实现服务发现,即动态映射:
ingress是一个API对象,和其他对象一样,通过yaml文件来配置。
ingress通过http或https暴露集群内部service,给service提供外部URL、负载均衡、SSL/TLS能力以及基于host的方向代理。ingress要依靠ingress-controller来具体实现以上功能。前一小节的图如果用ingress来表示,大概就是如下配置:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: abc-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- api.abc.com
secretName: abc-tls
rules:
- host: api.abc.com
http:
paths:
- backend:
serviceName: apiserver
servicePort: 80
- host: www.abc.com
http:
paths:
- path: /image/*
backend:
serviceName: fileserver
servicePort: 80
- host: www.abc.com
http:
paths:
- backend:
serviceName: feserver
servicePort: 8080
与其他k8s对象一样,ingress配置也包含了apiVersion、kind、metadata、spec等关键字段。
有几个关注的在spec字段中,tls用于定义https密钥、证书。rule用于指定请求路由规则。
这里值得关注的是metadata.annotations字段。
在ingress配置中,annotations很重要。
前面有说ingress-controller有很多不同的实现,
而不同的ingress-controller就可以根据"kubernetes.io/ingress.class:"来判断要使用哪些ingress配置,
同时,不同的ingress-controller也有对应的annotations配置,用于自定义一些参数。
列如上面配置的’nginx.ingress.kubernetes.io/use-regex: “true”’,最终是在生成nginx配置中,会采用location ~来表示正则匹配。
首先我们先看个简单的资源示例。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-nginx
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 8008
与所有其他 Kubernetes 资源一样,Ingress 需要使用 apiVersion
、kind
和 metadata
字段。
Ingress 对象的命名必须是合法的DNS子域名名称。
ingress规约提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。
Ingress 资源仅支持用于转发 HTTP 流量的规则。
每个 HTTP 规则都包含:
- 可选的
host
。在此示例中,未指定host
,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了host
(例如 foo.bar.com),则rules
适用于该host
。- 路径列表 paths(例如,
/testpath
),每个路径都有一个由serviceName
和servicePort
定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。backend
(后端)是Service文档中所述的服务和端口名称的组合。 与规则的host
和path
匹配的对 Ingress 的 HTTP(HTTPS )请求发送到列出的backend
。
通常在 Ingress 控制器中会配置 defaultBackend
(默认后端),服务不符合任何规约中 path
的请求。
没有 rules
的 Ingress 将所有流量发送到同一个默认后端。
defaultBackend
通常是ingress控制器的配置选项,而非在 Ingress 资源中指定。如果 hosts
或 paths
都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端
resource
后端是一个 ObjectRef
,指向同一名字空间中的另一个 Kubernetes,将其作为 Ingress 对象。Resource
与 Service
配置是互斥的,在 二者均被设置时会无法通过合法性检查。
Resource
后端的一种常见用法是将所有入站数据导向带有静态资产的对象存储后端。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-resource-backend
spec:
defaultBackend:
resource:
apiGroup: k8s.example.com
kind: StorageBucket
name: static-assets
rules:
- http:
paths:
- path: /icons
pathType: ImplementationSpecific
backend:
resource:
apiGroup: k8s.example.com
kind: StorageBucket
name: icon-assets
Ingress 中的每个路径都需要有对应的路径类型(Path Type)。
未明确设置 pathType
的路径无法通过合法性检查。当前支持的三种路径类型:
ImplementationSpecific
:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的pathType
处理或者与Prefix
或Exact
类型作相同处理。Exact
:精确匹配 URL 路径,且区分大小写。Prefix
:基于以/
分隔的 URL 路径前缀匹配。匹配区分大小写,并且对路径中的元素逐个完成。 路径元素指的是由/
分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。注意:如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配 (例如:
/foo/bar
匹配/foo/bar/baz
, 但不匹配/foo/barbaz
)。
类型 | 路径 | 请求路径 | 匹配与否? |
---|---|---|---|
Prefix | / |
(所有路径) | 是 |
Exact | /foo |
/foo |
是 |
Exact | /foo |
/bar |
否 |
Exact | /foo |
/foo/ |
否 |
Exact | /foo/ |
/foo |
否 |
Prefix | /foo |
/foo , /foo/ |
是 |
Prefix | /foo/ |
/foo , /foo/ |
是 |
Prefix | /aaa/bb |
/aaa/bbb |
否 |
Prefix | /aaa/bbb |
/aaa/bbb |
是 |
Prefix | /aaa/bbb/ |
/aaa/bbb |
是,忽略尾部斜线 |
Prefix | /aaa/bbb |
/aaa/bbb/ |
是,匹配尾部斜线 |
Prefix | /aaa/bbb |
/aaa/bbb/ccc |
是,匹配子路径 |
Prefix | /aaa/bbb |
/aaa/bbbxyz |
否,字符串前缀不匹配 |
Prefix | / , /aaa |
/aaa/ccc |
是,匹配 /aaa 前缀 |
Prefix | / , /aaa , /aaa/bbb |
/aaa/bbb |
是,匹配 /aaa/bbb 前缀 |
Prefix | / , /aaa , /aaa/bbb |
/ccc |
是,匹配 / 前缀 |
Prefix | /aaa |
/ccc |
否,使用默认后端 |
混合 | /foo (Prefix), /foo (Exact) |
/foo |
是,优选 Exact 类型 |
在某些情况下,Ingress 中的多条路径会匹配同一个请求。
这种情况下最长的匹配路径优先。
如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。
主机名可以是精确匹配(例如“foo.bar.com
”)或者使用通配符来匹配 (例如“*.foo.com
”)。
精确匹配要求 HTTP host
头部字段与 host
字段值完全匹配。
通配符匹配则要求 HTTP host
头部字段与通配符规则中的后缀部分相同。
主机 | host 头部 | 匹配与否? |
---|---|---|
*.foo.com |
bar.foo.com |
基于相同的后缀匹配 |
*.foo.com |
baz.bar.foo.com |
不匹配,通配符仅覆盖了一个 DNS 标签 |
*.foo.com |
foo.com |
不匹配,通配符仅覆盖了一个 DNS 标签 |
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-nginx-demo
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: "foo.bar.com" #主机精确匹配
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 8008
- host: "*.foo.com" #通配符匹配,bar.foo.com匹配,test.bar.foo.com不匹配
http:
paths:
- pathType: Prefix #以 / 分隔的 URL 路径前缀匹配,匹配区分大小写(必填)
path: "/foo"
backend:
service:
name: service2
port:
number: 8008
Ingress 可以由不同的控制器实现,不同的控制器 通常使用不同的配置。
每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。
IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。
# Source: ingress-nginx/templates/controller-ingressclass.yaml
# We don't support namespaced ingressClass yet
# So a ClusterRole and a ClusterRoleBinding is required
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
helm.sh/chart: ingress-nginx-4.0.15
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 1.1.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: nginx
namespace: ingress-nginx
spec:
controller: k8s.io/ingress-nginx
IngressClass 资源包含可选的 parameters
字段,可用于为该类引用额外的、 特定于具体实现的配置。
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: external-lb
spec:
controller: example.com/ingress-controller
parameters:
apiGroup: k8s.example.com
kind: IngressParameters
name: external-lb
参数(parameters)的具体类型取决于你在 .spec.controller 字段中指定的 Ingress 控制器.
parameters
字段中scope
和 namespace
字段,可用来引用特定 于名字空间的资源,对 Ingress 类进行配置。
scope
字段默认为 Cluster
,表示默认是集群作用域的资源。
将 scope
设置为 Namespace
并设置 namespace
字段就可以引用某特定 名字空间中的参数资源。
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: external-lb
spec:
controller: example.com/ingress-controller
parameters:
apiGroup: k8s.example.com
kind: IngressParameters
name: external-lb
namespace: external-configuration
scope: Namespace
可以将一个特定的 IngressClass 标记为集群默认 Ingress 类。
将 IngressClass 资源的 ingressclass.kubernetes.io/is-default-class
注解设置为 true
将确保新的未指定 ingressClassName
字段的 Ingress 能够分配为这个默认的 IngressClass.
注意: 如果集群中有多个 IngressClass 被标记为默认,准入控制器将阻止创建新的未指定
ingressClassName
的 Ingress 对象。解决这个问题只需确保集群中最多只能有一个 IngressClass 被标记为默
ingress的部署,需要考虑两个方面:
同样用deployment模式部署ingress-controller,并创建type为NodePort的服务。
这样,ingress就会暴露在集群节点ip的特定端口上。
由于nodeport暴露的端口是随机端口,一般会在前面,再搭建一套负载均衡器来转发请求。
该方式一般用于宿主机是相对固定的环境ip地址不变的场景。
NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响。
用DaemonSet结合nodeselector来部署ingress-controller到特定的node上,
然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。
这时,ingress-controller的node机器就很类似传统架构的边缘节点,比如机房入口的nginx服务器。
该方式整个请求链路最简单,性能相对NodePort模式更好。
缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。
比较适合大并发的生产环境使用。
如果要把ingress部署在公有云,那用这种方式比较合适。
用Deployment部署ingress-controller,创建一个type为LoadBalancer的service关联这组pod。
大部分公有云,都会为LoadBalancer的service自动创建一个负载均衡器,通常还绑定了公网地址。
只要把域名解析指向该地址,就实现了集群服务的对外暴露。
但是,单独用service暴露服务的方式,在实际生产环境中不太合适
ClusterIP的方式只能在集群内部访问。
NodePort方式的话,测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。
LoadBalance方式受限于云平台,且通常在云平台部署ELB还需要额外的费用。
生产环境,推荐模式二。
云服务器环境,推荐模式三。
首先来看看 nodePort的NAT性能问题
采用 NodePort 方式暴露服务面临问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且难以维护;
这时,如果使用一个ingress直接对内进行转发,ingress的流量是很大的
NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响
众所周知的是,Pod是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod上所监听的就是Node上的端口。
那么这又该如何实现呢?
简单的实现就是使用 DaemonSet 在每个 Node 上监听 80端口,然后写好规则,
因为 ingress 外面绑定了宿主机 80 端口,本身又在集群内,那么向后直接转发到相应Service IP就行了,不需要进行nat 了。
如下图所示:
这里使用daemonset+hostNetwork 模式部署,并且部署到特定node,
DaemonSet:守护进程控制器
DaemonSet 也是 Kubernetes 提供的一个 default controller,它实际是做一个守护进程的控制器,它能帮我们做到以下几件事情:
(1)首先能保证集群内的每一个节点都运行一组相同的 pod;
(2)同时还能根据节点的状态保证新加入的节点自动创建对应的 pod;
(3)在移除节点的时候,能删除对应的 pod;
(4)而且它会跟踪每个 pod 的状态,当这个 pod 出现异常、Crash 掉了,会及时地去 recovery 这个状态。
daemonset与deployment非常相似,区别是不需要设置replicas,因为daemonset是每节点启动的
首先,需要修改node的部分配置
先给要部署nginx-ingress的node打上特定标签,这里测试部署在"minikube"这个节点。
kubectl label node node-** isIngress="true"
在正式环境下,也是类似的,比如部署在 node02
kubectl get node
kubectl label node node02 ingress=true
kubectl get nodes --show-labels
kubectl delete -f ingresstest.yaml
kubectl delete -f deploy.yaml
vim mandatory.yaml
...
apiversion: apps/vl
kind: Daemonset #修改kind
...
hostNetwork: true #使用主机网络
nodeSelector:
ingress: "true" #选择节点运行
...
复制一份,改为deploy-daemonset.yaml,然后修改上面的deployment部分配置为daemonset:
# 修改api版本及kind
# apiVersion: apps/v1
# kind: Deployment
apiVersion: extensions/v1beta1
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:
serviceAccountName: nginx-ingress-serviceaccount
# 选择对应标签的node
nodeSelector:
isIngress: "true"
# 使用hostNetwork暴露服务
hostNetwork: true
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 33
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
Deployment 部署的副本 Pod 会分布在各个 Node 上,每个 Node 都可能运行好几个副本。
DaemonSet 的不同之处在于:每个 Node 上最多只能运行一个副本。
#主节点
kubectl delete -f deploy-daemonset.yaml
kubectl apply -f deploy-daemonset.yaml
kubectl logs -f nginx-ingress-controller-hrd46 -n ingress-nginx
kubernetes “hostNetwork: true”,这是一种直接定义Pod网络的方式。
如果在POD中使用"hostNetwork: true"配置网络,pod中运行的应用程序可以直接看到宿主主机的网络接口,宿主机所在的局域网上所有网络接口都可以访问到该应用程序及端口。
hostNetwork: true
# 使用主机网络
dnsPolicy: ClusterFirstWithHostNet
# 该设置是使POD使用k8s的dns,dns配置在/etc/resolv.conf文件中
# 如果不加,pod默认使用所在宿主主机使用的DNS,这样会导致容器
# 内不能通过service name访问k8s集群中其他POD
另外: HostNetwork 模式不需要创建service
kubectl describe pod ingress-nginx-controller-nvrrq -n ingress-nginx
可以使用下面的命令,查看端口:
netstat -natp | grep nginx
由于配置了hostnetwork, nginx已经在 node主机本地监听团9118/9443/8443端口。
其中 9118是nginx-controller默认配置的一个defaultbackend (Ingress资源没有匹配的 rule 对象时,流量就会被导向这个default backend
这样,只要访问 node主机有公网 TP,就可以直接映射域名来对外网暴露服务了。
如果要nginx高可用的话,可以在多个node上部署,并在前面再搭建一套LVS+keepalive做负载均衡。
部署完ingress-controller,接下来就按照测试的需求来创建ingress资源。
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
kubectl apply -f ingresstest-one.yaml
kubectl delete -f ingresstest-one.yaml
kubectl get svc -A
curl http://a.foo.com:30909/foo/
curl http://foo.bar.com:30808/demo-provider/swagger-ui.html
curl http://foo.bar.com:9118/bar/
curl http://a.foo.com:9118/foo/
查看日志
kubectl logs -f ingress-nginx-controller-nvrrq -n ingress-nginx
#主节点
kubectl delete -f deploy-daemonset.yaml
kubectl apply -f deploy-daemonset.yaml
kubectl get pod -n ingress-nginx -o wide
# 检查部署情况
kubectl get daemonset -n ingress-nginx
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
kubectl get po -n ingress-nginx -o wide
kubectl get po -A
kubectl get svc -n ingress-nginx
kubectl logs -f ingress-nginx-controller-j4cq4 -n ingress-nginx
kubectl describe pod ingress-nginx-controller-j4cq4 -n ingress-nginx
echo 192.168.49.2 foo.bar.com >> /etc/hosts
echo 192.168.49.2 a.foo.com >> /etc/hosts
查看日志
kubectl logs -n ingress-nginx -f
kubectl logs -f ingress-nginx-controller-nvrrq -n ingress-nginx
为了配置kubernetes中的ingress的高可用,对于kubernetes集群以外只暴露一个访问入口,需要使用keepalived排除单点问题。需要使用daemonset方式将ingress-controller部署在边缘节点上。
首先解释下什么叫边缘节点(Edge Node),所谓的边缘节点即集群内部用来向集群外暴露服务能力的节点,
集群外部的服务通过边缘节点(Edge Node)来调用集群内部的服务,
边缘节点是集群内外交流的一个Endpoint。
边缘节点要考虑两个问题
为了满足边缘节点的高可用需求,我们使用keepalived来实现。
在Kubernetes中添加了ingress后,在DNS中添加A记录,域名为你的ingress中host的内容,IP为你的keepalived的VIP,这样集群外部就可以通过域名来访问你的服务,也解决了单点故障。
选择Kubernetes的三个node作为边缘节点,并在前面再搭建一套LVS+keepalive做负载均衡。
在 k8s 前面部署 lvs, 并且使用 DR 模式, 解决单个 nginx-ingress 会成为性能瓶颈问题,
在尼恩的 读者群(50+)中,大家对K8S的四大Port 有很多的理解误区。
基于咱们的K8S学习圣经,把四种port底层原理介绍一下:nodePort、port、targetPort、containerPort
nodePort提供了集群外部客户端访问service的一种方式,nodePort提供了集群外部客户端访问service的端口,即nodeIP:nodePort提供了外部流量访问k8s集群中service的入口。
比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30082。
其他用户就可以通过浏览器http://node:30082访问到该web服务。
而这里的nodeIP,是pod启动所在的node 机器。
在咱们的ingress 测试中,node 机器 ip 是 192.168.49.2 ,
ingress 开启的node port 为 32661
ingress 的svc服务暴露了8008端口(参考前面的配置文件),
集群内其他容器通过8008端口访问ingress 的svc服务,但是外部流量不能通过该端口访问ingress 的svc服务,因为外部服务,访问不到这个 IP 10.102.112.207
这个ip 是 K8S管理的虚拟网络IP, 这个网络是跨 物理机器的,跨node节点, 一个统一的虚拟网络
外部的流量不能访问这个虚拟网络, 怎么办呢,需要配置NodePort访问,
NodePort 需要通过 完成NAT 转换, 转换成 10.102.112.207 虚拟IP,再通过svc的路由机制 kube-proxy +路由表 结合,路由到正确的POD。
port是暴露在cluster ip上的端口,port提供了集群内部客户端访问service的入口,即clusterIP:port。
对应的service.yaml如下:
相当于在虚拟网络上开的端口,一般和 pod的 targetPort 端口,保持一致。
targetPort是pod上的端口,
从nodePort-》上来的数据,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
targetPort与制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx(参考DockerFile)暴露8008端口。
我们这里设置为web服务端口8008。
containerPort是在pod控制器中定义的、pod中的容器需要暴露的端口。如spring boot的8080,mysql的3306等。咱们实例当中的web服务端口8008。
该端口只是起到specification作用,哪怕不在yaml中定义,也是可以通过nodePort->targetPort的流向(外部)或者port->targetPort流向(内部)进行访问。
如果设置的话,也是设置为docker镜像的暴露端口。
需要注意的,nodePort的使用只是实验性质,如果在生产环境上通过通过nginx等反向代理工具去管理nodePort绝对是灾难性的。
更多是需要通过外部LoadBalancer或者ingress去做管理。
问题:当每次有新服务加入,Ingress 又该如何修改 Nginx 配置呢?
回到 Nginx的原理,Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改nginx.conf即可实现,那在K8S中又该如何实现这种方式的调度呢?
关于Nginx的知识,请参见尼恩的《Java高并发核心编程 卷3 加强版》 PDF,里边同样介绍了lua 开发的知识,而 Ingress 就是在Nginx 做的 Lua 开发。
在Ingress中,假设后端的服务初始服务只有ecshop,后面增加了bbs和member服务,那么又该如何将这2个服务加入到Nginx-Pod进行调度呢?
当然,每次手动改Nginx的配置,就太low
Ingress 出现了,如果不算上面的Nginx,Ingress 包含两大组件:Ingress Controller 和 Ingress。
增加了新的服务,那么就是原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,
现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;
那么问题来了: Ingress 对象 如何 变成 Nginx 的配置文件呢? Ingress Controller 这东西就是承担这个 桥梁的角色。
Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取他,按照他自己模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,工作流程如下图
实际上Ingress也是Kubernetes API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发到指定的Service资源的规则。
用于将集群外部的请求流量转发到集群内部完成的服务发布。
Ingress 资源自身不能进行“流量穿透”,仅仅是一组规则的集合,这些集合规则还需要其他功能的辅助,比如监听某接口,然后根据这些规则的匹配进行路由转发,
这些能够为Ingress资源定义的规则,进行流量转发的组件就是Ingress Controller。里边包含了 反向代理负载均衡组件如 nginx。
Ingress 控制器不同于Deployment 控制器的是,Ingress控制器不直接运行为kube-controller-manager的一部分,它仅仅是Kubernetes集群的一个附件,需要在集群上单独部署。
实际ingress-nginx的有非常多的配置, 具体可以参考ingress-nginx的官方文档。
《吃透8图1模板,人人可以做架构》
《10Wqps评论中台,如何架构?B站是这么做的!!!》
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
《100亿级订单怎么调度,来一个大厂的极品方案》
《2个大厂 100亿级 超大流量 红包 架构方案》
… 更多架构文章,正在添加中
《响应式圣经:10W字,实现Spring响应式编程自由》
这是老版本 《Flux、Mono、Reactor 实战(史上最全)》
《Spring cloud Alibaba 学习圣经》 PDF
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
《Linux命令大全:2W多字,一次实现Linux自由》
《TCP协议详解 (史上最全)》
《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》
《Redis分布式锁(图解 - 秒懂 - 史上最全)》
《Zookeeper 分布式锁 - 图解 - 秒懂》
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《缓存之王:Caffeine 的使用(史上最全)》
《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》
4000页《尼恩Java面试宝典 》 40个专题
以上尼恩 架构笔记、面试题 的PDF文件,请到《技术自由圈》公众号领取↓↓↓