Wie Ivan den Fehler im Backend lokalisierte

In den Kommentaren zu einem meiner Artikel über grundlegende Linux-Shell-Befehle für Tester wurde zu Recht darauf hingewiesen, dass die Verwendung von Befehlen während des Testens nicht angegeben wurde. Ich dachte, es wäre besser spät als nie, und beschloss, die Geschichte des Backend-QS-Ingenieurs Vanya zu erzählen, der auf unerwartetes Serviceverhalten stieß und versuchte, genau herauszufinden, wo der Fehler aufgetreten war.







Was Wanja getestet hat



Vanya wusste, dass er das "nginx + service" -Paket testen würde.

Hier werde ich sofort eine Bemerkung machen: Ein solches Bundle wurde für diesen Artikel ausgewählt, einfach weil es die Verwendung verschiedener Dienstprogramme beim Debuggen eines Problems am deutlichsten demonstrieren kann und weil es sehr einfach zu konfigurieren und zu erhöhen ist. In Wirklichkeit kann es entweder nur ein Dienst oder eine Kette von Diensten sein, die sich gegenseitig Anfragen stellen.


Der Standard-HTTP-Server Python SimpleHTTPServer fungiert als Dienst, der als Antwort auf eine Anforderung ohne Parameter den Inhalt des aktuellen Verzeichnisses anzeigt:



[root@ivan test_dir_srv]# ls -l
total 0
-rw-r--r-- 1 root root 0 Aug 25 11:23 test_file
[root@ivan test_dir_srv]# python3 -m http.server --bind 127.0.0.1 8000
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...


Nginx ist wie folgt konfiguriert:



upstream test {
    server 127.0.0.1:8000;
}

server {
    listen       80;

    location / {
        proxy_pass http://test;
    }
}


Vanya musste einen einzelnen Testfall testen: um zu überprüfen, ob die Anfrage für / funktioniert. Er überprüfte und alles funktionierte:



MacBook-Pro-Ivan:~ ivantester$ curl http://12.34.56.78
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="test_file">test_file</a></li>
</ul>
<hr>
</body>
</html>


Aber dann haben die Entwickler irgendwann auf dem Prüfstand etwas aktualisiert, und Vanya hat einen Fehler erhalten:



MacBook-Pro-Ivan:~ ivantester$ curl http://12.34.56.78
<html>
<head><title>502 Bad Gateway</title></head>
<body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>


Er beschloss, diesen unverständlichen Fehler nicht an die Entwickler weiterzugeben, sondern ssh-Zugriff auf den Server zu erhalten und herauszufinden, was dort vor sich ging. Er hatte wenig Wissen auf dem Gebiet dieser Art des Debuggens von Problemen, aber er wollte wirklich lernen, also bewaffnete er sich mit Suchmaschinen, Logik und ging, um den Fehler zu lokalisieren.



Vanyas erster Gedanke: Protokolle



Wenn ein Fehler aufgetreten ist, müssen Sie ihn nur in der Protokolldatei finden. Aber zuerst müssen Sie die Protokolldatei selbst finden. Vanya ging zu Google und stellte fest, dass sich die Protokolle häufig im Verzeichnis / var / log befinden . In der Tat wurde das Nginx-Verzeichnis dort gefunden:



[root@ivan ~]# ls /var/log/nginx/
access.log  access.log-20200831  error.log  error.log-20200831


Ivan sah sich die letzten Zeilen des Fehlerprotokolls an und verstand, was falsch war: Die Entwickler haben einen Fehler in der Nginx-Konfiguration gemacht, ein Tippfehler hat sich in den Upstream-Port eingeschlichen.



[root@ivan ~]# tail /var/log/nginx/error.log
2020/08/31 04:36:21 [error] 15050#15050: *90452 connect() failed (111: Connection refused) while connecting to upstream, client: 31.170.95.221, server: , request: "GET / HTTP/1.0", upstream: "http://127.0.0.1:8009/", host: "12.34.56.78"


