Usando cURL para diagnóstico de problemas
Published: 2023-12-20 01:53:32

"O sistema está lento".

Que sysadmin/devops/SRE (ou seja lá qual o branding moderno para o responsável pela infra) não odeia essa declaração, porque ela não te diz absolutamente nada.

A aplicação está lenta? Ou é o servidor de aplicação? Ou o balanceador? Ou o storage? 

E se a aplicação for distribuída, o problema fica 100 vezes maior.

A maneira moderna de fazer diagnóstico deste tipo de problema é usar **Application Performance Monitoring**, ou monitoramento de performance de aplicações, geralmente caríssimas, para entregar a resposta que você quer.

Mas o assunto não é este hoje; a ideia é tentar usar o **cURL** para identificar certos problemas de maneira mais ágil.

## curl custom output

Você certamente está acostumado a usar cURL para testar conectividade com um site:

```bash
# Qual a versão do kubectl mais nova?
$ curl -L -s https://dl.k8s.io/release/stable.txt
v1.29.0
```

Ou ainda baixar um programa: 

```bash
# Vamos baixar o kubectl!
$ 
$ curl -LO "https://dl.k8s.io/release/$LATEST/bin/linux/amd64/kubectl"
...

$ ls -l kubectl
-rw-r--r-- 1 marcelo marcelo 49704960 Dec 19 20:30 kubectl
```

Mas é possível customizar a saída com diversas coisas. A que mais uso é, de longe, a resposta HTTP:

```bash
$ curl www.google.com -so /dev/null -w '%{http_code}\n'
200
```

Uma maneira extremamente ineficiente de testar se uma aplicação está com problemas é disparar uma rajada de curl sobre ela e analisar a saída:

```bash
$ for i in {1..100} ; do 
  curl -so /dev/null 127.0.0.1 -w '%{http_code}\n' ;
done | sort | uniq -c
     83 200
      3 500
      5 503
      9 504
```

Como eu falei, é uma maneira extremamente ineficiente - você se daria melhor executando testes com uma ferramenta de verdade. Mas quando você está on-call com notebook e te acionam...

[**Este link**](https://everything.curl.dev/usingcurl/verbose/writeout) tem as variáveis que podem ser usadas juntamente com o programa **--write-out**.

## Variáveis de writeout para exibir tempos

Suponha que você queira saber quanto tempo um download demora, muita gente gosta de usar o comando **time**:

```bash
$ LATEST="$( curl -L -s https://dl.k8s.io/release/stable.txt )"
$ time curl -sLO "https://dl.k8s.io/release/$LATEST/bin/linux/amd64/kubectl"

real    0m0.523s
user    0m0.132s
sys     0m0.141s
```

Mas a melhor maneira é pedir para o próprio cURL trazer esta métrica!

```bash
$ LATEST="$( curl -L -s https://dl.k8s.io/release/stable.txt )"
$ curl -sLO  -w '%{time_total}\n' \
"https://dl.k8s.io/release/$LATEST/bin/linux/amd64/kubectl"
0.441882
```

A parte interessante é que existem **muitas variáveis diferentes de tempo** para usar, o que pode ser um excelente recurso na depuração de certos problemas!

[Este blog](https://blog.josephscott.org/2011/10/14/timing-details-with-curl/) teve a ideia legal de criar um arquivo com o seguinte formato:

```bash
$ cat > curl-format.txt << EOF
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_pretransfer: %{time_pretransfer}\n
time_redirect: %{time_redirect}\n
time_starttransfer: %{time_starttransfer}\n
———\n
time_total: %{time_total}\n
EOF
```

E agora podemos passar este arquivo para o cURL:

```bash
$ curl -w "@curl-format.txt" -o /dev/null -s http://devsres.com

time_namelookup: 0.000964
time_connect: 0.002114
time_appconnect: 0.000000
time_pretransfer: 0.002148
time_redirect: 0.000000
time_starttransfer: 0.010617
———
time_total: 0.010684
```

Genial, não é?

Bem, para achar genial, você precisa saber para que serve cada etapa e como usá-las em diagnóstico.

Vou usar o primeiro, **time_namelookup** apenas como exemplo!

### time_namelookup

O poder de diagnóstico desta diretiva não pode ser mensurado!

Problemas de DNS tendem a ser de difícil diagnóstico para pessoas sem background de redes. Mas observe o seguinte teste em uma máquina que está "consistentemente lenta":

```bash
$ curl -w "@curl-format.txt" -o /dev/null -s http://devsres.com
time_namelookup: 5.139453
time_connect: 5.156280
time_appconnect: 0.000000
time_pretransfer: 5.156360
time_redirect: 0.000000
time_starttransfer: 5.172965
———
time_total: 5.173074
```

Apenas para referência, estes são os tempos esperados em um dia normal:
```bash
$ curl -w "@curl-format.txt" -o /dev/null -s http://devsres.com
time_namelookup: 0.046703
time_connect: 0.047609
time_appconnect: 0.000000
time_pretransfer: 0.047647
time_redirect: 0.000000
time_starttransfer: 0.050434
———
time_total: 0.050491
```

Sabe porque o primeiro teste levou mais de 5 segundos? Porque esse é o **tempo padrão** que o **linux resolver** espera para testar a resolução usando o seu servidor secundário!

> (man resolv.conf)
> **timeout:n**
> 
> Sets  the  amount of time the resolver will wait for a response from a remote name server before retrying the query via a different name server. This may not be the total time taken by any resolver API call and there is no guarantee that a single resolver API call maps to a single timeout.  Measured in seconds, the default is RES_TIMEOUT (currently 5, see <resolv.h>). The value for this option is silently capped to 30.

Configurar múltiplos servidores DNS no seu /etc/resolv.conf é útil para garantir resiliência, mas o ambiente ficará **severamente degradado** caso o primeiro servidor falhe se precisar resolver nomes para seu funcionamento!

É possível reduzir esse tempo de timeout com a seguinte diretiva em seu resolv.conf:

```bash
$ cat /etc/resolv.conf
options timeout:1
nameserver 192.168.0.53
nameserver 1.1.1.1
nameserver 8.8.8.8
```

A configuração entra em vigor imediatamente:
```bash
$ curl -w "@curl-format.txt" -o /dev/null -s http://devsres.com
time_namelookup: 1.115452
time_connect: 1.135235
time_appconnect: 0.000000
time_pretransfer: 1.135265
time_redirect: 0.000000
time_starttransfer: 1.151118
———
time_total: 1.151194
```

Se não funcionar, talvez seja necessário ajudar esse parâmetro usando **network manager**.

Neste caso, usamos este parâmetro para permitir identificar que a causa do problema é o não funcionamento do servidor DNS do cliente, e não a aplicação! É um excelente primeiro teste a fazer para identificar se realmente há um problema no seu ambiente, antes de fazer dezenas de verificações e constatar que está tudo ok!