最近非公開のブログをGitHubでよく書いていて、GitHub上の変更をすぐに本番環境へ反映できたらなあということを思ったので、CircleCIのWorkflowから自動でデプロイできるようにしてみました。

この記事ではその際に行った手順や用いた設定をまとめて紹介していきます。

 

実現すること・前提

GitHubのmasterブランチに変更が加えられたら各種ビルドが走り、エラーがなければ本番サーバーへ反映されるようにします。

アプリケーションはリモートサーバー上で稼働しており、PHPで書かれています。パッケージ管理のためにComposerや、フロントエンドではnpm、Sass、webpackを利用しています。本番サーバーはCentOS 7上でApacheが動いています。

CircleCIのWorkflowから、ビルド成功後にSSHでリモートサーバーへ接続し、デプロイ用のシェルスクリプトを実行します。シェルスクリプトではソースコードの取得、ビルド、公開ディレクトリへの配置を行います。

 

CircleCI Workflowについて

GitHubと手軽に連携できてプライベートリポジトリにも無料で対応できるので、CircleCIを重宝しています。

CircleCI 2.0からWorkflowが利用出来るようになっており、各工程(Job)について実行条件や順序の依存関係などを指定できます。

Using Workflows to Schedule Jobs - CircleCI

 

Workflowの設定

はじめに、CircleCIのWorkflowを設定します。パッケージ取得や静的アセットのビルドに成功したらSSHでリモートサーバーへ接続し、シェルスクリプトdeploy.shを実行します。

参考: Configuring Deploys - CircleCI

version: 2
jobs:
  build:
    #...
  deploy:
    machine:
      enabled: true
    steps:
      - run: ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "~/deploy.sh"
workflows:
  version: 2
  build-and-deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only: master

$SSH_PORTなどの環境変数は、CircleCIのWebサイトから自由に追加できます。

Using Environment Variables - CircleCI

requires:の記述によって、ビルド成功後にデプロイのJobが実行されるようになり、filters:以下の記述によって、GitHubのmasterブランチ以外ではデプロイのJobが実行されないようになります。

 

CircleCIからリモートサーバーへ接続するために、事前にSSH鍵の作成・登録が必要なので、こちらも公式のドキュメントを参考に行っておきます。

Adding an SSH Key to CircleCI - CircleCI

 

デプロイ用シェルスクリプトの作成

続いて、リモートサーバーに設置するデプロイ用シェルスクリプトdeploy.shを作成します。このスクリプトは特定のユーザーdeployerでの実行を想定しており、以下の内容を含んでいます。

  1. GitHubから最新のmasterブランチをクローン
  2. Composerパッケージのインストール
  3. npmパッケージのインストール
  4. 静的アセットのビルド(すでに用意されているnpm-scriptsを利用)
  5. アプリケーションのソースコードを公開ディレクトリへ配置
#! /bin/bash

set -eu

readonly WORKSPACE="/home/deployer/tmp"
readonly NPM_DIR="/home/deployer/.npm"
readonly DEST="/var/www/public"

if [ -d ${WORKSPACE} ]; then
    rm -rf ${WORKSPACE}
fi
mkdir ${WORKSPACE}

if [ -d ${NPM_DIR} ]; then
    rm -rf ${NPM_DIR}
fi
mkdir ${NPM_DIR}

cd ${WORKSPACE}

echo "Cloning git repository..."

GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone --depth 1 git@github.com:g737a6b/example.git .

echo "Building..."

docker run --rm -u `id -u`:`id -g` -v ${WORKSPACE}:/app composer:1.6 install
docker run --rm -u `id -u`:`id -g` -v ${WORKSPACE}:/app -v ${NPM_DIR}:/.npm -w /app node:9 npm install
docker run --rm -u `id -u`:`id -g` -v ${WORKSPACE}:/app -v ${NPM_DIR}:/.npm -w /app node:9 npm run build

echo "Reflecting..."

chown -R deployer:apache ${WORKSPACE}
rsync -rlOtgov ${WORKSPACE}/ ${DEST} --exclude='.git' --exclude='node_modules' --delete

echo "Done!"

ビルド用にComposerやNode.jsの環境を用意するのは面倒なので、Dockerを利用してビルドを行っています。この時-uオプション無しのdocker runでは生成されるファイルの所有者がrootになってしまうので、これを変更するには上記のように実行ユーザーを指定する必要があります。

 

おわり

GitHubからの自動デプロイ環境を構築するための主要な部分は以上です。これまでリリースの度にリモートサーバーへ接続して一連のコマンドを叩いていましたが、今回の取り組みによってGitHubを更新したら自動反映されるのを待つだけで良くなりました。

浮いた時間はもっと価値のある仕事に回せるようになるので、単純作業はどんどん自動化していきたいですね。