Gitlab Community Editionで自動デプロイさせてみる

自分がサボってる間に後輩がバイト先で便利なslackbotを作ってくれた。~~行動力羨ましい~~
変更のたびにsshしてpullするのはめんどくさいので自動でデプロイするようにしてみた。前例マシマシなので個人の記録として。ちなみにwebサーバーはnginx+php-fpmです。

# デプロイキーを登録する
これまでデプロイの意味すら知らなかったのでまずこれから。Gitlabでは読み込み専用の暗号化鍵を設定できるらしい。設定>リポジトリ>Deploy Keysの項目を開くと設定するメニューがでる。

* タイトル :  サーバーのお名前とか
* key : ssh-keygenで作った公開鍵

を入力して`Add key`をクリックすれば登録は完了。あとはサーバーのアカウント側で`.shh/config`を書けばssh経由でのclone/pullができるようになる。[Write access allowed]チェックを抜いたので上にも書いたとおりこのディレクトリからpushはできない。

# デプロイするスクリプトを書く
ちょろっとググった結果phpで書くのが良さそうなのでphpで書いた。
やりたいことは

* 今動いているslackbotのコンテナを止める
* masterをpullする
* dockerイメージをbuildする
* buildしたイメージでコンテナを起動する

できたのがこれ

````deploy.php
<?php

set_include_path(get_include_path() . PATH_SEPARATOR . 'phpseclib');
include('phpseclib/Net/SSH2.php');

$secret_key = "SECRETKEY"; //申し訳程度のセキュリティ要素
$LOG_FILE = dirname(__FILE__).'/hook.log'; //ログファイルの設定

//GETで渡されるKEYが上述のものと合わなかったら不正なアクセスとして記録
if ( !isset($_GET['key']) || !($_GET['key']==$secret_key) ) {
    file_put_contents($LOG_FILE, date("[Y-m-d H:i:s]")." invalid access: ".$_SERVER['REMOTE_ADDR']."\n", FILE_APPEND|LOCK_EX);
    exit;
}

file_put_contents($LOG_FILE, date("[Y-m-d H:i:s]")." ".$_SERVER['REMOTE_ADDR']." git issue recieved.\n", FILE_APPEND|LOCK_EX);


$ssh = new Net_SSH2('slackbot_server.local');
if (!$ssh->login('username', 'password')) {
        exit('ログイン失敗');
}
#docker stop
echo $ssh->exec('docker stop $(docker ps | grep slackbot | cut -c 1-12)');
echo $ssh->exec('cd /opt/slackbot&&git pull');
echo $ssh->exec('docker build /opt/slackbot -t slackbot');
echo $ssh->exec('docker run --rm -d slackbot:latest');
```

`deploy.php`と、参照しているライブラリ[phpseclib](https://github.com/phpseclib/phpseclib/archive/2.0.15.zip)をwebサーバーが公開しているディレクトリに置く。

# webhookを登録する
Gitlabリポジトリのページを開き 設定>リポジトリ>インテグレーション からWebhookの設定ができる。今回はローテクにGETで認証キーを渡して認証しているのでurlに`http://{deploy.phpのアドレス}?key=SECRETKEY`と打つだけ。1つ下にある[Secret Token]の項目は使わない。更に下にはいつwebhookを叩くかの[Trigger]の項目があるので必要なものにチェックを入れる。今回はmasterにpushされたときだけでいいので[Push events]にチェックを入れ対象のブランチ名をmasterとした。最後に[Add webhook]をクリックすれば登録完了、先程の[Add webhook]の下に登録したwebhookが現れる。念の為[test]を押してテストすることもできる。

# できたもの
pushすると以下のような流れで自動的に更新が反映される。
[Dev] --push--> [Gitlab] --webhook--> [webサーバー] --php/ssh--> [slackbotのサーバー]
slackbotのとこでwebサーバーを立てても良かったけどすでに立ってる他のところがあったので流用したかったからちょっと遠回りな方法にみえる。

# 引っかかったところ
1. webhookにローカルのwebサーバーのurlを入力するとエラーがでる
 
    ローカルに立っていても初期設定だとローカルへのwebhookは許可されていないらしい。管理者エリアから 設定>ネットワーク>Outbound requestsの項目を開くと`Allow requests to the local network from hooks and services`なる項目があるのでこれにチェックを入れ保存するとローカルのwebhookを指定できるようになる。
2. webhookは正常に動いているのが確認できたのにGitlab上でタイムアウトのエラーが出る

    `docker stop`したりするのにGitlabのデフォルトのタイムアウトが10秒になっているのが原因だった。Gitlabが立っているサーバーに入り、`/etc/gitlab/gitlab.rb`を以下のように編集した。

    ```/etc/gitlab/gitlab.rb
    (前略)
    #gitlab-rails['webhook_timeout'] = 10
    gitlab-rails['webhook_timeout'] = 200
    (後略)
    ```
    編集の後`sudo gitlab-ctl reconfigure`を実行して反映するとしっかり待ってくれるようになる。

# 参考
[GitlabのWebhookを使って簡易デプロイ（Webサーバへ）をしてみた - Qiita](https://qiita.com/redamoon/items/a47178621d8bd8d95edf)
[GitLab: ローカルを指した外向きWebhookはデフォルト禁止（10.6以降） - taikii blog](https://taikii.net/posts/2018/11/gitlab-disable-requests-to-the-local-network-from-hooks/)
[Hooks being called many times (#2229) · Issues · GitLab.org / GitLab Community Edition · GitLab](https://gitlab.com/gitlab-org/gitlab-ce/issues/2229)