目录:
(1)k8s指南-概述
(2)k8s指南-架构
(3)k8s指南-工作负载(1)
(4)k8s指南-工作负载(2)
(5)k8s指南-工作负载(3)
(6)k8s指南-工作负载(4)
(7)k8s指南-Service
(8)k8s指南-Ingress
(9)k8s指南-DNS与服务发现
(10)K8S指南-平滑升级与自动扩缩容
Ingress是k8s集群中负责管理服务外部访问的API对象。
通常情况下,service和pod仅可在集群内部网络中通过ip地址访问。如果想从集群外部访问集群内部,则必须要有Ingress。
Ingress则相当于一个服务网关,控制着从集群外部到集群内部的HTTP/HTTPS的访问路由。下面是一个简单的ingress对流量的路由示意图:
可以给Ingress配置提供外部可访问的URL、负载均衡、SSL和基于名称的虚拟主机等。
在Kubernetes中,为了使外部的应用能够访问集群内的service,最为常用的是使用NodePort
和LoadBalancer
两种类型的service。但这两种类型的服务各有缺点:NodePort
方式会占用很多集群机器的端口;而LoadBanlancer
类型要求k8s必须跑在支持的云上。当同时存在多个LoadBanlancer
类型的Service
时,就会占用大量公网ip地址,而Ingress
正式为了解决以上两种问题而生的。
Ingress必须要有Ingress控制器,单独的创建一个Ingress是没有任何效果的。通常我们说Ingress,实际上指的是Ingress配置和Ingress控制器。
Ingress控制器类似于nginx,Ingress类似于nginx的配置文件。如果只安装nginx,而没有nginx的配置文件,则此nginx将毫无意义;反过来,若只有nginx的配置文件,但没安装nginx,则配置文件无运行载体
下面是一个流量路由示意图:
从中可以看出,客户端首先对 ngdemo.qikqiak.com
执行 DNS 解析,得到 Ingress 控制器所在节点的 IP,然后客户端向 Ingress 控制器发送 HTTP 请求,然后根据 Ingress 对象里面的描述匹配域名,找到对应的 Service 对象,并获取关联的 Endpoints 列表,将客户端的请求转发给其中一个 Pod。
理想情况下,所有的Ingress控制器都应该符合参考规范,但实际上不同的Ingress控制器操作略有不同。
Ingress控制器有很多种,有官方维护的如ingress-nginx,AWS和GCE,也有第三方的。
下面安装Ingress-nginx控制器。
按照官方文档官方yaml文,直接运行yaml文件的话国内会由于访问不了google仓库而报错。网上有人建了一个中转站,将google镜像转成docker镜像,然后通过脚本拉下来之后重新打原来的标签。因此只需要执行脚本就能将镜像拉到本地了(传送门)。
下载官方yaml文件,将其中的image中的数字摘要去掉(结果如:image: registry.k8s.io/ingress-nginx/controller:v1.6.4),这样就能启动Ingress控制器了。执行kubectl get all -n ingress-nginx
,结果如下所示:
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-9jpfg 0/1 Completed 0 5h54m
pod/ingress-nginx-admission-patch-5c6qd 0/1 Completed 1 5h54m
pod/ingress-nginx-controller-5b648bf48-wrsnd 1/1 Running 0 5h54m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.98.207.53 localhost 80:31871/TCP,443:31482/TCP 5h54m
service/ingress-nginx-controller-admission ClusterIP 10.104.227.61 443/TCP 5h54m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 5h54m
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-5b648bf48 1 1 1 5h54m
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 7s 5h54m
job.batch/ingress-nginx-admission-patch 1/1 8s 5h54m
这个Ingress控制器创建之后会带一个名为nginx
的IngressClass
,接下来我们创建Ingress,指定ingressClassName: nginx
即可使用ingress-nginx控制器。当然,也要创建Ingress路由的service和pod:
# my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
app: my-nginx
spec:
ports:
- port: 80
protocol: TCP
name: http
selector:
app: my-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-nginx
namespace: default
spec:
ingressClassName: nginx # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
rules:
- host: ngdemo.qikqiak.com # 将域名映射到 my-nginx 服务
http:
paths:
- path: /
pathType: Prefix
backend:
service: # 将所有请求发送到 my-nginx 服务的 80 端口
name: my-nginx
port:
number: 80
# 不过需要注意大部分Ingress控制器都不是直接转发到Service
# 而是只是通过Service来获取后端的Endpoints列表,直接转发到Pod,这样可以减少网络跳转,提高性能
执行kubectl get ingress -n ingress-nginx
查看Ingress:
NAME CLASS HOSTS ADDRESS PORTS AGE
my-nginx nginx ngdemo.qikqiak.com localhost 80 94m
可以看到Ingress控制器地址为localhost,修改/etc/hosts
,将域名ngdemo.qikqiak.com
指向本地。然后在浏览器访问该域名,结果如下:
访问该域名,实际上就是访问Ingress控制器,Ingress控制器根据Ingress配置的规则,将请求转发给my-nginx服务,从而返回该页面。
下面是一个最简单的Ingress定义:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-example
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
如上所示,Ingress spec中包含配置loadbalancer
或proxy server
的所有信息。最重要的是,它包含了一个匹配所有入站请求的规则列表。目前,Ingress只支持HTTP(S)请求。
如果省略ingressClassName
,那么你应该定义一个默认的Ingress class
。
当然也有一些Ingress控制器不需要默认的IngressClass
。比如Ingress-NGINX
,它可以通过参数--watch-ingress-without-class
来配置,不过仍然推荐指定一个默认IngressClass
。
每个Http规则包含以下内容:
/testpath
)。每个路径都有一个与之关联的,由service.name
和service.port.name
或service.port.number
定义的后端服务(如test:80
)。只有当host和path都匹配了,才能被负载均衡器转发给对应的服务。通常会在Ingress 控制器中配置一个默认的后端,以便没有匹配入站规则的请求被转发给这个默认后端。
默认后端
如果没有设置Ingress规则,则所有的流量将被发送到默认后端 defaultBackend
,也就是说,如果没有设置rules
,则defaultBackend
是必须有的。注意,默认后端通常是Ingress控制器的常规配置项,而不是在Ingress中指定。
默认后端用于处理未匹配任何规则的请求,如果没有指定默认后端,则这些请求将被转发到Ingress控制器。
资源后端
事实上后端除了引用一个Service之外,还可以通过一个resource资源进行关联,这也是所谓的资源后端。资源后端是一个对象引用,指向同一个命名空间下的另一个k8s资源。资源后端和服务后端是互斥的,在二者均被设置时会无法通过合法性检查。
资源后端的一种常见用法是将所有入站数据转发到对象存储后端进行存储。
下面是一个资源Ingress:
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之后,你可以通过describe命令查看:
kubectl describe ingress ingress-resource-backend
Name: ingress-resource-backend
Labels:
Namespace: default
Address:
Ingress Class:
Default backend: APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
Host Path Backends
---- ---- --------
*
/icons APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:
Events:
该Ingress对象表明所有/icons
请求会被路由到相同命名空间下名为icon-assets
的StorageBucket
资源中去进行处理。
路径类型
Ingress中的每个路径都需要有对应的路径类型,没有路径类型是不合法的。当前支持的路径类型有三种:
/
分隔的URL路径前缀匹配,大小写敏感下面是一个匹配示例
路径类型 | 匹配路径 | 请求路径 | 是否匹配 |
---|---|---|---|
Prefix | / | (all paths) | 是 |
Prefix | /foo | /foo | 是 |
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 | 否 |
Mixed | /foo (Prefix), /foo (Exact) | /foo | 是,严格匹配优先 |
多重匹配
在某些情况下,Ingress中的多条路径会匹配同一请求,这种情况下最长的匹配路径优先。如果有两条相同的匹配路径,则精确路径优先于前缀路径。
主机名通配符
主机名可以精确匹配,也可以通过通配符来匹配。
下面是一个通配符匹配示例:
host | host header | match? |
---|---|---|
*.foo.com | bar.foo.com | 基于相同的后缀匹配 |
*.foo.com | baz.bar.foo.com | 不匹配 |
*.foo.com | foo.com | 不匹配 |
在上文中说过,如果在Ingress定义时省略了ingressClassName
,那么需要定义一个默认的Ingress class
。在Ingress class中,定义了实现Ingress的控制器。下面是一个示例:
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
其中,比较重要的属性是metadata.name
和spec.controller
,前者是这个Ingress Class的名称,也就是Ingress中的spec.ingressClassName
,后者是Ingress Controller的名称。另外,parameters
字段可用于引用其他资源以提供额外的配置,其类型由controller决定。
默认的Ingress Class用于处理所有没有指定Ingress Class的Ingress资源,只需要将ingressclass.kubernetes.io/is-default-class
设置为true
即可。
注意,当存在多个默认Ingress Class时,新的Ingress如果没有指定
ingressClassName
则不会被允许创建。解决这个问题只需要确保集群中最多只能有一个默认的IngressClass。
多个Ingress Controller
除了可能会有多个不同类型的Ingress Controller外,还可能存在多个相同的Ingress Controller,比如部署了两个NGINX Ingress Controller,一个负载处理外网访问,一个负责处理内网访问。
此时也可以通过上面的方式,为每个Controller指定唯一的一个class。
集群作用域和命名空间作用域
如果设置了spec.parameters
字段且未设置spec.parameters.scope
字段,或者将spec.parameters.scope
字段设置为Cluster时,则该IngressClass所引用的是一个集群作用域的资源。
除了集群作用域之外,Ingrss Class还可以设置Namespace
作用域。
单服务Ingress
暴露单个服务的Ingress,如下所示:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
defaultBackend:
service:
name: test
port:
number: 80
Simple fanout
简单扇出,也可以说是多服务Ingress。它可以将来自同一IP地址的流量路由到多个Service。如下图所示:
对应的Ingress如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /bar
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080
基于名称的虚拟托管
基于名称的虚拟主机支持将针对多个主机名的HTTP流量路由到同一IP地址上。如下图所示:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: bar.foo.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
如果创建的Ingress资源没有在rules中定义任何的hosts,则可以匹配指向Ingress控制器ip地址的任何网络流量,而无需基于名称的虚拟主机。
你可以通过设定包含TLS私钥和证书的Secret来保护Ingress。
你可以通过以下几种方式获得证书:
当你把TLS证书作为Kubernetes Secret添加到Ingress里去时,Ingress 控制器就能获取到证书,并使其变成自己的配置信息。
在Ingress-Nginx控制器中,TLS证书就是由nginx.conf动态地处理:
ssl_certificate_by_lua_block {
certificate.call()
}
TLS Secret的数据中必须包含以tls.crt保存的证书和以tls.key保存的私钥。例如:
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: base64 编码的证书
tls.key: base64 编码的私钥
type: kubernetes.io/tls
把TLS证书添加到Ingress中:
spec:
ingressClassName: nginx
tls:
- hosts:
- foo.bar.com
secretName: testsecret-tls
rules:
- hosts: "foo.bar.com"
http:
paths:
...
注意,默认规则上无法使用 TLS,因为需要为所有可能的子域名发放证书。 因此,tls 字段中的 hosts 的取值需要与 rules 字段中的 host 完全匹配。
默认情况下,TLS连接终止与Ingress节点,也就是说,Ingress控制器与Service及其Pod之间的流量都以明文传输。
如果想要全程加密,可以通过在ingress controller中添加支持的annotation。例如如果使用的是Ingress-Nginx控制器,可以在Ingress中添加注解:nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
各种Ingress控制器所支持的TLS功能之间存在差异。
Ingress控制器启动时会使用一些适用于所有Ingress的负载均衡策略设置,例如负载均衡算法等。更高级的负载均衡概念(例如持久会话、动态权重)尚未通过 Ingress 公开。
值得注意的是,健康检查不是通过Ingress直接暴露的。在Kubernetes中存在并行的概念,比如就绪检查,允许你实现相同的目的。
Ingress是7层的负载均衡,负载将HTTP/HTTPS请求路由到Service。接下来再由service通过iptables来将请求转发到具体的pod上[3]。
[1]. https://kubernetes.io/docs/concepts/services-networking/ingress/
[2]. https://cloud.tencent.com/developer/article/1925519
[3]. https://tech.citahub.com/k8s-service-iptables/