GitLab-CIでHerokuにデプロイするまでの奮闘記

業務で開発者一人（＋仕様検討一人）のプロジェクトをやってます。
開発を始めるにあたって、コードのGit管理は当たり前として、今後規模が大きくなった時のことも考えるとホスティングサービスは必須かなということでGitLabを導入することにしました。

※ほんとはGitHubでクラウド管理したいところですが、社内規定的にクラウドに業務情報を置けないのでオンプレ構築できるGitLabを採用しました

微妙にハマったところもあるので、備忘録がてら記事にしておこうかと思います。
※IPアドレスとかプロジェクト名なんかは、ところどころフェイク入れたりモザイク入れたりしてます。



# 構成

|環境|説明|
|:--|:--|
|ローカルPC|開発環境（Windowsマシン）|
|サーバー用ノートPC|使わなくなったノートPCにCentOS7をインストールしたもの（予算がね。。）<br> ※以降サーバーPC|
|Heroku|ステージング環境／本番環境|





# 初めてのGitLab構築
以下のページを参考に、サーバーPC上にDockerでインストールをします
参考：https://docs.gitlab.com/omnibus/docker/



### dockerコマンドでGitLabコンテナ作成

```
sudo docker run --detach \
	--hostname 192.168.33.20 \
	--publish 10080:10080 --publish 10022:22 \
	--name gitlab \
	--restart always \
	--volume /srv/gitlab/config:/etc/gitlab:z \
	--volume /srv/gitlab/logs:/var/log/gitlab:z \
	--volume /srv/gitlab/data:/var/opt/gitlab:z \
	gitlab/gitlab-ce:latest
```
*【公式からの変更点】*

- hostnameはサーバーPCのIPアドレスを指定
    - hostsに`192.168.33.20 gitlab.example.com`追加してとかメンバーに周知するのがめんどくさい
    - `http://192.168.33.20:10080`でアクセスできるようにしたかった
- サーバーPCにはGitLab以外にも機能を搭載しているため、ポート22,80は使わないようにした
- external_urlでポートまで指定すると起動ポートまで変わるみたいなので`100080:10080`で指定



### GitLab設定編集

```/srv/gitlab/config/gitlab.rb
・・・
external_url "http://192.168.33.20:10080/"
・・・
gitlab_rails['gitlab_shell_ssh_port'] = 10022
・・・
```
- 編集完了後、`docker exec gitlab gitlab-ctl reconfigure`で設定を反映させる
    - `docker restart gitlab`でコンテナ再起動でも良いかも？




### アクセスしてみる
external_urlで指定したURLにローカルPCのブラウザで`http://192.168.33.20:10080`にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/664ac36a-04ed-c52b-a780-bed9124b87f8.png)
⇒GitLabに接続できればOK！
　（最初はrootユーザーのパスワード設定画面が出る）

以下の画面が出る場合は、まだ起動中の場合が多いので、少し待つ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/714d4c9d-da91-14b6-31f3-650ee7187f63.png)


- `docker ps`でgitlabコンテナの起動状況を確認できます
    - `(health: starting)`となっていたら起動中
    - `(healty)`となっていたら起動完了
    - `(unhealthy)`となっていたら起動失敗
        - この場合はログを見て対応する





### GitLab-CIについて
開発が一人だけで本番環境へのデプロイもまだまだ先だということで、ずっとGitLab単体で作業してました。
あれやこれやとやってる内に本番環境に反映するタイミングが来てしまったんですが、最初のデプロイは手作業でやってしまいました。。。


先のことを考えるとまずいなと思い、GitLab-CIの設定をすることに:sweat_smile:

↓ちなみに、ローカルPCからHerokuにデプロイする場合（heroku cliでログインしたりは別途必要です）

```
git pull
git push heroku master
```

*【懸念】*

- pullし忘れて古いコードが本番に反映される可能性がある
- ローカルの修正が本番にデプロイされる可能性がある
- ．．．









# docker-composeで再構築
GitLab-CIを使うにあたって、GitLab-Runnerのコンテナを新しく立てる必要が出てきた。  

