エンジニアHubPowered by エン転職

若手Webエンジニアのための情報メディア

今日からはじめるCI/CD ─ CircleCI + Deployerでテストとデプロイを自動化しよう!【休日個人開発】

プライベートでも何か作りたい! そんなときの「今日からはじめる休日個人開発」シリーズ、今回はテストやデプロイをGitHubと連携し、自動化させてCI/CDに対応します。

CI/CD

皆さん、プライベートで何か開発していますか? 「何か作りたい」という気持ちはあるものの、いまひとつ何から始めたらいいのか分からず、動けないままの人も多いと思います。

そんな皆さんのために、「仕事以外にも休日に個人で気軽に何かを作ってみよう!」という企画の第3回です。第1回で用意した開発環境と、第2回で作成した画像を加工するツールを用いて、テストとデプロイの自動化を行います。

これまでの休日個人開発シリーズでは、Webアプリケーションを動作させる本番環境にログインし、動作しているファイルを直接修正して開発を行っていました。しかし、Web上でサービスを公開した場合、本番で動作しているファイルをいきなり修正するべきではありません。ローカルの開発環境などできちんと動作確認し、テストが実施されたアプリケーションのみを本番に反映してください。

そこで、ファイル一式をGitHubで管理し、ローカル環境で開発したものが、自動でテストされ、本番のサーバに反映される環境が構築できれば便利で安全です。

この記事では、次の2つを実現します。

  • GitHubでPull Requestを投げたら、自動的にテストが実行されて、その結果が確認できる
  • Pull Requestの確認が完了して、テストに成功するソースコードがmasterブランチへマージされたタイミングで、デプロイが自動的にされる

いわゆる、CI/CD(継続的インテグレーション/継続的デリバリ、Continuous Integration/Continuous Delivery)を実現させることがゴールです。

開発環境の確認

第1回から使用している開発環境をそのまま利用する場合には、まずサーバ内のパッケージを最新にしておきましょう。

コラム:開発環境を最新の状態にする

環境を構築してから時間が経っている方は、脆弱性対策のため、次の手順で最新の状態にしましょう。

$ sudo yum update

カーネル関連のアップデートが含まれている場合は、OSを再起動して、更新した内容を反映させます。

$ sudo reboot

また、今回の記事で使用する設定ファイルなどは、第2回の記事のサンプルソースコードと同じリポジトリにも追加しています。

GitHub - github-sample/tool

先ほどゴールとして挙げたように、今回はファイルをGitHubで管理していることが前提になりますが、このサンプルのリポジトリを自身のGitHubアカウントにフォークして編集し、記事の内容を試すこともできます(もちろん第2回までのファイルを自身でGitHubにプッシュしてもかまいません)

GitのインストールやGitHubのアカウント取得などについては「今日からはじめるGitHub」の記事を参考にしてください。

デプロイ先のサーバとしては、第2回まで使用してきた開発環境のWebサーバが使用できます。デプロイ先のディレクトリを新しく用意する場合は、あらかじめ権限設定をしておきましょう。

$ sudo mkdir 【デプロイ先のディレクトリ】
$ sudo chmod 777 【デプロイ先のディレクトリ】

Deployerを使ってデプロイを効率化

CI/CDに利用する便利なツールは世の中でたくさん開発され、フリーで利用できるものも数多く存在します。どのツールにもそれぞれ利点と欠点があり、かつ時代の流れもあって「これを使えば間違いない!」と言えるツールはなかなか存在しません。

実際には自分が使いたいものではなく、企業によって、サービスによって、プロジェクトによって、既に使われているツールがあり、それを利用しなければならない状況が多いでしょう。

そのため、何かのツールのエキスパートになるのではなく、CI/CDがどのようなものなのかを理解して、臨機応変にツールを使いこなせるスキルを身に着けることが大切です。その上で、よく使うツールのエキスパートになることはもちろん有意義なことではあります。

今回は、ツールの使い方そのものを理解するコストを下げるため、導入コストが比較的低いDeployerを利用してみます。

DeployerはPHPで開発されているツールで、手軽に使えることが利点ですが、細かいことをやりたければ他のツールに劣るという特徴もあります。また、現在も次のリポジトリで開発が進行中です。

Pulse · deployphp/deployer · GitHub

Deployerのダウンロード

開発環境にDeployerをダウンロードします。ここではPHPのバージョンによって、ダウンロードするバージョンを調整することがポイントです。

今回はCentOS 7でyumの標準リポジトリにおいてインストールされるPHP 5.4.16で試していますが、PHPのメジャーバージョンは7まで進んでおり(2018年3月1日現在の最新バージョンは7.2.3)、5系でも5.6までバージョンが上がっています。実際にサービスで利用する際には、可能な限り、新しいバージョンに入れ替えていきましょう。

