diff --git a/README.md b/README.md index d3707e16..8ea7ec08 100644 --- a/README.md +++ b/README.md @@ -70,3 +70,75 @@ This will: 1. Implement the controller in `./api//` which implements the `Controller` interface in `./internal/queue`. The runner name must be in the format `Runner`. E.g. queryRunner, csvExportRunner 2. Create a command in `./cmd//main.go` which creates a new instance from `./internal/queue` + +## Debugging in Containers + +This project supports debugging Go applications running inside Docker containers using [Delve](https://github.com/go-delve/delve), the Go debugger. + +### Building Debug Images + +The project includes two build tasks for different use cases: + +- `task docker:build` - Build the regular production image +- `task docker:build:debug` - Build the debug image with Delve + +The debug image is built using `build/Dockerfile.debug`, which: +- Installs Delve debugger with static linking for Alpine compatibility +- Builds Go binaries with debug symbols (`-gcflags="all=-N -l"`) +- Exposes port 2345 for debugger connections + +### Container Configuration for Debugging + +To enable debugging for a service in `deployments/compose.local.yaml`, modify the service configuration: + +```yaml +query_api: + image: queryorchestration:debug # Use debug image instead of latest + command: ["/dlv", "exec", "./queryAPI", "--listen=:2345", "--headless", "--api-version=2", "--accept-multiclient", "--continue"] + ports: + - "8080:8080" # Application port + - "2345:2345" # Debugger port + expose: + - 8080 + - 2345 +``` + +Key changes from production configuration: +1. **Image**: Use `queryorchestration:debug` instead of `queryorchestration:latest` +2. **Command**: Replace direct binary execution with Delve wrapper +3. **Ports**: Expose port 2345 for debugger connections +4. **Delve flags**: + - `--listen=:2345`: Listen on port 2345 for debugger connections + - `--headless`: Run without terminal interface + - `--accept-multiclient`: Allow multiple debugger connections + - `--continue`: Start the application immediately (don't wait for debugger) + +### Connecting a Debugger + +#### GoLand/IntelliJ IDEA +1. Go to **Run → Edit Configurations...** +2. Click **"+"** → **"Go Remote"** +3. Configure: + - **Name**: `Docker Debug` + - **Host**: `localhost` + - **Port**: `2345` +4. Click **Apply** and **OK** +5. Select the configuration and click **Debug** (Shift+F9) +6. Set breakpoints in your code and make HTTP requests to trigger them + +#### VS Code +1. Add to `.vscode/launch.json`: +```json +{ + "name": "Connect to Docker", + "type": "go", + "request": "attach", + "mode": "remote", + "remotePath": "/app", + "port": 2345, + "host": "localhost" +} +``` +2. Set breakpoints and start debugging + +The debugger will connect to the running container and pause execution when breakpoints are hit, allowing you to inspect variables, step through code, and debug issues in the containerized environment. diff --git a/build/Dockerfile.debug b/build/Dockerfile.debug new file mode 100644 index 00000000..09d72634 --- /dev/null +++ b/build/Dockerfile.debug @@ -0,0 +1,67 @@ +# syntax=docker/dockerfile:1 + +ARG GO_VERSION=1.24.0 + +FROM golang:${GO_VERSION} AS build + +WORKDIR /app + +RUN mkdir bin/ + +RUN apt-get update && \ + apt-get install -y --no-install-recommends git=1:2.* musl-tools=1.2.* && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install Delve debugger with static linking for Alpine +RUN CGO_ENABLED=0 go install github.com/go-delve/delve/cmd/dlv@latest + +RUN go install -a -installsuffix cgo std + +COPY --link go.mod . +COPY --link go.sum . +COPY --link vendor/ vendor/ + +RUN go mod verify + +COPY --link cmd/ cmd/ +COPY --link api/ api/ +COPY --link internal/ internal/ + +ARG TARGETARCH + +ENV CGO_ENABLED=1 +ENV GOARCH=${TARGETARCH} +ENV CC=musl-gcc + +ARG GIT_VERSION=dev +ARG GIT_COMMIT=unknown + +# Build with debug symbols (remove -s -w flags) +RUN go build \ + -mod vendor \ + -tags musl \ + -trimpath \ + -installsuffix cgo \ + -gcflags="all=-N -l" \ + -ldflags "-linkmode external -extldflags '-static -Wl,--as-needed -Wl,--gc-sections -Wl,-O1' -X main.version=${GIT_VERSION} -X main.gitCommit=${GIT_COMMIT}" \ + -o bin/ ./cmd/... + +FROM golang:${GO_VERSION}-alpine AS final + +ENV PWD=/app + +WORKDIR ${PWD} + +# Install ca-certificates +RUN apk --no-cache add ca-certificates + +# Copy Delve from build stage +COPY --from=build /go/bin/dlv /dlv + +COPY --from=build /app/bin/ . + +EXPOSE 8080 2345 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=1s --retries=10 \ + CMD ["./healthcheck"] \ No newline at end of file diff --git a/deployments/compose.local.yaml b/deployments/compose.local.yaml index 2c5358de..ff2f51c6 100644 --- a/deployments/compose.local.yaml +++ b/deployments/compose.local.yaml @@ -255,8 +255,8 @@ services: - server-network query_api: - image: queryorchestration:latest - command: ["./queryAPI"] + image: queryorchestration:debug + command: ["/dlv", "exec", "./queryAPI", "--listen=:2345", "--headless", "--api-version=2", "--accept-multiclient", "--continue"] depends_on: localstack: condition: service_healthy @@ -264,10 +264,13 @@ services: condition: service_healthy ports: - "8080:8080" + - "2345:2345" expose: - 8080 + - 2345 environment: LOG_LEVEL: DEBUG + DEBUG: true CLIENT_SYNC_URL: ${CLIENT_SYNC_URL} QUERY_VERSION_SYNC_URL: ${QUERY_VERSION_SYNC_URL} AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} diff --git a/scripts/docker.yml b/scripts/docker.yml index cdd75f7c..7c21dc83 100644 --- a/scripts/docker.yml +++ b/scripts/docker.yml @@ -26,3 +26,12 @@ tasks: -t {{.IMAGE_NAME}}:latest \ -t {{.IMAGE_NAME}}:{{.GIT_VERSION}} \ -f {{.DOCKERFILE}} . + build:debug: + run: once + cmd: | + # Build debug image with Delve debugger + docker build \ + --build-arg "GIT_VERSION={{.GIT_VERSION}}" \ + --build-arg "GIT_COMMIT={{.GIT_COMMIT}}" \ + -t {{.IMAGE_NAME}}:debug \ + -f build/Dockerfile.debug .