GitHubへのpush時に自動デプロイする方法まとめ

# はじめに

「GitHubへのpush時に自動デプロイする方法」を調べる機会がありましたので備忘録としてまとめます。
Webhookのみを使った単純なものからJenkinsやAWSとの高度な連携を行うものまで様々な形態がありますが、ここでは連携形態と使用シーンの代表的と思われる例をいくつかピックアップして下記します。

# 目次

- [GitHubのWebhookによる連携](#1-githubのwebhookによる連携)
- [Jenkinsとの連携](#2-jenkinsとの連携)
- [AWS CodeDeployとの連携](#3-aws-codedeployとの連携)
- [AWS CodeDeploy + 外部クラウドCIでの連携](#4-aws-codedeploy--外部クラウドciでの連携)
- [おわりに](#おわりに)

# 1. GitHubのWebhookによる連携

GitHubのレポジトリ設定にある「Webhook」機能を設定することで、通知先URL（サーバ）側でトリガーとなるプログラムを動かします。

連携手順は、

- pushを通知したいレポジトリの設定からWebhookを登録
- Webhookで指定したURLにトリガー用のプログラムを設置（PHP / Ruby / Node.js 等）
- 指定したレポジトリにpushが行われると、Webhook先のプログラムが呼ばれる
- プログラム側で前述のレポジトリから `git pull` してデプロイ処理を行う

との流れになります。

#### メリット

- 手軽に設置できる
- pushされたブランチ毎の処理切り分けが簡単にできる
- 小規模なシステム・単体サーバなどで複雑なビルドを必要としないシステムに向く
- クラウドサービス等の使用料が発生しない

#### デメリット

- 複数台のサーバで構成されるようなシステムへの適用には、別途、同期処理を考える必要がある
- 実行前にビルド・変換・キャッシュ生成などが必要な場合は独自に作成・構築が必要
- 複雑なデプロイ処理を要するシステムには手間がかかるため不向き

#### トリガー（デプロイ）用プログラム例

```php:config.php
<?php

// Config
$LOG_FILE     = dirname(__FILE__).'/hook.log';
$LOG_FILE_ERR = dirname(__FILE__).'/hook-error.log';
$SECRET_KEY   = 'secretkey';

// Command of each branch
$COMMANDS = array(
  'refs/heads/develop' => '', // develop br.
  'refs/heads/master'  => '', // master br.
);
```

```php:trigger.php
<?php

// Load config
require_once(dirname(__FILE__).'/config.php');

$header    = getallheaders();
$post_data = file_get_contents('php://input');
$hmac      = hash_hmac('sha1', $post_data, $SECRET_KEY);
$date      = date("[Y-m-d H:i:s]")

if (isset($header['X-Hub-Signature']) && $header['X-Hub-Signature'] === "sha1={$hmac}") {
  $payload = json_decode($post_data, true); // Request body (JSON)

  foreach ($COMMANDS as $branch => $command) {
    // Detect target branch
    if ($payload['ref'] == $branch) {
      if ($command !== '') {
        // Execute
        exec($command);
        file_put_contents(
          $LOG_FILE,
          "{$date} {$_SERVER['REMOTE_ADDR']} {$branch} {$payload['commits'][0]['message']}\n",
          FILE_APPEND|LOCK_EX
        );
      }
    }
  }
}
else {
  // Auth failure
  file_put_contents(
    $LOG_FILE_ERR,
    "{$date} invalid access: {$_SERVER['REMOTE_ADDR']}\n",
    FILE_APPEND|LOCK_EX
  );
}
```

#### 参考情報

- [GitHubにpushしたらブランチ毎に自動デプロイする仕組みをXサーバー上に作ってみた](https://yosiakatsuki.net/blog/github-auto-deploy/)<br>
　Webhook登録手順やPHP製トリガープログラムのサンプル
- [GitHubのwebhookを受け取って、自動でgit pullするスクリプト](https://qiita.com/oyas/items/1cbdc3e0ac35d4316885)<br>
　PHP / Ruby 製のトリガー用サンプルコードを掲載
- [GitHub REST API - PushEvent](https://developer.github.com/v3/activity/events/types/#pushevent)<br>
　Webhookで通知されるイベントタイプ＆ペイロード仕様

# 2. Jenkinsとの連携

<img src="https://qiita-image-store.s3.amazonaws.com/0/204647/ff3cfdd3-9fbe-cb81-c16c-cc9905b45c7c.png" width="550" alt="Jenkins"/>

CIツールの定番「Jenkins」をGitHubと連携させて自動テストやデプロイを行うことができます。
GitHubのレポジトリ設定にある「Webhook」または「Service」機能を設定することで、通知先URL（Jenkins）でトリガーとなるジョブ（タスク）を動かします。

連携手順は、

- Jenkinsサーバをセットアップ
- 必要に応じてJenkinsのプラグインを導入
- Jenkinsにデプロイ・テスト用のジョブを作成
- GitHub側で Webhook / Service を設定
- 指定したレポジトリにpushが行われると、Jenkins側のジョブがkickされる
- Jenkins側のジョブ実行

との流れになります。

また、[bitnami](https://bitnami.com/stack/jenkins)を使うとJenkinsをワンクリックでEC2インスタンスへインストールできるようなので、セットアップの面倒さを回避したい場合は有用かもしれません。

#### メリット

- CIツールなので柔軟なジョブ設定が可能
- プラグインによる拡張機能が強力かつ多彩（プルリクによるトリガー発報、Slack連携など）
- テスト＞デプロイ＞結果通知まで一連の処理をジョブとして定型的に自動化できる
- クラウドサービス等の使用料が発生しない

#### デメリット

- Jenkinsサーバの導入が必要で、設置先が必要＆設定と運用の手間が発生する
- [負荷問題](https://qiita.com/oguchy/items/f7a9b537396dad606881#jenkins%E3%81%AE%E3%81%97%E3%82%93%E3%81%A9%E3%81%84%E3%81%A8%E3%81%93%E3%82%8D)

#### 参考情報

- [JenkinsでGitHubのプッシュ時に自動デプロイする](http://sil.hatenablog.com/entry/jenkins-github-auto-deploy)
- [GithubとJenkinsを連携させてアプリケーションを自動デプロイさせる](https://qiita.com/hika7719/items/4cb50366a4d9fc8415f6)
- [Jenkinsのビルド結果をSlackに通知させる](https://qiita.com/unsoluble_sugar/items/a42795d108624fcd02d4)<br>
　Jenkinsのセットアップ～基本的な連携手順、結果共有方法など
- [GitHubでPullRequestが出ると、Jenkinsでテストした後でEC2に自動デプロイする設定](https://techblog.recochoku.jp/5299)<br>
　プルリクやマージをトリガーとした自動テスト・デプロイの話

# 3. AWS CodeDeployとの連携

![AWS CodeDeploy](https://qiita-image-store.s3.amazonaws.com/0/204647/473cb539-a708-6f74-dcec-3e3006dbedd7.png)

AWSに設置するシステムであればCodeDeployを使ったEC2への自動展開が構築できます。
また、[CodePipeline](https://aws.amazon.com/jp/codepipeline/) や [CodeBuild](https://aws.amazon.com/jp/codebuild/) 等を組み合わせることで自動ビルドや結果通知といったアクションまで含めた一連の流れを作ることも可能です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/204647/6dea2fe6-f480-a849-dc97-7c074cb6aca0.png" width="550" alt="AWS CodePipeline">

<img src="https://qiita-image-store.s3.amazonaws.com/0/204647/a2159c8a-452f-a7cd-29ca-6eb32892aa50.png" width="550" alt="AWS CodePipeline"/>
https://dev.classmethod.jp/cloud/aws/delivery-by-codepipeline-codecommit-codebuild-codedeploy/

連携手順は、

- AWSの事前準備（IAM, S3, EC2）
- CodeDeployから自動デプロイするアプリケーションを作成
- 自動ビルドするGitHubレポジトリで、Webhook・ServiceによるCodeDeploy連携を設定
- 前段をCodePipeline経由にすることで、CodeBuildでのビルドなど別処理を挟むことも可能
- 指定したレポジトリにpushが行われると、CodeDeployが実行される

との流れになります。

#### メリット

- AWS内部で処理完結する（マネジメントコンソールでの一元管理、テンプレート化しやすい）
- 外部のCIサービスに比べて格安で構築・運用できる
- サーバの追加設置は必要なし。WEB上で完結

#### デメリット

- デプロイ時にビルド、承認フロー等を挟みたい場合は一連の流れを自力で構築する必要がある
- それなりの手間と知識が必要で、構築すべき内容も多め

#### 参考情報

- [CodeDeployとGitHubを連携してEC2に簡単自動デプロイ](https://qiita.com/dq-nobuko-takatsuki/items/ba365966ae61e177a4da)
- [CodeDeployでGitHubのソースをEC2にデプロイするまとめ](https://qiita.com/saka-shin/items/b388f9c2d62a1ffadb8c)
- [Github の CodeDeploy インテグレーションサービスで自動デプロイする](https://dev.classmethod.jp/cloud/aws/github-codedeploy-integration/)
- [CodeDeploy導入 - CodePipelineでプッシュの度にEC2へ自動デプロイ & 結果をSlackに通知](https://qiita.com/hayashier/items/79ae7d5884faa518f430)
- [GitHub/CodeBuild/CodePipelineを利用してCloudFormationのCI/CDパイプラインを構築する](https://dev.classmethod.jp/cloud/aws/developing-cloudformation-ci-cd-pipeline-with-github-codebuild-codepipeline/)
- [CodePipeline で CodeCommit/CodeBuild/CodeDeploy を繋げてデリバリプロセスを自動化](https://dev.classmethod.jp/cloud/aws/delivery-by-codepipeline-codecommit-codebuild-codedeploy/)
- [CodeBuildで安く楽する話](https://qiita.com/oguchy/items/f7a9b537396dad606881)

# 4. AWS CodeDeploy + 外部クラウドCIでの連携

<img src="https://qiita-image-store.s3.amazonaws.com/0/204647/1c85f7b0-8eb9-d09c-533d-7534309ba6fd.jpeg" width="550" alt="AWS CircleCI"/>

AWSに設置するシステムであればCodeDeployによる自動展開は確かに便利です。
ただし、JenkinsのようなCIツールに比べるとできることは限られてしまいますし、「できること」をやるにも [CodePipeline](https://aws.amazon.com/jp/codepipeline/) や [CodeBuild](https://aws.amazon.com/jp/codebuild/) 等を組み合わせた一連の流れを自力で構築する必要があり、それなりの手間と知識が必要になってきます。

やはり、トリガー設定からテスト・ビルド・配置までを一連の流れで定型的に用意できるCIツールは便利なので CodeDeploy と組み合わせて使用したいところです。JenkinsをAWSに設置して利用する方法もありますが、クラウド同士で連携させやすい外部のCIサービスを組み合わせて使うのも手です。

代表的なCIサービスである [CircleCI](https://circleci.com/) をCodeDeployと組み合わせた場合、連携手順は

- AWSの事前準備（IAM, S3, EC2）
- CodeDeployから自動デプロイするアプリケーションを作成
- CircleCIの準備（AWS, GitHubアクセス関連）
- CodeDeployの動作設定ファイルを作成してGitHubレポジトリへ設置
- CircleCIの動作設定ファイルを作成してGitHubレポジトリへ設置
- レポジトリへのpushに反応して自動ビルド～デプロイが動く

との流れになります。

#### メリット

- 小規模なCIなら無料で使える（1コンテナ、MacOSを除く）
- CIツールなので柔軟なジョブ設定が可能
- サーバの追加設置は必要なし。WEB上で完結
- CodeDeployだけでなくTerraformなども連携できる
- iOSアプリケーションの自動ビルド・テストなど特殊用途向けにも使える

#### デメリット

- クラウドサービスにつき別途使用料が発生する
- 使用頻度の高さや、複数人での開発時は相応のランニングコストが発生

#### 参考情報

- [CircleCI + AWS CodeDeployによるテスト・デプロイの自動化](https://qiita.com/hkanazawa/items/4064bf447bd3866b1c3d)
- [CircleCI 2.0とcodedeployでデプロイを自動化する](https://dev.classmethod.jp/cloud/aws/deploy-with-codedeploy-circleci2-0/)
- [awsのec2に自動デプロイする](http://naughtldy.hatenablog.jp/entry/2017/04/11/080000)<br>
　CodeDeploy + CircleCI による連携とSlack通知の構築例
- [CodeDeployとCircleCIを利用したBlue/Greenデプロイメント](https://dev.classmethod.jp/cloud/aws/bg-deploy-with-codedeploy-and-circleci/)<br>
　CodeDeploy (Dev)、Terraform (Ops) との連携例
- [Jenkinsより使いやすい、開発効率を上げてくれるCIツール10選](http://blog-ja.sideci.com/entry/2014/03/14/181030)
- [はじめての CircleCI](https://www.slideshare.net/mogproject/circleci-51253223)
- [CircleCIで出来るコト](https://qiita.com/noboru_i/items/7d300eb63ae667bf8dc2)<br>
　Jenkins代替のクラウドCIツール情報。やはり [CircleCI](https://circleci.com/) がスタンダードか。

# おわりに

複雑なビルド等の処理が必要・自動テストを継続的に回したい（結果も保存したい）場合は外部のCIサービスを使うことになりますが、既にJenkinsが動いている環境をお持ちの場合は、資産を有効活用する意味でも[Jenkinsと連携](#2-jenkinsとの連携)させるのが一番簡単です。

ただしJenkinsはジョブを増やしていくことで負荷によるエラーや処理遅延が顕著となってくるので、利用者（プロジェクト等）毎の使用パターンや時間帯を考慮に入れておく必要があります。

もし、デプロイ対象のシステムをAWSに構築している・する予定ならJenkinsよりもAWSの機能として用意されている[CodeDeployやCodePipelineを使って](#3-aws-codedeployとの連携)自動デプロイの仕組みを構築するのが望ましいです。余分なサーバ構築や負荷問題から開放されますし、コストもそれ程発生しません。

CloudFormationやTerraformのテンプレート化ができるメリットもあります。

逆に、サーバ１台のシンプルな環境で自動デプロイするのであれば[GitHubのWebhookによる連携](#1-githubのwebhookによる連携)が最も手軽です。サーバが数台、簡単なビルドも実行したい（キャッシュ生成コマンドやTypeScriptのコンパイル実行など）程度の要件なら [Capistrano ](http://capistranorb.com/) や [Rocketeer](http://rocketeer.autopergamene.eu/) といったデプロイツールとの連携で比較的容易に実現できます。