K8S 之 Ingress Nginx

摘要

Ingress 简介

  • Ingress(缩写为 ing) 提供从集群外部到集群内服务的 HTTP 和 HTTPS 路由(7层)。 流量路由由 Ingress 资源所定义的规则来控制。

  • 实际上 Ingress-Nginx 内置了 nginx,由 nginx 其负责接收请求并转发给后端服务。

  • 下面是 Ingress 的一个简单示例,可将所有流量都发送到同一 Service

安装 Ingress-Nginx

  • 安装时要注意 ingress-nginx 的版本和 Kubernetes 的版本兼容性,参考 Github:ingress-nginx

  • 获取对应版本的yaml文件

1
2
3
# 下载最新的 v1.13.0 版本,其支持 Kubernetes 1.33 版本
export INGRESS_NGINX_VERSION=v1.13.0
curl -L -o ingress-nginx-controller.yaml https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-${INGRESS_NGINX_VERSION}/deploy/static/provider/cloud/deploy.yaml
  • 安装 ingress-nginx

这里要注意,ingress-nginx 的 service/ingress-nginx-controller 默认使用 LoadBalancer 类型,所以需要先使 k8s 集群支持 LoadBalancer 类型,可以参考 K8S 之 Service 中 LoadBalancer 部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
$ kubectl apply -f ingress-nginx-controller.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

# 查看ingress-nginx资源
$ k get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-controller-95f6586c6-nznwq 1/1 Running 0 2m46s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.96.112.234 10.211.55.201 80:30168/TCP,443:30600/TCP 2m46s
service/ingress-nginx-controller-admission ClusterIP 10.96.245.155 <none> 443/TCP 2m46s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 2m46s

NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-95f6586c6 1 1 1 2m46s

# 查看 IngressClass 资源
$ k get ingressclasses.networking.k8s.io
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 14m

创建 Ingress 资源

  • ingress-demo.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: networking.k8s.io/v1
kind: Ingress # Ingress 资源
metadata:
name: web-ingress # Ingress 名称
namespace: default # Ingress 所在的命名空间
spec:
ingressClassName: nginx # Ingress 使用的 IngressClass
rules:
- host: nginx.hanqunfeng.com # 转发域名,支持通配符 *.hanqunfeng.com
http:
paths:
- pathType: Prefix # 路径匹配规则, 前缀匹配
path: / # 路径
backend: # 后端服务
service: # 声明后端是一个 service
name: nginx # service名称,提前创建好 service,类型可以是ClusterIP、NodePort、LoadBalancer任意,推荐ClusterIP,因为使用 ingress 的话,就没必要创建其它类型的 service 了
port: # 端口
number: 80 # service的端口
  • 查看 ingress 资源

1
2
3
$ k get ing -n ns1
NAME CLASS HOSTS ADDRESS PORTS AGE
web-ingress nginx nginx.hanqunfeng.com 10.211.55.201 80 80s
  • 访问 web-ingress

1
2
3
# 将域名 nginx.hanqunfeng.com 解析到 10.211.55.201,如果是内部域名可以通过 hosts 文件添加解析
# 只能通过 nginx.hanqunfeng.com 访问,不能通过 10.211.55.201 访问
curl http://nginx.hanqunfeng.com

让 Ingress 支持 HTTPS 访问

  • 创建证书,可以在阿里云上申请一个免费的证书

  • 创建 secret

1
2
3
4
kubectl create secret tls nginx-tls \
--key=nginx.hanqunfeng.com.key \
--cert=nginx.hanqunfeng.com.pem \
-n default
  • 也可以通过yaml创建

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata:
name: nginx-tls
namespace: default
data:
tls.crt: base64 编码的证书,而非文件路径
tls.key: base64 编码的私钥,而非文件路径
type: kubernetes.io/tls
  • 你的 Ingress 资源需要增加 tls 字段,引用一个 Secret 存储的 TLS 证书。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: Ingress # Ingress 资源