PHP 7系の場合

利用しているPHPが7系の場合、最新のDeployerを利用可能です(2018年3月1日現在でバージョン6.0.5)

$ curl -LO https://deployer.org/deployer.phar

PHP 5/6系の場合

利用しているPHPが5系や6系の場合、それぞれのPHPに対応したDeployerのバージョンを明記してダウンロードする必要があります。広く利用されているPHP 5系では、次の表がそれぞれサポートされている最新バージョンです(2018年3月1日現在)

PHP Deployer
PHP 5.6 Deployer 4.3.1
PHP 5.5 Deployer 3.3.0
PHP 5.4 Deployer 3.0.11

各バージョンのpharファイルのダウンロード先は以下を参照してください。

Deployer — Download

ここでは前述したようにPHP 5.4.16に対応したバージョン3.0.11をダウンロードします。

$ curl -LO https://deployer.org/releases/v3.0.11/deployer.phar
pharPHP Archive)は、PHPをアーカイブ化した拡張モジュールです。

なお、PHP 5系で最新のDeployerをダウンロードすると、利用時に以下のようなエラーが発生します。

$ dep
Upgrade to php7
Deployer 5.x supports only php7 and above.
If you want to use older php version use Deployer 4.x

また、最新のDeployerでは設定項目などが変更されている場合があります。公式サイトのドキュメントを参照してください。

Deployerを配置する

pharファイルのダウンロードが完了したら、depコマンドでDeployerが実行されるように配置します。

$ sudo mv deployer.phar /usr/local/bin/dep
$ chmod +x /usr/local/bin/dep

Deployerの実行テスト

depコマンドでDeployerを試してみます。以下のページも参考にしてください。

Deployer — Getting Started

次のコマンドで、Deployerのタスクなどを定義する設定ファイルのひな形を生成することができます。

$ dep init

また、設定ファイルはコマンドを利用せず、deploy.phpというファイル名で直接手動で用意することも可能です。

まず、Deployerの挙動を確認するために、deploy.phpを以下のようにしてみます。

<?php
task('test', function () {
  writeln('Hello world');
});

この記述では、Hello worldと表示するtestタスクを定義しています。これを実行してみます。

$ dep test
➤ Executing task test
Hello world
✔ Ok

問題なくDeployerが動作することが確認できました。

次は、GitHubからソースコードをデプロイできるdeploy.phpを記述してみましょう。

デプロイ用の設定

上記のテストでタスク(task)を直接定義しましたが、Deployerには便利なレシピがあります。ここではComposerレシピ(recipe/composer.php)を利用します。

デプロイ先のホスト情報をproductionとして定義し、付随する情報をいくつか設定します。【……】としている箇所を、各自の環境に合わせて記述してください。

<?php
require 'recipe/composer.php';
// デプロイ先
server('production', '【デプロイ先のホスト名 or IP】', 【SSH接続先ポート番号】)
  ->user('【ユーザー名】')
  ->identityFile()
  ->forwardAgent()
  ->env('deploy_path', '【デプロイ先のディレクトリ】');
set('ssh_type', 'native');
// リポジトリ
set('repository', '【GitHubのクローン元】');

これでデプロイ用の設定は準備完了です。

次に、これがCircleCI上で自動的に実行されるように連携させていきます。このdeploy.phpファイルはGitHub上で、リポジトリ直下(第2回で作成したindex.phpなどと同じ場所)に置いておいてください。

CircleCIを使ってテストやデプロイを自動化

続いて、GitHubのPull Requestをフックにし、テストやデプロイを自動化させてみます。

Pull Requestを投げたら自動的にテストを実行、Pull Requestの確認が完了してmasterブランチへマージされたタイミングでデプロイが自動的にされるようにします。

今回は、CircleCIを用います。これはCircle Internet Services, Inc.が運営する継続的インテグレーションのサービスで、1コンテナであれば無料で利用可能です。

CircleCIのアカウント作成

CircleCIにアクセスし、「Sign Up」を選択します。

f:id:blog-media:20180309203209p:plain

「Sign Up with GitHub」を選択します。

f:id:blog-media:20180309203206p:plain

GitHubアカウントにログインし、CircleCIに権限を付与します。

f:id:blog-media:20180309203122p:plain

以下のような画面が表示されたら成功です。

f:id:blog-media:20180309203126p:plain

次に、「Projects」から、連携したいリポジトリの「Setup project」を選択します。

f:id:blog-media:20180309203130p:plain

