Build Go container image with `ko`

June 1, 2023

Testing `ko` tool to build Go container image

From the project home page, ko

makes building Go container images easy, fast, and secure by default.

lets start with simple Go http project using gin.

$ mkdir simple
$ cd simple
$ go mod init saktidwicahyono.name/simple
go: creating new go.mod: module saktidwicahyono.name/simple
$ go get -u github.com/gin-gonic/gin

Now create simple main.go

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "ok"})
    })
    r.Run()
}

Installing ko

You can install using brew

$ brew install ko

Or using go

$ go install github.com/google/ko@latest

Building container image

To decide where the container image registry as the push target, ko use env var KO_DOCKER_REPO. For this example I use my docker hub handle saktidc.

$ export KO_DOCKER_REPO=saktidc

and then to build

$ ko build
2023/06/01 11:12:08 Using base cgr.dev/chainguard/static:latest@sha256:ee47224a2afc674c1f1089b9dea97d5ee400cf2fff3797398778450a4cfb2a8d for saktidwicahyono.name/simple
2023/06/01 11:12:09 Building saktidwicahyono.name/simple for linux/amd64
2023/06/01 11:12:10 Publishing saktidc/simple-c16baa5c7902f141ca35ab0eba518e77:latest
2023/06/01 11:12:14 existing blob: sha256:365a9bc05fc55ad9a0b77656877aeef86439c94307d00385a1096eccb437d47b
2023/06/01 11:12:15 pushed blob: sha256:50e3901fbf4cb45efb05f06dab3002caa814e0d1febcd8a46b4db06b70152937
2023/06/01 11:12:16 index.docker.io/saktidc/simple-c16baa5c7902f141ca35ab0eba518e77:sha256-26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990.sbom: digest: sha256:3873c56fd9a7f0c629417d05dec2ced75bf459961915dc9f45a744695e2fc3a0 size: 374
2023/06/01 11:12:16 Published SBOM index.docker.io/saktidc/simple-c16baa5c7902f141ca35ab0eba518e77:sha256-26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990.sbom
2023/06/01 11:12:17 existing blob: sha256:9a94261fe7288ed0aa24077c7668fb25f929d3f51a955d17b44938cf06356736
2023/06/01 11:12:17 existing blob: sha256:7d2e0f7d141e2e962603f0cdd19542fa01b454a49f5fd5a966fa3c44f37eb0a1
2023/06/01 11:12:17 existing blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2023/06/01 11:12:17 existing blob: sha256:9a7a7857408c2c77b99ae8c182e8420cb8729ff42a8adbdbea5d24a4da039899
2023/06/01 11:12:18 saktidc/simple-c16baa5c7902f141ca35ab0eba518e77:latest: digest: sha256:26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990 size: 1210
2023/06/01 11:12:18 Published saktidc/simple-c16baa5c7902f141ca35ab0eba518e77@sha256:26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990
saktidc/simple-c16baa5c7902f141ca35ab0eba518e77@sha256:26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990
Ensure you have auth setup to the registry of your choice. Check `ko login --help` if you does not have docker in your system.

The ko build command build the Go binary, containerize it, and publish it. By default will use following format as destination

registry.example.com/repo/<app/cli>-<md5>

If you want to disable md5 suffix you can use option --base-import-paths / -B, lets try it again:

$ ko build -B

2023/06/01 11:17:15 Using base cgr.dev/chainguard/static:latest@sha256:ee47224a2afc674c1f1089b9dea97d5ee400cf2fff3797398778450a4cfb2a8d for saktidwicahyono.name/simple
2023/06/01 11:17:16 Building saktidwicahyono.name/simple for linux/amd64
2023/06/01 11:17:17 Publishing saktidc/simple:latest
2023/06/01 11:17:20 pushed blob: sha256:b1d00a125b2e1d12e952a7173fed2990110229725b1c8a3db850804627fb1e84
2023/06/01 11:17:21 pushed blob: sha256:365a9bc05fc55ad9a0b77656877aeef86439c94307d00385a1096eccb437d47b
2023/06/01 11:17:21 index.docker.io/saktidc/simple:sha256-26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990.sbom: digest: sha256:5925704c870a9ec82ba350e3600dd0bfcc1263041aaeb5f5125b0ed512052125 size: 374
2023/06/01 11:17:21 Published SBOM index.docker.io/saktidc/simple:sha256-26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990.sbom
2023/06/01 11:17:24 pushed blob: sha256:7d2e0f7d141e2e962603f0cdd19542fa01b454a49f5fd5a966fa3c44f37eb0a1
2023/06/01 11:17:24 pushed blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2023/06/01 11:17:27 pushed blob: sha256:9a7a7857408c2c77b99ae8c182e8420cb8729ff42a8adbdbea5d24a4da039899
2023/06/01 11:17:27 pushed blob: sha256:9a94261fe7288ed0aa24077c7668fb25f929d3f51a955d17b44938cf06356736
2023/06/01 11:17:27 saktidc/simple:latest: digest: sha256:26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990 size: 1210
2023/06/01 11:17:27 Published saktidc/simple@sha256:26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990
saktidc/simple@sha256:26f86626dd1fdc39fce9b0b5b00636ec8d4ec4658dfd759aeaa99e5157600990

Will push the image to https://hub.docker.com/r/saktidc/simple then test it using

$ docker run --rm -it -p 8080:8080 saktidc/simple

# in other terminal
$ curl localhost:8080
{"message":"ok"}%

# succeed

The options

You can create .ko.yaml file config, to customize the ko behaviour. For example to have both arm64 and amd64 build:

# .ko.yaml
defaultPlatforms:
- linux/arm64
- linux/amd64

Or you can override it using env var KO_DEFAULTPLATFORMS=linux/arm64,linux/amd64. For complete option check official documentation https://ko.build/configuration/.

Also if your app have multiple cmd or you want to specify explicitly, put it as ko build argument.

$ ko build ./cmd/app1
$ ko build ./cmd/app2

Limitation

Currently only work with static compile, work best without dependency to underlying image, the build executed using CGO_ENABLED=0 by default.

To install OS dependency you should configure the base image https://ko.build/configuration/#overriding-base-images.

Return to blog

footer