`systemtap-toolkit` dan `stapxx`: Bagaimana Menggunakan Data untuk Menyelesaikan Masalah yang Sulit?
API7.ai
December 22, 2022
Seperti yang diperkenalkan dalam artikel sebelumnya, sebagai insinyur pengembangan sisi server, kita tidak melakukan pembelajaran mendalam tentang alat-alat debugging dinamis, tetapi kebanyakan hanya berada pada tingkat penggunaan dan menulis beberapa skrip sederhana paling banyak. Tingkat yang lebih rendah, seperti cache CPU, arsitektur, kompiler, dll., adalah domain insinyur kinerja.
Ada dua proyek sumber terbuka di OpenResty: openresty-systemtap-toolkit dan stapxx. Mereka adalah alat yang dibungkus berbasis systemtap untuk analisis dan diagnosis real-time NGINX dan OpenResty. Mereka dapat mencakup on-CPU, off-CPU, kamus bersama, pengumpulan sampah, latensi permintaan, pooling memori, pooling koneksi, akses file, dan fitur serta skenario debugging umum lainnya.
Dalam artikel ini, kita akan melihat alat-alat ini dan penggunaan yang sesuai, untuk membantu kita dengan cepat menemukan alat untuk melokalisasi masalah ketika kita menghadapi masalah troubleshooting dengan NGINX dan OpenResty. Di dunia OpenResty, belajar menggunakan alat-alat ini adalah cara pasti untuk maju, dan cara yang sangat efektif untuk berkomunikasi dengan pengembang lain - setelah semua, data yang dihasilkan oleh alat akan lebih akurat dan detail daripada yang dapat kita jelaskan dengan kata-kata.
Namun, penting untuk dicatat bahwa mode LuaJIT GC64 diaktifkan secara default di OpenResty versi 1.15.8, tetapi openresty-systemtap-toolkit dan stapxx tidak mengikuti perubahan yang sesuai, yang akan menyebabkan alat di dalamnya tidak berfungsi dengan benar. Jadi, kita sebaiknya menggunakan alat-alat ini di versi lama OpenResty 1.13.
Kebanyakan kontributor proyek sumber terbuka adalah paruh waktu dan tidak berkewajiban untuk menjaga alat tetap berfungsi, yang perlu kita sadari saat menggunakan proyek sumber terbuka.
Contoh: shared dict
Mari kita mulai dengan alat yang mungkin paling kita kenal dan paling mudah untuk memulai, ngx-lua-shdict, sebagai contoh untuk memulai posting hari ini.
ngx-lua-shdict adalah alat yang menganalisis shared dict NGINX dan melacak operasinya. Anda dapat menggunakan opsi -f untuk menentukan dict dan key untuk mendapatkan data dalam shared dict. Opsi --raw memungkinkan Anda mengekspor nilai mentah dari key yang ditentukan.
Berikut adalah contoh baris perintah untuk mendapatkan data dari shared dict.
# Asumsikan PID Worker NGINX adalah 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
Demikian pula, kita dapat menggunakan opsi -w untuk melacak penulisan dict untuk key tertentu:
$./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
Mari kita lihat bagaimana alat ini diimplementasikan. ngx-lua-shdict adalah skrip Perl, tetapi implementasinya tidak ada hubungannya dengan Perl, yang hanya digunakan untuk menghasilkan skrip stap dan menjalankannya.
open my $in, "|stap $stap_args -x $pid -" or die "Cannot run stap: $!\n";
Kita dapat menulisnya dalam Python, PHP, Go, atau bahasa apa pun yang kita suka. Poin kunci dalam skrip stap adalah baris kode berikut:
probe process("$nginx_path").function("ngx_http_lua_shdict_set_helper")
Ini adalah probe yang kita sebutkan sebelumnya, memprobe fungsi ngx_http_lua_shdict_set_helper. Panggilan ke fungsi ini semua ada di modul lua-nginx-module file lua-nginx-module/src/ngx_http_lua_shdict.c.
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); }
Dengan cara ini, kita dapat melacak semua operasi dari shared dict hanya dengan memprobe fungsi ini.
on-CPU, off-CPU
Dalam menggunakan OpenResty, masalah yang paling sering kita temui seharusnya adalah kinerja. Ada dua jenis utama kinerja yang buruk, yaitu QPS rendah: penggunaan CPU terlalu tinggi dan penggunaan CPU terlalu rendah. Hambatan yang pertama mungkin karena tidak menggunakan metode optimasi kinerja yang kami perkenalkan sebelumnya, sedangkan yang kedua mungkin karena fungsi yang memblokir. Grafik api on-CPU dan off-CPU yang sesuai dapat membantu kita mengidentifikasi akar penyebab utama.
Untuk menghasilkan grafik api on-CPU tingkat C, Anda perlu menggunakan sample-bt di systemtap-toolkit; sedangkan grafik api on-CPU tingkat Lua dihasilkan oleh lj-lua-stacks di stapxx.
Mari kita ambil sample-bt sebagai contoh cara menggunakannya. sample-bt adalah skrip yang mengambil sampel stack panggilan dari proses pengguna apa pun yang kita tentukan (tidak hanya proses NGINX dan OpenResty).
Misalnya, kita dapat mengambil sampel proses Worker NGINX yang sedang berjalan (dengan PID 8736) selama 5 detik dengan kode berikut:
$ ./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
Ini menghasilkan file hasil a.bt, yang dapat digunakan untuk menghasilkan grafik api menggunakan alat FlameGraph:
stackcollapse-stap.pl a.bt > a.cbt flamegraph.pl a.cbt > a.svg
a.svg adalah grafik api yang dihasilkan, yang dapat kita buka di browser untuk dilihat. Namun, selama periode pengambilan sampel, kita harus mempertahankan sejumlah permintaan. Jika tidak, tidak akan ada cara untuk menghasilkan grafik api jika jumlah sampel adalah 0.
Selanjutnya, kita akan melihat cara mengambil sampel off-CPU, menggunakan skrip sample-bt-off-cpu di systemtap-toolkit, yang mirip dengan sample-bt, sebagai berikut:
$ ./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
Di stapxx, alat untuk menganalisis latensi adalah epoll-loop-blocking-distr, yang mengambil sampel proses pengguna yang ditentukan dan mengeluarkan distribusi latensi antara panggilan sistem epoll_wait yang berurutan.
$ ./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
Seperti yang dapat kita lihat, output ini menunjukkan bahwa sebagian besar penundaan kurang dari 1 ms, tetapi ada beberapa yang di atas 200 ms, dan ini adalah yang perlu diperhatikan.
Upstream dan Pelacakan Fase
Selain masalah kinerja yang mungkin terjadi dalam kode OpenResty itu sendiri, ketika OpenResty berkomunikasi dengan layanan upstream melalui modul upstream seperti cosocket atau proxy_pass, jika layanan upstream itu sendiri memiliki latensi yang besar, itu juga dapat memiliki dampak yang signifikan pada kinerja keseluruhan.
Pada saat ini, kita dapat menggunakan alat ngx-lua-tcp-recv-time, ngx-lua-udp-recv-time dan ngx-single-req-latency untuk analisis. Berikut adalah contoh ngx-single-req-latency.
Alat ini tidak sama dengan kebanyakan alat di dalam toolkit. Kebanyakan alat lain didasarkan pada sejumlah besar sampel dan analisis statistik untuk menarik kesimpulan matematis tentang distribusi. Sebaliknya, ngx-single-req-latency menganalisis permintaan individu dan melacak waktu yang dihabiskan pada permintaan individu di berbagai fase di OpenResty, seperti fase rewrite, access, dan content, serta upstream.
Kita dapat melihat contoh kode spesifik:
# membuat alat ./stap++ terlihat di PATH: $ export PATH=$PWD:$PATH # asumsikan pid proses worker nginx adalah 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
Alat ini akan melacak permintaan pertama yang ditemui setelah dimulai. Outputnya sangat mirip dengan opentracing, dan kita bahkan dapat menganggap systemtap-toolkit dan stapxx, sebagai versi non-intrusif dari APM (Application Performance Management) di OpenResty.
Ringkasan
Selain alat-alat umum yang telah saya bicarakan hari ini, OpenResty secara alami menyediakan lebih banyak alat yang dapat Anda jelajahi dan pelajari sendiri.
Ada satu perbedaan signifikan antara OpenResty dan bahasa pengembangan serta platform lain dalam hal teknologi pelacakan, yang dapat kita apresiasi secara perlahan.
Jaga kode tetap bersih dan stabil, jangan tambahkan probe ke dalamnya, tetapi ambil sampel melalui teknik pelacakan dinamis eksternal.
Alat apa yang telah Anda gunakan untuk melacak dan menganalisis masalah saat menggunakan OpenResty? Anda dipersilakan untuk membagikan artikel ini, dan kita akan berbagi dan maju bersama.