Ingress-nginx工作原理和实践
本文記錄/分享 目前項(xiàng)目的 K8s 部署結(jié)構(gòu)和請求追蹤改造方案
這個(gè)圖算是一個(gè)通用的前后端分離的 k8s 部署結(jié)構(gòu):
Nginx Ingress 負(fù)責(zé)暴露服務(wù)(nginx前端靜態(tài)資源服務(wù)), 根據(jù)十二要素應(yīng)用的原 則,將后端 api 作為 nginx 服務(wù)的附加動(dòng)態(tài)資源。
Ingress vs Ingress-nginx
Ingress 是一種向 k8s 集群外部的客戶端公開服務(wù)的方法,?Ingress 在網(wǎng)絡(luò)協(xié)議棧的應(yīng)用層工作,
根據(jù)請求的主機(jī)名 host 和路徑 path 決定請求轉(zhuǎn)發(fā)到的服務(wù)。
在應(yīng)用Ingress 對象提供的功能之前,必須強(qiáng)調(diào)集群中存在Ingress Controller, Ingress資源才能正常工作。
我這里web項(xiàng)目使用的是常見的Ingress-nginx (官方還有其他用途的 Ingress),Ingress-nginx 是使用nginx 作為反向代理和負(fù)載均衡器的 K8s Ingress Controller, 作為Pod運(yùn)行在kube-system?命名空間。
了解 Ingress 工作原理,有利于我們與運(yùn)維人員打交道。
下面通過 Ingress-nginx 暴露 Kibana 服務(wù):
--- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata:name: kibanalabels:app: kibanaannotations:kubernetes.io/ingress.class: "nginx"nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"nginx.ingress.kubernetes.io/proxy-read-timeout: "1800"nginx.ingress.kubernetes.io/proxy-send-timeout: "1800"nginx.ingress.kubernetes.io/proxy-body-size: "8m"nginx.ingress.kubernetes.io/ssl-redirect: "true" spec:tls:- hosts:- 'https://logging.internal.gridsum.com/'secretName: tls-certrules:- host: 'https://logging.internal.gridsum.com'http:paths:- path: /backend:serviceName: kibanaservicePort: 5601?? Ingress-nginx 中最讓我困惑的是它的Paths分流與rewrite-target注解。
?Paths 分流 一般用于 根據(jù)特定的 Path,將請求轉(zhuǎn)發(fā)到特定的后端服務(wù) Pod,后端服務(wù) Pod 能接收到 Path 這個(gè)信息。一般后端服務(wù)是作為 api。?rewrite-target 將請求重定向到后端服務(wù), 那有什么用處呢?
答:以上面暴露的 kibana 為例, 我們已經(jīng)可以在https://logging.internal.gridsum.com/?訪問完整的 Kibana, 如果我想利用這個(gè)域名暴露 ElasticSearch 站點(diǎn),怎么操作?這時(shí)就可以利用rewrite-target,
--- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata:name: elasticsearchlabels:app: kibanaannotations:kubernetes.io/ingress.class: "nginx"nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"nginx.ingress.kubernetes.io/proxy-read-timeout: "1800"nginx.ingress.kubernetes.io/proxy-send-timeout: "1800"nginx.ingress.kubernetes.io/proxy-body-size: "8m"nginx.ingress.kubernetes.io/ssl-redirect: "true"nginx.ingress.kubernetes.io/rewrite-target: "/$2" spec:tls:- hosts:- 'logging.internal.gridsum.com'secretName: tls-certrules:- host: 'logging.internal.gridsum.com'http:paths:- path: /es(/|$)(.*)backend:serviceName: elasticsearchservicePort: 9200在此 Ingress 定義中,由(.*)捕獲的所有字符都將分配給占位符$2,然后將其用作重寫目標(biāo)注解中的參數(shù)。這樣的話:https://logging.internal.gridsum.com/es?將會(huì)重定向到后端 elasticsearch 站點(diǎn),并且忽略了 es 這個(gè) path
Ingress-nginx 到 webapp 的日志追蹤
熟悉我的朋友知道, 我寫了《一套標(biāo)準(zhǔn)的ASP.NET Core容器化應(yīng)用日志收集分析方案》,這里面主要是 BackEnd App 的日志,從我上面的結(jié)構(gòu)圖看,
Ingress-nginx----> Nginx FrontEnd App--->BackEnd App 需要一個(gè)串聯(lián)的追蹤 Id, 便于觀察運(yùn)維網(wǎng)絡(luò)和業(yè)務(wù)應(yīng)用。
幸好 Ingress-nginx, Nginx 強(qiáng)大的配置能力幫助我們做了很多事情:
?客戶端請求到達(dá) Ingress-Nginx Controllerr,Ingress-Nginx Controller 會(huì)自動(dòng)添加一個(gè)X-Request-ID的請求 Header (隨機(jī)值)---- 這個(gè)配置是默認(rèn)的?請求達(dá)到 Nginx FrontEnd App, Nginx 有默認(rèn)配置proxy_pass_request_headers on;, 自動(dòng)將請求頭都傳遞到上游的 Backend App
這樣跨越整個(gè)結(jié)構(gòu)圖的 request_id 思路已經(jīng)清楚了,最后一步只需要我們在 Backend App 中提取請求中攜帶的X-Request-ID, 并作為日志的關(guān)鍵輸出字段。
?? 這就涉及到怎么自定義日志的LayoutRender。
下面為Asp.NETCore NLog 自定義名為x_request_id的 Render,該 Render 從請求的 X-Request-ID 標(biāo)頭中提取值。
① 定義 NLog Render
/// <summary>/// Represent a unique identifier to represent a request from the request HTTP header X-Request-Id./// </summary>[LayoutRenderer("x_request_id")]public class XRequestIdLayoutRender : HttpContextLayoutRendererBase{protected override void Append(StringBuilder builder, LogEventInfo logEvent){var identityName = HttpContextAccessor.HttpContext?.Request?.Headers?["X-Request-Id"].FirstOrDefault();builder.Append(identityName);}}/// <summary>/// Represent a http context layout renderer to access the current http context./// </summary>public abstract class HttpContextLayoutRendererBase : LayoutRenderer{private IHttpContextAccessor _httpContextAccessor;/// <summary>/// Gets the <see cref="IHttpContextAccessor"/>./// </summary>protected IHttpContextAccessor HttpContextAccessor { get { return _httpContextAccessor ?? (_httpContextAccessor = ServiceLocator.ServiceProvider.GetService<IHttpContextAccessor>()); } }}internal sealed class ServiceLocator{public static IServiceProvider ServiceProvider { get; set; }}② 從請求中獲取 X-Request-Id 依賴 IHttpContextAccessor 組件
這里使用依賴查找的方式獲取該組件,故請?jiān)赟tartup ConfigureService 中生成服務(wù)
③ 最后在 Program 中注冊這個(gè)NLog Render:
public static void Main(string[] args) {LayoutRenderer.Register<XRequestIdLayoutRender>("x_request_id");CreateHostBuilder(args).Build().Run(); }這樣從 Ingress-Nginx 產(chǎn)生的request_id,將會(huì)流轉(zhuǎn)到 Backend App, 并在日志分析中起到巨大作用,也便于劃清運(yùn)維/開發(fā)的故障責(zé)任。
?https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#generate-request-id?http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_request_headers
總結(jié)
1.了解了Ingress在應(yīng)用層工作,根據(jù)Host和Path暴露k8s服務(wù)。2.本文梳理了Ingress和常見的Ingress-nginx的關(guān)系。3.對于應(yīng)用了Ingress的應(yīng)用,梳理了從Ingress-nginx到WebApp的日志追蹤id, 便于排查網(wǎng)絡(luò)/業(yè)務(wù)故障。
總結(jié)
以上是生活随笔為你收集整理的Ingress-nginx工作原理和实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core 集成 Rea
- 下一篇: Dotnet洋葱架构实践