از زندگی با Kubernetes: چگونه سرور HTTP به نفع اسپانیایی ها نبود

از زندگی با Kubernetes: چگونه سرور HTTP به نفع اسپانیایی ها نبود

نماینده مشتری ما، که پشته برنامه هایش در ابر مایکروسافت (Azure) قرار دارد، به یک مشکل رسیدگی کرد: اخیراً برخی از درخواست های برخی از مشتریان از اروپا با خطای 400 پایان یافتند (درخواست بد). همه برنامه ها در دات نت نوشته شده اند و در Kubernetes مستقر شده اند...

یکی از برنامه های کاربردی API است که در نهایت تمام ترافیک از طریق آن می آید. این ترافیک توسط سرور HTTP گوش داده می شود کیستر، توسط مشتری دات نت پیکربندی شده و در یک پاد میزبانی می شود. با اشکال زدایی، ما خوش شانس بودیم به این معنا که یک کاربر خاص وجود داشت که به طور مداوم مشکل را بازتولید می کرد. با این حال، همه چیز توسط زنجیره ترافیک پیچیده شد:

از زندگی با Kubernetes: چگونه سرور HTTP به نفع اسپانیایی ها نبود

خطای Ingress به این صورت بود:

{
   "number_fields":{
      "status":400,
      "request_time":0.001,
      "bytes_sent":465,
      "upstream_response_time":0,
      "upstream_retries":0,
      "bytes_received":2328
   },
   "stream":"stdout",
   "string_fields":{
      "ingress":"app",
      "protocol":"HTTP/1.1",
      "request_id":"f9ab8540407208a119463975afda90bc",
      "path":"/api/sign-in",
      "nginx_upstream_status":"400",
      "service":"app",
      "namespace":"production",
      "location":"/front",
      "scheme":"https",
      "method":"POST",
      "nginx_upstream_response_time":"0.000",
      "nginx_upstream_bytes_received":"120",
      "vhost":"api.app.example.com",
      "host":"api.app.example.com",
      "user":"",
      "address":"83.41.81.250",
      "nginx_upstream_addr":"10.240.0.110:80",
      "referrer":"https://api.app.example.com/auth/login?long_encrypted_header",
      "service_port":"http",
      "user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
      "time":"2019-03-06T18:29:16+00:00",
      "content_kind":"cache-headers-not-present",
      "request_query":""
   },
   "timestamp":"2019-03-06 18:29:16",
   "labels":{
      "app":"nginx",
      "pod-template-generation":"6",
      "controller-revision-hash":"1682636041"
   },
   "namespace":"kube-nginx-ingress",
   "nsec":6726612,
   "source":"kubernetes",
   "host":"k8s-node-55555-0",
   "pod_name":"nginx-v2hcb",
   "container_name":"nginx",
   "boolean_fields":{}
}

در همان زمان، Kestrel گفت:

HTTP/1.1 400 Bad Request
Connection: close
Date: Wed, 06 Mar 2019 12:34:20 GMT
Server: Kestrel
Content-Length: 0

حتی با حداکثر پرحرفی، خطای Kestrel بسیار زیاد بود اطلاعات مفید کمی:

{
   "number_fields":{"ThreadId":76},
   "stream":"stdout",
   "string_fields":{
      "EventId":"{"Id"=>17, "Name"=>"ConnectionBadRequest"}",
      "SourceContext":"Microsoft.AspNetCore.Server.Kestrel",
      "ConnectionId":"0HLL2VJSST5KV",
      "@mt":"Connection id "{ConnectionId}" bad request data: "{message}"",
      "@t":"2019-03-07T13:06:48.1449083Z",
      "@x":"Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Malformed request: invalid headers.n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.<ProcessRequestsAsync>d__185`1.MoveNext()",
      "message":"Malformed request: invalid headers."
   },
   "timestamp":"2019-03-07 13:06:48",
   "labels":{
      "pod-template-hash":"2368795483",
      "service":"app"
   },
   "namespace":"production",
   "nsec":145341848,
   "source":"kubernetes",
   "host":"k8s-node-55555-1",
   "pod_name":"app-67bdcf98d7-mhktx",
   "container_name":"app",
   "boolean_fields":{}
}

به نظر می رسد که فقط tcpdump به حل این مشکل کمک می کند ... اما در مورد زنجیره ترافیک تکرار می کنم:

از زندگی با Kubernetes: چگونه سرور HTTP به نفع اسپانیایی ها نبود

تحقیق

بدیهی است که بهتر است به ترافیک گوش دهید روی آن گره خاص، جایی که Kubernetes یک pod مستقر کرده است: حجم تخلیه به اندازه ای خواهد بود که حداقل می توان چیزی را خیلی سریع پیدا کرد. و در واقع، هنگام بررسی آن، قاب زیر مورد توجه قرار گرفت:

