如何自定义通过 nginx 入口控制器的默认后端提供的错误页面



我正在我的Kubernetes集群上运行通过Helm安装的Nginx Ingress Controller。我想更改默认后端服务中的 HTML/CSS 以解决某些特定错误(例如 404)。

此链接提供有关默认后端的一些常规信息。但是,没有提到如何实际自定义提供的HTML/CSS文件。

> 2021 年 8 月更新:

原始答案包含在 kubernetes 上部署自定义默认后端所需的步骤。

但是,最新版本的 ingress-nginx 允许用户只指定要拉取的 docker 镜像 - 不需要其他 k8s 资源文件(即服务和部署)。

我当前的values.yaml用于nginx入口控制器以允许自定义默认后端:

defaultBackend:
enabled: true
name: custom-default-backend
image:
repository: dvdblk/custom-default-backend
tag: "latest"
pullPolicy: Always
port: 8080
extraVolumeMounts:
- name: tmp
mountPath: /tmp
extraVolumes:
- name: tmp
emptyDir: {}

下面是自定义后端的 GitHub 存储库。

<小时 />

旧答案:

好的,这些答案的某些部分有助于寻找完整的解决方案,尤其是来自@Matt的解决方案。但是,我花了相当长的时间才能完成这项工作,因此我决定编写自己的答案,其中包含其他人可能也难以解决的所有必要细节。

第一件事是创建一个Docker映像服务器,该服务器能够响应具有404内容的任何请求,除了/healthz/metrics。正如@Matt提到的,这可能是一个Nginx实例(我已经使用过)。总结一下:

  • /healthz应该返回200
  • /metrics是可选的,但它应该返回 Prometheus 可读取的数据,以防您将其用于 k8s 指标。Nginx可以提供一些Prometheus可以读取的基本数据。如果您想获得与Nginx的完整Prometheus集成,请考虑此链接。
  • /返回包含自定义 HTML 内容的 404。

因此,Dockerfile如下所示:

FROM nginx:alpine
# Remove default NGINX Config
# Take care of Nginx logging
RUN rm /etc/nginx/conf.d/default.conf && 
ln -sf /dev/stdout /var/log/nginx/access.log && 
ln -sf /dev/stderr /var/log/nginx/error.log
# NGINX Config
COPY ./default.conf /etc/nginx/conf.d/default.conf
# Resources
COPY content/ /var/www/html/
CMD ["nginx", "-g", "daemon off;"]

在 Dockerfile 所在的同一文件夹中,创建此default.confNginx 配置文件:

