-
docker version:20.10.2
-
kubernetes version:1.20.1
本文概述Kubernetes Ingress基本原理和官方维护的Nginx-Ingress的基本安装使用。
Ingress是对集群中服务的外部访问进行管理的API对象,典型的访问方式是HTTP。Ingress可以提供负载均衡、SSL termination和基于名称的虚拟托管。
虽然K8S集群内部署的Pod、Service都有自己的IP,但是无法对外提供灵活的访问,NodePort虽然能够提供对外访问,但是灵活性有限。
Ingress是什么
Ingress公开了从集群外部到集群内服务的HTTP和HTTPS路由。流量路由由Ingress资源上定义的规则控制。
下面是一个将所有流量都发送到同一Service的简单Ingress示例:
可以将Ingress配置为服务提供外部可访问的URL、负载均衡流量、终止SSL/TLS,以及提供基于名称的虚拟主机等能力。Ingress控制器通常负责通过负载均衡器来实现Ingress,尽管它也可以配置边缘路由器或其他前端来帮助处理流量。
Ingress不会公开任意端口或协议。将HTTP和HTTPS以外的服务公开到Internet时,通常使用Service.Type=NodePort
或Service.Type=LoadBalancer
类型的服务。
Ingress Controller
要使Ingress资源工作,集群必须有一个运行的Ingress控制器
。
与作为kube-controller-manager可执行的一部分运行的其他类型的控制器不同,Ingress控制器不是随集群自动启动的。
Ingress controller能够监控集群变化,使集群始终保持期望的状态。能够实施感知Ingress路由规则集合的变化,再与API Server交互,获取Service、Pod在集群中的信息,而后根据信息更新代理服务。
Kubernetes项目目前维护AWS、GCE和Nginx Ingress控制器。
其他主流第三方项目:
- Apache APISIX Ingress 控制器
- HAProxy Ingress
- Traefik
- Envoy
- Nginxinc NGINX-Ingress
注意:Kubernetes社区维护的Nginx ingress与Nginx官方开发维护的并不是同一版本,Kubernetes官方维护的使用Go/Lua实现,Nginx官方维护的使用Go/Python实现。
可以在集群中部署任意数量的ingress控制器。创建ingress时,使用适当的ingress.class
注解每个Ingress以表明在集群中如果有多个Ingress控制器时,应该使用哪个Ingress控制器。
如果不定义ingress.class
,云提供商可能使用默认的Ingress控制器。
安装Ingress Controller
在自建的Kubernetes上部署Ingress Controller,选用Kubernetes官方维护的ingress-nginx:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.44.0/deploy/static/provider/baremetal/deploy.yaml
注意:官方的默认文件中使用的镜像是从Google镜像站点下载的,由于某些原因可能无法访问,请确保能够访问下载,不然会导致无法成功安装。也可以下载github上此项目的release版本进行安装。
在不同的平台上搭建Kubernetes集群,则部署方式中的配置也略有不同,官方提供各平台的常用一键部署方式,具体查看官方资料
此部署清单会使用NodePort方式暴露服务,但未指定NodePort具体端口,使用随机端口。此外会部署到集群中所有节点上,在正式环境使用中可能不符合需求,可以通过节点亲和性、污点容忍度等方式指定部署到有需要的节点上。如果需要修改则更改部署清单文件中相关配置即可。
部署清单中会创建一个名为ingress-nginx的Namespace,其属于Namespace级别的相关服务都会创建到此Namespace下。还会使用到ServiceAccount、ConfigMap、RBAC、Job控制器等,均会自动创建,需要了解则详细查看此部署清单即可。
Ingress使用示例
创建web服务,使用Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- name: http
containerPort: 80
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo $HOSTNAME>/usr/share/nginx/html/hostname.html"]
创建svc:
apiVersion: v1
kind: Service
metadata:
name: ingress-demo-svc
spec:
selector:
app: nginx
ports:
- name: web-port
protocol: TCP
port: 80
targetPort: 80
创建Ingress资源:
kind: Ingress
metadata:
name: ingress-demo
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: myingress.foo.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ingress-demo-svc
port:
number: 80
测试效果:
# 获取Ingress Controller的NodePort端口;ingress-nginx-controller service的NodePort使用部署文件默认随机端口
kubectl get svc -n ingress-nginx
# 在集群外部主机上添加解析记录:myingress.foo.org指定到集群中任意节点,然后访问
for i in {{0..5}}; do curl myingress.foo.org:32733/hostname.html ;done
nginx-deployment-7c88b85cf8-2kfr4
nginx-deployment-7c88b85cf8-2kfr4
nginx-deployment-7c88b85cf8-ngb49
nginx-deployment-7c88b85cf8-ngb49
nginx-deployment-7c88b85cf8-2kfr4
nginx-deployment-7c88b85cf8-2kfr4
#
Ingress控制器通过创建一个NodePort方式的svc作为代理将服务暴露,用户通过创建Ingress资源,将集群内的svc的Endpoint绑定到Ingress控制器的代理Upstream中,从而用户访问Ingress控制器暴露的资源,而后被代理到真正的应用Pod上。
与Service不同的是,Ingress能够实现更多的负载均衡代理策略,能够提供HTTPS的代理,能够更好的管理统一入口。
NodePort方式在多个节点上向外暴露提供了服务,却还是缺少统一性管理入口。在云环境服务商上,可以通过部署清单申请LB作为前置,在自建环境中则需要自部署一些相关处理方式,如裸机环境中的扩展中描述的。
Ingress资源
如上示例,Ingress同样需要使用apiVersion、kind和metadata字段。Ingress经常使用注解(annotations)来配置一些选项,具体取决于Ingress控制器,例如重写目标注解。不同的Ingress控制器支持不同的注解。
Ingress规约提供了配置负载均衡器或者代理服务器所需的所有信息。最重要的是,其中包含与所有传入请求匹配的规则列表。Ingress资源仅支持用于转发HTTP流量的规则。
常用字段:
ingress.spec.defaultBackend:默认后端。没有rules的Ingress将所有流量发送到同一后端。defaultBackend通常是Ingress控制器的配置选项,而非在Ingress资源中指定。
ingress.spec.ingressClassName:在Kubernetes1.18版本引入ingressClassName
字段,对ingressClass资源的引用,且包含用来替代注解中的kubernetes.io/ingress.class
字段。
ingress.spec.rules:Ingress规则。
ingress.spec.rules.host:基于域名代理。被代理服务的域名,指定了此字段,访问时必须使用域名。
ingress.spec.rules.http:基于路径代理。
ingress.spec.rules.http.paths.path:代理的路径。
ingress.spec.rules.http.paths.pathType:路径类型;ImplementationSpecific,Exact,Prefix。
ingress.spec.rules.http.paths.backend.resource:资源后端,指向ObjectRef。
ingress.spec.rules.http.paths.backend.service:服务后端,指向服务。
ingress.spec.rules.http.paths.backend.service.name:后端的名称,即svc的名称。
ingress.spec.rules.http.paths.backend.service.port:后端的端口,支持name
或number
方式。
ingress.spec.tls:TLS类型代理。
ingress.spec.tls.hosts:代理的域名。
ingress.spec.tls.secretName:secret中保存此域名证书的名称。
资源后端
Resource后端是一个ObjectRef。Resource后端的一种常见用法是将所有入站数据导向带有静态资产的对象存储后端。
注意:backend中的resource和service不能同时使用。
示例:
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中的每个路径都需要有对应的路径类型(pathType)。未明确设置pathType
无法通过合法性检查。
当前支持的路径类型有三种:
- ImplementationSpecific:对于这种路径类型,匹配方法取决于
IngressClass
。具体实现可以将其作为单独的pathType
处理或者与Prefix
或Exact
类型作相同处理。 - Exact:精确匹配URL路径,区分大小写。
- Prefix:基于以
/
分隔的的URL路径前缀匹配,匹配区分大小写。
Exact
是精确匹配,如路径/foo
只能够匹配到请求路径/foo
,/foo/
视为不匹配。
Prefix
是前缀匹配,以/
作为分隔。如路径/foo
可以被请求路径/foo
、/foo/
、/foo/abc
匹配到。路径/
能够匹配所有请求路径。
注意:在某些情况下,Ingress中的多条路径会匹配同一个请求。这种情况下最长的匹配路径优先
。如果仍然有两条同等的匹配路径,则精确路径类型优先
于前缀路径类型。
主机名通配符
主机名可以是精确匹配(如foo.bar.com
)或者使用通配符来匹配(如*.bar.com
)。精确匹配要求HTTP host
头部字段与host
字段值完全匹配。通配符匹配则要求HTTP host
头部字段与通配符规则中的后缀部分相同
。
主机 | host | 匹配与否 |
---|---|---|
*.foo.com | bar.foo.com | 匹配,后缀相同 |
*.foo.com | baz.bar.foo.com | 不匹配,通配符仅覆盖了一个DNS标签 |
*.foo.com | foo.com | 不匹配,通配符仅覆盖了一个DNS标签 |
Ingress类
Ingress可以由不同的控制器实现,通常使用不同的配置。每个Ingress应当指定一个类,也就是一个对IngressClass资源的引用。IngressClass资源包含额外的配置,其中包括应当实现该类的控制器名称。
IngressClass资源包含一个可选的parameters
字段,可用于为该类引用额外配置。
示例:
具体解释可通过命令查看kubectl explain ingressClass
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
在Kubernetes 1.18版本引入Ingress Class资源和ingressClassName字段之前,Ingress类是通过Ingress中的一个kubernetes.io/ingress.class
注解来指定的。这个注解从未被正式定义过,但是得到了Ingress控制器的广泛支持。
默认Ingress类
将一个IngressClass资源的ingressclass.kubernetes.io/is-default-class
注解设置为true
,使其成为默认Ingress类
。新创建的Ingress资源未指定IngressClassName字段时,将使用此默认Ingress类。集群中最多只能有一个IngressClass被标记为默认。
TLS示例
使用Ingress TLS必须首先创建TLS secret,secret必须包含名为tls.crt
和tls.key
的键名。
测试使用时,自签HTTP证书:
# 申请证书
openssl genrsa -out tls.key 2048
openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Guangdong/L=Shenzhen/O=DevOps/CN=mytls.website.com
# 创建TLS secret
kubectl create secret tls mytls-ingress-tls --cert=tls.crt --key=tls.key
# 查看TLS secret
kubectl describe secret mytls-ingress-tls
创建TLS类型的Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-tls
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: mytls.website.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ingress-demo-svc
port:
number: 80
tls:
- hosts:
- mytls.website.com
secretName: mytls-ingress-tls
使用之前示例中的svc绑定到ingress资源下,创建好tls类型的ingress后,在集群外节点上通过tls访问即可。注意要使用Ingress 443端口的NodePort端口访问。
裸机环境中的扩展
在云环境下,网络负载均衡器是按需提供的,只需要有部署清单申请了即可使用向外部提供联系。在裸机环境中(自建私有Kubernetes集群环境)缺少这种提供,需要额外来设置。
以下解决方案:
- MetalLB
- NodePort
- 共享主机网络
- 自部署边缘网络组件(如HAproxy),运行在Kubernetes集群环境之外
- External IPs
具体使用可根据自身需求查阅相关资料。