metadata:
name: web-ingress # Ingress 名称
namespace: default # Ingress 所在的命名空间
spec:
ingressClassName: nginx # Ingress 使用的 IngressClass
tls: # 配置 TLS
- hosts: # 域名列表
- nginx.hanqunfeng.com # 域名
secretName: nginx-tls # 存放证书的 Secret
rules:
- host: nginx.hanqunfeng.com #转发域名
http:
paths:
- pathType: Prefix # 路径匹配规则, 前缀匹配
path: / # 路径
backend: # 后端服务
service: # service
name: nginx # service名称
port: # 端口
number: 80 # service的端口
1
2
3
4
5
6
7
8
9
10
# 重新运行ingress
k apply -f nginx-ingress.yaml

# 查看ingress,此时多了一个 443 端口
$ k get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
web-ingress nginx nginx.hanqunfeng.com 10.211.55.201 80, 443 51s

# 访问https,但此时 80 端口访问不了,需要配置 80 端口重定向到 443
curl https://nginx.hanqunfeng.com

80端口重定向到443

  • nginx ingress controller,它内置支持重定向,你可以在 Ingress 上添加 annotation

1
2
3
metadata:
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" # 80 重定向到 443
  • 完整的yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- nginx.hanqunfeng.com
secretName: nginx-tls
rules:
- host: nginx.hanqunfeng.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: nginx
port:
number: 80
  • 访问测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 重新运行ingress
k apply -f nginx-ingress.yaml

# 默认情况下 curl 不会自动跳转,使用浏览器访问会自动重定向,308 表示永久重定向
$ curl http://nginx.hanqunfeng.com
<html>
<head><title>308 Permanent Redirect</title></head>
<body>
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx</center>
</body>
</html>

# 添加 -L 参数,curl 会自动跳转
$ curl -L http://nginx.hanqunfeng.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

后记

ssl 证书过期怎么办?

1
2
3
4
5
# apply 会直接更新 Secret,而不是删除重建
kubectl create secret tls nginx-tls \
--key=新的.key \
--cert=新的.pem \
-n default --dry-run=client -o yaml | kubectl apply -f -
  • Ingress Controller(如 nginx-ingress)通常会自动监听 Secret 变化,大概 30 秒 ~ 1 分钟内自动热加载新证书,无需重启 Pod。

pathType 可以设置哪些值?

  • 在 Kubernetes Ingress 的 pathType 字段中,一共可以设置以下三种值

pathType 值 含义 匹配规则说明
Exact 精确匹配 路径必须完全匹配
Prefix 前缀匹配 以指定路径为前缀即可匹配
ImplementationSpecific 由 Ingress Controller 自己决定 nginx 常常表现为前缀匹配
  • Exact 精确匹配,且区分大小写

1
2
3
4
# 只匹配 http://example.com/foo
# 不会匹配 /foo/abc 或 /foo/
- pathType: Exact
path: /foo
  • Prefix 前缀匹配,且区分大小写

1
2
3
4
5
6
7
# 匹配所有以 /foo 开头的路径:
# /foo
# /foo/ # 会忽略尾部的 /
# /foo/abc
# /foo/bar/test
- pathType: Prefix
path: /foo
  • ImplementationSpecific 控制器自定义(不推荐)

1
2
3
4
5
# 匹配规则由 Ingress Controller 决定;
# 在 nginx-ingress 中通常行为类似于 Prefix,但不保证兼容性;
# Kubernetes 官方不推荐生产使用,建议显式使用 Prefix 或 Exact。
- pathType: ImplementationSpecific
path: /foo

设置缺省的 ingressclass

  • 我们可以设置一个缺省的 ingressclass 为默认值,这样,当创建ingress时,如果未指定ingressclass,则使用缺省值

  • 可以通过 kubectl patch 为 ingressclass 直接打上默认标识(通过 k edit ingressclass ingressclass-name 为其添加注解也可以)

1
2
3
4
5
6
7
# 查看 ingressclasses
$ k get ingressclasses
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 3h3m

# 设置 nginx 为默认 ingressclass,就是为其加上如下注解
$ kubectl patch ingressclass nginx -p '{"metadata": {"annotations":{"ingressclass.kubernetes.io/is-default-class":"true"}}}'

ingerss-nginx 应用示例

  • 同一域名不同路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec: # 未指定 ingressClassName ,则使用 上文中设置的默认 ingressClassName
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
  • 不同域名的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 进行修改,修改保存(:wq)后会立刻生效

1
kubectl edit ingress web-ingress