Welche Schlussfolgerung kann daraus gezogen werden? Protokolle sind der beste Freund von Testern und Entwicklern bei der Lokalisierung von Fehlern. Wenn der Dienst ein unerwartetes Verhalten aufweist, die Protokolle jedoch nichts enthalten, ist dies ein Grund, die Aufgabe mit der Anforderung zum Hinzufügen von Protokollen an die Entwicklung zurückzugeben. Wenn nginx nicht über einen erfolglosen Versuch, den Upstream zu erreichen, in das Protokoll geschrieben hätte, wäre es dann schwieriger gewesen, nach einem Problem zu suchen?


In diesem Moment dachte Vanya: „Was wäre, wenn sich die Nginx-Protokolle in einem anderen Verzeichnis befänden? Wie würde ich sie finden? " In ein paar Jahren wird Vanya mehr Erfahrung mit Diensten unter Linux haben und wissen, dass der Pfad zur Protokolldatei häufig als Befehlszeilenargument an den Dienst übergeben wird oder in einer Konfigurationsdatei enthalten ist, deren Pfad häufig auch als Befehlszeilenargument an den Dienst übergeben wird. Im Idealfall sollte der Pfad zur Protokolldatei in der Servicedokumentation angegeben werden.



Übrigens finden Sie in der Konfigurationsdatei den Pfad zur Protokolldatei in nginx:



[root@ivan ~]# ps ax | grep nginx | grep master
root     19899  0.0  0.0  57392  2872 ?        Ss    2019   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
[root@ivan ~]# grep "log" /etc/nginx/nginx.conf
error_log  /var/log/nginx/error.log warn;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    access_log  /var/log/nginx/access.log  main;


Was ist, wenn die Protokolle nichts enthalten?



In seiner Freizeit beschloss Vanya darüber nachzudenken, wie er mit der Aufgabe fertig geworden wäre, wenn Nginx nichts in das Protokoll geschrieben hätte. Vanya wusste, dass der Dienst Port 8000 abhört, und beschloss daher, den Datenverkehr auf diesem Port auf dem Server zu überprüfen. Das Dienstprogramm tcpdump half ihm dabei . Bei der richtigen Konfiguration wurde eine Anfrage und eine Antwort angezeigt:



