Kubernetes 入口服务不适用于 URL 与 root 不同
Kubernetes ingress service not working for URL different than root
我正在本地 k8s 集群上测试一个简单的应用程序,该集群由 React FE 和 Spring Boot BE 组成。集群在 docker 桌面 docker 内 windows 版本 1.14.8(docker 桌面 2.1.0.5)。
我的问题是配置的入口服务似乎无法路由 BE 部署的流量,而 FE 运行良好(我实际上可以在浏览器中看到 React 应用程序,但其余部分调用 BE失败)。我尝试了不同的解决方案,但我不明白我的配置有什么问题。
FE镜像暴露3000端口,BE镜像暴露8080端口(根路径/apptest),运行将镜像与docker 运行 两者都按预期工作,响应这些端口上的请求。
对于 k8s 配置,我已经为两个图像定义了部署,其中 containerPort 3000 用于 FE,containerPort 8080 用于 BE。然后我创建了两个 ClusterIP 服务,一个用于端口 3000 和目标端口 3000 的 FE,一个用于端口 8080 和目标端口 8080 的 BE。
入口服务被配置为响应路径为 / 到服务端口 3000(FE)的任何请求以及任何以 /[=89= 开头的请求] 到 servicePort 8080(BE,在这种情况下删除 'api' 位)。 FE配置为以/api路径启动后端调用。
在 k8s 集群上应用文件时,它在 pods 中全部正确启动,没有错误,我可以访问 http://localhost 上的 React 应用程序。但是,如果我尝试使用 url http://localhost/api/apptest 调用后端,它们将失败并给出 502 Bad Gateway 错误。
FE Dockerfile
FROM node:12-alpine as builder
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx
EXPOSE 3000
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/build /usr/share/nginx/html
FE Nginx 配置:
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
BE Dockerfile:
FROM java:8
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
FE 部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apptest-fe-deployment
spec:
replicas: 1
selector:
matchLabels:
component: apptest-fe # uguale al template specificato sotto
template:
metadata:
labels:
component: apptest-fe
spec:
containers:
- name: apptest-fe
imagePullPolicy: Always
image: registryipaddress:5000/apptestgroup/apptest-fe:latest
resources:
limits:
memory: "128Mi"
cpu: "10m"
ports:
- containerPort: 3000
FE ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: apptest-fe-cluster-ip
spec:
type: ClusterIP
selector:
component: apptest-fe
ports:
- port: 3000
targetPort: 3000
BE 部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apptest-deployment
spec:
replicas: 1
selector:
matchLabels:
component: apptest # uguale al template specificato sotto
template:
metadata:
labels:
component: apptest
spec:
containers:
- name: apptest
imagePullPolicy: Always
# Di default kubernetes va su docker hub a recuperare l'immagine.
# Se il tag dell'immagine inizia con un indirizzo ip, lo interpreta come
# il registro da cui pullare l'immagine.
image: registryipaddress:5000/apptestgroup/apptest:latest
resources:
limits:
memory: "128Mi"
cpu: "10m"
ports:
- containerPort: 8080
BE ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: apptest-cluster-ip
spec:
type: ClusterIP
selector:
component: apptest
ports:
- port: 8080
targetPort: 8080
入口服务:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: apptest-ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /?(.*)
backend:
serviceName: apptest-fe-cluster-ip
servicePort: 3000
- path: /api/?(.*)
backend:
serviceName: apptest-cluster-ip
servicePort: 8080
来自 Chrome 的 502 错误:
xhr.js:172 POST http://localhost/api/apptest/documents/base64/aaa333 502 (Bad Gateway)...
createError.js:16 Uncaught (in promise) Error: Request failed with status code 502
at e.exports (createError.js:16)
at e.exports (settle.js:17)
at XMLHttpRequest.f.onreadystatechange (xhr.js:59)
我确信问题出在为 ReactApp 服务的 FE 容器内的 nginx,它以某种方式绕过了入口服务并尝试将流量路由到它不知道的路径,但我是不确定如何找到解决方法。
更新
我尝试将FE映射到ingress服务中的/app,检查是不是容器内的nginx问题。导航到 http:///localhost/app 反应应用程序工作,即使不完全,但尝试与邮递员联系 http:///localhost/api/apptest 仍然给出 502 错误
第一个入口规则每次都会匹配,请求不会到达 BE。检查位置块是如何生成的:https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/
您将在 nginx 中得到以下内容:
location ~* ^/?(.*) {
...
}
location ~* "^/api/?(.*)" {
...
}
作为遇到入口问题时的建议,请始终检查日志以了解哪个服务收到了请求。并且在调试时直接在浏览器中访问http://localhost/api/apptest/documents/base64/aaa333,比通过前端更不容易出错。
这不是 Ingress 问题,我已经重现了您的场景并且您的语法是正确的。
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress-service
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: hello-world.info
http:
paths:
- path: /?(.*)
backend:
serviceName: web
servicePort: 8080
- path: /api/?(.*)
backend:
serviceName: webv2
servicePort: 8080
测试输出:
user@minikube:~$ curl http://hello-world.info/aaa.any
Hello, world! Version: 1.0.0
Hostname: web-9bbd7b488-hlxd4
user@minikube:~$ curl http://hello-world.info/api/bbb.any
Hello, world! Version: 2.0.0
Hostname: web2-74cf4946cc-8c586
user@minikube:~$ curl http://hello-world.info/
Hello, world! Version: 1.0.0
Hostname: web-9bbd7b488-hlxd4
user@minikube:~$ curl http://hello-world.info/api/
Hello, world! Version: 2.0.0
Hostname: web2-74cf4946cc-8c586
Github Ingress-nginx Docs - 入口路径匹配:
In order to enable more accurate path matching, ingress-nginx first orders the paths by descending length before writing them to the NGINX template as location blocks
您的上次更新表明问题出在您的后端应用程序中,因为它返回相同的 502 Bad Gateway
。
请仔细阅读。
原来是后端应用的资源分配问题。我将它们设置得非常低,因为在我的电脑上,否则 pod 将无法启动。我向部署配置添加了一点点资源,现在一切都按预期工作了。
让我认为问题出在其他地方的是,即使资源不足,pod 仍会正常启动(并保持运行),并且不会报告容器内的任何问题,即使 tomcat 在 spring 启动应用程序中显然没有正常运行。
我正在本地 k8s 集群上测试一个简单的应用程序,该集群由 React FE 和 Spring Boot BE 组成。集群在 docker 桌面 docker 内 windows 版本 1.14.8(docker 桌面 2.1.0.5)。
我的问题是配置的入口服务似乎无法路由 BE 部署的流量,而 FE 运行良好(我实际上可以在浏览器中看到 React 应用程序,但其余部分调用 BE失败)。我尝试了不同的解决方案,但我不明白我的配置有什么问题。
FE镜像暴露3000端口,BE镜像暴露8080端口(根路径/apptest),运行将镜像与docker 运行 两者都按预期工作,响应这些端口上的请求。
对于 k8s 配置,我已经为两个图像定义了部署,其中 containerPort 3000 用于 FE,containerPort 8080 用于 BE。然后我创建了两个 ClusterIP 服务,一个用于端口 3000 和目标端口 3000 的 FE,一个用于端口 8080 和目标端口 8080 的 BE。
入口服务被配置为响应路径为 / 到服务端口 3000(FE)的任何请求以及任何以 /[=89= 开头的请求] 到 servicePort 8080(BE,在这种情况下删除 'api' 位)。 FE配置为以/api路径启动后端调用。
在 k8s 集群上应用文件时,它在 pods 中全部正确启动,没有错误,我可以访问 http://localhost 上的 React 应用程序。但是,如果我尝试使用 url http://localhost/api/apptest 调用后端,它们将失败并给出 502 Bad Gateway 错误。
FE Dockerfile
FROM node:12-alpine as builder
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx
EXPOSE 3000
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/build /usr/share/nginx/html
FE Nginx 配置:
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
BE Dockerfile:
FROM java:8
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
FE 部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apptest-fe-deployment
spec:
replicas: 1
selector:
matchLabels:
component: apptest-fe # uguale al template specificato sotto
template:
metadata:
labels:
component: apptest-fe
spec:
containers:
- name: apptest-fe
imagePullPolicy: Always
image: registryipaddress:5000/apptestgroup/apptest-fe:latest
resources:
limits:
memory: "128Mi"
cpu: "10m"
ports:
- containerPort: 3000
FE ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: apptest-fe-cluster-ip
spec:
type: ClusterIP
selector:
component: apptest-fe
ports:
- port: 3000
targetPort: 3000
BE 部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apptest-deployment
spec:
replicas: 1
selector:
matchLabels:
component: apptest # uguale al template specificato sotto
template:
metadata:
labels:
component: apptest
spec:
containers:
- name: apptest
imagePullPolicy: Always
# Di default kubernetes va su docker hub a recuperare l'immagine.
# Se il tag dell'immagine inizia con un indirizzo ip, lo interpreta come
# il registro da cui pullare l'immagine.
image: registryipaddress:5000/apptestgroup/apptest:latest
resources:
limits:
memory: "128Mi"
cpu: "10m"
ports:
- containerPort: 8080
BE ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: apptest-cluster-ip
spec:
type: ClusterIP
selector:
component: apptest
ports:
- port: 8080
targetPort: 8080
入口服务:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: apptest-ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /?(.*)
backend:
serviceName: apptest-fe-cluster-ip
servicePort: 3000
- path: /api/?(.*)
backend:
serviceName: apptest-cluster-ip
servicePort: 8080
来自 Chrome 的 502 错误:
xhr.js:172 POST http://localhost/api/apptest/documents/base64/aaa333 502 (Bad Gateway)...
createError.js:16 Uncaught (in promise) Error: Request failed with status code 502
at e.exports (createError.js:16)
at e.exports (settle.js:17)
at XMLHttpRequest.f.onreadystatechange (xhr.js:59)
我确信问题出在为 ReactApp 服务的 FE 容器内的 nginx,它以某种方式绕过了入口服务并尝试将流量路由到它不知道的路径,但我是不确定如何找到解决方法。
更新
我尝试将FE映射到ingress服务中的/app,检查是不是容器内的nginx问题。导航到 http:///localhost/app 反应应用程序工作,即使不完全,但尝试与邮递员联系 http:///localhost/api/apptest 仍然给出 502 错误
第一个入口规则每次都会匹配,请求不会到达 BE。检查位置块是如何生成的:https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/
您将在 nginx 中得到以下内容:
location ~* ^/?(.*) {
...
}
location ~* "^/api/?(.*)" {
...
}
作为遇到入口问题时的建议,请始终检查日志以了解哪个服务收到了请求。并且在调试时直接在浏览器中访问http://localhost/api/apptest/documents/base64/aaa333,比通过前端更不容易出错。
这不是 Ingress 问题,我已经重现了您的场景并且您的语法是正确的。
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress-service
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: hello-world.info
http:
paths:
- path: /?(.*)
backend:
serviceName: web
servicePort: 8080
- path: /api/?(.*)
backend:
serviceName: webv2
servicePort: 8080
测试输出:
user@minikube:~$ curl http://hello-world.info/aaa.any
Hello, world! Version: 1.0.0
Hostname: web-9bbd7b488-hlxd4
user@minikube:~$ curl http://hello-world.info/api/bbb.any
Hello, world! Version: 2.0.0
Hostname: web2-74cf4946cc-8c586
user@minikube:~$ curl http://hello-world.info/
Hello, world! Version: 1.0.0
Hostname: web-9bbd7b488-hlxd4
user@minikube:~$ curl http://hello-world.info/api/
Hello, world! Version: 2.0.0
Hostname: web2-74cf4946cc-8c586
Github Ingress-nginx Docs - 入口路径匹配:
In order to enable more accurate path matching, ingress-nginx first orders the paths by descending length before writing them to the NGINX template as location blocks
您的上次更新表明问题出在您的后端应用程序中,因为它返回相同的 502 Bad Gateway
。
请仔细阅读。
原来是后端应用的资源分配问题。我将它们设置得非常低,因为在我的电脑上,否则 pod 将无法启动。我向部署配置添加了一点点资源,现在一切都按预期工作了。
让我认为问题出在其他地方的是,即使资源不足,pod 仍会正常启动(并保持运行),并且不会报告容器内的任何问题,即使 tomcat 在 spring 启动应用程序中显然没有正常运行。