El vivo kun Kubernetes: Kiel la HTTP-servilo ne favoris la hispanojn

El vivo kun Kubernetes: Kiel la HTTP-servilo ne favoris la hispanojn

Reprezentanto de nia kliento, kies aplikaĵa stako loĝas en la Microsoft-nubo (Azure), traktis problemon: lastatempe, iuj petoj de iuj klientoj el Eŭropo komencis finiĝi kun eraro 400 (Malbona Peto). Ĉiuj aplikaĵoj estas skribitaj en .NET, deplojitaj en Kubernetes...

Unu el la aplikoj estas la API, tra kiu finfine venas la tuta trafiko. Ĉi tiu trafiko estas aŭskultata de la HTTP-servilo Krestrel, agordita de la .NET-kliento kaj gastigita en pod. Kun senararigado, ni estis bonŝancaj en la senco ke ekzistis specifa uzanto kiu konstante reproduktis la problemon. Tamen ĉio estis komplikita de la trafikĉeno:

El vivo kun Kubernetes: Kiel la HTTP-servilo ne favoris la hispanojn

La eraro en Ingress aspektis jene:

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

En la sama tempo, Kestrel donis:

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

Eĉ kun maksimuma verboseco, la Kestrel-eraro enhavis ekstreme malmulte da utilaj informoj:

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

Ŝajnus, ke nur tcpdump helpos solvi ĉi tiun problemon... sed mi ripetos pri la trafika ĉeno:

El vivo kun Kubernetes: Kiel la HTTP-servilo ne favoris la hispanojn

Esploro

Evidente, estas pli bone aŭskulti trafikon sur tiu specifa nodo, kie Kubernetes deplojis pod: la volumeno de la rubejo estos tia, ke eblos trovi almenaŭ ion sufiĉe rapide. Kaj efektive, ekzameninte ĝin, oni rimarkis la jenan kadron:

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

Post pli proksima inspektado de la rubejo, la vorto estis rimarkita M.laga. Estas facile konjekti, ke ne ekzistas M.laga urbo en Hispanio (sed ekzistas Málaga). Kaptante ĉi tiun ideon, ni rigardis la Ingress-agordojn, kie ni vidis tiun enmetitan antaŭ monato (laŭ peto de la kliento) "sendanĝera" fragmento:

    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;

Post malŝalto de la plusendado de ĉi tiuj kaplinioj, ĉio fariĝis bona! (Baldaŭ evidentiĝis, ke la aplikaĵo mem ne plu bezonas ĉi tiujn kapliniojn.)

Nun ni rigardu la problemon pli ĝenerale. Ĝi povas esti facile reproduktita ene de la aplikaĵo farante telnet-peton al 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

... revenas 401 Unauthorized, kiel atendite. Kio okazas se ni faras:

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

?

Revenos 400 Bad request — en la aplikaĵa protokolo ni ricevos eraron, kiu jam estas konata al ni:

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

Rezultoj

Specife Turfalko ne povas ĝuste prilabori HTTP-kapojn kun la ĝustaj signoj en UTF-8, kiuj estas enhavitaj en la nomoj de sufiĉe granda nombro da urboj.

Plia faktoro en nia kazo estas, ke la kliento nuntempe ne planas ŝanĝi la efektivigon de Kestrel en la aplikaĵo. Tamen, problemoj en AspNetCore mem (NeXX, NeXX) ili diras, ke tio ne helpos...

Resume: la noto ne plu temas pri la specifaj problemoj de Kestrel aŭ UTF-8 (en 2019?!), sed pri tio, ke atenteco kaj konsekvenca studo Ĉiu paŝo, kiun vi faras dum serĉado de problemoj, pli aŭ malpli frue donos fruktojn. Bonŝancon!

PS

Legu ankaŭ en nia blogo:

fonto: www.habr.com

Aldoni komenton