Dump-Verkehr auf Port 8000
[root@ivan ~]# tcpdump -nn -i lo -A port 8000
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
09:10:42.114284 IP 127.0.0.1.33296 > 127.0.0.1.8000: Flags [S], seq 3390176024, win 43690, options [mss 65495,sackOK,TS val 830366494 ecr 0,nop,wscale 8], length 0
E..<..@.@..............@.............0.........
1~c.........
09:10:42.114293 IP 127.0.0.1.8000 > 127.0.0.1.33296: Flags [S.], seq 4147196208, ack 3390176025, win 43690, options [mss 65495,sackOK,TS val 830366494 ecr 830366494,nop,wscale 8], length 0
E..<..@.@.<..........@...110.........0.........
1~c.1~c.....
09:10:42.114302 IP 127.0.0.1.33296 > 127.0.0.1.8000: Flags [.], ack 1, win 171, options [nop,nop,TS val 830366494 ecr 830366494], length 0
E..4..@.@..............@.....111.....(.....
1~c.1~c.
09:10:42.114329 IP 127.0.0.1.33296 > 127.0.0.1.8000: Flags [P.], seq 1:88, ack 1, win 171, options [nop,nop,TS val 830366494 ecr 830366494], length 87
E.....@.@..b...........@.....111...........
1~c.1~c.GET / HTTP/1.0
Host: test
Connection: close
User-Agent: curl/7.64.1
Accept: */*

09:10:42.114333 IP 127.0.0.1.8000 > 127.0.0.1.33296: Flags [.], ack 88, win 171, options [nop,nop,TS val 830366494 ecr 830366494], length 0
E..4R/@.@............@...111...p.....(.....
1~c.1~c.
09:10:42.115062 IP 127.0.0.1.8000 > 127.0.0.1.33296: Flags [P.], seq 1:155, ack 88, win 171, options [nop,nop,TS val 830366494 ecr 830366494], length 154
E...R0@.@............@...111...p...........
1~c.1~c.HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.7.2
Date: Mon, 07 Sep 2020 13:10:42 GMT
Content-type: text/html; charset=utf-8
Content-Length: 340

09:10:42.115072 IP 127.0.0.1.33296 > 127.0.0.1.8000: Flags [.], ack 155, win 175, options [nop,nop,TS val 830366494 ecr 830366494], length 0
E..4.@.@..............@...p.11......(.....
1~c.1~c.
09:10:42.115094 IP 127.0.0.1.8000 > 127.0.0.1.33296: Flags [P.], seq 155:495, ack 88, win 171, options [nop,nop,TS val 830366494 ecr 830366494], length 340
E...R1@.@..<.........@...11....p.....|.....
1~c.1~c.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="test_file">test_file</a></li>
</ul>
<hr>
</body>
</html>

09:10:42.115098 IP 127.0.0.1.33296 > 127.0.0.1.8000: Flags [.], ack 495, win 180, options [nop,nop,TS val 830366494 ecr 830366494], length 0
E..4.
@.@..............@...p.13......(.....
1~c.1~c.
09:10:42.115128 IP 127.0.0.1.8000 > 127.0.0.1.33296: Flags [F.], seq 495, ack 88, win 171, options [nop,nop,TS val 830366494 ecr 830366494], length 0
E..4R2@.@............@...13....p.....(.....
1~c.1~c.
09:10:42.115264 IP 127.0.0.1.33296 > 127.0.0.1.8000: Flags [F.], seq 88, ack 496, win 180, options [nop,nop,TS val 830366495 ecr 830366494], length 0
E..4..@.@..............@...p.13 .....(.....
1~c.1~c.
09:10:42.115271 IP 127.0.0.1.8000 > 127.0.0.1.33296: Flags [.], ack 89, win 171, options [nop,nop,TS val 830366495 ecr 830366495], length 0
E..4R3@.@............@...13 ...q.....(.....
1~c.1~c.
^C
12 packets captured
24 packets received by filter
0 packets dropped by kernel




Bei einer falschen Konfiguration (mit Port 8009 im Nginx-Upstream) gab es keinen Verkehr auf Port 8000. Vanya war begeistert: Auch wenn die Entwickler bei Netzwerkfehlern vergessen haben, in das Protokoll zu schreiben, können Sie zumindest herausfinden, ob der Datenverkehr zum gewünschten Host oder Port geleitet wird.



Welche Schlussfolgerung kann aus dieser Geschichte gezogen werden? Auch wenn keine Protokolle vorhanden sind, verfügt Linux über Dienstprogramme, die bei der Lokalisierung von Problemen helfen können.


Und wenn nicht ein Netzwerk?



Alles hat gut funktioniert, aber eines Tages bekam Wanja einen weiteren Fehler, diesmal anders:



MacBook-Pro-Ivan:~ ivantester$ curl http://12.34.56.78
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
        "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>Error response</title>
    </head>
    <body>
        <h1>Error response</h1>
        <p>Error code: 404</p>
        <p>Message: File not found.</p>
        <p>Error code explanation: HTTPStatus.NOT_FOUND - Nothing matches the given URI.</p>
    </body>
</html>


Vanya ging erneut zum Server, aber dieses Mal hing das Problem nicht mit dem Netzwerk zusammen. Im Serviceprotokoll stand auch, dass die Datei nicht gefunden wurde , und Wanja beschloss herauszufinden, warum ein solcher Fehler plötzlich auftrat. Er weiß, dass es einen Prozess python3 -m http.server gibt , aber er weiß nicht, in welchem ​​Verzeichnis dieser Dienst angezeigt wird (oder mit anderen Worten, welches das aktuelle Arbeitsverzeichnis dieses Prozesses ist). Er findet es mit dem Befehl lsof heraus :



[root@ivan ~]# ps aux | grep python | grep "http.server"
root     20638  0.0  0.3 270144 13552 pts/2    S+   08:29   0:00 python3 -m http.server
[root@ivan ~]# lsof -p 20638 | grep cwd
python3 20638 root  cwd    DIR     253,1      4096 1843551 /root/test_dir_srv2


Dies kann auch mit dem Befehl pwdx oder über das Verzeichnis proc erfolgen :



[root@ivan ~]# pwdx 20638
20638: /root/test_dir_srv2
[root@ivan ~]# ls -l /proc/20638/cwd
lrwxrwxrwx 1 root root 0 Aug 31 08:37 /proc/20638/cwd -> /root/test_dir_srv2


Ein solches Verzeichnis existiert tatsächlich auf dem Server und enthält eine Datei mit dem Namen test_file . Was ist los? Ivan gegoogelt und fand den strace - Dienstprogramm , mit dem Sie sehen , welches System einen Prozess führt Anrufe (von der Art und Weise gibt es einen guten Artikel auf strace auf Habré, und nicht einmal einen ). Sie können entweder einen neuen Prozess über strace starten oder mit diesem Dienstprogramm eine Verbindung zu einem bereits ausgeführten Prozess herstellen. Die zweite Option passte zu Wanja:



Strace Ausgabe
[root@ivan ~]# strace -ff -p 20638
strace: Process 20638 attached
restart_syscall(<... resuming interrupted poll ...>) = 0
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 1 ([{fd=4, revents=POLLIN}])
accept4(4, {sa_family=AF_INET, sin_port=htons(57530), sin_addr=inet_addr("127.0.0.1")}, [16], SOCK_CLOEXEC) = 5
clone(child_stack=0x7f2beeb28fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f2beeb299d0, tls=0x7f2beeb29700, child_tidptr=0x7f2beeb299d0) = 21062
futex(0x11204d0, FUTEX_WAIT_PRIVATE, 0, NULLstrace: Process 21062 attached
 <unfinished ...>
[pid 21062] set_robust_list(0x7f2beeb299e0, 24) = 0
[pid 21062] futex(0x11204d0, FUTEX_WAKE_PRIVATE, 1) = 1
[pid 20638] <... futex resumed> )       = 0
[pid 20638] futex(0x921c9c, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 27, {1598879772, 978949000}, ffffffff <unfinished ...>
[pid 21062] futex(0x921c9c, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x921c98, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
[pid 20638] <... futex resumed> )       = 0
[pid 20638] futex(0x921cc8, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 21062] futex(0x921cc8, FUTEX_WAKE_PRIVATE, 1) = 1
[pid 20638] <... futex resumed> )       = 0
[pid 20638] futex(0x921cc8, FUTEX_WAKE_PRIVATE, 1) = 0
[pid 20638] poll([{fd=4, events=POLLIN}], 1, 500 <unfinished ...>
[pid 21062] recvfrom(5, "GET / HTTP/1.1\r\nConnection: upgr"..., 8192, 0, NULL, NULL) = 153
[pid 21062] stat("/root/test_dir_srv/", 0x7f2beeb27350) = -1 ENOENT (No such file or directory)
[pid 21062] open("/root/test_dir_srv/", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
[pid 21062] write(2, "127.0.0.1 - - [31/Aug/2020 09:16"..., 70) = 70
[pid 21062] write(2, "127.0.0.1 - - [31/Aug/2020 09:16"..., 60) = 60
[pid 21062] sendto(5, "HTTP/1.0 404 File not found\r\nSer"..., 184, 0, NULL, 0) = 184
[pid 21062] sendto(5, "<!DOCTYPE HTML PUBLIC \"-//W3C//D"..., 469, 0, NULL, 0) = 469
[pid 21062] shutdown(5, SHUT_WR)        = 0
[pid 21062] close(5)                    = 0
[pid 21062] madvise(0x7f2bee329000, 8368128, MADV_DONTNEED) = 0
[pid 21062] exit(0)                     = ?
[pid 21062] +++ exited with 0 +++
<... poll resumed> )                    = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500)   = 0 (Timeout)
poll([{fd=4, events=POLLIN}], 1, 500^Cstrace: Process 20638 detached
 <detached ...>




Normalerweise ist die Ausgabe von strace ziemlich umfangreich (und möglicherweise sehr groß), daher ist es bequemer, sie sofort in eine Datei umzuleiten und dann nach den erforderlichen Systemaufrufen darin zu suchen. In diesem Fall können Sie sofort feststellen, dass der Dienst versucht, das Verzeichnis / root / test_dir_srv / zu öffnen. Jemand hat es umbenannt und den Dienst danach nicht neu gestartet. Daher wird 404 zurückgegeben.



Wenn Sie sofort verstehen, welche Systemaufrufe Sie anzeigen müssen, können Sie die Option verwenden -e :



[root@ivan ~]# strace -ff -e trace=open,stat -p 20638
strace: Process 20638 attached
strace: Process 21396 attached
[pid 21396] stat("/root/test_dir_srv/", 0x7f2beeb27350) = -1 ENOENT (No such file or directory)
[pid 21396] open("/root/test_dir_srv/", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
[pid 21396] +++ exited with 0 +++
^Cstrace: Process 20638 detached


: « » , strace. , , (, / ), . ltrace.


- ?



Vanya hörte hier nicht auf und fand heraus, dass es einen GNU Project Debugger - GDB gibt . Mit seiner Hilfe können Sie in den Prozess " einsteigen" und ihn sogar geringfügig ändern. Und Vanya beschloss, den letzten Fehler mit GDB zu finden . Er schlug vor, dass Sie versuchen können, einen Haltepunkt für die Funktion open () zu setzen, um zu sehen, was passiert , da der Dienst den Inhalt des Verzeichnisses anzeigt :

Ausgabe des Gdb-Dienstprogramms
[root@ivan ~]# gdb -p 23998
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 23998
… <        debugging symbols...>
...
0x00007f2284c0b20d in poll () at ../sysdeps/unix/syscall-template.S:81
81	T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
Missing separate debuginfos, use: debuginfo-install keyutils-libs-1.5.8-3.el7.x86_64 krb5-libs-1.15.1-34.el7.x86_64 libcom_err-1.42.9-13.el7.x86_64 libgcc-4.8.5-36.el7.x86_64 libselinux-2.5-14.1.el7.x86_64 openssl-libs-1.0.2k-16.el7.x86_64 pcre-8.32-17.el7.x86_64 zlib-1.2.7-18.el7.x86_64
(gdb) set follow-fork-mode child
(gdb) b open
Breakpoint 1 at 0x7f2284c06d20: open. (2 locations)
(gdb) c
Continuing.
[New Thread 0x7f227a165700 (LWP 24030)]
[Switching to Thread 0x7f227a165700 (LWP 24030)]

Breakpoint 1, open64 () at ../sysdeps/unix/syscall-template.S:81
81	T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) n
83	T_PSEUDO_END (SYSCALL_SYMBOL)
(gdb) n
_io_FileIO___init___impl (opener=<optimized out>, closefd=<optimized out>, mode=<optimized out>, nameobj=0x7f227a68f6f0, self=0x7f227a68f6c0) at ./Modules/_io/fileio.c:381
381	                Py_END_ALLOW_THREADS
(gdb) n
379	                self->fd = open(name, flags, 0666);
(gdb) n
381	                Py_END_ALLOW_THREADS
(gdb) print name
$1 = 0x7f227a687c90 "/root/test_dir_srv/"
(gdb) q
A debugging session is active.

	Inferior 1 [process 23998] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/local/bin/python3.7, process 23998
[Inferior 1 (process 23998) detached]




Nach dem Befehl c ( Fortfahren ) startete Vanya Curl in einer anderen Konsole , erreichte einen Haltepunkt im Debugger und begann Schritt für Schritt, dieses Programm (dh den Dienst) auszuführen. Sobald er gefunden offen in einem gewissen Weg Name , druckte er den Wert dieser Variablen und Säge „ / root / test_dir_srv / “.

GDB ist ein leistungsstarkes Tool, und hier ist der einfachste Anwendungsfall. Manchmal kann es bei der Reproduktion komplexer Fälle hilfreich sein (z. B. können Sie den Vorgang zum richtigen Zeitpunkt anhalten und die Rennbedingungen reproduzieren), und es hilft auch beim Lesen von Core-Dump-Dateien.


Was ist, wenn Docker?



Zu einem bestimmten Zeitpunkt entschied DevOps, dass der Dienst jetzt mit einem Docker-Container bereitgestellt werden sollte, und es war erforderlich, alle von Vanya gefundenen Fälle erneut zu testen. Wanja googelte leicht Folgendes:



  1. Sie können tcpdump , strace und gdb in einem Container verwenden, müssen jedoch die Linux-Funktionen berücksichtigen (in einem Artikel wird erläutert, warum strace in einem Container ohne --cap-add = SYS_PTRACE nicht funktioniert hat ).
  2. Die Option --pid kann verwendet werden .


Aber er fragte sich, ob es möglich war, den gesamten Verkehr direkt vom Host zum Container (oder vom Container) zu sehen. In tcpdump haben Sie die Möglichkeit, eine beliebige Verkehrsschnittstelle anzuzeigen (Option -i ), jeder Container entspricht einer virtuellen Schnittstelle Veth (dies kann beispielsweise über die ifconfig oder die ip a gesehen werden ), aber woher wissen Sie, welchem ​​Container welche Schnittstelle entspricht? Wenn der Container kein Host-Netzwerk verwendet , befindet sich in ihm eine Netzwerkschnittstelle eth0 , über die er über das Netzwerk mit anderen Containern und dem Host kommunizieren kann. Sie müssen nur noch den ifindex ermitteln, dessen Schnittstelle auf dem Host mit der iflink- Schnittstelle übereinstimmteth0 des Containers (was dies bedeutet, können Sie hier lesen ).



[root@ivan ~]# for f in `ls /sys/class/net/veth*/ifindex`; do echo $f; cat $f; done | grep -B 1 `docker exec test_service_container cat /sys/class/net/eth0/iflink` | head -1
/sys/class/net/veth6c18dba/ifindex


Jetzt können Sie tcpdump auf der Schnittstelle veth6c18dba ausführen :



tcpdump -i veth6c18dba


Es gibt jedoch einen einfacheren Weg: Sie können die IP-Adresse des Containers in seinem Netzwerk finden und den Datenverkehr darauf abhören:



[root@ivan ~]# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' test_service_container
172.17.0.10
[root@ivan ~]# tcpdump -i any host 172.17.0.10


Fazit: Das Debuggen in einem Docker-Container ist keine große Sache. Die Dienstprogramme arbeiten darin, und Sie können Docker-Protokolle verwenden , um die Protokolle zu lesen .


Schlussfolgerungen



Als verantwortungsbewusster Ingenieur beschloss Vanya, kurz neue Informationen für sich in der internen Wissensbasis zu skizzieren. Folgendes hat er geschrieben:



  • Protokolle sind der beste Freund des Menschen. Wenn ein unerwartetes Verhalten des Dienstes auftritt und gleichzeitig nichts in das Protokoll geschrieben wird, ist dies ein Grund, die Entwickler aufzufordern, Protokolle hinzuzufügen.
  • , , . , Linux , .
  • tcpdump. , .
  • «» strace, ltrace gdb.
  • , Docker-.
  • /proc/PID. , /proc/PID/fd .
  • Die Dienstprogramme ps , ls , stat , lsof , ss , du , top , free , ip , ldconfig , ldd und andere können ebenfalls dazu beitragen, verschiedene Informationen über das System, Dateien oder Prozesse abzurufen .


Ich hoffe, diese Geschichte hat Ihnen geholfen, und mindestens einmal hilft sie Ihnen zu verstehen, was los ist, wenn Sie etwas unter Linux debuggen. Wenn Wanja etwas verpasst hat, teile es in den Kommentaren!



All Articles