Remote debugging v Golangu
Publikováno: 20.1.2022
Zprvu si vystačíte s lokálním debuggingem přímo z IDE. Ale na každého jednou dojde… Podívejme se na řešení.
Text vyšel původně na webu autora.
V Golangu dělám už druhým rokem a je to láska. Je to první jazyk, po kterém sáhnu, když potřebuju udělat nějaký PoC. Nejčastěji používám GoLand IDE, občas píšu ve Vimu s vim-go pluginem. Zkoušel jsem i VS Code, ale nějak jsme se neskamarádili.
Vzhledem k tomu, že poslední tři roky píšu cloudovou infrastrukturu, jsou moje aplikace relativně jednoduché micro-servisy. A tak jsem si prozatím vystačil jen s lokálním debuggingem přímo z IDE. Ale na každého jednou dojde…
Pokud tomu rozumím, stačí logování a testy. Debugguju, jen když nechápu, jak to funguje. ~ SoftWare Samuraj
Tak co se to stalo, že jsem najednou potřeboval remote debugging? Tož, to bylo tak…
Měl jsem napsanou aplikaci, krásně otestovanou, lokálně běhala jak víno. Pak jsem ji vrazil do Dockeru a najednou nefungovala. Marně jsem měnil parametry, marně jsem přidával logovací hlášky, marně jsem pročítal zdrojáky třetích stran. Musel jsem si přiznat, že nic jiného, než remote debugging mi nepomůže.
Čili otázka zní: jak debuggovat Golang aplikaci běžící v Dockeru?
Golang debugger
Golang debugger se jmenuje Delve a žije na GitHubu. Instalace je přímočará a jakmile ji dokončímě, měli bychom vidět:
$ go get github.com/go-delve/delve/cmd/dlv
$ dlv version
Delve Debugger
Version: 1.5.1
Build: $Id: bca418ea7ae2a4dcda985e623625da727d4525b5 $
Jelikož se chci věnovat pouze remote debuggingu, tak pominu ostatní finesy, které Delve nabízí a zaměřím se pouze na dva následující příkazy: dlv attach
& dlv exec
.
dlv attach
Příkaz dlv attach
způsobí, že se debugger připojí k běžícímu procesu a začne novou debug session. Po skončení session je možné proces buď nechat běžet, nebo ukončit. Syntaxe je jednoduchá:
dlv attach <pid>
dlv exec
Příkaz dlv exec
spustí danou binárku a nastartuje novou debug session. Při exitování se opět můžeme rozhodnout, jestli proces killnout, nebo nechat žít. Syntaxe taktéž jednoduchá:
dlv exec <path-to-binary>
Options pro remote debugging
Když jsem před chvílí říkal, že syntaxe je jednoduchá, tak jsem trochu lhal. Aby se ty dva předešlé příkazy daly použít pro remote debugging, je potřeba je trochu vylepšit pár nutnými, či vhodnými přepínači. Jsou to:
--listen=:2345
port, ev. adresa, na kterém naslouchá debug server.--headless=true
spustí debug server v headless modu.--api-version=2
nutné, aby si s debug serverem rozuměl Goland (IDEA).--accept-multiclient
debug server akceptuje více klientských spojení.
Nastavení Goland IDE
Konfigurace v Golandu (identické platí pro IntelliJ IDEA) je jednoduchoučká: stačí nastavit adresu host
a port
na kterém běží debug server.
Jelikož my budeme debugovat proces běžící v (lokálním) Dockeru, můžeme nechat localhost
.
Příprava Docker image
Nachystat si Docker image pro remote debugging také není nijak složité, stačí dvě věci:
- Zkopírovat binárku debuggeru
dlv
do Docker image. - Spustit debugovanou aplikaci pomocí
dlv exec
.
Spouštěcí příkaz bude vypadat takto:
dlv --listen=:2345 --headless=true --api-version=2 \
--accept-multiclient exec ./remote-debug
Ukázkový Dockerfile
:
FROM debian:stable-slim LABEL maintainer="SoftWare Samuraj" WORKDIR /home/samuraj # Copy remote-debug app COPY remote-debug . # Copy Delve - Golang debugger COPY bin/dlv . # CMD ["./remote-debug"] CMD ["./dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "./remote-debug"]
Remote debugging in Action
Nyní již máme všechno nachystáno a můžeme začít debuggovat:
1) Spustit Docker image
Docker image se spustí běžným příkazem — Docker o probíhajícím debuggování nic neví. Debuggovaná aplikace čeká na připojení debuggeru z IDE. Na výpisu vidíme: API server listening at: [::]:2345
.
container="golang-remote-debug"
image="sw-samuraj/${container}"
docker run --rm --net=host --name "${container}" "${image}"
2) Nastavit breakpoint v IDE a spustit debug session
Debuggovat umíte, ne? A jak se to dělá ve vašem IDE snad taky víte, ne?
3) Spuštění kódu s breakpointem
V ukázkovém projektu uvedeném na konci článku, běží v Dockeru jednoduchá webová služba poslouchající na portu 4040
. Stačí ji provolat pomocí curl
:
curl -v localhost:4040
4) Klasický debugging
Teď by se mělo ozvat naše IDE a je čas na klasický debugging: step over, step into, atd.
5) Ukončení debuggingu
Našli jsme chybu? Paráda!
V IDE ukončíme debug session a rozhodneme, jestli ukončíme i debuggovaný process v Dockeru nebo ho nacháme běžet dál.
Ukázkový projekt
Jednoduchý projekt na otestování popsaného postupu lze nají na GitHubu:
Pokud jsem nic neopomněl, mělo by stačit pustit následující příklady a nastavit ve vašem IDE remote debugging session.
./build-docker.sh
./run-docker.sh