من الحياة مع Kubernetes: كيف لم يفضل خادم HTTP الإسبان

من الحياة مع Kubernetes: كيف لم يفضل خادم HTTP الإسبان

قام ممثل عميلنا، الذي توجد مجموعة تطبيقاته في سحابة Microsoft (Azure)، بمعالجة مشكلة: في الآونة الأخيرة، بدأت بعض الطلبات من بعض العملاء من أوروبا تنتهي بالخطأ 400 (اقتراح غير جيد). تتم كتابة كافة التطبيقات في .NET، ونشرها في Kubernetes...

أحد التطبيقات هو واجهة برمجة التطبيقات (API)، التي تأتي من خلالها كل حركة المرور في النهاية. يتم الاستماع إلى حركة المرور هذه بواسطة خادم HTTP العاسوق، تم تكوينه بواسطة عميل .NET واستضافته في حجرة. مع تصحيح الأخطاء، كنا محظوظين، بمعنى أن هناك مستخدمًا محددًا قام بإعادة إنتاج المشكلة باستمرار. ومع ذلك، كان كل شيء معقدًا بسبب سلسلة المرور:

من الحياة مع Kubernetes: كيف لم يفضل خادم HTTP الإسبان

بدا الخطأ في الدخول كما يلي:

{
   "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":{}
}

وفي الوقت نفسه، أعطى كيستريل:

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

حتى مع أقصى قدر من الإسهاب، احتوى خطأ كيستريل للغاية القليل من المعلومات المفيدة:

{
   "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 بنشر جراب: سيكون حجم التفريغ بحيث سيكون من الممكن العثور على شيء ما على الأقل بسرعة كبيرة. وبالفعل عند فحصه لوحظ الإطار التالي:

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

إضافة تعليق