--- title: "ko: fast Kubernetes microservice development in Go" linkTitle: "ko for microservice development" date: 2018-12-18 description: "Using ko for fast microservices development in Kubernetes." type: "blog" ---

I originally wrote `ko` to help Knative developers. I was prompted to write this introductory post by the positive feedback from the community, including an IBM booth talk on `ko` during recent Kubecon Seattle 2018. I hope you enjoy using `ko` as much as we do, and I look forward to your feedback on slack.knative.dev .

Over the past few years, there has been a lot of hype about containers. Docker, Kubernetes and related technology have taken the public cloud by storm (pun intended). At the same time, it seems, as software projects grow increasingly more complex, so too does the development process.

What starts as:

Quickly becomes:

Tools such as skaffold can wrap this process for arbitrary languages and Dockerfiles to make it easier to manage (and faster), but you still need to write artisanal hand-crafted Dockerfiles , and typically need to write more yaml (or other) to tell the tooling how to orchestrate this (e.g. what gets pushed where?):

`ko` takes a different approach that leans into Go idioms to eliminate configuration.

One such Go idiom is that binaries are referenced by “import paths”; a typical way of installing a Go binary would be:

    # e.g. installing ko itself
    go get github.com/google/go-containerregistry/cmd/ko

Getting started with `ko` does not take any additional configuration files, you simply replace references to container images with import paths:

    # This example is based on:
    # https://github.com/google/ko/blob/master/cmd/ko/test/test.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: kodata
    spec:
      containers:
      - name: test
        # ko builds and publishes this Go binary, and replaces this
        # with an image name.
        image: github.com/google/go-containerregistry/cmd/ko/test
      restartPolicy: Never

That’s it.

How do I consume this with `ko` ?

`ko` also needs to know where the user wants to publish their images. This is defined outside of the yaml manifest as generally each developer on your team will use their own.

For example, developing on Knative, I use this in my `.bashrc` file:

    export KO_DOCKER_REPO=gcr.io/mattmoor-private/ko

NOTE: for DockerHub users (and possibly others), this should be: `docker.io/username` as DockerHub does not support multi-level repository names.

After that, the command-line interface is modeled after `kubectl`:

    ko apply -f directory/ -f file.yaml

This will have the same net effect as `kubectl apply`, but it will also build, containerize, and publish the Go microservices referenced from the yamls as well, with significantly less configuration:

You only write Kubernetes yamls and code. No Dockerfiles, no Makefiles. You run one command and your latest code is running.

Following the above example (trimmed for width):

    ~/go/src/github.com/google/go-containerregistry
    $ ko apply -f cmd/ko/test/test.yaml
    Using base .. for github.com/google/go-containerregistry/cmd/ko/test
    Publishing gcr.io/mattmoor-public/test-01234abcd:latest
    mounted blob: sha256:deadbeef
    mounted blob: sha256:baadf00d
    pushed blob sha256:deadf00d
    pushed blob sha256:baadbeef
    pushed blob sha256:beeff00d
    gcr.io/mattmoor-public/test-01234abcd:latest: digest: ... size: 915
    Published gcr.io/mattmoor-public/test-01234abcd@...
    pod/kodata created

    ~/go/src/github.com/google/go-containerregistry$ kubectl get pods
    NAME     READY   STATUS      RESTARTS   AGE
    kodata   0/1     Completed   0          1

Just the image

The simplest trick that `ko` supports is to simply containerize and publish an image. One neat thing about this is that it works with most Go binaries without any knowledge of `ko`.

For example (trimmed for width):

    ~/go/src/golang.org/x/tools
    $ ko publish ./cmd/goimports/
    Using base .. for golang.org/x/tools/cmd/goimports
    Publishing gcr.io/mattmoor-public/goimports-01234:latest
    mounted blob: sha256:deadbeef
    mounted blob: sha256:baadf00d
    mounted blob: sha256:deadf00d
    pushed blob sha256:baadbeef
    pushed blob sha256:beeff00d
    gcr.io/mattmoor-public/goimports-01234:latest: digest: ... size: 914
    Published gcr.io/mattmoor-public/goimports-01234@...

`ko` is for releases too!

You can also use `ko` to publish things for redistribution via:

    # This does everything `apply` does except it pipes to
    # stdout instead of kubectl
    ko resolve -f config/ > release.yaml

    # Later...
    kubectl apply -f release.yaml

For example, we use this to release all of the Knative components.

Try it out, and tell us what you think.

This just scratches the surface of what you can do with `ko`, and what `ko` does for you. For more information check out the README.md. If you have questions: #ko on slack.knative.dev, or reach me on Twitter @mattomata.

Some Common Pitfalls

A couple of things to be careful of with ko because of how heavily it relies on convention:

1. You need to be on `${GOPATH}` or it will not know what package you are in.
2. Typos are the worst. Because `ko` is insensitive to schemas, it will ignore any string that is not the import path of a “main” package, so if you have a simple typo in your import path then it will be left as is and you will likely see your Pod `ErrImagePull`.