基于 Kubernetes 1.22 版本
一、基础概念
Ingress 用于管理对集群中服务的外部访问,它可以提供负载均衡、SSL 加密和基于名称的虚拟主机。它可以将不同 URL 的访问请求转发到后端不同的 Service ,以实现 HTTP 层的业务路由机制,流量路由由 Ingress 资源上定义的规则控制。
使用 Ingress 进行负载分发时, Ingress 控制器将基于 Ingress 规则将客户端请求直接转发到 Service 对应的后端 Endpoint (即 Pod )上,这样会跳过 kube-proxy 的转发功能, kube-proxy 不再起作用。如果 Ingress Controller 提供的是对外服务,则实际上实现的是边缘路由器的功能。
在使用 Ingress 工作之前,集群必须有一个 Ingress 控制器在运行。常用的有 Nginx、HAProxy、Istio、Traefik 等,这里我选择 Nginx-Ingress。
Ingress 官方文档:https://kubernetes.io/docs/concepts/services-networking/ingress/
Nginx-Ingress 相关文档:https://kubernetes.github.io/ingress-nginx/、https://github.com/kubernetes/ingress-nginx
二、基本使用
安装 Nginx-Ingress:
# 安装 Nginx-Ingress 控制器
[root@master ~]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml
# 检查安装是否成功
[root@master ~]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create--1-8m7rj 0/1 Completed 0 33s
ingress-nginx-admission-patch--1-j8sdk 0/1 Completed 1 33s
ingress-nginx-controller-644555766d-2mc5c 1/1 Running 0 33s
测试:
# 创建一个简单的 Web 服务器和相关的服务:
[root@master ~]# kubectl create deployment nginx-test --image=nginx --port=80
deployment.apps/nginx created
[root@master ~]# kubectl expose deployment nginx-test
service/nginx exposed
# 定义一个ingress
[root@master ~]# vim nginx-test-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ngix-test-ingress
spec:
ingressClassName: nginx
rules:
- host: www.test.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-test
port:
number: 80
[root@master ~]# kubectl apply -f nginx-test-ingress.yaml
ingress.networking.k8s.io/ngix-test-ingress created
[root@master ~]# kubectl get ingress ngix-test-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ngix-test-ingress nginx www.test.com 80 8s
[root@master ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.1.34.184 <none> 80:30912/TCP,443:30820/TCP 130m
ingress-nginx-controller-admission ClusterIP 10.1.189.224 <none> 443/TCP 130m
三、定义详解
Ingress 定义说明官方文档:https://kubernetes.io/docs/reference/kubernetes-api/service-resources/
我们来详解下上述例子中 Igress 定义:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ngix-test-ingress
spec:
ingressClassName: nginx # Ingress控制器名称
rules: # Ingress主机规则列表,如果未指定或没有规则匹配,则所有流量都将发送到默认后端。
- host: www.test.com # 主机名称
http: # 指向后端的http选择器列表
paths: # 将请求映射到后端的路径集合
- path: / # 路径
pathType: Prefix # 路径类型
backend: # 后端,定义了流量将转发到的服务端点
service: # 服务名称
name: nginx-test
port: # 服务端口
number: 80
1、路径
注意 Ingress path 定义需要与后端真实 Service 提供的 path 一致,否则将会转发到一个不存在的 path 上引发 404 错误。
路径类型(pathType):
• Prefix:基于 URL 路径前缀按 / 分割的匹配。匹配区分大小写,并按路径元素逐个进行。路径元素是指路径中由 / 分隔符分割的标签列表
• Exact:精确匹配 URL 路径并区分大小写。
• ImplementationSpecific:对于这种路径类型,匹配取决于 IngressClass 类型。可以将其视为单独的路径类型,或将其视为与 Prefix 或 Exact 路径类型相同。
路径匹配示例:
路径类型 | 路径 | 请求路径 | 是否匹配 |
---|---|---|---|
Prefix | / | (all paths) | Yes |
Exact | /foo | /foo | Yes |
Exact | /foo | /bar | No |
Exact | /foo | /foo/ | No |
Exact | /foo/ | /foo | No |
Prefix | /foo | /foo, /foo/ | Yes |
Prefix | /foo/ | /foo, /foo/ | Yes |
Prefix | /aaa/bb | /aaa/bbb | No |
Prefix | /aaa/bbb | /aaa/bbb | Yes |
Prefix | /aaa/bbb/ | /aaa/bbb | Yes,忽略尾部斜杠 |
Prefix | /aaa/bbb | /aaa/bbb/ | Yes,匹配尾部斜杠 |
Prefix | /aaa/bbb | /aaa/bbb/ccc | Yes,匹配子路径 |
Prefix | /aaa/bbb | /aaa/bbbxyz | No,不匹配字符串前缀 |
Prefix | /, /aaa | /aaa/ccc | Yes,匹配/aaa前缀 |
Prefix | /, /aaa, /aaa/bbb | /aaa/bbb | Yes,匹配/aaa/bbb前缀 |
Prefix | /, /aaa, /aaa/bbb | /ccc | Yes,匹配/前缀 |
Prefix | /aaa | /ccc | No,使用默认后端 |
Mixed | /foo (Prefix), /foo (Exact) | /foo | Yes,Exact优先匹配 |
在某些情况下,一个 Ingress 中的多个路径会匹配一个请求。在这些情况下,最长的匹配路径将首先获得优先权。如果两个路径仍然相等匹配,则优先于具有确切路径类型的路径而不是前缀路径类型。
主机可以是精确匹配(例如"foo.bar.com")或通配符(例如"*.foo.com")。精确匹配要求 HTTP host 标头与 host 字段匹配。通配符匹配要求 HTTP host 标头等于通配符规则的后缀。
主机 | 主机头 | 是否匹配 |
---|---|---|
.foo.com | bar.foo.com | 基于共享后缀的匹配 |
.foo.com | baz.bar.foo.com | 不匹配,通配符仅涵盖单个 DNS 标签 |
*.foo.com | foo.com | 不匹配,通配符仅涵盖单个 DNS 标签 |
四、Ingress 策略配置
创建两个不同版本的Web服务器和相关的服务用于以下示例测试,为了方便就不配置后端服务的 path 了,以版本区分(默认的 404 页面会显示 Nginx 服务器的版本号)。
[root@master ingress]# kubectl create deployment service1 --image=nginx:1.18.0 --port=80
[root@master ingress]# kubectl create deployment service2 --image=nginx:1.20.0 --port=80
[root@master ingress]# kubectl expose deployment service1
[root@master ingress]# kubectl expose deployment service2
[root@master ingress]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service1 ClusterIP 10.1.34.114 <none> 80/TCP 7m20s
service2 ClusterIP 10.1.111.52 <none> 80/TCP 7m19s
[root@master ingress]# kubectl get svc -n=ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.1.34.184 <none> 80:30912/TCP,443:30820/TCP 6d22h
ingress-nginx-controller-admission ClusterIP 10.1.189.224 <none> 443/TCP 6d22h
1、基于URL的相同虚拟主机
即同一虚拟主机下,将不同 URL 路径被转发到不同的服务上。这种配置常用于一个网站通过不同的路径提供不同的服务,例如:
# 定义ingress
[root@master ingress]# vim url.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: url-test
spec:
ingressClassName: nginx
rules:
- host: foo.bar.com
http:
paths:
- path: /18
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
- path: /20
pathType: Prefix
backend:
service:
name: service2
port:
number: 80
[root@master ingress]# kubectl apply -f url.yaml
[root@master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
url-test nginx foo.bar.com 10.0.0.102 80 10m
[root@master ~]# curl -sH 'Host:foo.bar.com' http://10.0.0.100:30912/18 | grep nginx
<hr><center>nginx/1.18.0</center>
[root@master ~]# curl -sH 'Host:foo.bar.com' http://10.0.0.100:30912/20 | grep nginx
<hr><center>nginx/1.20.0</center>
2、基于名称的虚拟主机
即不同的虚拟主机被转发到不同的服务上。这种配置常用于一个网站通过不同虚拟主机提供不同的服务,例如:
[root@master ingress]# vim name.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-test
spec:
ingressClassName: nginx
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
[root@master ingress]# kubectl apply -f name.yaml
[root@master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
name-test nginx foo.bar.com,bar.foo.com 80 21s
[root@master ingress]# curl -sH 'Host:foo.bar.com' http://10.0.0.100:30912/version | grep nginx
<hr><center>nginx/1.18.0</center>
[root@master ingress]# curl -sH 'Host:bar.foo.com' http://10.0.0.100:30912/version | grep nginx
<hr><center>nginx/1.20.0</center>
3、无虚拟主机
没有在规则中定义任何虚拟主机,则可以匹配到 Ingress 控制器 IP 地址的任何 Web 流量,而无需基于名称的虚拟主机。
以下将没有在请求中定义主机名的 IP 地址的任何流量(即没有提供请求标头)到转发 service1。
[root@master ingress]# vim no.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: noname-test
spec:
ingressClassName: nginx
rules:
- http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
[root@master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
noname-test nginx * 10.0.0.102 80 8s
[root@master ingress]# curl -s 10.0.0.100:30912/1 | grep nginx
<hr><center>nginx/1.18.0</center>
五、TLS
我们可以给 Ingress 中的域名设置 TLS 安全证书,使得 Ingress 可以提供 HTTPS 安全访问。
设置的步骤如下:
• 准备好证书文件。
• 将证书保存到 Kubemetes 的一个 Secret 资源对象上。
• 将该 Secret 对象设置到 Ingress 中。
Ingress 资源仅支持单个 TLS 端口 443,并假设 TLS 在入口点终止(服务及其 Pod 的流量为明文)。
如果 Ingress 中的 TLS 配置部分指定了不同的主机,它们会根据通过 SNI TLS 扩展指定的主机名(前提是 Ingress 控制器支持 SNI)在同一端口上复用。
TLS Secret 必须包含已命名的密钥 tls.crt、tls.key,其中包含用于 TLS 的证书和私钥。
Secret 定义方式:
1、命令定义
kubectl create secret tls secret-test --key tls.key --cert tls.crt
2、文件定义
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
type: kubernetes.io/tls
1、单域名
创建自签名证书文件
[root@master ingress]# openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout tls-test-www.key -out tls-test-www.crt -subj "/CN=www.test.com"
[root@master ingress]# ll tls*
-rw-r--r-- 1 root root 1103 Nov 22 23:45 tls-test-www.crt
-rw-r--r-- 1 root root 1708 Nov 22 23:45 tls-test-www.key
选项说明:
-x509:输出x509结构而不是证书
-nodes:不要加密输出密钥
-days:-x509生成的证书有效的天数。
-newkey:生成大小为多少位的RSA密钥
-keyout:要将密钥发送到的文件
-out:输出到的文件
-subj:设置或修改请求主题
创建Secret
[root@master ingress]# kubectl create secret tls test-www --key tls-test-www.key --cert tls-test-www.crt
[root@master ingress]# kubectl get secrets
NAME TYPE DATA AGE
test-www kubernetes.io/tls 2 34s
定义ingress
[root@master ingress]# vim tls-single.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-single
spec:
ingressClassName: nginx
tls:
- hosts:
- www.test.com
secretName: test-www
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
[root@master ingress]# kubectl apply -f tls-single.yaml
[root@master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
tls-single nginx www.test.com 80, 443 10s
[root@master ingress]# curl -skH 'Host:www.test.com' 'https://10.0.0.100:30820/1' | grep nginx
<hr><center>nginx/1.18.0</center>
2、多域名
(1)单域名证书
创建自签名证书和 Secret:
[root@master ingress]# openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout tls-test-login.key -out tls-test-login.crt -subj "/CN=login.test.com"
[root@master ingress]# kubectl create secret tls test-login --key tls-test-login.key --cert tls-test-login.crt
[root@master ingress]# kubectl get secrets
NAME TYPE DATA AGE
test-login kubernetes.io/tls 2 3s
test-www kubernetes.io/tls 2 9m24s
定义ingress:
[root@master ingress]# vim tls-multiple1.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-multiple1
spec:
ingressClassName: nginx
tls:
- hosts:
- www.test.com
secretName: test-www
- hosts:
- login.test.com
secretName: test-login
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: login.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
[root@master ingress]# kubectl apply -f tls-multiple1.yaml
[root@master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
tls-multiple1 nginx www.test.com,login.test.com 80, 443 6s
[root@master ingress]# curl -skH 'Host:www.test.com' 'https://10.0.0.100:30820/1' | grep nginx
<hr><center>nginx/1.18.0</center>
[root@master ingress]# curl -skH 'Host:login.test.com' 'https://10.0.0.100:30820/1' | grep nginx
<hr><center>nginx/1.20.0</center>
(2)多域名证书
创建自签名多域名证书:
1、创建多域名配置文件
[root@master ingress]# cat test.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
commonName = *.test.com
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.test.com
DNS.2 = login.test.com
2、生成csr文件
# 创建私钥
[root@master cert]# openssl genrsa -out test.key 2048
# 生成csr文件
[root@master cert]# openssl req -new -nodes -key test.key -out test.csr -config test.conf
3、生成自签名证书
[root@master cert]# openssl x509 -req -days 3650 -in test.csr -signkey test.key -out test.cert -extensions v3_req -extfile test.conf
Signature ok
subject=/CN=*.test.com
Getting Private key
[root@master cert]# openssl x509 -text -noout -in test.cert | grep -i dns
DNS:www.test.com, DNS:login.test.com
4、私钥/CSR/证书匹配校验
# 输出Modulus值要相同
openssl x509 -noout -modulus -in test.cert
openssl rsa -noout -modulus -in test.key
openssl req -noout -modulus -in test.csr
定义ingress:
[root@master ingress]# kubectl create secret tls test --key cert/test.key --cert cert/test.cert
[root@master ingress]# vim tls-multiple2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-multiple2
spec:
ingressClassName: nginx
tls:
- hosts:
- www.test.com
- login.test.com
secretName: test
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: login.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
[root@master ingress]# kubectl apply -f tls-multiple2.yaml
[root@master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
tls-multiple2 nginx www.test.com,login.test.com 10.0.0.102 80, 443 12s
[root@master ingress]# curl -skH 'Host:www.test.com' 'https://10.0.0.100:30820/1' | grep nginx
<hr><center>nginx/1.18.0</center>
[root@master ingress]# curl -skH 'Host:login.test.com' 'https://10.0.0.100:30820/1' | grep nginx
<hr><center>nginx/1.20.0</center>
参考文章:
https://kubernetes.io/docs/
《Kubernetes 权威指南》