GitHub
Jenkins
自動デプロイ
CircleCI
CodeDeploy

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

はじめに

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

目次

1. GitHubのWebhookによる連携

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

連携手順は、

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

との流れになります。

メリット

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

デメリット

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

トリガー(デプロイ)用プログラム例

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.
);
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
  );
}

参考情報

2. Jenkinsとの連携

Jenkins

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

連携手順は、

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

との流れになります。

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

メリット

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

デメリット

  • Jenkinsサーバの導入が必要で、設置先が必要&設定と運用の手間が発生する
  • 負荷問題

参考情報

3. AWS CodeDeployとの連携

AWS CodeDeploy

AWSに設置するシステムであればCodeDeployを使ったEC2への自動展開が構築できます。
また、CodePipelineCodeBuild 等を組み合わせることで自動ビルドや結果通知といったアクションまで含めた一連の流れを作ることも可能です。

AWS CodePipeline

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上で完結

デメリット

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

参考情報

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

AWS CircleCI

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

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

代表的なCIサービスである CircleCI をCodeDeployと組み合わせた場合、連携手順は

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

との流れになります。

メリット

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

デメリット

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

参考情報

おわりに

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

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

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

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

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