Build and push a Docker/OCI image with kaniko + GitHub Packages
Tags: docker, github, kaniko
Published: 2019-12-04 03:41:08

[GitHub Packages](https://github.com/features/packages) provides a Docker container management platform.

This article introduces how to push an image using it as a repository of [kaniko](https://github.com/GoogleContainerTools/kaniko).

kaniko is a tool for Kubernetes, but you can use it without Kubernetes.

By using kaniko, there is a benefit of using the cache with GitHub Packages.


## Push image from the local environment

### Generate a personal access token

Generate a personal access token to access the registry in your [GitHub settings](https://github.com/settings/tokens/new?scopes=repo,write:packages,read:packages,delete:packages).

The token needs the following scopes:

- repo
- write:packages
- read:packages
- delete:packages


### Create a config.json

Get your GitHub username and token encoded in base64:

```sh
$ echo -n username:access_token | base64
```

Create a `config.json` file with GitHub package registry hostname and the previous generated base64 string:

```json
{
  "auths": {
    "docker.pkg.github.com": {
      "auth": "xxxxxxxxxxxxxxxxx"
    }
  }
}
```


### Run kaniko

Run kaniko with the `config.json`.

Also, put the `Dockerfile` you want to build in the same directory.

The command to execute is as follows:

```sh
$ docker run \
  -v $(pwd):/workspace \
  -v $(pwd)/config.json:/kaniko/.docker/config.json:ro \
  gcr.io/kaniko-project/executor:latest \
    --context dir:///workspace/ \
    --dockerfile Dockerfile \
    --destination docker.pkg.github.com/<org>/<repos>/<image>:<tag> \
    --cache=true \
    --cache-repo docker.pkg.github.com/<org>/<repos>/<image>-cache
```

The cache repository is separate from the destination because GitHub Packages does not support `<org>/<repos>/<image>/cache` style.


## Push image from GitHub Actions

Full example: https://github.com/ganta/example-kaniko-with-github-packages

In this example, the workflow runs under the following conditions:

- When pushed to a branch except for the master, build an image
- When pushed to the master branch, add the latest tag and push the image
- When you push a tag in the version number format, add the tag of that number and push the image

Create two workflows to build and push.

To run the build workflow on a branch excluding master (`.github/workflows/build.yml`):

```yaml
on:
  push:
    branches-ignore:
      - master
```

To run the push workflow on the master branch or a version number format tag (`.github/workflows/push.yml`):

```yaml
on:
  push:
    branches:
      - master
    tags:
      - '[0-9]+.[0-9]+.[0-9]+'
      - '[0-9]+.[0-9]+.[0-9]+-*'
```

Add a step to generate `config.json` to pass GitHub Packages authentication:

```yaml
      - name: Generate a config.json
        run: |
          cat <<JSON > config.json
          {
            "auths": {
              "docker.pkg.github.com": {
                "auth": "$(echo -n :${{ secrets.GITHUB_TOKEN }} | base64)"
              }
            }
          }
          JSON
      ...
```

When using `secrets.GITHUB_TOKEN`, the user name is not required.

The execution method of kaniko is the same as when executing locally.

For builds, use the `--no-push` option instead of the` --destination` option.

```yaml
      - name: Build an image
        run: |
          docker run \
            -v $(pwd):/workspace \
            -v $(pwd)/config.json:/kaniko/.docker/config.json:ro \
            gcr.io/kaniko-project/executor:latest \
              --context dir:///workspace/ \
              --dockerfile Dockerfile \
              --cache=true \
              --cache-repo docker.pkg.github.com/${GITHUB_REPOSITORY}/${IMAGE_NAME}-cache \
              --no-push
```

In the case of a push, you can get the tag to attach to the image from the `GITHUB_REF` environment variable.

```yaml
      - name: Build and push an image
        run: |
          tag=${GITHUB_REF##*/}

          if [[ ${tag} == master ]]
          then
            tag="latest"
          fi
          echo "tag: ${tag}"

          docker run \
            -v $(pwd):/workspace \
            -v $(pwd)/config.json:/kaniko/.docker/config.json:ro \
            gcr.io/kaniko-project/executor:latest \
              --context dir:///workspace/ \
              --dockerfile Dockerfile \
              --destination docker.pkg.github.com/${GITHUB_REPOSITORY}/${IMAGE_NAME}:${tag} \
              --cache=true \
              --cache-repo docker.pkg.github.com/${GITHUB_REPOSITORY}/${IMAGE_NAME}-cache
```

## Conclusion

In this way, you can smoothly run kaniko with GitHub Packages.

Also, you can use `secrets.GITHUB_TOKEN` in GitHub Actions, so you don't have to struggle with authentication.

Even if you are not in Kubernetes, you can benefit from caching by using kaniko instead of `docker build`.