以下のような画面が表示されるので、Operating Systemは[Linux]、Languageは[PHP]を選択します。

f:id:blog-media:20180309203117p:plain

続く手順が「Next Steps」に記されていますが、今回は先にSSH鍵のFingerprintの値をYAMLファイルconfig.ymlに記載したいため、手順を入れ替えて、先に「Start building」を押してください。この段階ではまだ設定が完了していないためエラーになります。

デプロイ用のSSH鍵

CircleCI + Deployerでデプロイする際のSSH鍵を用意・登録します。

デプロイ用にパスフレーズなしの鍵を作成しましょう。公開鍵はデプロイ先のホストで、接続に利用するユーザーの.ssh/authorized_keysに追記しておきます。

鍵の作成に関しては、「今日からはじめるGitHub」の記事の「SSHの鍵を取得する」のセクションを参考にしてください。

ProjectsのSettingsにある[SSH Permissions]で[Add SSH Key]をクリックしてSSHの秘密鍵を登録します。

f:id:blog-media:20180309203135p:plain

Hostnameを空欄にした場合は、このリポジトリのデプロイ先すべてでこの鍵が利用されます。今回は空欄にし、Private Keyのみ入力します。

パスフレーズありの鍵の場合、エラーによって保存ができない仕組みのようなので、保存ができないときはパスフレーズを指定していないかどうかを確認してみると良いでしょう。また、「-----BEGIN RSA PRIVATE KEY-----」「-----END RSA PRIVATE KEY-----」の行もすべて貼り付ける必要があるので注意しましょう。

f:id:blog-media:20180309203138p:plain

保存ができたら以下のような表示になります。Fingerprintの値は後ほど利用します。

f:id:blog-media:20180309203142p:plain

CircleCIの設定ファイルを用意

GitHubの対象リポジトリに.circleci/config.ymlを用意し、CircleCIに実行させたい挙動を記載していきます。

ファイルの全体は以下の通りです。【……】としている箇所を、各自の環境に合わせて記述してください。

# PHP CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-php/ for more details
#
version: 2
jobs:
  build-job:
    docker:
      - image: centos:7
    working_directory: ~/repo
    steps:
      - checkout
      # Download and cache dependencies
      - restore_cache:
          keys:
          - v1-dependencies-{{ checksum "composer.json" }}
          # fallback to using the latest cache if no exact match is found
          - v1-dependencies-
      # install make
      - run: yum -y install make
      # install git
      - run: yum -y install git
      # install php
      - run: yum -y install php-pear php-devel gcc ImageMagick ImageMagick-devel ImageMagick-perl
      # install composer
      - run: php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
      - run: php composer-setup.php --filename=composer
      - run: ./composer install -n --prefer-dist
      # install Imagick
      - run: pecl install imagick
      - run: echo "extension=imagick.so" >> /etc/php.ini
      - save_cache:
          paths:
            - ./vendor
          key: v1-dependencies-{{ checksum "composer.json" }}
      # run tests!
      - run: ./vendor/bin/phpunit
  # deployer
  deploy-job:
    docker:
      - image: centos:7
    steps:
      - checkout
      - add_ssh_keys:
          fingerprints:
            - "【先程SSH鍵を登録した際に表示されていたFingerprint】"
      - run: echo "User 【ユーザー名】" >> $HOME/.ssh/config
      - run: echo "ForwardAgent yes" >> $HOME/.ssh/config
      - run: yum -y install php-pear php-devel
      - run: curl -LO https://deployer.org/releases/v3.0.11/deployer.phar
      - run: mv deployer.phar dep
      - run: chmod +x ./dep
      - run: ./dep deploy production
workflows:
  version: 2
  build-deploy:
    jobs:
      - build-job
      - deploy-job:
          requires:
            - build-job
          filters:
            branches:
              only: master

設定ファイルの記載内容の詳細

記載内容でお決まりの部分以外を順に説明していきます。

version: 2

これはCircleCI 2.0を利用することを意味しています。

    docker:
      - image: centos:7

ここでは、テストを実行するDockerのイメージを指定しています。CircleCIでもPHP実行環境は用意されていますが、そのイメージで利用されているOSはUbuntuであるため、今回はCentOSのDockerイメージを指定しました。テストを実行させる際に利用したい環境を適宜指定しましょう。

      # install make
      - run: yum -y install make
      # install git
      - run: yum -y install git
      # install php
      - run: yum -y install php-pear php-devel gcc ImageMagick ImageMagick-devel ImageMagick-perl
      # install composer
      - run: php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
      - run: php composer-setup.php --filename=composer
      - run: ./composer install -n --prefer-dist
      # install Imagick
      - run: pecl install imagick
      - run: echo "extension=imagick.so" >> /etc/php.ini

