1 环境准备
下面的 yaml 文件内容,是使用 sts 创建两个 web 服务,并配置对应的 servcie。web 服务的首页内容使用 configmap 配置并挂载到各自的 POD 中。
apiVersion: v1
kind: Namespace
metadata:
name: shark-test
---
apiVersion: v1
kind: ConfigMap
metadata:
namespace: shark-test
name: index.html
data:
# 类属性键;每一个键都映射到一个简单的值
web1.index.html: |
web1 站点
web2.index.html: |
web2 站点
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: shark-test
name: web1
spec:
selector:
matchLabels:
app: web1 # 必须匹配 .spec.template.metadata.labels
serviceName: "web1"
replicas: 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: web1 # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: web1
image: nginx:1.21.6-alpine
ports:
- containerPort: 80
name: web1
volumeMounts:
- name: index-html
mountPath: /usr/share/nginx/html
volumes:
- name: index-html # 给 volumeMounts[].name使用
configMap:
name: index.html # configmap 的名称
items:
- key: web1.index.html # configmap 对象中 data 中的一个 key
path: index.html # 挂载到 pod 后,被创建的文件名称
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: shark-test
name: web2
spec:
selector:
matchLabels:
app: web2 # 必须匹配 .spec.template.metadata.labels
serviceName: "web2"
replicas: 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: web2 # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: web2
image: nginx:1.21.6-alpine
ports:
- containerPort: 80
name: web2
volumeMounts:
- name: index-html
mountPath: /usr/share/nginx/html
volumes:
- name: index-html # 给 volumeMounts[].name使用
configMap:
name: index.html # configmap 的名称
items:
- key: web2.index.html # configmap 对象中 data 中的一个 key
path: index.html # 挂载到 pod 后,被创建的文件名称
---
apiVersion: v1
kind: Service
metadata:
namespace: shark-test
name: web1
labels:
app: web1
spec:
type: ClusterIP
ports:
- port: 8080
name: web1-http
targetPort: 80
clusterIP: None
selector:
app: web1
---
apiVersion: v1
kind: Service
metadata:
namespace: shark-test
name: web2
labels:
app: web2
spec:
type: ClusterIP
ports:
- port: 8080
name: web2-http
targetPort: 80
clusterIP: None
selector:
app: web2
2 什么是 Ingress
Ingress 是管理集群外部访问集群内部服务的流量的 API 对象,是 kubernetes 中对 service 的反向代理。 流量的去向由 Ingress 资源所定义的规则来控制。
可提供负载均衡、SSL 和基于名称的虚拟主机功能。
主要的访问方式是 HTTP/HTTPS,不支持四层协议。下一代替代 Ingress 的产品 Gateway API 可以实现 四层和七层协议流量管理。
Ingress 在 kubernentes v1.28 版本停止更新,并推出了可以实现更多功能的 Gateway API。
2.1 认识 Ingress 资源
Ingress 资源的 YAML 文件,可以理解为是对 Nginx 子配置文件的抽象,因为它和 nginx 中关于虚拟机主机 server 配置块的功能一致 。
下面是 Nginx 子配置文件和Ingress 的 yaml 文件的对比图。
- 红色框就是请求中的 url
- 黄色框就是 nginx 的 rewrite ,用于实现地址(url)重写,这里实现的功能是去掉请求 url 中的前缀。
- 绿色框就是 后端服务的连接信息,nginx 中是 upstream 的名称,kubernetes 中是 service 名称
- 紫色框就是后端服务的监听端口。
资源名称: Ingress 对象的命名必须是合法的 DNS 子域名名称。
- 不能超过 253 个字符
- 只能包含小写字母、数字,以及 ‘-’ 和 ‘.’
- 必须以字母数字开头
- 必须以字母数字结尾
注解(annotations): Ingress 经常使用一些注解来配置一些选项,以便扩展功能,例如rewrite-target
注解。 不同的 Ingress 控制器支持不同的注解。
规则(rule): 其中包含对所有入站请求进行匹配的规则列表。
2.2 Ingress 控制器(controller)
为了让 Ingress 对象有效的创建和工作,需要有一个 Ingress Controller 。
Ingress Controller 的角色是用一个反向代理实现的。反向代理可以是 nginx、haproxy 等其中的一个,因此 Ingress Controller 可以有很多种。
这里我用最常用的 Ingress-Nginx Controller 为例说明。
当一个 Ingress 对象被成功创建后,Ingress Controller 会把 Ingress 对象中的内容转换成 nginx 的子配置文件,并让 Ingress Controller 使用。
后续会详细介绍。
2.3 Ingress 规则
一个简单的 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: "/web1"
pathType: Prefix
backend:
service:
name: web1
port:
number: 8080
每个 HTTP 规则都包含以下信息:
- 可选的 host。在此示例中,未指定
host
,因此该规则适用于使用指定 IP 地址进行入站 HTTP 请求的情况。 如果提供了host
(例如 foo.shark.com),则 rules 适用于所指定的主机( foo.shark.com)。 path
路径列表(例如 /web1)。每个路径都有一个由service.name
和service.port.name
或service.port.number
确定的关联后端。入站请求的内容都必须与host
和path
的值相匹配,负载均衡器才会将流量引导到所引用的 Service,backend
(后端)是 Service 中所定义的 名称和端口的组合, 或者是通过 CRD 方式来实现的自定义资源后端。
通常会在 Ingress 控制器中配置 defaultBackend
(默认后端), 以便为无法与规约中任何路径匹配的所有请求提供服务,也就是通常会返回 404 页面。
2.4 pathType 路径类型
Ingress 中的每个路径都需要有对应的路径类型(Path Type)。未明确设置 pathType 的路径无法通过合法性检查。当前支持的路径类型有三种:
-
ImplementationSpecific:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者作与 Prefix 或 Exact 类型相同的处理。nginx 的 ingress 是支持
path
使用正则。 -
Exact:精确匹配 URL 路径,且区分大小写。
-
Prefix:基于以
/
分隔的 URL 路径前缀匹配。匹配区分大小写, 并且对路径中各个元素逐个执行匹配操作。 路径元素指的是由/
分隔符分隔的路径中的标签列表。
说明: 如果
path
值的最后一个元素是请求路径中最后一个元素的子字符串,则不会被视为匹配 (例如:/foo/bar 匹配 /foo/bar/baz, 但不匹配 /foo/barbaz)。
2.5 多重匹配
有的时候,一个请求会和一个 Ingress 中的多个 path
匹配,这时 path
最长者优先匹配。 如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。
2.6 Ingress 类
Ingress 可以由不同的控制器实现,通常使用不同的配置。 每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。 IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
app.kubernetes.io/component: controller
name: nginx-example
spec:
controller: k8s.io/ingress-nginx
设置默认的 Ingress 类,只需要在注解中添加:
ingressclass.kubernetes.io/is-default-class: "true"
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
app.kubernetes.io/component: controller
name: nginx-example
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx
2.7 TLS
2.7.1 生成证书
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj “/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima.com”
2.7.2 创建密钥
可以通过设定包含 TLS 私钥和证书的Secret 来保护 Ingress。TLS Secret 的数据中必须包含键名为 tls.crt
的证书和键名为 tls.key
的私钥, 才能用于 TLS 目的。例如:
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: base64 编码的证书
tls.key: base64 编码的私钥
type: kubernetes.io/tls
也可以使用下面的方法创建
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
注意,不能针对默认规则使用 TLS,因为这样做需要为所有可能的子域名签发证书。 因此,
tls
字段中的hosts
的取值需要与rules
字段中的host
完全匹配。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- https-example.foo.com
secretName: testsecret-tls
rules:
- host: https-example.foo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
3 自定义配置
有三种方式可以自定义NGINX:
-
ConfigMap:使用ConfigMap在NGINX中设置全局配置。
-
annotations 注解:如果您想要特定Ingress规则的特定配置,请使用此注释。
-
Custom NGINX template 自定义模板:当需要更具体的设置时,如open_file_cache,将侦听选项调整为rcvbuf,或者当无法通过ConfigMap更改配置时。
3.1 使用 ConfigMap(作用于全局)
ConfigMap API资源将配置数据存储为键-值对。该数据提供了nginx控制器的系统组件配置。
ConfigMap 的配置会是全局生效,就是会组用于所有使用默认值的 location 中的配置,如果某个 location 已经已经创建并且指定了自定义的值,ConfigMap 的值不会覆盖这个 location 的值。
ingress-nginx 的 controller 使用的 ConfigMap 是在 ingress-nginx controller
的 Deployment 中通过 args
参数指定的,如下所示:
spec:
containers:
- args:
- /nginx-ingress-controller
- --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
- --election-id=ingress-nginx-leader
- --controller-class=k8s.io/ingress-nginx
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller # 我在这里
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
为了覆盖 ingress-nginx 的配置值,可以在 config-map 中使用 key - value 的方式进行配置。
3.1.1 配置 ssl 算法
some-configmap.yml
kind: ConfigMap
apiVersion: v1
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
allow-snippet-annotations: "true" # 默认的 ConfigMap 中自带的,如果不设置,默认值会是 false. true 表示可以使用注解配置片段。后面会讲到。
ssl-ciphers: 'ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!eNULL:!LOW:!ADH:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS'
如上配置中,ssl-ciphers
是新增的配置,它会覆盖原有配置的值。
可以使用如下命令验证:
kubectl -n ingress-nginx exec -it ingress-nginx-controller-64946b8fcf-kr8f5 -- cat nginx.conf |grep ssl
上面命令中的 pod
ingress-nginx-controller-64946b8fcf-kr8f5
更换为你自己环境中的 pod。
如果在 configmap
中表示 true
、false
或者数字,需要加上双引号 ""
。
这里的 ConfigMap
对象的名称 ingress-nginx-controller
是固定的。
ConfigMap 中可以使用的配置选项只能是官方文档已公布的配置选项,具体可以参考这里的官方文档
3.1.2 配置全局的允许客户端请求文件大小
ConfigMap 的配置会是全局生效,就是会组用于所有使用默认值的 location 中的配置,如果某个 location 已经已经创建并且指定了自定义的值,ConfigMap 的值不会覆盖这个 location 的值。
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
allow-snippet-annotations: "true"
proxy-body-size: 1020M
3.2 使用 注解 annotations
前提条件: 需要在 Ingress-controller 对象使用的 ConfigMap 中添加 allow-snippet-annotations: "true"
安全隐患: 这将允许可创建或更新入口对象的用户可使用自定义代码段功能获取集群中的所有密钥
你可以通过在 Ingress 资源里添加不同的 annotations ,对 Ingress 扩展不同的功能。例如,地址重写。
annotations 的键和值只能是字符串。其他类型,如布尔值或数值,必须加上英文的双引号,例如“true”、“false”、“100”。
3.2.1 地址重写
可以使用如下注解,控制地址重写。
注解名称 | 描述 | 注解的值 |
---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | 必须重定向流量的目标URI | string |
nginx.ingress.kubernetes.io/ssl-redirect | 表明 location 部分是否只能通过SSL访问(当Ingress包含证书时,默认为True) | bool |
nginx.ingress.kubernetes.io/force-ssl-redirect | 即使 Ingress 未启用TLS,也强制重定向到HTTPS | bool |
nginx.ingress.kubernetes.io/app-root | 定义控制器必须将请求根 / 路径转发到此注解指定的值 | string |
nginx.ingress.kubernetes.io/use-regex | Ingress上定义的 path 的值是否可以使用正则表达式 | bool |
示例1:重定向根到指定路径
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: shark-test
name: minimal2-ingress
annotations:
nginx.ingress.kubernetes.io/app-root: /api/v1 # 重定向到 /api/v1
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: web1
port:
number: 8080
验证
示例2: 将去除前缀的地址重写(无正则)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: shark-test
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: "/web1"
pathType: Prefix
backend:
service:
name: web1
port:
number: 8080
- path: "/web2"
pathType: Prefix
backend:
service:
name: web2
port:
number: 8080
验证
示例3: 将去除前缀的地址重写(有正则)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: shark-test
name: regex-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: "/web-a(/|$)(.*)"
pathType: ImplementationSpecific # 注意这里的类型
backend:
service:
name: web1
port:
number: 8080
- path: "/web-b(/|$)(.*)"
pathType: ImplementationSpecific
backend:
service:
name: web2
port:
number: 8080
验证
3.2.2 使用配置片段 设置变量,添加请求头
使用注解,可以在服务器配置块中添加自定义配置
注解名称 | 描述 | 注解的值 |
---|---|---|
nginx.ingress.kubernetes.io/server-snippet | 向 Nginx 的 server {…} 配置块中添加配置 | string |
nginx.ingress.kubernetes.io/configuration-snippet | 向 Nginx 的 location {…} 配置块中添加配置 | string |
示例1: 向 server 配置块中添加自定义配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: shark-test
name: server-snippet-ingress
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set $cors '';
if ($http_origin ~* "^http://.*$") {
set $cors $http_origin;
}
add_header Access-Control-Allow-Origin $cors;
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
if ($request_method = 'OPTIONS') {
return 204;
}
spec:
ingressClassName: nginx
...
配置成功后,会在 Ingress controller POD 中的 /etc/nginx/nginx.conf
配置文件中出现如下配置
示例2: 向 location 配置块中添加自定义配置
会向此次yaml 文件中所有已配置的 path 中添加这些自定义配置
ingress-location-snippert.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: shark-test
name: location-snippet-ingress
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Request-Id: $req_id";
set $cors '';
if ($http_origin ~* "^http://.*$") {
set $cors $http_origin;
}
add_header Access-Control-Allow-Origin $cors;
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
if ($request_method = 'OPTIONS') {
return 204;
}
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: "/shark"
pathType: Prefix
backend:
service:
name: web1
port:
number: 8080
- path: "/xiguatian"
pathType: Prefix
backend:
service:
name: web2
port:
number: 8080
创建成功后会在 Ingress controller POD 中的 nginx.conf
中增加如下配置内容:
location ~* "^/shark" {
此处省略一万字
more_set_headers "Request-Id: $req_id";
set $cors '';
if ($http_origin ~* "^http://.*$") {
set $cors $http_origin;
}
add_header Access-Control-Allow-Origin $cors;
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://upstream_balancer;
proxy_redirect off;
}
location ~* "^/xiguatian" {
此处省略一万字
more_set_headers "Request-Id: $req_id";
set $cors '';
if ($http_origin ~* "^http://.*$") {
set $cors $http_origin;
}
add_header Access-Control-Allow-Origin $cors;
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://upstream_balancer;
proxy_redirect off;
}
3.2.3 引用自定义的 ConfigMap 添加请求头
你可以定义任意一个符合要求的 ConfigMap 名称的对象,之后在创建 ingress-nginx-controller
ConfigMpa 使用引用。例如:
custom-addheader.yml
apiVersion: v1
data:
X-Different-Name: "true"
X-Request-Start: t=${msec}
X-Using-Nginx-Controller: "true"
kind: ConfigMap
metadata:
name: custom-headers # 这个名称需要稍后使用
namespace: ingress-nginx # 这个需要和 ingress-controller 一致
注意 data
部分的内容将会是添加到 每个 location 中自定义请求头的内容。
创建这个 ConfigMap
kubectl apply -f custom-addheader.yml
此时,ingress-nginx 还不能直接使用这个 ConfigMap 中的内容。还需要在 ingress-controller 所在的 namespace (默认是 ingress-nginx)中创建一个名为 ingress-nginx-controller 的 ConfigMap, 并且 data
部分需要使用 proxy-set-headers 键,其中需要引用上面我们创建的 ConfigMap, 格式为: namespace/configmap的名称
,如下所示:
apiVersion: v1
data:
proxy-set-headers: "ingress-nginx/custom-headers" # 这里的值: custom-headers 是引用上面创建 configmap
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
3.2.4 使用 auth-url 设置额外的认证
假如你希望访问以 /shark
开头的图片资源都需要先到认证服务 shark-auth-server 的 /api/v1/auth/check
认证,认证成功并返回 2xxx
后才可以继续访问 /shark/
开头的图片资源。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: middleware
name: middle-minio
annotations:
# 设置认证 url, 因为 /auth-images 是此 ingress Pod 所设置的一个location 所以,这里使用的 127.0.0.1
nginx.ingress.kubernetes.io/auth-url: "http://127.0.0.1/auth-images"
nginx.ingress.kubernetes.io/rewrite-target: "/$1"
# nginx.ingress.kubernetes.io/configuration-snippet: |
# auth_request /auth-images;
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: "/shark/(.*)"
pathType: ImplementationSpecific
backend:
service:
name: minio
port:
number: 9000
使用注解: nginx.ingress.kubernetes.io/auth-url: "http://127.0.0.1/auth-images"
或者使用如下注解都可以实现:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request /auth-images;
同时需要添加如下 ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: spms-standard
name: spms-standard-ingress-auth
annotations:
nginx.ingress.kubernetes.io/rewrite-target: "/api/v1/auth/check"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-Original-URI $request_uri;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: "/auth-images"
pathType: Prefix
backend:
service:
name: spms-standard-support-gateway
port:
number: 8080
4 查看内存中的后端
进入 ingress-nginx-controller Pod 内执行如下命令
curl localhost:10246/configuration/backends