CircleCI2.0でDockerをビルドしてDocker HubにPushするまで


[CircleCI2.0ハンズオン！](https://qiita.com/sawadashota/items/6e366b57cb977272713c)シリーズです。

今回はCircleCI2.0でDockerをビルドしてDocker HubにPushするまでやってみます。

# config.yml

Tag作成をトリガーにGitHubのTagと同じTagのイメージと`latest`を作成します。

```yaml:.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/golang:1.9.3
    working_directory: /go/src/github.com/project/repo
    steps:
      - setup_remote_docker:
          version: 17.11.0-ce
          docker_layer_caching: true
      - checkout
      - run:
          name: Build
          command: docker build -t ${OWNER}/${IMAGE_NAME}:latest .
      - run:
          name: Tag to latest
          command: docker tag ${OWNER}/${IMAGE_NAME}:latest ${OWNER}/${IMAGE_NAME}:$CIRCLE_TAG
      - run:
          name: Docker Login
          command: docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
      - run:
          name: Push image to registory
          command: docker push ${OWNER}/${IMAGE_NAME}
workflows:
  version: 2
  build:
    jobs:
      - build:
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /.*/
```

## ビルドにcircleciのイメージを使う

そもそも`setup_remote_docker`は`machine:true`で動かないらしいので、dockerイメージを使ってビルドしています。

> Note: `setup_remote_docker` is not curently compatible with the `machine` executor.

また、`circleci/golang:1.9.3`を使用していますが、素の`golang:1.9.3`には当然、`docker`などビルドするときに欲しいコマンドがありません。

そこで、ビルドに必要なコマンドが揃っている`circleci`のイメージを使っています。

> Docker provides official images for popular languages and services that are aimed to work in common context, whether in development or in production. CircleCI highly recommends using them for creating custom images that are more appropriate for users.
> 
> However, we frequently found them lacking some tools making them appropriate for dev/CI use. CircleCI publishes this image to extend the images in the following ways:
> 
> 1. Common tools used in development and CI are installed e.g. git, ssh, tar, ca-certificates, curl, wget.
2. __Docker tools: latest docker, docker-compose, and dockerize are installed__
3. Variants for common use cases, e.g. images with common browsers installed and configured to run in a containized environment
4. Use a non-root user (namely circleci) by default. Many applications refuse to run as run (e.g. chrome) or their behavior differs when run as root (e.g. tar file ownership behavior)

https://hub.docker.com/r/circleci/golang/

## `setup_remote_docker`で`version`を指定する

2018年1月28日現在、`setup_remote_docker`は以下のdockerバージョンに対応しています。

defaultで`17.03.0-ce`に設定されてしまうので、新しいバージョンを指定しておきます。

特に[multi-stage builds](https://docs.docker.com/engine/userguide/eng-image/multistage-build/)なんかをやる場合は`17.05.0-ce`以上のバージョンでないとコケます。

> The currently supported versions are:

> * `17.03.0-ce` (default)
* `17.05.0-ce`
* `17.06.0-ce`
* `17.06.1-ce`
* `17.07.0-ce`
* `17.09.0-ce`
* `17.10.0-ce`
* `17.11.0-ce`

https://circleci.com/docs/2.0/building-docker-images/#docker-version

## workflowsを設定する

`build`をただ実行するだけなら、workflowを設定する必要がありませんが、Tagがついてるときのみにしたかったので、「全ブランチ無視」「全タグトリガー」にしました。

# 通常のcommitはビルドだけ行い、TagがついたらPushする

`build`で`docker image save`で`.tar`に書き出し、`push`では`docker load`で展開するようにしました。

```yaml:.circleci/config.yml
alias:
  default_env: &default_env
    docker:
      - image: circleci/golang:1.9.3
    working_directory: /go/src/github.com/project/repo
  setup_remote_docker: &setup_remote_docker
    version: 17.11.0-ce
    docker_layer_caching: true

version: 2
jobs:
  build:
    <<: *default_env
    steps:
      - setup_remote_docker: *setup_remote_docker
      - checkout
      - run:
          name: Build
          command: docker build -t ${OWNER}/${IMAGE_NAME}:latest .
      - run:
          name: Save image
          command: docker image save ${OWNER}/${IMAGE_NAME}:latest > ./docker-image.tar
      - persist_to_workspace:
          root: .
          paths:
            - ./docker-image.tar
  push:
    <<: *default_env
    steps:
      - setup_remote_docker: *setup_remote_docker
      - attach_workspace:
          at: .
      - run:
          name: Load image from tar
          command: docker load -i ./docker-image.tar
      - run:
          name: Tag to latest
          command: docker tag ${OWNER}/${IMAGE_NAME}:latest ${OWNER}/${IMAGE_NAME}:$CIRCLE_TAG
      - run:
          name: Docker Login
          command: docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
      - run:
          name: Push image to registory
          command: docker push ${OWNER}/${IMAGE_NAME}
workflows:
  version: 2
  build:
    jobs:
      - build:
          filters:
            tags:
              only: /.*/
      - push:
          requires:
            - build
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /.*/
```

# まとめ

Docker Hubでも[Automated builds](https://docs.docker.com/docker-cloud/builds/automated-build/)がありますが、遅すぎてイライラすることが多かったので、CircleCIでビルドすればそのストレスから解放されそうです。