この部分では、第1回第2回で用意した環境と同じになるよう、必要なパッケージのインストールを行っています。

      # run tests!
      - run: ./vendor/bin/phpunit

この部分でテストが実施されます。

  # deployer
  deploy-job:
    docker:
      - image: centos:7
    steps:
      - checkout
      - add_ssh_keys:
          fingerprints:
            - "【先程SSH鍵を登録した際に表示されていたFingerprint】"
      - run: echo "User 【ユーザー名】" >> $HOME/.ssh/config
      - run: echo "ForwardAgent yes" >> $HOME/.ssh/config
      - run: yum -y install php-pear php-devel
      - run: curl -LO https://deployer.org/releases/v3.0.11/deployer.phar
      - run: mv deployer.phar dep
      - run: chmod +x ./dep
      - run: ./dep deploy production

ここは、Deployerでデプロイするための記述です。Docker上のCentOS 7にDeployerをインストールし、実行させています。

ポイントはadd_ssh_keysで、先程CircleCIに登録したSSHの秘密鍵をFingerprintの値を用いて呼び出しています。そして、SSH接続に必要な設定を.ssh/configに記載することにより、CircleCIからデプロイ先への接続を可能にしています。

この部分を置き換えれば、Deployerではないツールとの連携も可能です。

workflows:
  version: 2
  build-deploy:
    jobs:
      - build-job
      - deploy-job:
          requires:
            - build-job
          filters:
            branches:
              only: master

ここで、テストを実行するbuild-jobと、デプロイを実行するdeploy-jobのフローを制御しています。build-jobは共通で実行され、ブランチがmasterの場合のみ、build-jobの実行後にdeploy-jobを実行するように指定しています。

これにより、Pull Requestを作成したタイミングや、新しいコミットを追加プッシュした際にbuild-jobが実行されて、テストを自動実行します。

自動テストと自動デプロイの実行

設定後にPull Requestを作成すると、以下のようにCircleCIのジョブへのリンクが追加されます。

f:id:blog-media:20180309203145p:plain

テストが完了し、すべてのテストに成功すると以下のような表示になります。Pull Requestをレビューする際には、このテスト結果も確認するようにしましょう。テストに失敗していれば、そもそもPull Requestに出された修正内容に問題があるはずなので、確認してもらうようにコメントします。

f:id:blog-media:20180309203148p:plain

CircleCIにてジョブの実行結果の詳細を確認できます。ジョブが失敗した際には、このページでどのステップでエラーが発生したのかを確認していくことになります。

f:id:blog-media:20180309203151p:plain

Pull Requestをマージした際にはmasterブランチへの変更になるため、build-jobdeploy-jobが実行されます。

f:id:blog-media:20180309203156p:plain

deploy-jobが成功すればDeployerで指定されたデプロイ先に、GitHub上へプッシュしてあるソースが反映されます。

f:id:blog-media:20180309203159p:plain

デプロイ先を確認すると、以下のようなディレクトリが反映されているはずです。

f:id:blog-media:20180309203203p:plain

currentreleases内の最新版ソースへのシンボリックリンクです。リリース時に反映させたい内容がこのcurrentです。releasesはデフォルトの設定では過去3バージョン分のソースが保存されます。sharedはリリースする度に入れ替わるreleasesとは異なり、ログファイルなどずっと保持されるファイルを保持する際に利用するものです。

今回の実装例ではこのDeployerによるディレクトリ構成のままなので、Webアプリケーションとして公開できる適切なURLから、シンボリックリンクを使ってcurrentにアクセスできるように調整しましょう。

おわりに

今回は、DeployerとCircleCIを例にCI/CD対応を行いました。各ツールの仕様はそれぞれ異なるので、ドキュメントを読んだり試行錯誤は必要になります。しかし、それはCI/CDに限らず新しいものを試す際には常に必要になることです。CI/CDがどのようなものなのかを知っておけば、ツールが変わったとしても対応はできるでしょう。

世の中には便利なツールが大量に存在し、日々新しいものも誕生しています。新しいものに恐れず、気軽に試してみるマインドも大切なので、いろいろ挑戦していきましょう。

執筆者プロフィール

池田健人(いけだ・けんと) @ikenyal

id:ikenyal
サーバサイドエンジニア。学生時代に研究室や学部の各種サーバ・ネットワーク構築などを経験し、プログラミング以外の技術も学ぶ。2011年に某Web系IT企業に入社。エンジニア職でありつつも、校正スキルには自信あり。現在はリーダーとしてマネジメントスキルを習得中。

関連記事

編集:薄井千春(ZINE)