このブログはMiddleman Blogで構築していますが、今更ながら継続的デリバリーできる仕組みを整えました。
以前は手元でmiddleman build
して温かみある手動 scp でデプロイしてましたが、2017 年にもなって自動化もできてないのは笑われるので、Travis CI から自分の VPS に自動デプロイできるようにしました。
構成
よくある感じです。GitHub 上へ master ブランチが Push されると、Travis CI が自動でビルドして Production にデプロイします。 Travis CI に SSH の秘密鍵、Production に公開鍵を登録しておいき、その鍵で認証します。
デプロイされたファイルはタイムスタンプのディレクトリに格納され、current
というシンボリックリンクが最新のディレクトリを指します。
フロントの Web サーバは current
をドキュメントのルートディレクトリになるよう設定します。
$ ls -l /home/www/
total 20
drwxr-xr-x 18 www www 4096 Jun 25 15:03 2017-06-25T20:56:47.097207716Z
drwxr-xr-x 17 www www 4096 Jun 25 21:00 2017-06-25T21:01:02.149269682Z
drwxr-xr-x 17 www www 4096 Jun 25 21:04 2017-06-25T21:04:35.115184087Z
drwxr-xr-x 17 www www 4096 Jun 25 21:33 2017-06-25T21:33:41.943710869Z
drwxr-xr-x 17 www www 4096 Jun 25 21:40 2017-06-25T21:41:16.808334145Z
lrwxrwxrwx 1 www www 30 Jun 25 21:41 current -> 2017-06-25T21:41:16.808334145Z
SSH 鍵を作る
Travis CI から Production に認証するための SSH 鍵を作ります。
$ ssh-keygen -t rsa -N '' -f deploy_key
秘密鍵を Travis CI が提供している CLI ツールから行います。 ファイルは暗号化して保存され、リポジトリにひも付きます。 詳しくは公式ドキュメントをどうぞ。
$ gem install travis
$ travis login
$ travis encrypt-file deploy_key -r organization/repository
ファイル登録時に複合鍵(*.enc
ファイル)と復元方法が表示されるので、そのファイルはリポジトリに追加します。
その複合鍵を使って、デプロイ時に秘密鍵を復元します(後述)。
Web 用のユーザを作る
Production に www ユーザを作ります。 このユーザのホームディレクトリに生成物がデプロイされます。
$ sudo sh <<EOF
groupadd www
useradd -g www -s /bin/sh www
mkdir -p /home/www/
chmod www:www /home/www/
EOF
そして公開鍵を埋め込みます。
$ sudo -u www sh <<EOF
mkdir /home/www/.ssh
chmod 700 /home/www/.ssh
echo 'sh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' >>/home/www/.ssh/authorized_keys
chmod 600 /home/www/.ssh/authorized_keys
EOF
デプロイスクリプトを書く
デプロイ先情報はリポジトリではなく Travis CI に保存したいので、Travis CI の Repository Settings から REMOTE_HOST
, REMOTE_PORT
, REMOTE_USER
を設定します。
これで第三者はデプロイ先を見ることができません。
詳しくは公式ドキュメントをどうぞ。
次にデプロイスクリプトをシェルスクリプトで書きます。
#!/bin/bash -e
fatal() {
echo $@
exit 1
}
# sshコマンドのラップ
remote() {
ssh -q -o "StrictHostKeyChecking no" -i deploy_key -l "$REMOTE_USER" -p "$REMOTE_PORT" "$REMOTE_HOST" $@
}
[ -z "$REMOTE_HOST" ] && fatal 'REMOTE_HOST is not set'
[ -z "$REMOTE_PORT" ] && fatal 'REMOTE_PORT is not set'
[ -z "$REMOTE_USER" ] && fatal 'REMOTE_USER is not set'
# buildディレクトリをProductionにコピー
tar cvf - build | remote tar xvf -
remote <<EOF
set -e
# buildディレクトリをタイムスタンプにmvして、currentを更新
last_built=\$(date +'%Y-%m-%dT%H:%M:%S.%NZ')
mv build \$last_built
ln -sf \$last_built -T current.tmp
mv -T current.tmp current
# 5件より古いファイルは削除
rm -rf \$(ls -I current | head -n-5)
EOF
最後に.travis.yml
にビルド処理とデプロイ処理を書きます。
before_deploy
に、先ほど作成した秘密鍵を復元する処理を書きます。
そして deploy
に、記述したスクリプトを呼び出します。
# .travis.yml
script:
- bundle exec middleman build
before_deploy:
- openssl aes-256-cbc -K $encrypted_xxxxxxxxxxxx_key -iv $encrypted_xxxxxxxxxxxx_iv -in deploy_key.enc -out deploy_key -d
- chmod 600 deploy_key
deploy:
provider: script
script:
- "./bin/deploy"
on:
branch: master
以上で設定は終わりです。
master に push してみて、Production に生成物が配置されてcurrent
が切り替われば OK です。
念の為 Travis CI のログに、見られたらいけない情報が流れていないかも念入りにチェックしましょう。