dockerコマンドでも構築できるが、構成を確認しづらいと思ったので、docker-composeで構築しなおすことにした。
（docker-compose.ymlはテキストだからGit管理できるし、見れば構成がわかるし、コマンドも`docker-compose up`だけなので、簡単）

docker-composeでの構築にあたって、[こちらのページ](https://mikoto2000.blogspot.com/2018/07/docker-compose-gitlab-gitlab-runner.html)を参考にさせていただきました。



## データのバックアップ
何かあったら怖いので、バックアップを取っておきます

```
docker exec gitlab gitlab-rake gitlab:backup:create
```
⇒デフォルトでは`/srv/gitlab/data/backups`下に作られます

※もし、再構築ついでにGitLabを最新版にしたいなら、先に現在のGitLabを最新版にバージョンアップしてからバックアップを取って下さい。


## 構築済みGitLabバージョンを調べておく
gitlabコンテナイメージのバージョンが前回構築時と異なる場合、動かなくなる可能性があります。。。

バージョンは「？⇒Help」から確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/516f2c24-8db3-e085-2b96-f55807fff0f4.png)



## docker-compose.yml

```docker-compose.yml
version: '3'
services:
    gitlab:
        image: gitlab/gitlab-ce:11.8.10-ce.0
        restart: always
        container_name: gitlab
        volumes:
            - /srv/gitlab/config:/etc/gitlab:z
            - /srv/gitlab/logs:/var/log/gitlab:z
            - /srv/gitlab/data:/var/opt/gitlab:z
        environment:
            GITLAB_OMNIBUS_CONFIG: |
                external_url 'http://192.168.33.20:10080/'
                gitlab_rails['gitlab_shell_ssh_port'] = 10022
        ports:
            - "10080:10080"
            - "10022:22"
        networks:
            gitlab_net:
                ipv4_address: 172.16.238.2
    gitlab-runner:
        image: gitlab/gitlab-runner:v11.8.0
        restart: always
        container_name: gitlab-runner
        volumes:
            - /srv/gitlab-runner/config:/etc/gitlab-runner:z
            - /var/run/docker.sock:/var/run/docker.sock
        depends_on:
            - gitlab
        extra_hosts:
            - "192.168.33.20:172.16.238.2"
        networks:
            gitlab_net:
                ipv4_address: 172.16.238.3
 
networks:
    gitlab_net:
        ipam:
            config:
            - subnet: 172.16.238.0/24
```

- [参考にさせていただいたページ](https://mikoto2000.blogspot.com/2018/07/docker-compose-gitlab-gitlab-runner.html)のymlをほぼ流用してます
- 変更点
    - hostnameを削除（あるとうまくいかなかったので。。。）
    - コンテナ名を変更
    - GITLAB_OMNIBUS_CONFIGの内容を変更
    - volumeは前回構築時に指定したディレクリに変更
    - イメージのバージョンを前回構築時のバージョンと同じものを指定
        - 最初`latest`にしてたらバージョンの違いのせいでプロジェクトが見れなくなりました。。。
    - 一番下のvolumesを削除


## GitLab-Runnerの登録


### RunnerのURLおよびトークンの調べ方

Runnerの登録前に登録用のURLとトークンを調べておきます。

- プロジェクトの「Settings⇒CI/CD」からRunner項目を展開する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/48d1f333-d2bd-1738-19a9-1840aaeec2b6.png)

- 展開した中にURLとトークンがあるため、コピーする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/e8a12ee2-d5c3-c19b-fa1c-76e7c2849cfc.png)


### Runnerの登録を行う

