Кубернетеспен өмірден: HTTP сервері испандарға қалай ұнамады

Кубернетеспен өмірден: HTTP сервері испандарға қалай ұнамады

Қолданбалар стегі Microsoft бұлтында (Azure) орналасқан клиентіміздің өкілі мәселеге тоқталды: жақында Еуропадан келген кейбір клиенттердің сұраулары 400 қатемен аяқтала бастады (Сұраныс қате). Барлық қолданбалар .NET-те жазылған, Kubernetes-те орналастырылған...

Қолданбалардың бірі API болып табылады, ол арқылы барлық трафик ақыр соңында келеді. Бұл трафикті HTTP сервері тыңдайды Кестрел, .NET клиенті арқылы конфигурацияланған және бөлімшеде орналастырылған. Түзету арқылы мәселені дәйекті түрде қайта шығаратын белгілі бір пайдаланушы бар деген мағынада біз бақытты болдық. Дегенмен, бәрі трафик тізбегімен қиын болды:

Кубернетеспен өмірден: 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":{}
}

Сонымен бірге Кестрел:

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 көмектесетін сияқты ... бірақ мен трафик тізбегі туралы қайталаймын:

Кубернетеспен өмірден: HTTP сервері испандарға қалай ұнамады

Тергеу

Көлік қозғалысын тыңдаған дұрыс екені анық нақты түйінде, мұнда Кубернетес подводты орналастырды: қоқыс көлемі кем дегенде бір нәрсені тез табуға болатындай болады. Шынында да, оны зерттеген кезде келесі кадр байқалды:

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
}

Нәтижелері

Атап айтқанда, Kestrel мүмкін емес көптеген қалалардың атауларында қамтылған UTF-8 ішіндегі дұрыс таңбалармен HTTP тақырыптарын дұрыс өңдеу.

Біздің жағдайдағы қосымша фактор - клиент қазіргі уақытта қолданбадағы Kestrel енгізуін өзгертуді жоспарламайды. Дегенмен, AspNetCore ішіндегі мәселелер (№4318, №7707) бұл көмектеспейді дейді...

Қорытындылай келе: ескерту енді Kestrel немесе UTF-8 (2019 жылы ?!) нақты проблемалары туралы емес, бұл факт туралы. зейін және дәйекті зерттеу Мәселелерді іздеу кезінде жасаған әрбір қадамыңыз ерте ме, кеш пе өз жемісін береді. Іске сәт!

PS

Біздің блогта да оқыңыз:

Ақпарат көзі: www.habr.com

пікір қалдыру