server {
root /var/www/html;
index 404.html;
location / {

}
location /healthz {
access_log off;
return 200 "healthyn";
}

location /metrics {
# This creates a readable and somewhat useful response for Prometheus
stub_status on;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}

最后,根据自己的喜好提供一个带有HTML/CSS的content/404.html文件。

现在使用以下命令构建 Docker 映像:

docker build --no-cache -t custom-default-backend .

标记此映像,以便准备好将其推送到 DockerHub(或您自己的私有 docker 注册表)中:

docker tag custom-default-backend:latest <your_dockerhub_username>/custom-default-backend

将映像推送到 DockerHub 存储库:

docker push <your_dockerhub_username>/custom-default-backend

现在是将此自定义默认后端映像集成到 Helm 安装中的部分。为此,我们首先需要创建这个 k8s 资源文件 (custom_default_backend.yaml):

---
apiVersion: v1
kind: Service
metadata:
name: custom-default-backend
namespace: ingress-nginx
labels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
spec:
selector:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
ports:
- port: 80
targetPort: 80
name: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-default-backend
namespace: ingress-nginx
labels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: custom-default-backend
app.kubernetes.io/part-of: ingress-nginx
spec:
containers:
- name: custom-default-backend
# Don't forget to edit the line below
image: <your_dockerhub_username>/custom-default-backend:latest
imagePullPolicy: Always
ports:
- containerPort: 80

假设我们已经创建了一个 k8s 命名空间ingress-nginx我们可以创建这两个资源。

kubectl apply -f custom_default_backend.yaml

现在,为了将Nginx入口控制器与我们的新服务绑定,我们可能只需编辑入口控制器的部署。但我决定通过 Helm 完全删除它:

helm delete nginx-ingress -n ingress-nginx

并使用此命令再次安装它(确保您拥有包含正确参数的--set标志):

helm install nginx-ingress --namespace ingress-nginx stable/nginx-ingress --set defaultBackend.enabled=false,controller.defaultBackendService=ingress-nginx/custom-default-backend

通过这些步骤,您应该最终获得一个有效的自定义默认后端实现。这是一个 GitHub 存储库,其中包含我在此答案中使用的文件。

使用官方ingress-nginx/nginx-errors图像并从 ConfigMap 映射您的自定义页面。这样,您就不必建立自己的映像。

请参阅 nginx-ingress 官方文档 https://kubernetes.github.io/ingress-nginx/examples/customization/custom-errors/

在使用 Helm 进行部署的情况下,这里是示例values.yaml文件。

# See https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/values.yaml
controller:
custom-http-errors: "404,500,503" # See https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#custom-http-errors
defaultBackend:
enabled: true
image:
registry: k8s.gcr.io
image: ingress-nginx/nginx-errors # Source https://github.com/kubernetes/ingress-nginx/tree/main/images/custom-error-pages
tag: "0.48.1" # Check latest version on https://github.com/kubernetes/ingress-nginx/blob/main/docs/examples/customization/custom-errors/custom-default-backend.yaml
extraVolumes:
- name: error_page
configMap:
name: error_page
items:
- key: "error_page"
path: "404.html"
- key: "error_page"
path: "500.html"
- key: "error_page"
path: "503.html"
extraVolumeMounts:
- name: error_page
mountPath: /www

示例错误页面配置映射文件:

apiVersion: v1
kind: ConfigMap
metadata:
name: error_page
namespace: ingress-nginx
data:
error_page: |
<!DOCTYPE html>
<html>
<head>
<title>ERROR PAGE</title>
</head>
<body>
ERROR PAGE
</body>
</html>

该项目提供了 Go 自定义错误应用程序,该应用程序可以内置到容器映像中以替换default-backenderrorHandler函数发挥了魔力。

最后,它是一个Web服务器,可以响应任何具有404内容的请求,除了/healthz/metrics。如果需要,您可以使用nginx实例和html错误页面来做到这一点。

你可能不想使用完整的自定义错误处理,这与入口控制器将从常规应用后端查找某些 HTTP 状态代码并将其传递到默认后端进行处理略有不同。这会导致大多数应用程序出现问题,除非它们从一开始就设计为使用它。

您必须使用后端配置创建示例部署:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-errors
labels:
app.kubernetes.io/name: nginx-errors
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: nginx-errors
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: nginx-errors
app.kubernetes.io/part-of: ingress-nginx
spec:
containers:
- name: nginx-error-server
image: quay.io/kubernetes-ingress-controller/custom-error-pages-amd64:0.3
ports:
- containerPort: 8080

然后,您必须为其指定服务:

apiVersion: v1
kind: Service
metadata:
name: nginx-errors
labels:
app.kubernetes.io/name: nginx-errors
app.kubernetes.io/part-of: ingress-nginx
spec:
selector:
app.kubernetes.io/name: nginx-errors
app.kubernetes.io/part-of: ingress-nginx
ports:
- port: 80
targetPort: 8080
name: http

如果您还没有运行 NGINX 入口控制器的实例,请根据部署指南进行部署,然后按照以下步骤操作:

  1. 编辑 nginx 入口控制器部署并设置 新名称的--default-backend-service标志 创建了错误后端。
  2. 编辑 nginx 配置配置映射并创建密钥 自定义 http 错误,值为404

请注意分配给 NGINX 入口控制器服务的 IP 地址。

$ kubectl get svc ingress-nginx
NAME            TYPE        CLUSTER-IP  EXTERNAL-IP   PORT(S)          AGE
ingress-nginx   ClusterIP   10.0.0.13   <none>        80/TCP,443/TCP   10m

在此示例中,ingress-nginx 服务的类型为 ClusterIP。这可能因环境而异。在继续此示例的其余部分之前,请确保您可以使用该服务访问 NGINX。

用于后端 Pod 的 NGINX 配置由 ConfigMap 定义。二进制配置映射创建在 RPS 测试中返回给客户端的文件。因此,如果您想在以下文件中进行修改,请在配置映射中指定它们并应用更改。

入口控制器有一个职责 - 实施群集中定义的入口规则。要提供静态内容,您应该有一个执行此操作的 pod,这确实意味着 ie。在你的堆栈中运行第二个nginx。问题是,您应该将入口控制器视为提供通用集群功能的基础架构的一部分,并且从某个位置(或容器,如果它们被版本控制/构建为 docker 映像)提供静态文件实际上是应用程序的一部分。

看看这里:configmap-kubernetes-ingress,static-files-nginx-ingress default-backend,custom-ingress-errors。

官方文档:nginx-ingress-controller。

您需要创建和部署自定义默认后端,这将返回自定义错误页面。按照文档部署自定义默认后端,并通过修改部署 yaml 来配置 nginx 入口控制器以使用此自定义默认后端。

自定义默认后端的部署 yaml 在此处,源代码在此处。

默认 http后端的 docker 可以在以下位置找到: https://github.com/interlegis/nginx-ingress-controller-defaultbackend

在root/etc/nginx文件夹下,您可以根据需要修改nginx.conf。 然后生成更新 docker 并进行部署。(你可以查看上面的答案如何部署) 与其删除入口控制器然后安装,不如修改已部署的服务,使其指向您的容器,您部署该服务以替换默认 http 后端。

也许上面的答案可以让你更轻松。但是,如果您已经在操作生产环境,我不建议删除nginx入口控制器并将其重新安装。

我以前遇到过这个问题,.. 但是我有一个想法,将路由路径"/"用于服务。因此,入口控制器不会再次显示 404 页面。它有效!!

  1. 创建简单服务的部署,就我而言,我正在使用这个 使用端口 80

  2. 然后,将部署公开为服务

    kubectl 公开部署部署名称 --port=80

  3. 最后一步,我们应该配置入口资源以将此服务路由到"/"路径


apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: default-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: service-name
servicePort: 80

There is another way to provide the custom error page in ingress-nginx, for that one has to modify the template of ingress-Nginx.(/etc/nginx/template).

volumeMounts:
- name: custom-errors
mountPath: /usr/local/nginx/html/
readOnly: true
- name: nginx-ingress-template-volume
mountPath: /etc/nginx/template
readOnly: true

在上面的 YAML 示例中,使用 path (/usr/local/nginx/html) 挂载自定义错误页面。必须从/etc/nginx/template/复制nginx.tmpl,然后对其进行自定义。在nginx模板默认服务器中,默认服务器看起来像这样(见下面的代码片段)。

...
# backend for when default-backend-service is not configured or it does not have endpoints
server {
listen {{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};
{{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Default }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }}
set $proxy_upstream_name "internal";
access_log off;
root /usr/local/nginx/html/;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location / {
return 404;
}
location = /404.html {
internal;
}
location = /50x.html {
internal;
}
} 
...

确保在根目录 (/usr/local/nginx/html/) 内提供自定义的 404.html 和 50xhtml 页面。下面的代码片段将帮助您使用自定义页面挂载卷。

volumes:
- name: custom-errors
configMap:
# Provide the name of the ConfigMap you want to mount.
name: custom-ingress-pages
items:
- key: "404.html"
path: "404.html"
- key: "50x.html"
path: "50x.html"
- key: "index.html"
path: "index.html"

此解决方案不需要您生成任何类型的其他服务或 pod 即可工作,这将在入口 nginx 控制器 pod 部署(或守护程序集)中处理。无需为自定义错误消息(或页面)预热群集以获得额外服务。

更多参考: https://engineering.zenduty.com/blog/2022/03/02/customizing-error-pages

最新更新