これも[参考にさせていただいたページ](https://mikoto2000.blogspot.com/2018/07/docker-compose-gitlab-gitlab-runner.html)通りに実行

```
docker-compose exec gitlab-runner gitlab-runner register \
    --non-interactive 
    --name gitlab-runner 
    --url http://192.168.33.20:10080/ 
    --registration-token "{YOUR-TOKEN} "
    --executor docker 
    --env "GODEBUG=netdns=cgo" 
    --docker-image debian:stretch-slim 
    --tag-list test `
    --docker-extra-hosts "192.168.33.20:172.16.238.2"
```



### エラーが出た。。。

ローカルのVM上で試したときはすんなりと登録出来たのに、サーバーPCだと登録できませんでした。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/a2f8a0d2-cc55-7be2-5f32-66b0fff114ab.png)




```
ERROR: Registering runner... failed       runner=XXXXX status=couldn't execute POST against http://～/api/v4/runners: Post http://～/api/v4runners: dial tcp ～: get sockopt: no route to host
PANIC: Failed to register_this runner. Perhaps you are having network problems
```

ふむ、なんかしらのネットワークエラーが起こってるっぽい。。。
ネットワークわからん:sob:

とりあえず、gitlab-runnerコンテナ内から`192.168.33.20`が見えてないってことだと思う
gitlab-runnerコンテナ内で`curl http://192.168.33.20:10080`がno routeになったし。


### とりあえず、強引に登録してみる
urlのホスト名をgitlabコンテナで指定したIPアドレスに変更してみました

```
docker-compose exec gitlab-runner gitlab-runner register \
    --non-interactive 
    --name gitlab-runner 
    --url http://172.16.238.2:10080/ 
    --registration-token "{YOUR-TOKEN} "
    --executor docker 
    --env "GODEBUG=netdns=cgo" 
    --docker-image debian:stretch-slim 
    --tag-list test `
    --docker-extra-hosts "192.168.33.20:172.16.238.2"
```


登録出来た！:satisfied:





## パイプラインを実行してみた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/4bbb2616-1a3d-c141-4a1b-c780918eb962.png)

`Host is unreachable`。。。
ちなみにこの時の`http://gitlab-ci-token:xxxx@～:10080`の～部分に表示されたURLは`172.16.238.20`ではなく、`192.168.33.20`の方だった:rolling_eyes:

クローン元のURLはRunner登録時の`--url`で指定するURLとは別らしい。



## 原因調査
色々調べてる中で以下の記事を発見
https://gitlab.com/gitlab-org/gitlab-runner/issues/1036

- external_urlを指定するんだよ！
- extra_hostsに追加してみた？
- linksにgitlabを指定すれば良いんだよ！

みたいなのが、色々と書いてあって、最後までず～っと読んでると、`clone_url`を指定すれば良いんだよ！という回答が！！




## clone_urlを指定してみる
公式ドキュメントに書いてありました
https://docs.gitlab.com/runner/configuration/advanced-configuration.html#how-clone_url-works

一旦、作成したrunnerを削除し、登録コマンドに`--clone-url 172.16.238.2`を追加して登録しなおしてみる。

↓パイプラインの実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/7e0bd73c-5567-0c71-f897-97a5071f9670.png)
⇒クローン元のホストは変わったが、`Operation timed out`。。。

一応、`Host is unreachable`は解消されたのかな？



## 大きな勘違いに気づいた
ふとした瞬間に`docker ps -a`でコンテナ一覧を確認した。
すると、`runner-xxxxx`のようなコンテナの残骸が大量にあった。。。

まさか。。。
再度パイプラインを動かし、動作中に`docker ps`してみると、案の定runner-xxxというコンテナが動いていた。

ということは。。。
パイプライン動作中にデフォルトネットワークを確認してみる。
`docker network inspect bridge`
※ネットワーク指定なしだと、`bridge`に属するっぽい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/555627f6-62d1-6803-d60a-defdeb7116da.png)


いたーーーーーーーーーーーーーーーーーー
要はCI用のコンテナがgitlab_net内にいないために、gitlabコンテナが参照できない状態となっていたってことらしい。


GitLab-CIでdockerを指定した場合、gitlab-runnerコンテナ内でCI用コンテナが動くもん（Docker-in-Docker）だと思ってましたが、実際は`gitlab`や`gitlab-runner`と同じ階層で起動するようです。

gitlab-runnerコンテナ内からホスト側へのネットワークが通じてないのかな～？みたいな調査をしてましたが、見当違いだったみたいです:sob:



## ついに解決！！！


### network_mode

ネットワークの設定とかできる項目ないかな～と公式ページを探していたら、以下の一覧に`network_mode`なる項目が！！！

