`systemtap-toolkit` et `stapxx` : Comment utiliser les données pour résoudre des problèmes complexes ?
API7.ai
December 22, 2022
Comme introduit dans l'article précédent, en tant qu'ingénieurs de développement côté serveur, nous n'approfondissons pas l'apprentissage des outils de débogage dynamique, mais nous nous limitons principalement à leur utilisation et écrivons au plus quelques scripts simples. Les aspects plus bas niveau, tels que le cache CPU, l'architecture, le compilateur, etc., relèvent du domaine des ingénieurs en performance.
Il existe deux projets open source dans OpenResty : openresty-systemtap-toolkit
et stapxx
. Ce sont des ensembles d'outils basés sur systemtap pour l'analyse et le diagnostic en temps réel de NGINX et OpenResty. Ils couvrent les fonctionnalités et scénarios de débogage courants tels que on-CPU, off-CPU, dictionnaire partagé, garbage collection, latence des requêtes, pooling de mémoire, pooling de connexions, accès aux fichiers, etc.
Dans cet article, nous allons parcourir ces outils et leur utilisation correspondante, afin de nous aider à trouver rapidement des outils pour localiser les problèmes lorsque nous rencontrons des problèmes de dépannage avec NGINX et OpenResty. Dans le monde d'OpenResty, apprendre à utiliser ces outils est un moyen sûr de progresser et une manière très efficace de communiquer avec d'autres développeurs - après tout, les données générées par les outils seront plus précises et détaillées que ce que nous pouvons décrire avec des mots.
Cependant, il est essentiel de noter que le mode LuaJIT GC64 est activé par défaut dans la version 1.15.8
d'OpenResty, mais openresty-systemtap-toolkit
et stapxx
ne suivent pas les changements correspondants, ce qui empêchera les outils internes de fonctionner correctement. Par conséquent, il est préférable d'utiliser ces outils dans l'ancienne version 1.13
d'OpenResty.
La plupart des contributeurs de projets open source sont à temps partiel et ne sont pas obligés de maintenir les outils fonctionnels, ce dont nous devons être conscients lors de l'utilisation de projets open source.
Exemple : shared dict
Commençons par un outil que nous connaissons probablement le mieux et qui est le plus facile à utiliser, ngx-lua-shdict
, comme exemple pour démarrer cet article.
ngx-lua-shdict
est un outil qui analyse les shared dict
s de NGINX et suit leurs opérations. Vous pouvez utiliser l'option -f
pour spécifier le dict
et la key
pour obtenir les données dans le shared dict
. L'option --raw
vous permet d'exporter la valeur brute de la key
spécifiée.
Voici un exemple de ligne de commande pour obtenir des données d'un shared dict
.
# En supposant que le PID du Worker NGINX est 5050
$ ./ngx-lua-shdict -p 5050 -f --dict dogs --key Jim --luajit20
Tracing 5050 (/opt/nginx/sbin/nginx)...
type: LUA_TBOOLEAN
value: true
expires: 1372719243270
flags: 0xa
De même, nous pouvons utiliser l'option -w
pour suivre les écritures dans le dict pour une key
donnée :
$./ngx-lua-shdict -p 5050 -w --key Jim --luajit20
Tracing 5050 (/opt/nginx/sbin/nginx)...
Hit Ctrl-C to end
set Jim exptime=4626322717216342016
replace Jim exptime=4626322717216342016
^C
Voyons comment cet outil est implémenté. ngx-lua-shdict
est un script Perl, mais l'implémentation n'a rien à voir avec Perl, qui est simplement utilisé pour générer le script stap
et l'exécuter.
open my $in, "|stap $stap_args -x $pid -" or die "Cannot run stap: $!\n";
Nous pourrions l'écrire en Python, PHP, Go, ou tout autre langage que nous aimons. Le point clé dans le script stap
est la ligne de code suivante :
probe process("$nginx_path").function("ngx_http_lua_shdict_set_helper")
C'est la probe
dont nous avons parlé précédemment, sondant la fonction ngx_http_lua_shdict_set_helper
. Les appels à cette fonction se trouvent tous dans le fichier lua-nginx-module/src/ngx_http_lua_shdict.c
du module lua-nginx-module
.
static int
ngx_http_lua_shdict_add(lua_State *L)
{
return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD);
}
static int
ngx_http_lua_shdict_safe_add(lua_State *L)
{
return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD
|NGX_HTTP_LUA_SHDICT_SAFE_STORE);
}
static int
ngx_http_lua_shdict_replace(lua_State *L)
{
return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_REPLACE);
}
De cette manière, nous pouvons suivre toutes les opérations du shared dict en sondant simplement cette fonction.
on-CPU, off-CPU
En utilisant OpenResty, le problème le plus courant que nous rencontrons est probablement la performance. Il existe deux principaux types de mauvaise performance, c'est-à-dire un faible QPS : une utilisation trop élevée du CPU et une utilisation trop faible du CPU. Le premier goulot d'étranglement peut être dû au fait de ne pas utiliser les méthodes d'optimisation de performance que nous avons introduites précédemment, tandis que le second peut être dû à des fonctions bloquantes. Les graphiques enflammés on-CPU et off-CPU correspondants peuvent nous aider à identifier la cause racine ultime.
Pour générer des graphiques enflammés on-CPU au niveau C, vous devez utiliser sample-bt
dans systemtap-toolkit
; tandis que les graphiques enflammés on-CPU au niveau Lua sont générés par lj-lua-stacks
dans stapxx
.
Prenons sample-bt
comme exemple pour voir comment l'utiliser. sample-bt
est un script qui échantillonne la pile d'appels de tout processus utilisateur que nous spécifions (pas seulement les processus NGINX et OpenResty).
Par exemple, nous pouvons échantillonner un processus Worker NGINX en cours d'exécution (avec un PID de 8736
) pendant 5 secondes avec le code suivant :
$ ./sample-bt -p 8736 -t 5 -u > a.bt
WARNING: Tracing 8736 (/opt/nginx/sbin/nginx) in user-space only...
WARNING: Missing unwind data for module, rerun with 'stap -d stap_df60590ce8827444bfebaf5ea938b5a_11577'
WARNING: Time's up. Quitting now...(it may take a while)
WARNING: Number of errors: 0, skipped probes: 24
Il génère le fichier de résultat a.bt
, qui peut être utilisé pour générer un graphique enflammé à l'aide de l'ensemble d'outils FlameGraph :
stackcollapse-stap.pl a.bt > a.cbt
flamegraph.pl a.cbt > a.svg
Le fichier a.svg
est le graphique enflammé généré, que nous pouvons ouvrir dans le navigateur pour le visualiser. Cependant, pendant la période d'échantillonnage, nous devons maintenir un certain nombre de requêtes. Sinon, il sera impossible de générer le graphique enflammé si le nombre d'échantillons est 0
.
Ensuite, nous verrons comment échantillonner off-CPU, en utilisant le script sample-bt-off-cpu
dans systemtap-toolkit
, qui est similaire à sample-bt
, comme suit :
$ ./sample-bt-off-cpu -p 10901 -t 5 > a.bt
WARNING: Tracing 10901 (/opt/nginx/sbin/nginx)...
WARNING: _stp_read_address failed to access memory location
WARNING: Time's up. Quitting now...(it may take a while)
WARNING: Number of errors: 0, skipped probes: 23
Dans stapxx
, l'outil pour analyser la latence est epoll-loop-blocking-distr
, qui échantillonne le processus utilisateur spécifié et génère la distribution de latence entre les appels successifs du système epoll_wait
.
$ ./samples/epoll-loop-blocking-distr.sxx -x 19647 --arg time=60
Start tracing 19647...
Please wait for 60 seconds.
Distribution of epoll loop blocking latencies (in milliseconds)
max/avg/min: 1097/0/0
value |-------------------------------------------------- count
0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 18471
1 |@@@@@@@@ 3273
2 |@ 473
4 | 119
8 | 67
16 | 51
32 | 35
64 | 20
128 | 23
256 | 9
512 | 2
1024 | 2
2048 | 0
4096 | 0
Comme nous pouvons le voir, cette sortie montre que la grande majorité des délais sont inférieurs à 1 ms, mais il y en a quelques-uns qui dépassent 200 ms, et ce sont ceux-là qu'il faut surveiller.
Suivi des Upstreams et des Phases
En plus des problèmes de performance qui peuvent survenir dans le code d'OpenResty lui-même, lorsque OpenResty communique avec des services upstream via des modules comme cosocket
ou proxy_pass
, si le service upstream lui-même a une grande latence, cela peut également avoir un impact significatif sur la performance globale.
À ce moment-là, nous pouvons utiliser les outils ngx-lua-tcp-recv-time
, ngx-lua-udp-recv-time
et ngx-single-req-latency
pour l'analyse. Voici un exemple de ngx-single-req-latency
.
Cet outil est un peu différent de la plupart des outils de l'ensemble d'outils. La plupart des autres outils sont basés sur un grand nombre d'échantillons et une analyse statistique pour tirer une conclusion mathématique sur la distribution. Au lieu de cela, ngx-single-req-latency
analyse des requêtes individuelles et suit le temps passé sur des requêtes individuelles à différentes phases dans OpenResty, telles que les phases rewrite
, access
et content
, ainsi que les upstreams.
Nous pouvons regarder un exemple de code spécifique :
# rendre l'outil ./stap++ visible dans PATH :
$ export PATH=$PWD:$PATH
# en supposant que le PID d'un processus Worker nginx est 27327
$ ./samples/ngx-single-req-latency.sxx -x 27327
Start tracing process 27327 (/opt/nginx/sbin/nginx)...
POST /api_json
total: 143596us, accept() ~ header-read: 43048us, rewrite: 8us, pre-access: 7us, access: 6us, content: 100507us
upstream: connect=29us, time-to-first-byte=99157us, read=103us
$ ./samples/ngx-single-req-latency.sxx -x 27327
Start tracing process 27327 (/opt/nginx/sbin/nginx)...
GET /robots.txt
total: 61198us, accept() ~ header-read: 33410us, rewrite: 7us, pre-access: 7us, access: 5us, content: 27750us
upstream: connect=30us, time-to-first-byte=18955us, read=96us
Cet outil suivra la première requête qu'il rencontre après son démarrage. La sortie est très similaire à opentracing
, et nous pouvons même considérer systemtap-toolkit
et stapxx
comme des versions non intrusives d'APM (Application Performance Management) dans OpenResty.
Résumé
En plus des outils courants dont j'ai parlé aujourd'hui, OpenResty fournit naturellement de nombreux autres outils que vous pouvez explorer et apprendre par vous-même.
Il y a une autre différence significative entre OpenResty et d'autres langages et plateformes de développement en termes de technologie de traçage, que nous pouvons apprécier lentement.
Gardez le code propre et stable, n'ajoutez pas de sondes à celui-ci, mais échantillonnez-le via des techniques de suivi dynamique externes.
Quels outils avez-vous utilisés pour suivre et analyser des problèmes lors de l'utilisation d'OpenResty ? Vous êtes invités à partager cet article, et nous partagerons et progresserons ensemble.