Inside Container Debugging with Go

Roy Iarchy
June 21, 2022
4
min read

As developers, a good amount of our time is spent debugging. But sometimes, due to setup constraints and a lack of debugging tools for container development, it isn’t possible to do.

When working locally, all you have to do is click *Debug* and everything works like magic.

When dealing with a remote environment or running inside containers, however, we no longer have this *Debug* option and we can’t compile the binary in debug mode.

Some IDEs try to solve this problem, but most of the time, their solution isn’t good enough so, instead, we’re forced to rely on debug logs.

This is mainly because debugging isn’t natively supported within container development. It wasn’t built for that. As a result, having containerized environments becomes more complex and developers end up spending a lot of time finding a workaround.

At the end of the day, we essentially have 3 options when facing with this dilemma:

### 1. We can decide not to debug in conventional ways

Because debugging takes so much time, it rarely gets the full attention it needs. We can always add “debug logs” and investigate the bug, but this process is extremely time consuming, unconventional and not always worth it.

That’s why, depending on the magnitude and impact of the bug, this may be a good option when there’s a constraint on resources - and there always is. It’s important to keep in mind that eventually solving the bug is a must, in this specific scenario, it will be by using workaround such as debug logs to avoid “traditional” debugging.

### 2. We can run each service as a container, except for the one we are working on

We can then choose to connect our service (the one we’re working on) to the other services as if it *were* inside the container. This is a great option if you have the ability to create and configure multiple dev environments, but it’s generally not realistic for most teams since it requires a lot of  involvement and you need to decide which services to run inside the container vs locally.

### 3. We can debug inside the container by changing the binary and installing the required dependencies

This is the most scalable approach to debugging inside containers as it provides a quick workaround and allows our teams to work distraction free (and without halting our workflows).

In this article, we’ll dive deep into option #3 and explore how to debug with GO inside the container. To make this work, we’ll need to install a debugging infrastructure and create the ability to compile within the container.

# Debugging with GO inside a running container

The following should take place in your container definition:

### Step 1: Change how you build the binary

```docker
FROM golang:1.18-alpine3.15 as builder

WORKDIR /code/
COPY ./go.mod ./go.sum /code/
RUN --mount=type=cache,target=/go/pkg/mod go mod download

COPY . .
RUN go build -o /code/build/server main.go

FROM alpine:3.15
COPY --from=builder /code/build/server /server

ENTRYPOINT ["/server"]
```

### Step 1: Change how you build the binary

```docker
FROM golang:1.18-alpine3.15 as builder

WORKDIR /code/
COPY ./go.mod ./go.sum /code/
RUN --mount=type=cache,target=/go/pkg/mod go mod download

COPY . .
RUN go build -gcflags "all=-N -l" -o /code/build/server main.go

FROM alpine:3.15
COPY --from=builder /code/build/server /server

ENTRYPOINT ["/server"]
```

### Step 2: Install the required dependencies (such as Delve)

```docker
FROM golang:1.18-alpine3.15 as builder

RUN apk add build-base
RUN go install github.com/go-delve/delve/cmd/dlv@latest

WORKDIR /code/
COPY ./go.mod ./go.sum /code/
RUN --mount=type=cache,target=/go/pkg/mod go mod download

COPY . .
RUN go build -gcflags "all=-N -l" -o /code/build/server main.go

FROM alpine:3.15
COPY --from=builder /go/bin/dlv /
COPY --from=builder /code/build/server /

ENTRYPOINT ["/server"]
```

### **Step 3: Run the process in the correct way**

(instead of just running the server binary)

```docker
FROM golang:1.18-alpine3.15 as builder

RUN apk add build-base
RUN go install github.com/go-delve/delve/cmd/dlv@latest

WORKDIR /code/
COPY ./go.mod ./go.sum /code/
RUN --mount=type=cache,target=/go/pkg/mod go mod download

COPY . .
RUN go build -gcflags "all=-N -l" -o /code/build/server main.go

FROM alpine:3.15
COPY --from=builder /go/bin/dlv /
COPY --from=builder /code/build/server /

EXPOSE 2345
ENTRYPOINT ["/dlv", "--listen=:2345", "--headless=true", "--api-version=2", \
           "--accept-multiclient", "exec", "/server"]
```

# Container debugging, like it’s local

In the world of R&D, it’s so crucial to have efficient and easy-to-use dev environments and workflows so we can work distraction-free. This is especially true when debugging as serious thought, energy, and time is put into these processes.

But the lack of support in debugging tools within dev environments themselves brings constant  distractions and halts the workflow and thinking process of your people.

That’s why having the ability - and the simplicity - of debugging in containerized services as if they were local can provide immense benefits for your teams.

Even if containers were never intended to be used by dev in such a fashion, with Raftt, debugging containerized services becomes as easy as running it natively on your machine.

## About Raftt

Raftt creates modern flexible environments synced with your tools, features and workflows so you can explore and develop freely. Our cloud platform liberates you from the limitations of your hardware, allowing you to spawn an unlimited number of environments, collaborate and share, and enjoy stability, consistency, and performance.

**Get started now with our sample project.**

Roy Iarchy

Get everything you need to concentrate on your code in containerized environments

The ability to focus on doing what you love best can be more than a bottled-up desire lost in a sea of frustration. Make it a reality — with Raftt.

No credit card required. Free Forever