GET /back/user HTTP/1.1
Host: api.app.example.com
X-Request-ID: 27ceb14972da8c21a8f92904b3eff1e5
X-Real-IP: 83.41.81.250
X-Forwarded-For: 83.41.81.250
X-Forwarded-Host: api.app.example.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-URI: /front/back/user
X-Scheme: https
X-Original-Forwarded-For: 83.41.81.250
X-Nginx-Geo-Client-Country: Spain
X-Nginx-Geo-Client-City: M.laga
Accept-Encoding: gzip
CF-IPCountry: ES
CF-RAY: 4b345cfd1c4ac691-MAD
CF-Visitor: {"scheme":"https"}
pragma: no-cache
cache-control: no-cache
accept: application/json, text/plain, */*
origin: https://app.example.com
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
referer: https://app.example.com/auth/login
accept-language: en-US,en;q=0.9,en-GB;q=0.8,pl;q=0.7
cookie: many_encrypted_cookies; .AspNetCore.Identity.Application=something_encrypted; 
CF-Connecting-IP: 83.41.81.250
True-Client-IP: 83.41.81.250
CDN-Loop: cloudflare

HTTP/1.1 400 Bad Request
Connection: close
Date: Wed, 06 Mar 2019 12:34:20 GMT
Server: Kestrel
Content-Length: 0

با بررسی دقیق تر زباله، این کلمه متوجه شد M.laga. به راحتی می توان حدس زد که هیچ شهر M.laga در اسپانیا وجود ندارد (اما وجود دارد بندر مالاگا). با استفاده از این ایده، به تنظیمات Ingress نگاه کردیم، جایی که یک ماه پیش (به درخواست مشتری) درج شد. قطعه "بی ضرر":

    ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header X-Nginx-Geo-Client-Country $geoip_country_name;
      proxy_set_header X-Nginx-Geo-Client-City $geoip_city;

بعد از غیرفعال کردن فوروارد این هدرها همه چیز درست شد! (به زودی مشخص شد که خود برنامه دیگر نیازی به این هدرها ندارد.)

حالا بیایید به مشکل نگاه کنیم به طور کلی. می توان آن را به راحتی در داخل برنامه با درخواست telnet به تکثیر کرد localhost:80:

GET /back/user HTTP/1.1
Host: api.app.example.com
cache-control: no-cache
accept: application/json, text/plain, */*
origin: https://app.example.com
Cookie: test=Desiree

... برمی گردد 401 Unauthorized، همانطور که انتظار میرفت. اگر انجام دهیم چه اتفاقی می افتد:

GET /back/user HTTP/1.1
Host: api.app.example.com
cache-control: no-cache
accept: application/json, text/plain, */*
origin: https://app.example.com
Cookie: test=Désirée

?

بر خواهد گشت 400 Bad request - در گزارش برنامه ما خطایی را دریافت خواهیم کرد که قبلاً برای ما آشناست:

{
   "@t":"2019-03-31T12:59:54.3746446Z",
   "@mt":"Connection id "{ConnectionId}" bad request data: "{message}"",
   "@x":"Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Malformed request: invalid headers.n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.<ProcessRequestsAsync>d__185`1.MoveNext()",
   "ConnectionId":"0HLLLR1J974L9",
   "message":"Malformed request: invalid headers.",
   "EventId":{
      "Id":17,
      "Name":"ConnectionBadRequest"
   },
   "SourceContext":"Microsoft.AspNetCore.Server.Kestrel",
   "ThreadId":71
}

نمایش نتایج: از

به طور خاص کسترل نمیتونم هدرهای HTTP را با کاراکترهای صحیح در UTF-8 که در نام تعداد نسبتاً زیادی از شهرها موجود است، به درستی پردازش کنید.

یک عامل اضافی در مورد ما این است که مشتری در حال حاضر برنامه ای برای تغییر اجرای Kestrel در برنامه ندارد. با این حال، مشکلات در خود AspNetCore (№ 4318, № 7707) آنها می گویند که این کمکی نمی کند ...

به طور خلاصه: یادداشت دیگر در مورد مشکلات خاص Kestrel یا UTF-8 (در سال 2019؟!) نیست، بلکه در مورد این واقعیت است که ذهن آگاهی و مطالعه مداوم هر قدمی که هنگام جستجوی مشکلات برمی‌دارید دیر یا زود به ثمر می‌رسد. موفق باشید!

PS

در وبلاگ ما نیز بخوانید:

منبع: www.habr.com

اضافه کردن نظر