# Building Go containers for Cloud Run

As I learned by writing my Go tutorial series, Go is great for making lightweight web apps, and it also lends itself very easily to being packaged and run in a docker container. Go compiles to a static binary (most of the time), which means your runtime container can be extremely small and efficient.

Once your app is containered up, deploying it to Google’s [Cloud Run](https://cloud.google.com/run/docs/) is really easy and gives you out-of-the-box auto-scaling and a secure HTTPS front-end. To follow along below, make sure you have the [Google Cloud SDK](https://cloud.google.com/sdk/docs/quickstarts/) set up on your local system.

First we’ll use a multi-stage docker build. In the directory of your Go web app, simply add the following `Dockerfile`:

```dockerfile
FROM golang:1.13 as build
WORKDIR /go/src/app
COPY . .
RUN go build -v -o app .

FROM gcr.io/distroless/base
COPY --from=build /go/src/app/. /
CMD ["/app"]
```

In the first stage of this file, we use the `golang:1.13` image to give us a build environment for our app. We copy everything from our local filesystem into the `/go/src/app` directory inside the container environment. Then we run `go build` to compile everything. Simple!

The next stage is the clever part. We start a new image from `gcr.io/distroless/base` and copy over just the files from our build stage (including our compiled runtime). In this example, we’re assuming there are supporting files to copy as well (for example, HTML and other static content), but we could refine this even more by just copying the binary application. Google’s [distroless project](https://github.com/GoogleContainerTools/distroless) contains just enough Linux to run our compiled binary. There’s no package manager, no shell, so it makes for a very efficient image.

You can build this image locally with Docker and push it to Google [Container Registry](https://cloud.google.com/container-registry/docs/), or just use Google’s [Cloud Build](https://cloud.google.com/cloud-build/docs/) to do the work for you:

```bash
gcloud builds submit --tag gcr.io//<your-project-id>/<your-image-name> .
```

Now you can deploy your app with just one more command:

```bash
gcloud run deploy <deployment-name> gcr.io/<your-project-id>/<your-image-name>
```

*(Don’t forget to replace &lt;your-project-id&gt;, &lt;your-image-name&gt; and &lt;deployment-name&gt; in these examples)*

In the [Cloud Run console](https://console.cloud.google.com/run) you should now see your new deployed service, complete with its URL.

![Gopher in a box](https://cdn.hashnode.com/res/hashnode/image/upload/v1723551267242/1e348359-0f14-4fee-92d0-cb681ebe38ca.png align="center")

Cloud Run offers some great features like traffic [splitting and migration](https://cloud.google.com/run/docs/rollouts-rollbacks-traffic-migration) and [custom domains](https://cloud.google.com/run/docs/mapping-custom-domains). Deploying container based applications has never been easier! Except when things go wrong…

## Missing libraries

You may have successfully tested your Docker image locally, but it’s failing when you deploy it. If you’re really unlucky, you may receive this error message in your docker logs:

```plaintext
exec user process caused "no such file or directory"
```

This error is almost completely useless, and will probably send you down the path of debugging the contents of your image looking for missing files or directories. What’s actually at fault is that there are *system libraries* missing that Go needs, because they are not part of your base image. This happens when Go’s statically compiled binaries aren’t quite as static as we’d like them to be.

To fix this, we need to send some extra parameters to the `go build` command so that it also compiles in any libraries it needs:

```bash
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
```

This should fix the problem most of the time.

## But I need CGO!

The above fix works great as long as switching off [CGO](https://golang.org/cmd/cgo/) isn’t a problem. But some Go libraries require CGO (for example, the rather handy `go-sqlite3`). So to create a completely static binary while still allowing CGO, we have to update our build line again.

```dockerfile
RUN go get -d -v ./...
RUN CGO_ENABLED=1 GOOS=linux go build -a -ldflags '-linkmode external -extldflags "-static"' -o app .
```

First we run `go get` to include any external dependencies. Then we send extra parameters to `go build` to make sure it *really* includes everything. This results in a much longer build process, but hopefully a static binary that works.

Hooray for write once, run *almost* everywhere! 😊
