GitlabのWebhookを使って簡易デプロイ（Webサーバへ）をしてみた


以前、[bitbucket-syncを使ってサーバ自動化をしてみた](http://qiita.com/redamoon/items/7ac784b3eb53d09b6e93)をやって簡易デプロイを行なう方法を公開しましたが、GitlabのWebhookがうまくいきませんでした。[cronを使いGitlabリポジトリをWEBサーバ側に反映させる](http://qiita.com/redamoon/items/cf806971b278775bd5b7)などで代替してみたり社内の環境は紆余曲折な状態でした。

ググればGitlabのサーバデプロイとかは上がってくるのですが、中々自分の環境ではうまくいかずいました。
何気に再チャレンジするとうまくいったので、忘れないためにもメモとして残しておきます。

きちんとしたアプリケーションを作る場合は、CIとかは必須ですがWebサイト程度で簡易デプロイする方法になります。

※社内の環境を想定した作りのため、ご自身の環境には合わない可能性はあるかもしれません。
※権限の問題や鍵など不明点あったりするので、間違っている部分あるかもしれません。

# 必要な環境

* Webサーバ1台（当たり前ですが。。。）
* Gitlabサーバ 1台（社内は、bitnamiのGitlabを使用）
* WebサーバはGitコマンドが使えるようにしておく（yumで）
* Webhook用のドキュメントルートとWebサイト用のドキュメントルートを用意（VirtualHostにて）
* .gitを配置している開発ドメインのドキュメントルートを用意
* WebサーバはPHPがインストールされていること

# 今回の構成

## Gitサーバ

* bitnami製のGitlab（AWSのMARKET PLACEで提供されているAMI）

## Webサーバ

* CentOS6
* Apache
* PHP
* Git
* MySQL（静的サイトの場合はなし）

### Webサーバのディレクトリ構成

サーバの記事等を参照するとWebデータは、大体は/var/で配置しています。※ご自身の環境に合わせて調整してください。
弊社の環境構築はWebデータをすべて/homeに配置していたため、このような構成にしています。

※サブドメインディレクトリの配下にpublic_htmlを配置しなかったのは、Gulpなどを使いHTMLテンプレートエンジン構成でコーディングを行なうため編集用と公開用を分けているため以下のような形になっています。

```
/home/
  サブドメイン名/（dev.example.com）
    www/
      .git/
      public_html/（Webサイトのドキュメントルート）
  webhook/（webhook.example.com）
    public_html/（Webhookのドキュメントルート）
      サブドメイン名/
        release.php（Webhookが実行するプログラムデータ）
```

# サイト反映までのイメージ図

![images.png](https://qiita-image-store.s3.amazonaws.com/0/34020/dac1eaa1-1a5f-4f6e-692b-a555b4244260.png)

* ローカルで作業してgit push
* Gitlabのリモートリポジトリに反映
* 反映後、Web hookでWebサーバのプログラムを実行（webhook.example.com/release.php）
* 実行後、開発環境ドメインのドキュメントルートに配置した.gitがgit pullを行なう
* Webサイトに反映（dev.example.com/index.html）

# サーバの設定

Webサーバの設定を行います。
.confに2つのドメインのVirtualHostの記述をして再起動を行います。

※必要に応じてconfファイルを分けて下さい。※今回はhttpd.confに直接記述します。

## dev.example.comのVirtualHost

※ご自身の環境に合わせて設定してください。
※必要に応じてベーシック認証の設定（弊社の環境では設定しています）

### サブドメインを作成

* ユーザの作成とユーザのディレクトリを作成
* ユーザのパスワードを入力
* ユーザグループを追加するsftp-users（sftp-usersというグループで管理しているため）
* chrootの設定を行なう（chrootの設定）

```
# useradd -g users -d /home/サブドメイン サブドメイン
# passwd サブドメイン
# usermod -aG sftp-users サブドメイン
# chown root:root /home/サブドメイン
```

* サブドメインディレクトリに移動
* wwwディレクトリを作成
* wwwに移動
* Gitの初期化
* git remoteでリポジトリを入力し紐付けを行なう

```
# cd /home/サブドメイン/
# mkdir www/
# chown -R apache:apache www/
# git init
# git remote add origin git@リポジトリ.git
```

### .htpasswdを作成

* ベーシック認証作成

```
# htpasswd -c /home/dev.example.com/.htpasswd dev
パスワードを求められるので入力して完了
```

```
# vi /etc/httpd/conf/httpd.conf

<VirtualHost *:80>
        DocumentRoot "/home/dev.example.com/www/public_html"
        ServerName dev.example.com # 開発環境のドメインを入力
        ServerAdmin メールアドレスを入力
        ErrorLog /home/dev.example.com/logs/error_log
        SetEnvIf Request_URI "\.(gif)|(jpg)|(png)$" no_log
        CustomLog /home/dev.example.com/logs/access_log combined env=!no_log
        <Directory "/home/dev.example.com/www/public_html">
                AuthType Basic
                AuthUserFile /home/dev.example.com/.htpasswd
                AuthName IDを入力
                Require valid-user
                Options ExecCGI FollowSymLinks Includes
                AllowOverride All
                AddType application/x-httpd-cgi .cgi .pl
                AddHandler image/gif .gif
                AddHandler image/jpg .jpg
        </Directory>
</VirtualHost>
```

## webhook.example.comのVirtualHost

※ご自身の環境に合わせて設定してください。
※VirtualHost内にベーシック認証の設定も行なっています。（気持ち的にだれでも閲覧できてしまうのはどうかと思いますので、ベーシック認証入れておきました。。）

### webhookを作成

* ユーザの作成とユーザのディレクトリを作成
* ユーザのパスワードを入力
* ユーザグループを追加するsftp-users（sftp-usersというグループで管理しているため）
* chrootの設定を行なう（chrootの設定）
* webhookのログファイル作成
* ファイルの所有者をapacheユーザに変更
* サブドメイン_webhook_log.txtに書き込み権限を与える

```
# useradd -g users -d /home/webhook webhook
# passwd webhook
# usermod -aG sftp-users webhook
# chown root:root /home/webhook
# touch サブドメイン名_webhook_log.txt
# chown apache:apache サブドメイン_webhook_log.txt
# chmod 777 サブドメイン_webhook_log.txt
```

### .htpasswdを作成

* ベーシック認証作成

```
# htpasswd -c /home/webhook/.htpasswd webhook
パスワードを求められるので入力して完了
```

## VirtualHostの設定

```
<VirtualHost *:80>
        DocumentRoot "/home/webhook/public_html"
        ServerName webhook.example.com
        ServerAdmin メールアドレス
        ErrorLog /home/webhook/logs/error_log
        SetEnvIf Request_URI "\.(gif)|(jpg)|(png)$" no_log
        CustomLog /home/webhook/logs/access_log combined env=!no_log
        <Directory "/home/webhook/public_html">
                AuthType Basic
                AuthUserFile /home/webhook/.htpasswd
                AuthName webhook
                Require valid-user
                Options ExecCGI FollowSymLinks Includes
                AllowOverride All
                AddType application/x-httpd-cgi .cgi .pl
                AddHandler image/gif .gif
                AddHandler image/jpg .jpg
        </Directory>
</VirtualHost>
```

### プログラムデータを設置

PHPは普段書かないので、ググった記事で一番良かった「[GitLabからwebhook+PHPで超簡易自動デプロイシステムを作る](http://qiita.com/ikuwow/items/5794123bf4c379771d1c)」のサンプルファイルを使わせて頂きました。

* webhookの公開ディレクトリに移動
* サブドメイン用のディレクトリを作成（開発サーバが増える想定でサブドメインごとにディレクトリを切りました）
* 作成サブドメインディレクトリに移動
* サブメインディレクトリにrelease.phpを作成

```
# cd /home/webhook/public_html
# mkdir サブドメイン
# cd サブドメイン
# vi release.php
```

* Gitlabユーザのパスワードを入力
* 作成したログファイル名を書き換える（サブドメイン名_webhook_log.txt）
* リポジトリまでのパスを記述（wwwの理由は、Gulpで編集したファイルをpublic_htmlで出力するため：公開ディレクトリは、/home/サブメイン/www/public_html）
* $git_pathにリポジトリURLを記述（.gitで終わるURLになります。例：gitlab.example.com/GROUP/リポジトリ.git）
* 上記のソースをコピー・ペーストして保存（:wp）

```
<?php

$secret_key = "キーを入力してください"; // GitLab以外からのリクエストを受け付けないためのキー
$git_username = "管理者ID"; // gitlabのID
$git_password = "管理者IDのパスワードを入力"; // gitlabのログインパスワード
$log_file = '/home/webhook/サブドメイン名_webhook_log.txt'; // ログの出力場所
$date = (new DateTime())->format('Y-m-d H:i:s');
$path_to_repository = '/home/サブドメイン名/www';
$branch_name = 'master';
$git = '/usr/local/bin/git';
$git_path = 'gitlab.example.com/リポジトリ（.git）'; // GitlabのリポジトリURL

if ( !isset($_GET['key']) || !($_GET['key']==$secret_key) ) {
    echo "Invalid key";
    $is_logged = error_log("[{$date}] Error: Invalid key\n",3,$log_file);
    exit;
}

$json_string = file_get_contents('php://input');
$json = json_decode($json_string,true);

error_log("[{$date}] Pushed to {$json['ref']}\n",3,$log_file);

if ($json['ref']=="refs/heads/{$branch_name}") { // releaseブランチのpushイベントの時のみ実行
    $git_remote = "http://{$git_username}:{$git_password}@{$git_path}";
    $command = "cd {$path_to_repository} && ${git} pull {$git_remote} {$branch_name} 2>&1";
    exec($command, $output, $exit_status);

    if ($exit_status > 0) {
        $error_msg = "[{$date}] Error: \n";
        foreach ($output as $line) {
            $error_msg .= "    {$line}\n";
        }
        error_log($error_msg,3,$log_file);
    } else {
        error_log("[{$date}] Updated successfully\n",3,$log_file);
    }
}
```

# ローカル・リモートリポジトリに開発環境をセットアップする

Gulp環境などを考慮してた形を取ります。（編集データと公開データは別々になっているため）
簡易的なファイル構成（※Gulpなどを使用する場合は、いつも使用しているテンプレートを入れて下さい。）
※public_htmlが公開ディレクトリになりますので、かならず入れて下さい。

ローカル・リモートのリポジトリイメージは、以下を想定しています

```
・ .git
・ _develop（編集ディレクトリ）
・ public_html（公開ディレクトリ）
   > index.html
```

この状態でGitlabへ初回のpushをする

# Gitlab側の設定

* adminユーザでログイン
* 同期を取りたいリポジトリにアクセス
* リポジトリのページにアクセスしたらSettingsをクリックする

![git-repository.png](https://qiita-image-store.s3.amazonaws.com/0/34020/86a3304d-da32-eba0-f76b-0ca20b5f50ac.png)

* Deploy KeysとWeb Hooksをクリック（設定順番は任意）

![git-setting.png](https://qiita-image-store.s3.amazonaws.com/0/34020/0f8e42f9-b28e-10e1-ad77-b672fa56adb1.png)

## Deploy Keysの設定

WEBサーバ側でGitリポジトリをcloneするために鍵の設定が必要になります。

* New Deploy Keyをクリック
* サーバにアクセスできる公開鍵を入れる
* すでにセットされているためEnableを押すと、そのリポジトリにKeyがセットされる

![git-deploy.png](https://qiita-image-store.s3.amazonaws.com/0/34020/eb5541cd-4883-43f4-08a6-090d5d210bb2.png)

## Web Hooksの設定

* WebHooksのURLを入力（ここの入力URLは「プログラムデータをサーバにセットする」を参照）
* Push eventsにチェック
* Add Web Hookをクリック
* Webサーバのセットアップできたら、Test HookをクリックしてWeb Hookのログをチェックしてください

WebHooksのURLは以下になります。

* @の前はwebhookのベーシック認証
* @以降は、配置したプログラムデータまでのパスを入力
* ?key=設定したキーは、攻撃を受けないため対策（release.phpの$secret_keyで変数の値変更可能）

```
http://ベーシック認証ID:ベーシック認証PASS@webhook.example.com/サブドメインディレクトリ/release.php?key=設定したキー
```

![git-webhook.png](https://qiita-image-store.s3.amazonaws.com/0/34020/a5203dfc-5029-c59c-67bb-12f61c615978.png)

# Webサーバ側のApacheを再起動

* Apache conf テストコマンド

```
# apachectl configtest
```

* Apacheの再起動

```
/etc/init.d/httpd restart
```

GitlabのTest hookをクリックしてサイトが反映されれば、簡易デプロイは成功です。

# 参考記事

* [Why is git push gerrit HEAD:refs/for/master used instead of git push origin master](http://stackoverflow.com/questions/10461214/why-is-git-push-gerrit-headrefs-for-master-used-instead-of-git-push-origin-mast)
* [GitLabからwebhook+PHPで超簡易自動デプロイシステムを作る](http://qiita.com/ikuwow/items/5794123bf4c379771d1c)
* [git pushでremote rejectedと怒られた時にやったことメモ](http://qiita.com/ssm_snow/items/3041be842cadf68e6d67)