NGINX 动态代理传递解析器

NGINX 动态代理传递解析器

尝试使用 nginx 在 Kubernetes 中设置 api 网关。我尝试遵循单个子域模式,并使用指定服务和版本的路径。

api.domain.com/service/v0/api/resource/10 -> http://servicev0/api/resource/10

暂时忽略版本...

这确实解决了。

location ~/(?<service>(\w+))/(?<version>(v[0-9]+(\.[0-9]+)*)) {
    resolver 169.254.169.250;
    proxy_pass http://theservice;
}

这无法通过 api.domain/theservice/v0/ 解决

location ~/(?<service>(\w+))/(?<version>(v[0-9]+(\.[0-9]+)*)) {
    resolver 169.254.169.250;
    proxy_pass http://$service;
}

错误

*1 theservice could not be resolved (110: Operation timed out), 

还需要重写请求以删除$service$version

答案1

据我理解,问题的主要问题是proxy_pass指令中变量的 DNS 解析。

根据官方文档以及来自 Nginx 支持人员的评论,只有商业版的 Nginx 才支持上游动态解析。免费版的 Nginx 仅支持启动时解析。因此,要更新上游 IP 地址,您必须重新启动 nginx 进程(看起来重新加载也有效)。

nginx 进程启动时,该service变量未定义,导致解析过程中断,proxy_pass 获取到不正确的值,从而产生 502 错误。

有两种可能的方法可以实现类似于请求的结果,但有一定的缺点。

笔记:我已经调整了问题示例中的正则表达式来生成接近问题中提到的路径:

api.domain.com/service/v0/api/resource/10 -> http://servicev0/api/resource/10

有关正则表达式的详细信息,可以通过以下方式获取关联

1. 使用上游模块

缺点:您必须提前为所有可能的服务定义上游,但您可以使用正则表达式调整路径。

以下部分添加到/etc/nginx/conf.d/default.conf允许解析服务名称并将正确的路径发送到后端:

resolver 10.96.0.10;
upstream dynamic {
    server servicea0;
}

server {
...
    location ~ \/(?<service>\w+)\/v(?<version>[0-9]+(\.[0-9]+)*)(?<path>.*) {
        proxy_pass http://dynamic/$service$version$path;
    }

我使用默认的 nginx 容器进行测试。目前它的版本是 1.19.5

要重新加载容器中的 nginx 进程配置,我已将其 exec 到 Pod 中并运行:

root@nginx1-74cf9547fb-8bldg:/# nginx -s reload

以下命令也在 nginx 容器内运行以跳过网络配置详细信息:

root@nginx1-74cf9547fb-8bldg:/# curl -v 127.0.0.1/serviceA/v0/api/resource/17
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /serviceA/v0/api/resource/17 HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.64.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.19.5

# Respond from backend shows request headers
{
  "path": "/serviceA0/api/resource/17",
  "headers": {
    "host": "dynamic",
    "connection": "close",
    "user-agent": "curl/7.64.0",
    "accept": "*/*"
  },
  "method": "GET",
  "protocol": "http",

如果直接定义服务主机名proxy_pass,则它会发送不做任何更改的请求路径:

proxy_pass http://servicea0;

# Respond from backend shows request headers
{
  "path": "/serviceA/v0/api/resource/17",

如果proxy_path使用如下变量指定,它也会破坏解析过程,并且 proxy_pass 会获取不正确的值并返回 502 错误。

proxy_pass http://servicea0/$service$version$path;

2. 使用重定向

缺点:它增加了一个步骤,并且第二个请求将从客户端发起,而不是从反向代理发起,这限制了它的使用案例,因为服务发现或 DNS 解析问题。(例如仅适用于 Kubernetes 集群内部)。

添加了以下部分/etc/nginx/conf.d/default.conf

location ~ \/(?<service>\w+)\/v(?<version>[0-9]+(\.[0-9]+)*)(?<path>.*) {
    resolver 10.96.0.10;
    #proxy_pass http://$service$version$path;
    return 301 http://$service$version$path;
}

以下命令也在 nginx Pod 容器内运行:

root@nginx1-74cf9547fb-8bldg:/# curl -v 127.0.0.1/serviceA/v0/api/resource/10

* Expire in 0 ms for 6 (transfer 0x561d30fecf50)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x561d30fecf50)
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /serviceA/v0/api/resource/10 HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.64.0
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.19.5
< Date: Tue, 02 Mar 2021 16:45:12 GMT
< Content-Type: text/html
< Content-Length: 169
< Connection: keep-alive
< Location: http://serviceA0/api/resource/10    <--- redirect link looks as expected
< 
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.19.5</center>
</body>
</html>

我从第二个测试输出中删除了不重要的行:

root@nginx1-74cf9547fb-8bldg:/# curl -v 127.0.0.1/serviceC/v2.3.8/api/resource/12

* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /serviceC/v2.3.8/api/resource/12 HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.64.0
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.19.5
< Location: http://serviceC2.3.8/api/resource/12
< 
<html>
<head><title>301 Moved Permanently</title></head>

从支持重定向的集群微服务来看它可能是什么样子:
curl -L- 跟随重定向)

root@nginx1-74cf9547fb-8bldg:/# curl -L 127.0.0.1/serviceA/v0/api/resource/17
{
  "path": "/api/resource/17",
  "headers": {
    "host": "serviceA0",
    "user-agent": "curl/7.64.0",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "serviceA0",
  "ip": "::ffff:192.168.228.99",
  "ips": [],
  "protocol": "http",
  "query": {},
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "servicea0-66fcd9f788-2z4pk"
  },
  "connection": {}
}

答案2

我建议你看一下位置格式,这里有好文章。不过我在这个回复的底部已经给了你一个直接的答案。

我的大部分位置都是“位置(已修改)(字符串)”格式,例如下面的

location ~*  \.(jpg|jpeg|png|gif|css|js)$ {"
location = /wp-login.php {
location ~* (load_google_fonts|display_gallery_iframe) {
location ~ \.(hh|php)$ {
location ~*  "wp-content\/uploads\/(\d{4,}\/\d{2,}\/.*|galleries\/.*)" {

唯一没有修饰符的位置是精确匹配的位置

location / {
location = /robots.txt {
location /favicon.ico {

我怀疑您需要更多类似的东西 - 唯一的变化是在“位置”后面的 ~* 和空格周围。~* 是不区分大小写的正则表达式匹配。

location ~* /(?<service>(\w+))/(?<version>(v[0-9]+(\.[0-9]+)*)) {

相关内容