https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section



### 最終的なRunner登録コマンド

こんな感じに仕上がりました。

```
docker-compose exec gitlab-runner gitlab-runner register \
    --non-interactive \
    --name gitlab-runner \
    --url "http://gitlab:10080/" \
    --clone-url "http://gitlab:10080" \
    --registration-token "YOUR_TOKEN" \
    --executor docker \
    --env "GODEBUG=netdns=cgo" \
    --docker-image "debian:stretch-slim" \
    --tag-list test \
    --docker-network-mode "gitlab_gitlab_net" \
    --docker-pull-policy "if-not-present"
```

- docker-network-modeでnetworkを指定
    - `docker network ls`で実際のネットワーク名を確認（gitlab_gitlab_netでした）
- urlのホストはgitlabコンテナ名を指定


これで、Runnerの登録ができて、サンプルのパイプラインも動作するようになりました！！

(追記 2019/11/1)
　・clone-urlがないとダメっぽいので追記しました
　・毎度イメージをプルしないようにpull-policyを追加しました

# Herokuへのデプロイ
Herokuへのデプロイ方法は以下のページを参考にする。
https://docs.gitlab.com/ee/ci/examples/README.html

## .gitlab-ci.yml作成

今回Node.jsで開発しているのですが、公式ページだと情報がなかったので、[こちらのページ](https://medium.com/@mattchewone/gitlab-ci-cd-testing-nodejs-app-642734503e47)も参考にさせてもらいました。

```.gitlab-ci.yml
image: node:9-alpine

stages:
  - deploy

deploy-staging:
  image: ruby:latest
  stage: deploy
  tags:
    - test
  script:
  - gem install dpl
  - dpl --provider=heroku --app=$HEROKU_APP_STAGING --api-key=$HEROKU_CI_API_KEY
  only:
  - staging

deploy-production:
  image: ruby:latest
  stage: deploy
  tags:
    - test
  script:
  - gem install dpl
  - dpl --provider=heroku --app=$HEROKU_APP --api-key=$HEROKU_CI_API_KEY
  only:
  - master

```
とりあえず、ステージングと本番のデプロイをするだけです


## GitLabに変数を登録

|変数名|内容|
|:--|:--|
|HEROKU_APP_STAGING |Heroku上のプロジェクト名（ステージング）|
|HEROKU_APP |Heroku上のプロジェクト名（本番）|
|HEROKU_CI_API_KEY|Herokuから取得したAPIキー|

GitLabへの登録は対象プロジェクトの「設定⇒CI/CD⇒Environment variables」から行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/393c81b8-f67f-7c8e-fc47-b60f50691ce0.png)



### HerokuのCI用APIキーの取得方法

- Herokuにログインし、`Account settings`を選択


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/123d37c1-8178-1520-0396-7035b5f9b9a7.png)

- Accountタブの下の方に`API Key`があります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/bef9a170-0795-6e34-5ad2-54aa34c1d402.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/685e3b2c-78e0-52e3-22a3-fb337781c66a.png)



## いざHerokuへ

これで準備はできました！！
いざ、デプロイ！！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/ef4fcc87-821c-17aa-c67c-51a47a5e27df.png)
（中略）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325108/03f06e85-0b35-e9ac-7ad6-dae20118b3bc.png)

キターーーー:joy:


# 最後に

長い道のりでしたが、無事にHerokuへのデプロイまでできるようになりました:grin:
インフラとかネットワーク、Dockerあたりの知識は必要だな～と痛いほどに実感しました。。。



# 参考
[GitLab Docs](https://docs.gitlab.com/)
[Docker Compose で GitLab + GitLab Runner の環境を整える](https://mikoto2000.blogspot.com/2018/07/docker-compose-gitlab-gitlab-runner.html)
[Heroku Tips(4) Gitlabで自動デプロイ](https://hub.appirio.jp/tech-blog/heroku-tips-4)
[Gitlab CI/CD Testing Nodejs App](https://medium.com/@mattchewone/gitlab-ci-cd-testing-nodejs-app-642734503e47)