Travis CIを使ってMiddleman Blogを継続的デリバリー

    このブログはMiddleman Blogで構築していますが、今更ながら継続的デリバリーできる仕組みを整えました。 以前は手元でmiddleman buildして温かみある手動 scp でデプロイしてましたが、2017 年にもなって自動化もできてないのは笑われるので、Travis CI から自分の VPS に自動デプロイできるようにしました。

    構成

    Deployment Architecture

    よくある感じです。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 のログに、見られたらいけない情報が流れていないかも念入りにチェックしましょう。


    Profile picture

    Shin'ya Ueoka

    B2B向けSaaSを提供する会社の、元Webエンジニア。今はエンジニアリング組織のマネジメントをしている。