private Docker Hub を作ってみた

# はじめに
ここまで自分が投稿した記事を使って、下記が作れるようになりました。

* [private docker registry](http://qiita.com/DG0426/items/b8006c3168e245628005)
* [GitLab](http://qiita.com/DG0426/items/b63520e0e63a6c1efe70)
* [Jenkins](http://qiita.com/DG0426/items/dc502b4fb795fde73de2)

これらを利用して、private Docker Hub を作ってみます。

# 今回作成する private Docker Hub の仕組み
* ローカルで作成したDockerfileをGitLabにpush
* GitLabからWebHookでJenkinsに通知
* JenkinsにてDockerfileをビルド
 * Jenkinsはdockerコンテナで起動している
 * Jenkinsコンテナ内でDockerfileをビルドしているわけではない
 * ホストのdockerAPI（docker-py経由）を利用している
* docker build
 * 成功した場合、private registryにpush
 * 失敗した場合、Gmailに通知を行う

# 環境
* OS: Ubuntu14.04
* docker version: 1.4.1
* /etc/default/dockerに[こちら](http://qiita.com/DG0426/items/14d006cf6ffcb290c9fb)の設定を行う
* 上記ホストを2台用意

下記のような構成を目指します。(全てdockerコンテナです。)

* ホストA
 * private dockcer registry
 * private dockcer registry(Web UI)
* ホストB
 * GitLab
 * Jenkins

ローカルGitリポジトリは下記のような構成にすること

* ubuntu1404_test
 * Dockerfile
* hoge
 * Dockerfile
* fuga
 * Dockerfile

# 設定（ホストA）
## private dockcer registry
下記を参考にregistryコンテナを用意します。

* [private docker registry を使ってみる](http://qiita.com/DG0426/items/b8006c3168e245628005)
* [続：private docker registry を使ってみる(WebUI)](http://qiita.com/DG0426/items/1f250791a0d5d74306f3)

```bash
$ sudo docker ps
IMAGE                                        PORTS
konradkleine/docker-registry-frontend:latest 443/tcp, 0.0.0.0:8080->80/tcp
registry:latest                              0.0.0.0:5000->5000/tcp
```
※CONTAINER IDなどは省略

# 設定（ホストB）
## GitLab 初期設定
下記を参考にGitLabコンテナを用意します。

* [GitLab in docker でLDAP認証を利用する](http://qiita.com/DG0426/items/b63520e0e63a6c1efe70)

```bash
$ sudo docker ps
IMAGE                   PORTS                                                   NAMES
sameersbn/gitlab:latest 443/tcp, 0.0.0.0:10022->22/tcp, 0.0.0.0:10080->80/tcp   hungry_ardinghelli
sameersbn/mysql:latest  3306/tcp                                                gitlab-mysql
sameersbn/redis:latest  6379/tcp                                                gitlab-redis
```
※CONTAINER IDなどは省略

GitLabにログイン後、プロジェクトを作成し、ローカルと連携します。
なお、ubuntu1404_testディレクトリの中には簡単なDockerfileが入っています。
※ディレクトリ名をイメージタグに使用するので、重複などしないように注意して下さい。

![gitlab001.png](https://qiita-image-store.s3.amazonaws.com/0/58725/aa03e5dc-465f-87cc-c427-86c13071f2f7.png)

![gitlab002.png](https://qiita-image-store.s3.amazonaws.com/0/58725/c04f96ba-9a34-1191-f08f-b6b1713df257.png)

## Jenkins 初期設定
下記を参考にしつつ、必要なモジュールを追加したJenkinsコンテナを用意します。

* [doragon/Dockerfile/debian/8.0/jenkins/](https://github.com/doragon/Dockerfile/tree/master/debian/8.0/jenkins)

```bash
$ sudo git clone https://github.com/doragon/Dockerfile.git
$ sudo mv Dockerfile/debian/8.0/jenkins/ ./
$ sudo rm -rf Dockerfile
$ cd jenkins/
$ mkdir jenkins_home
$ chmod 777 jenkins_home
```

```bash
$ sudo docker build -t koide/jenkins .

$ sudo docker run -p 28080:8080 -d \
 -v [PATH]/jenkins/jenkins_home:/var/jenkins_home \
 -v [PATH]/jenkins/workspace:/var/workspace \
 koide/jenkins

$ sudo docker ps
IMAGE                PORTS
koide/jenkins:latest 50000/tcp, 0.0.0.0:28080->8080/tcp
```

ここで、GitLabと連携を行うため、jenkinsユーザーの個別設定を行います。
※本来ならDockerfileで完結すべきなのですが、rootユーザー以外での権限設定がうまくいかないためコンテナ内で作業を行います。

```bash
$ sudo docker exec -it 3fbc958b8006 bash
jenkins@3fbc958b8006:/$
jenkins@3fbc958b8006:/$ cd
jenkins@3fbc958b8006:~$ pwd
/var/jenkins_home

jenkins@3fbc958b8006:~$ mkdir .ssh
jenkins@3fbc958b8006:~$ cd .ssh
jenkins@3fbc958b8006:~/.ssh$ ssh-keygen -t rsa -f id_rsa -C "jenkins@test.com" -N ""
jenkins@3fbc958b8006:~/.ssh$ chmod 744 $HOME/.ssh
jenkins@3fbc958b8006:~/.ssh$ chmod 700 $HOME/.ssh/id_rsa
jenkins@3fbc958b8006:~/.ssh$ vim config
```

```conf:$HOME/.ssh/config
Host [GITLAB_ON_DOCKER_HOST]
 User git
 Hostname [GITLAB_ON_DOCKER_HOST]
 Port [DOCKER_HOST_PORT_MAPPED_GITLAB_PORT_22]
 IdentityFile ~/.ssh/id_rsa
```
※[GITLAB_ON_DOCKER_HOST]、[DOCKER_HOST_PORT_MAPPED_GITLAB_PORT_22]は適宜修正して下さい。
※[DOCKER_HOST_PORT_MAPPED_GITLAB_PORT_22]はここでは10022です。（上述のGitLabコンテナ作成時を参照）

後ほど、GitLabに登録するため、id_rsa.pubを控えておきます。

```bash
jenkins@3fbc958b8006:~/.ssh$ cat id_rsa.pub
```

```bash
jenkins@3fbc958b8006:~/.ssh$ git config --global user.email "jenkins@test.com"
jenkins@3fbc958b8006:~/.ssh$ git config --global user.name "jenkins"
```

ここまでで、Jenkinsコンテナ内での作業は終わりです。
次にJenkinsのWebUIにて、必要なプラグインをインストールします。
Jenkinsの管理 ⇒ プラグインの管理 ⇒ 利用可能
から「Gitlab Hook Plugin」をインストールします。

![jenkins001.png](https://qiita-image-store.s3.amazonaws.com/0/58725/ebf500b7-5271-c691-7642-5c80198215c5.png)

## GitLab へ Jenkins ユーザーを追加
GitLabにAdmin権限を持つユーザーでログインします。
Admin area ⇒ New user
からjenkinsユーザーを作成します。

![gitlab003.png](https://qiita-image-store.s3.amazonaws.com/0/58725/d727a720-0de5-be10-5d18-9a9467f1527e.png)

※パスワードはユーザー作成後に、編集できます。

次に、Admin権限を持つユーザーをログアウトして、作成したjenkinsユーザーでログインします。
Profile settings ⇒ SSH Keys ⇒ Add SSH Key
を選択し、先ほどjenkinsコンテナ内で生成したid_rsa.pubを設定して下さい。

![gitlab004.png](https://qiita-image-store.s3.amazonaws.com/0/58725/547aaa2b-1ece-7663-7500-32fe0b0f04a7.png)

再度、ログアウトをし、Dockerfile_CIプロジェクトを作成したユーザーでログインします。
ProjectのSettings ⇒ Members ⇒ New project member
を選択し、jenkinsユーザーを追加します。

![gitlab005.png](https://qiita-image-store.s3.amazonaws.com/0/58725/0b02877b-5e33-ffad-db0d-e1bf23687318.png)

続いて、jenkinsへのweb hook設定を行います。
ProjectのSettings ⇒ Web Hooks

```URL
http://[JENKINS_ON_DOCKER_HOST]:[DOCKER_HOST_PORT_MAPPED_JENKINS_PORT_8080]/job/Dockerfile_CI/build?delay=0sec
```

※[JENKINS_ON_DOCKER_HOST]:[DOCKER_HOST_PORT_MAPPED_JENKINS_PORT_8080]は適宜修正して下さい。
※[DOCKER_HOST_PORT_MAPPED_JENKINS_PORT_8080]はここでは28080です。（上述のJenkinsコンテナ作成時を参照）

![gitlab007.png](https://qiita-image-store.s3.amazonaws.com/0/58725/8dd93763-178d-47c1-e443-6dafa87cfc38.png)

## Jenkins にてジョブの作成
新規ジョブ作成 ⇒ フリースタイル・プロジェクトのビルド
を選択します。（ジョブ名は何でも構いません。）

![jenkins002.png](https://qiita-image-store.s3.amazonaws.com/0/58725/edf11d82-a4bb-c351-d3af-d67e58b3f358.png)

ソースコード管理 ⇒ Git ⇒ Repositories ⇒ Credentials ⇒ Add
を選択し、ユーザー名jenkinsを作成します。

![jenkins003.png](https://qiita-image-store.s3.amazonaws.com/0/58725/ec722672-a755-e7c8-d21a-d90327892cab.png)

Repository URLにはGitLabプロジェクトのSSHを入力して下さい。
なお、ここでは[DOCKER_HOST]を書いているため、エラーを出力していますが、正しいGitLabプロジェクトのSSHを入力することで、このエラーは出力されなくなります。

![jenkins004.png](https://qiita-image-store.s3.amazonaws.com/0/58725/85fff63e-f6d9-02e5-0339-907b39450f09.png)

最後に
ビルド・トリガ ⇒ SCMをポーリング にチェック
ビルド ⇒ シェルの実行

![jenkins005.png](https://qiita-image-store.s3.amazonaws.com/0/58725/4da31f7b-5237-552a-45e5-ee2f3b315727.png)


```bash:jenkins_shell
BASEURL='tcp://[DOCKER_HOST_B]:4243'
VERSION='1.12'

PATH=`pwd`
REGISTRY='[DOCKER_HOST_A]:5000'

for f in *; do
  if test $f = "README.md" ; then
    continue
  fi

  WORKPATH=$PATH/$f
  echo $WORKPATH
  TAG='jenkins/'$f
  echo $TAG
  /usr/bin/python /var/workspace/dockerclient.py $WORKPATH $TAG $REGISTRY $BASEURL $VERSION
done
```

## 通知の設定

このままではビルド失敗時に何も通知されません。
今回はGmailを利用した通知の設定を行います。

Jenkinsの管理 ⇒ システムの設定 ⇒ Jenkinsの位置 ⇒ システム管理者のメールアドレス

* システム管理者のメールアドレス： xxx@gmail.com

Jenkinsの管理 ⇒ システムの設定 ⇒ E-mail 通知

* SMTPサーバー： smtp.gmail.com
* SMTP認証にチェック
* ユーザー名： xxx@gmail.com
* パスワード： xxxxxx
* SSLにチェック
* SMTPポート： 465
* 文字セット： UTF-8

上記を設定し、保存します。続いて、

ジョブを選択 ⇒ 設定 ⇒ ビルド後の処理 ⇒ E-mail通知

* 宛先： hoge@gmail.com,fuga@gmail.com

これで設定は全て終了です。

# CIの流れ（例）

* ローカルでDockerfileを修正する
* GitLabへpushする
* Jenkinsでビルド結果の確認
* private docker registryにpushされているか確認

![gitlab008.png](https://qiita-image-store.s3.amazonaws.com/0/58725/e58a0db1-1fc0-7a0f-f32f-f839cd5b4cc5.png)

![jenkins006.png](https://qiita-image-store.s3.amazonaws.com/0/58725/960ab361-9f10-9d5c-1649-b6512c5b7cd7.png)

![registry001.png](https://qiita-image-store.s3.amazonaws.com/0/58725/36603592-ef5f-ae16-3c48-bbc20c3abf33.png)

build失敗時は次のようになる。

![jenkins007.png](https://qiita-image-store.s3.amazonaws.com/0/58725/00684148-1dec-ccff-bdaf-0bd555632a0a.png)

![gmail0001.png](https://qiita-image-store.s3.amazonaws.com/0/58725/9792f070-aba5-a251-f497-041f033b45ad.png)


# 補足
Web Hookの部分はJenkinsのビルド実行をそのまま叩いていますが、下記のような設定方法もあります。

```bash
http://[JENKINS_ON_DOCKER_HOST]:[DOCKER_HOST_PORT_MAPPED_JENKINS_PORT_8080]/gitlab/build_now
```

元々こちらを使用するつもりでしたが、ポートの指定が異なるためか、うまくWebHookが通らないので、今回は避けました。

# 今後の課題
今回の実装では、confファイルなどを考慮していません。
そのため、Dockerfileと同階層に別のファイルがあった場合、おそらくエラーになります。（未確認）

解決策はdockerclient.py内へ設定ファイル込みのbuildを記述することと、Jenkinsシェルの設定を追記することですが、そちらはまだ未実装です。

# おわりに
少し長くなってしまいましたが、最後までお読み頂き、ありがとうございました。