原创内容:转载请注明出处
尽管通过 NodePort 访问的操作尽管简单,但同时也存在着致命的问题:
通过云服务商的 LB 功能也可以达到相应的效果,同时也有一些小问题:
Ingress 事实上不是一种服务类型,它处于多个服务的前端,扮演着“智能路由”或者集群入口的角色
所以 Ingress 的基本功能包含了以下功能:
Ingress 资源是在 Kubernetes V1.1 中引入的
Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制
举个简单的例子, 我们从对应的 ingress 发送流量到对应的 Service 中
在开始前先描述下 ingress 和 ingress-controllers 的区别:
针对 ingress-class 我们再补充几点:
试想一下,当我们的集群中存在多个 ingress-controllers (可以是不同的 控制器类型或者是同一种控制器但全局配置不同); 那么当前配置的这个 ingress 该交给谁来处理和解释呢?
这个时候就可以通过 ingress-class 来区分对应的ingress解析工作交给谁去处理
从 1.14 开始必须要设置对应的 IngressClass, 否则会报错
在 Kubernetes 1.18 版本引入 IngressClass 资源和 ingressClassName 字段之前,Ingress 类是通过 Ingress 中的一个 kubernetes.io/ingress.class 注解来指定的
近期针对 K8S ingress-nginx 的对应版本的变化
ingress-nginx 源码阅读链接 — 入口函数
Dockerfile 脚本内容
Dockerfile 脚本内容
ingress-controllers 创建成功后,会自动的在集群中创建指定数量的 Pod
此刻我们进入 Pod 中, 查看对应的进程树信息
bash-5.1$ pstree
dumb-init---nginx-ingress-c-+-nginx-+-2*[nginx---32*[{nginx}]]
| `-nginx
`-14*[{nginx-ingress-c}]
bash-5.1$ pstree -p
dumb-init(1)---nginx-ingress-c(7)-+-nginx(27)-+-nginx(31)-+-{nginx}(67)
| | |-{.....}(68)
| | `-{nginx}(98)
| |-nginx(32)-+-{nginx}(35)
| | |-{.....}(36)
| | `-{nginx}(66)
| `-nginx(33)
|-{nginx-ingress-c}(8)
|-{.......}(9)
`-{nginx-ingress-c}(99)
bash-5.1$ ps
PID USER TIME COMMAND
1 www-data 0:00 /usr/bin/dumb-init -- /nginx-ingress-controller --election-id=ingress-controller-leader-nginx --ingress-class=nginx --watch-ingress-without-class --controller-class=k8s.io/ingress-nginx --configmap=kube-system/nginx
8 www-data 1:36 /nginx-ingress-controller --election-id=ingress-controller-leader-nginx --ingress-class=nginx --watch-ingress-without-class --controller-class=k8s.io/ingress-nginx --configmap=kube-system/nginx-configuration --tcp-s
27 www-data 0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
138 www-data 0:58 nginx: worker process
139 www-data 1:02 nginx: worker process
140 www-data 0:00 nginx: cache manager process
259 www-data 0:00 /bin/sh -c TERM=xterm-256color; export TERM; LANG=C.UTF-8; export LANG; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) || exec /bin/sh
265 www-data 0:00 /bin/bash
266 www-data 0:00 ps
如果获取不到 PPID 我们可以通过 top 指令来查看
根据上面得到的信息,我们就可以绘制出如下图所示的内容:
程序通过 dumb-init 启动了 nginx-ingress-controller, nginx-ingress-controller 唤起 nginx
针对nginx的相关进程启动顺序,和运行模式这里不多做介绍
相关资料查阅关键字: master-worker 进程模型
nginx-ingress-c 通过与 api server
交互,监听存储于 etcd 中的 ingress、service、endpoint 等资源的变化
当数据产生变化后则会自动的更新 nginx 对应的配置信息,并 reload nginx 进程; 在此过程中, Pod 和 容器都不会重新创建、重启
程序运行后,主程序开启两个协程用于处理同步的数据:
Store
和 api server
建立连接,监听 ingress 等资源的变化;当变化到达的时候,会写入一个名为 updateChannel 的通道,主程序消费数据并向同步队列追加同步任务(隔离开可以更好的对数据进行缓冲和处理);SyncQueue
定期的拉取处理任务,并向 Store
进行查询对应的 ingress/Service 的详细信息,根据模板生成对应的 nginx.conf 数据;根据变更的数据,判断是否执行重启操作
kubernetes中informer机制基础设计原理
ingress-nginx nginx.conf 模板列表
客户端首先通过DNS查找,DNS 返回对应的 IP 地址(前方可能存在防火墙、4层高可用代理); 客户端接着向 Ingress控制器 发送 HTTP 请求,并在 Host 头中指定对应的域名 a.domain.com. 控制器从该头部确认该客户端访问哪个服务,通过与服务关联的 Endpoint 对象查看 Pod IP,并将请求转发给其中一个 Pod
在 1.9+ 相关的资源配置清单中,会存在差异
我们可以通过相关命令查看对应的资源类型
[root@simple-master-100 ~]# kubectl api-resources|grep ingress
ingresses ing extensions/v1beta1 true Ingress
ingressclasses networking.k8s.io/v1 false IngressClass
ingresses ing networking.k8s.io/v1 true Ingress
此处针对 ingress 的资源清单在不同版本中对应的配置不同;重点就在于 后端服务和资源的配置区别 ingresses.spec.rules.http.paths.backend
[root@simple-master-100 ~]# kubectl explain ingresses.spec.rules.http.paths.backend
KIND: Ingress
VERSION: networking.k8s.io/v1
RESOURCE: backend
IngressClass 常用注解
注解名 | 字段类型 | 说明 |
---|---|---|
ingressclass.kubernetes.io/is-default-class | bool | 默认 IngressClass 所有未指定 IngressClass 的 Ingress 都会使用这个 Class 解析 |
Ingress-nginx 外部认证的注解说明
Ingress-nginx 注解说明 — annotations
注解名 | 字段类型 | 说明 |
---|---|---|
kubernetes.io/ingress.class | string | 多 Ingress controllers 时用于指定ingress制器类型 1.14 + 必须设置 1.18 + 兼容待废弃 v1.22+ 废弃 |
nginx.ingress.kubernetes.io/auth-url | string | 外部认证 URL 地址 |
nginx.ingress.kubernetes.io/auth-method | string | 外部认证 URL 发起请求的方式 |
nginx.ingress.kubernetes.io/auth-response-headers | string | 透传到后端的 Header 头 |
nginx.ingress.kubernetes.io/enable-cors | bool | 启用跨域资源共享 (CORS) |
nginx.ingress.kubernetes.io/cors-allow-methods | string | 跨域资源共享 方法 |
nginx.ingress.kubernetes.io/cors-allow-headers | string | 跨域资源共享 标头 |
nginx.ingress.kubernetes.io/cors-allow-origin | string | 跨域资源共享 源 |
nginx.ingress.kubernetes.io/configuration-snippet | string | nginx.conf 下 location 配置片段 |
nginx.ingress.kubernetes.io/server-snippet | string | nginx.conf 下 server 配置片段 |
lua-nginx-module 文档索引
OpenResty 官方网站
引用文档的介绍
ngx_http_lua_module - 将 Lua 的强大功能嵌入到 Nginx HTTP 服务器中(将LuaJIT 2.0/2.1嵌入到 Nginx 中)
该模块是OpenResty的核心组件。如果你在使用这个模块,那么你实际上是在使用 OpenResty
该模块不随 Nginx 源一起分发。请参阅安装说明
这是 OpenResty 的核心组件。如果您使用的是这个模块,那么您实际上是在使用 OpenResty
特殊说明
在原生的 Nginx 中安装这个模块其实是非常麻烦的
如果没有特殊的必要,建议直接使用 OpenResty
openresty 指令说明
lua_need_request_body 参数说明
body_filter_by_lua_block 过滤器说明
整个请求的生命周期分为四个阶段:
在各个阶段中都设置了对应的钩子(过滤器), 我们可以通过在不同的过滤器中设置对应功能块代码来达到相应的处理目的
特别注意:
所有 *_block 的指令都是阻塞 I/O 模式;他们不支持 非阻塞 I/O;
所以我们要尽量避免在代码块中使用一些耗时的处理逻辑针对不同的阶段,可以使用的函数和变量都不同;
应该注意文档对其变量的说明
ingress-nginx 自定义插件开发文档说明
ingress-nginx 默认nginx.conf模板
根据手册我们了解到 ingress-nginx
总共支持五种对应的过滤器
上图展示了各个阶段的过滤器在一次请求中所处的位置
特别说明的是,init_worker
是在 Nginx 的 Worker进程
初始化的时候工作的,和一次请求的维度不同;所以没有特别的标注出来
那么我们在对应的阶段需要常见的是做些什么呢?
过滤器 | 常见场景 |
---|---|
init_worker | 初始化全局常量、变量;做一些全局性的工作 |
rewrite | 修改请求、更改标头、重定向、丢弃请求、进行身份验证等 |
header_filter | 修改响应头 |
body_filter | 修改、记录响应内容,隐私保护等 |
log | 日志记录,日志格式化处理 |
ingress-nginx 插件包源码
ingress-nginx 生成 nginx.conf 默认模板
lua require 函数文档说明
通过上面的描述,我们知道 ingress-nginx-controllers
会默认的根据对应的nginx.conf 模板生成nginx.conf
配置文件
首先查看 nginx.conf 模板 载入插件包的过程和相关代码
// 载入插件扩展包
ok, res = pcall(require, "plugins")
if not ok then
error("require failed: " .. tostring(res))
else
plugins = res
end
-- 初始化插件扩展包
-- 遍历配置 $cfg.Plugins 载入插件
plugins.init({ {{ range $idx, $plugin := $cfg.Plugins }}{{ if $idx }},{{ end }}{{ $plugin | quote }}{{ end }} })
接下来插件包会根据对应的名称查找和载入响应的插件
local function load_plugin(name)
-- 格式化插件名
local path = string_format("plugins.%s.main", name)
-- 按照路径 plugins/{plugins-name}/main.lua 查找文件
local ok, plugin = pcall(require, path)
if not ok then
-- 打印日志
ngx_log(ERR, string_format("error loading plugin \"%s\": %s", path, plugin))
return
end
-- 赋值默认插件名
local index = #plugins
if (plugin.name == nil or plugin.name == '') then
plugin.name = name
end
-- 写入变量
plugins[index + 1] = plugin
end
在 nginx.conf 模板 调用处查看到,这里调用了 plugins.run()
方法
header_filter_by_lua_block {
lua_ingress.header()
plugins.run()
}
看看插件包是如何循环遍历的
function _M.run()
-- 获取当前执行阶段
local phase = ngx.get_phase()
-- 顺序遍历插件集合
for _, plugin in ipairs(plugins) do
-- 存在指定阶段函数
if plugin[phase] then
ngx_log(INFO, string_format("running plugin \"%s\" in phase \"%s\"", plugin.name, phase))
-- 执行调用成员函数
local ok, err = pcall(plugin[phase])
if not ok then
ngx_log(ERR, string_format("error while running plugin \"%s\" in phase \"%s\": %s",
plugin.name, phase, err))
end
end
end
end
从上面的源码分析中我们得知几个关于插件相关的重要信息:
plugins
加载顺序执行)为了避免概念混淆,我们做如下约定
Nginx 参数lua_package_path - Lua 模块搜索路径
Nginx 参数lua_package_cpath - Lua C 模块搜索路径
lua require 功能加载路径 - LUA_PATH
在 nginx.conf 模板 我们观察到,这里设置了 lua_package_path
默认的搜寻路径
http {
lua_package_path "/etc/nginx/lua/?.lua;;";
}
stream {
lua_package_path "/etc/nginx/lua/?.lua;/etc/nginx/lua/vendor/?.lua;;";
}
所以对应的 扩展组件 可以存放在指定目录下:/etc/nginx/lua/?.lua
注意
同时为了更加符合标准,建议将 扩展组件 存放在
/etc/nginx/lua/plugins
的子目录中
第三方库,为了让全局都能使用,建议存放在 lua 的默认搜索 路径下;我们通过查看环境变量 LUA_PATH
确认对应目录
bash-5.1$ export |grep LUA
declare -x LUA_CPATH="/usr/local/lib/lua/?/?.so;/usr/local/lib/lua/?.so;;"
declare -x LUA_PATH="/usr/local/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/?.lua;;"
所以对应的 第三方库 可以存放在指定目录下:/usr/local/lib/lua/?.lua
注意
为了结构更加清晰,建议新建一个对应的目录用于存放一些网络上的第三方库
内部使用子目录进行包裹,可以更加的清晰
举个例子,需要使用到 扩展组件 unique-id
, 第三方库 snowflake
两个模块, 那么我们的目录就可以这样组织
/etc/nginx/lua/plugins
|
|-- unique-id [定义目录,满足加载规则]
| |
| |-- main.lua [扩展组件]
/usr/local/lib/lua
|
|-- github [随便自定义一个,作为存放第三方库的目录]
| |
| |-- snowflake [存放在子目录会更加清晰]
| | |
| | |-- snowflake.lua [第三方库]
ingress-nginx 扩展组件示例 - openidc
openresty lua-nginx-module 文档 - 扩展组件文档手册
正常情况下,我们可以按照如下格式来编写 扩展组件
local _M = {}
-- 按照指定阶段和对应的函数名一一对应
function _M.rewrite()
-- 业务代码
...
end
return _M
比如,官方的默认扩展组件 helloWord、openidc
内部相关的语法和注意事项可以查阅 手册文档
从前面我们知道,如果有使用到第三方组件的时候,就需要将目录拷贝到 /usr/local/lib/lua/
底下;而自定义的 扩展组件直接拷贝到 /etc/nginx/lua/plugins
底下就行了
这就是 安装插件 的全部过程了:拷贝代码到指定目录
由于,我们使用的是K8S环境,所以我们需要在本地打包一下对应的本地镜像,然后推送到远端
下方为示例的 Dockerfile
# 应用 ACK 对应的镜像文件
FROM registry.cn-hangzhou.aliyuncs.com/acs/aliyun-ingress-controller:v1.1.0-aliyun.2
# 指定工作目录到 nginx lua 脚本目录中
WORKDIR /etc/nginx/lua
# 在组件目录下创建指定的文件夹
RUN mkdir plugins/unique-id
# 拷贝第三方库到插件目录
COPY ./lib/resty/ /usr/local/lib/lua/resty/
# 拷贝扩展组件到组件目录
COPY ./unique-id ./plugins/unique-id
本项目使用的
基础镜像
为 ASK 自研的镜像文件registry.cn-hangzhou.aliyuncs.com/acs/aliyun-ingress-controller:v1.1.0-aliyun.2
如果需要本地使用, 则需要更换对应的
基础镜像
需要注意的是,
基础镜像
必须为ingress-nginx
的镜像
光安装完毕,还没有启用;所以需要在指定的 ConfigMap 中添加指定的扩展组件名来启用这个组件
在 ingress-nginx
的 configmap
中添加指定的配置项 plugins
hello_word, ....., unique-id
此时 IC 就会自动的重载 Nginx 的配置
使用部署工具安装 Kubernetes
ingress-nginx 官方部署文档 — Installation Guide
借助 阿里云 旗下相关服务 实现如下规划的项目
使用 ingress-nginx 作为唯一网关入口;
在 access 阶段,使用外部认证,解析指定 header 头认证密钥;在外部进行校验,并将数据设置到对应的 Header 头中传递给后端服务;
由于后端服务处于初期改造迁移阶段,不考虑做认证不通过的拦截;在相关位置会做备注说明
接下来根据 path 将请求分流到不同的 后端服务
path | pathType | Backend |
---|---|---|
/v3 | Prefix | api-go-v3 |
/ | Prefix | api-php-v1 |
在访问日志中增加响应的内容的数据,然后将访问日志接入到分析型数据库中进行存储
接下来会详细说明对应不同功能模块的作用和区别
使用外部 auth-service 对请求连接进行认证
使用外部 auth-service 对请求连接进行认证 – 译文
Nginx 执行阶段顺序
Nginx 外部认证模块文档
请认证观看完上方文章后,再阅读下方文章
Nginx 在处理外部认证使用了 ngx_http_auth_request_module
模块
ingress 控制器通过注解,生成对应的配置信息;在 location 处根据配置生成了对应的代理转发配置
Nginx 在处理连接时,按照对应流程,在 Access 处的 Auth_request 流程对其进行请求转发和判断
ingress-nginx 会根据对应的配置生成如下配置信息
完整配置如下:
location = /_external-auth-L3YzLw-Prefix {
internal;
# ngx_auth_request module overrides variables in the parent request,
# therefore we have to explicitly set this variable again so that when the parent request
# resumes it has the correct value set for this variable so that Lua can pick backend correctly
set $proxy_upstream_name "default-api-go-v3-80";
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-Proto "";
proxy_set_header X-Request-ID $req_id;
proxy_method GET;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Scheme $pass_access_scheme;
proxy_set_header Host auth-token-resolve-v1.default.svc.cluster.local;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Sent-From "nginx-ingress-controller";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Auth-Request-Redirect $request_uri;
proxy_buffering off;
proxy_buffer_size 4k;
proxy_buffers 4 4k;
proxy_request_buffering on;
proxy_http_version 1.1;
proxy_ssl_server_name on;
proxy_pass_request_headers on;
client_max_body_size 20m;
# Pass the extracted client certificate to the auth provider
set $target http://auth-token-resolve-v1.default.svc.cluster.local/authenticate;
proxy_pass $target;
}
location /v3/ {
.....
# this location requires authentication
auth_request /_external-auth-L3YzLw-Prefix;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# 将解析好的 UserId 写入对应的转发头中
auth_request_set $authHeader0 $upstream_http_x_user_id;
proxy_set_header 'X-User-Id' $authHeader0;
# 这里是模拟了附加信息
auth_request_set $authHeader1 $upstream_http_x_user_attr;
proxy_set_header 'X-User-attr' $authHeader1;
....
}
DNS A记录 同步解析到防火墙中,同时负责对证书进行卸载;
相关内容可以参阅 阿里云WAF 相关文档;(本服务属于第三方服务,所以不做太多解释)
WAF 产品文档手册
WAF 域名接入
本次使用到的镜像,请移步 4.9-2 kubernetes - 自定义全局认证案例 - api-go-v3 准备说明
查看详细的内容
本次的请求链路和端口如下所示
# -------------------------------------------------------
# api-go-v3 的 SVC
# -------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: api-go-v3 # 指定对应的 SVC 名称
namespace: default # SVC 对应的命名空间
spec:
selector: # 对应的 Pod 选择器
app: api-go-v3
release: devtest
ports:
- name: http
targetPort: 9999 # 目标的 Pod 对接端口
port: 80 # 当前 SVC 开放的端口
---
# -------------------------------------------------------
# api-go-v3 的 控制器
# -------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-go-v3-pod
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: api-go-v3
release: devtest
template:
metadata:
labels:
app: api-go-v3
release: devtest
spec:
containers:
- name: web
image: registry.cn-hangzhou.aliyuncs.com/7799520/test-2022040710-api-go-v3:v1 # 容器镜像
ports:
- name: http
containerPort: 9999 # 容器端口
小贴士:
如果 SVC设置了标签选择器,K8S 会自动的创建 Endpoint
本次使用到的镜像,为默认的nginx镜像
本次的请求链路和端口如下所示
# -------------------------------------------------------
# api-php-v1 的 SVC
# -------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: api-php-v1 # 指定对应的 SVC 名称
namespace: default # SVC 对应的命名空间
spec:
selector: # 对应的 Pod 选择器
app: api-php-v1
release: devtest
ports:
- name: http
targetPort: 80 # 目标的 Pod 对接端口
port: 80 # 当前 SVC 开放的端口
---
# -------------------------------------------------------
# api-php-v1 的 控制器
# -------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-php-v1-pod
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: api-php-v1
release: devtest
template:
metadata:
labels:
app: api-php-v1
release: devtest
spec:
containers:
- name: web
image: nginx:1.20
ports:
- name: http
containerPort: 80
我们可以通过 为外部的存储服务 提供一个统一的服务名; 如果 我们需要对外部服务进行地址更改等操作,只需要对该 SVC 进行变更即可,无需对所有的服务进行重启和修改
以下两种模式,根据需要自行选择一种模式即可
使用 URI 模式进行连接的服务,可以使用 ExternalName 进行别名映射
# -------------------------------------------------------
# 第三方服务, URI 模式连接的外服服务
# -------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: token-cache # SVC 名称
namespace: default
spec:
type: ExternalName
externalName: r-bp108c1255527b14pd.redis.rds.aliyuncs.com # 外部 redis 访问地址
ports:
- name: access
port: 6379 # 内部访问端口
protocol: TCP
targetPort: 6379 # 外部 redis 访问端口
使用 IP 模式进行连接的服务,可以使用自定义Endpoints进行端口映射
# -------------------------------------------------------
# 自己类服务, IP 模式连接的外服服务
# -------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: token-cache # SVC 名称
namespace: default
spec:
ports:
- name: token-cache
port: 6379 # 服务内部使用端口
protocol: TCP
targetPort: 31079 # endpoint 流量端口
type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
name: token-cache
namespace: default
subsets:
- addresses:
- ip: 47.103.129.166 # 外部服务 IP 地址
ports:
- name: token-cache
port: 31079 # 外部服务 端口地址
protocol: TCP
本次使用到的镜像,请移步 4.9-2 kubernetes - 自定义全局认证案例 - auth-token-resolve-v1 准备说明
查看详细的内容
本次的请求链路和端口如下所示
apiVersion: v1
kind: Service
metadata:
name: auth-token-resolve-v1
namespace: default
spec:
selector:
app: auth-token-resolve-v1
release: devtest
ports:
- name: http
targetPort: 9999 # 目标的 Pod 对接端口
port: 80 # 当前 SVC 开放的端口
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-token-resolve-v1-pod
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: auth-token-resolve-v1
release: devtest
template:
metadata:
labels:
app: auth-token-resolve-v1
release: devtest
spec:
containers:
- name: web
image: registry.cn-hangzhou.aliyuncs.com/7799520/test-2022040710-auth-token-resolve-v1:v1 # 认证服务镜像
ports:
- name: http
containerPort: 9999
本次技术选型,直接采用的 Ingress-nginx 作为 Ingress控制器
由于测试环境的K8S版本为 v1.20, 生产环境版本为v1.22;且v1.22之前针对Ingress存在较大改动,废弃了对应 IngressClass 的注解,所以在部署上有区别;本文以下内容默认为最新版本的 v1.22 相关文档,默认不讨论 v1.20 的资源清单编写;
详细内容可以查询命令:
kubectl explain ingresses
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-api.7799520 # Ingress 对应名称
namespace: default
annotations:
nginx.ingress.kubernetes.io/auth-url: http://auth-token-resolve-v1.default.svc.cluster.local/authenticate
nginx.ingress.kubernetes.io/auth-response-headers: X-User-Id, X-User-attr
nginx.ingress.kubernetes.io/auth-method: GET
nginx.ingress.kubernetes.io/enable-cors: "true"
spec:
ingressClassName: nginx # 1.22+ 必须要设置 IngressClass
rules:
- host: api.7799520.com # 对应的域名
http:
paths:
- pathType: Prefix # 匹配模式采用前缀匹配
path: /v3 # 匹配的路径
backend: # 后端服务集合
service:
name: api-go-v3 # SVC 名称
port:
number: 80 # SVC 端口
- pathType: Prefix
path: /
backend:
service:
name: api-php-v1
port:
number: 80
在仔细阅读上面的步骤之后,我们执行各个yaml的运行操作,之后准备进入测试了
bash-5.1# kubectl get pods
NAME READY STATUS RESTARTS AGE
api-go-v3-pod-5d8b4cb775-fsfgq 1/1 Running 0 24h
api-php-v1-pod-6fb98dd485-q7kw9 1/1 Running 0 24h
auth-token-resolve-v1-pod-67cbf79d5c-cndvr 1/1 Running 0 19h
当看到我们的三个Pod的状态都是 Running 的时候,表示三个 Pod 所有 Pod 都在运行了
bash-5.1# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
api-go-v3 ClusterIP 172.21.0.7 80/TCP 24h
api-php-v1 ClusterIP 172.21.4.255 80/TCP 24h
auth-token-resolve-v1 ClusterIP 172.21.15.154 80/TCP 23h
token-cache ClusterIP 172.21.2.126 6379/TCP 19h
所有 SVC 都创建成功了,接下来分别测试各自的连通性
bash-5.1# curl -Lv0 api-go-v3.default.svc.cluster.local
* Trying 172.21.0.7:80...
* Connected to api-go-v3.default.svc.cluster.local (172.21.0.7) port 80 (#0)
> GET / HTTP/1.0
> Host: api-go-v3.default.svc.cluster.local
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Fri, 08 Apr 2022 03:56:24 GMT
< Content-Length: 17
< Content-Type: text/plain; charset=utf-8
<
* Closing connection 0
Other!!!!!!!!!!!!
bash-5.1# curl -Lv0 api-php-v1.default.svc.cluster.local
* Trying 172.21.4.255:80...
* Connected to api-php-v1.default.svc.cluster.local (172.21.4.255) port 80 (#0)
> GET / HTTP/1.0
> Host: api-php-v1.default.svc.cluster.local
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.20.2
< Date: Fri, 08 Apr 2022 03:57:44 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 16 Nov 2021 14:44:02 GMT
< Connection: close
< ETag: "6193c3b2-264"
< Accept-Ranges: bytes
<
........
Thank you for using nginx.
........
* Closing connection 0
此处有点不同,因为请求的认证 URL 是确定的,所以我们应该检查 Header 头是否能够正常追加信息
bash-5.1# curl -Lv0 auth-token-resolve-v1.default.svc.cluster.local/authenticate
* Trying 172.21.15.154:80...
* Connected to auth-token-resolve-v1.default.svc.cluster.local (172.21.15.154) port 80 (#0)
> GET /authenticate HTTP/1.0
> Host: auth-token-resolve-v1.default.svc.cluster.local
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< X-User-Attr: 中文附加信息测试
< X-User-Id:
< Date: Fri, 08 Apr 2022 03:59:01 GMT
< Content-Length: 2
< Content-Type: text/plain; charset=utf-8
<
* Closing connection 0
此处需要使用 redis 客户端进行连接测试连通性
bash-5.1# redis-cli -h token-cache.default.svc.cluster.local -p 6379 -a 2E97fc51
r-bp108c1255527b14.redis.rds.aliyuncs.com:6379>
打开 api-go-v3-pod 观察对应的日志信息
打开 auth-token-resolve-v1-pod 观察对应的日志信息
查看对应 ingress 控制器的端点
接下来我们使用 POSTMAN 来测试具体的请求
切换请求路径可以看到内容变更,路由更能被正常处理了
接下来我们检查token是否能被校验出来; (token 被设置为 “aaaaaa”, 所以 redis 键应该为 “test_token_aaaaaa”)
先发送一次请求看一下是否存在 Token 解析后的 X-User-Id
检查 api-go-v3-pod 日志信息,没有发现 X-User-Id
, 但附加信息追加了 X-User-Attr
此时在 redis 中增加了对应的 KEY test_token_aaaaaa
内容
再次请求,观察 api-go-v3-pod 日志, 两个参数的信息都带上了 X-User-Id
、X-User-Attr
在 ingress-nginx 中,对应的增加了一些相关的配置信息,这些配置信息默认是放在 ConfigMap 中的 nginx-configuration
nginx-ingress-controller 根据设置的配置的日志格式,生成 nginx.conf 配置
输出到 access_log
, err_log
后, 通过 logstash 收集到 sls 日志收集平台
k8s 相关 ingress 文档
ingress-nginx 官方网站
ingress-nginx Github 源码
ingress-nginx 外部认证例子
ingress-nginx 注解说明
ingress 官网说明
ingress-controllers 官网说明
ingress-nginx 参数调优相关文章
Kubernetes V1.22版本升级说明
V1.22 API 版本说明
Nginx 官方 针对 ingress-nginx 的介绍说明文档
kubernetes中informer机制基础设计原理
ingress-nginx nginx.conf 模板列表
WAF 产品文档手册
ingress-nginx 全部注解解析源码
ingress-nginx auth-url 注解解析源码
ingress-nginx store协程源码位置
Nginx 外部认证模块文档
Nginx 执行阶段顺序
使用外部 auth-service 对请求连接进行认证
使用外部 auth-service 对请求连接进行认证 – 译文
Nginx-ingress 官方文档针对插件开发的说明
openresty lua-nginx-module 使用手册
Nginx respon body filters – 应答过滤器说明
Ingress-Nginx body_filter_by_lua_block lua 插件添加到 ingress-nginx – 修复说明
Ingress-Nginx 默认 log 阶段过滤器模板 – line:1160