gitlab-ciを使ってdocker build&pushを自動化したい

# なぜやるの?
dockerを使って開発を行っている.そこでgitlabにソースをpushするだけで[GitLab Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html)にimageを作ってほしい

# どうやるの?
 
- .gitlab-ci.yamlにステージを追加する
- docker imageを指定する
- docker loginをする
- docker buildをする
- docker pushする

## 全体のyaml

```yaml
stages:
  - docker-login
  - docker-build

docker-login:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-login
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

branch-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch ./service/.
    - docker push registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch


master-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:latest ./service/.
    - docker push registry.gitlab.com/empenguin/momoka-slacker/alpine-php:latest
  only:
    - master

```

## .gitlab-ciにステージを追加する

```yaml
stages:
  - docker-login
  - docker-build
```
今回はdocker-loginステージとdocker-buildステージを追加する
docker-loginはdocker loginできるかどうかテストするために今回用意したもの
なので実際に使うときはdocker-loginはいらない

## docker imageを指定する
```yaml
docker-login:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-login
```
imageをdocker:stableにしているほかのタグが使いたいなら[dockerのdocker hub](https://hub.docker.com/r/library/docker/tags/)を見るとほかの物もあることが分かる

ちなみにdocker-loginはログインできるかどうかの確認用なので

```yaml
branch-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
```
こちらでも同じことをやっている



## docker loginをする
```yaml
branch-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
```
docker loginしている ,CI_REGISTRY_USER,$CI_REGISTRY_PASSWORD
それぞれあらかじめ変数として追加しておいたものgitlab.comのアカウントを追加しておくといい
[ここを参考にするといいと思う](https://docs.gitlab.com/ee/ci/variables/)


## docker buildをする
```yaml
branch-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch ./service/.
```
ここではbuildをやっているdocker buildするときはpushする先と同じでなければエラーになってしまうためpush先と同じ名前でbuildしておく
タグはbranchにしている・・・まぁdocker hubにbuildしたことがある人なら大丈夫だろう

## docker pushする
```yaml
branch-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch ./service/.
    - docker push registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch
```

同じようにbranchタグを追加してpushする試してはないけどタグなしでもpushできるはず
ちなみに

```yaml
master-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:latest ./service/.
    - docker push registry.gitlab.com/empenguin/momoka-slacker/alpine-php:latest
  only:
    - master
```
onlyを付け加えることでそのブランチにプッシュ or マージされたときだけjobを実行することができる
この場合の例だとmasteブランチにマージされたときだけmaste-buildの内容が実行されてタグがlatestで追加される
k8sを使っている我々としてはmasterにマージされたものはコンテナとして動くことは保証されているので
k8sのアップデートをすることで常に新しいcontainerが立つ

## まとめ
onlyタグをつけることでjobを分けることができるのは大きいと感じている
なぜなら新たに開発を行うときも,本番で動かしたいときも常にlatestタグを引っ張ってくればいいからだ
逆に開発を行っているときはbranchタグをつけるようになっているため誰かと近いところを開発しているときはbranchタグが上書きされてしまうときがある.これは改善するべきタスクだと考えている.今考えている改善案として
gitlabの最初から使うことができる変数`CI_COMMIT_SHA`を使う方法を考えているこれはcommitのハッシュが入っているはず.これを使って一意にタグを決めてあげることで解決できるのではないかと考えている
また解消できたときに記事を上げようと思う

ありがとうございました

# 追記
便利なやり方を教えていただいたため追記します

## 何を教えていただいたか
docker loginに$CI_JOB_TOKENを使う方法を教えていただいた

## 現状の何が問題か
docker loginするときに変数CI_REGISTRY_PASSWORDを定義しているが
これは管理者権限を持ったアカウントのパスワードを渡している.
もしアカウントのパスワードを変更したときには変数CI_REGISTRY_PASSWORDの変数を手動で変更しなければならない

## どう解決するか
docker login時にユーザを`gitlab-ci-token`,パスワードを`$CI_JOB_TOKEN`に設定することでプロジェクトのユーザとしてログインすることができる

全体はこうなった

```yaml
stages:
  - docker-login
  - docker-build

docker-login:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-login
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY

branch-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch ./service/.
    - docker push registry.gitlab.com/empenguin/momoka-slacker/alpine-php:branch


master-build:
  image: docker:stable
  services:
    - docker:dind
  stage: docker-build
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build -t registry.gitlab.com/empenguin/momoka-slacker/alpine-php:latest ./service/.
    - docker push registry.gitlab.com/empenguin/momoka-slacker/alpine-php:latest
  only:
    - master
```
全体ではこうなる

どこが変わったかというと

```yaml
docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
```
gitlab.comの記事では
> You have to use the special gitlab-ci-token user created for you in order to push to the Registry connected to your project.
[gitlab.comより](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#using-the-gitlab-container-registry)

なるほどgitlab-ci-tokenを使ってプロジェクトのレジストリにプッシュしないと行けない,推奨してるということだろう(間違ってたらごめんなさい)

そして,そのgitlab-ci-tokenのパスワードは$CI_JOB_TOKENに最初から定義されている

ありがとうございました