【Vue CLI 3】npm scriptsでデプロイを自動化する

所得税・住民税・事業税・国保計算シミュレーションはvue-cli3のプロジェクトで構築しています。
本番環境へのデプロイ手動でやってましたが、手間と感じてきたためそろそろ自動化しようと思います。

方法は色々あると思います。Gitと連携してcommitイベントをhookしてデプロイを動かすとかが一般的でしょうか。
今回のプロジェクトはGitではなくBitBucketをソース管理で使用していますので、Gitのイベントフックと同じようなことはできそうです。ただ、正直そこまでしなくてももっと簡単にできる方法で構築したいと考え、最終的にnpm scriptsで完結するようにしました。

環境

Windows環境にVagrantにてCentOSを動かし、そこにNode.js等必要なパッケージをインストールする形です。ホストとなるWindowsには最低限の環境のみで、なるべくゲストOS側に環境を閉じ込める形にしています。

※ ただ、所得税・住民税・事業税・国保計算シミュレーションはVue.jsで実装しており、SSRもしておらずサーバサイドは今の所ないため、Windows側にNode.jsをインストールし仮想は使わなくてもいいかなぁと感じ始めてるところ。。

ホスト

  • Windows 10
  • VirtualBox 5.2
  • Vagrant 2.1.2

ゲスト

  • CentOS 7.4
  • Node.js V8.12.0
  • Vue CLI 3.1.3
  • webpack 4

本番環境(デプロイ先)

  • CentOS7.4
  • SSH接続は公開鍵認証
  • Node.jsは未インストール

自動デプロイ方法の要件定義

まずは、どのような方法でデプロイするかの方針をまとめます。

  • 本番環境にNodeは入れていないので、ローカルでビルドしたものを本番環境にに反映する
  • npmコマンドでビルド後に自動で本番環境に反映したい
  • ビルド済ファイルはGitに上げない
  • ビルド済ファイリをGitで管理しないので、Gitのイベントフックは使わない
  • SSH接続によるscpかrsyncが楽かもしれない

ふむ、ゲストOSはCentOSを使っているので「rsync」を使ってビルドしたファイルをリモートのサーバと同期する方法が一番簡単かなぁと考えました。
rsyncをnodeスクリプトからキックできれば簡単にできそうですね。それでもいいけど、もしかしたらnodeのパッケージとしてあるかもしれないなぁと思ったので検索してみたら見事にありました! いやぁ、便利ですね。

rsync以外にもscpベースのもあるようです。よさそうなのは以下4つ。

  • ssh-webpack-plugin
  • ssh-deploy-release
  • scp2
  • deploy-rsync

webpack4を使用しているので、webpackのプラグインとなっているssh-webpack-pluginを使ってみます。

Vue CLI 3プロジェクトとデプロイ先の情報

デプロイ先

SSHの接続は以下の設定で接続できるものとします。

設定項目
ホスト名 kawadev.net
ポート 22
ユーザー名 hoge
デプロイ先のパス /home/hoge/public_html/calc-tax-insurance

ゲストOS側vue-cli3のディレクトリ構造

ざっくりとした構造がこちら

/workspace/calc-tax-insurance
  ├── vue.config.js     → vueやwebpackの設定ファイル
  ├── dist              → ビルド済みファイルの出力先
  ├── public            → 静的ファイル
  └── src               → vue,js等の各種プログラムファイル

SSH接続用の秘密鍵のパス

/workspace/ssh/id_rsa

ssh-webpack-pluginの使い方

SSHの接続情報とコンパイル済みファイルのディレクトリおよびデプロイ先のパスを設定します。

デプロイは、mode=productionで実行した際のみ起動するようにしています。

vue.config.js

module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      const SshWebpackPlugin = require('ssh-webpack-plugin')
      config.plugins.push(new SshWebpackPlugin({
        host: 'kawadev.net',
        port: 22,
        username: 'hoge',
        privateKey: require('fs').readFileSync('/workspace/ssh/id_rsa'),
        from: 'dist/',
        to: '/home/hoge/public_html/calc-tax-insurance',
        cover: false,
        zip: true
      }))
    }
  }
}

※ 本来は他の設定も含まれますが、分かりやすくするためにssh-webpack-pluginの設定のみを抜き出してます。

ちなみに追加で以下の設定を行っています

項目 設定値 説明
cover false デプロイ先以下のファイルを削除する
zip true ファイル転送時に圧縮する

※ cover:falseにしている理由は、コンパイルされるJSのファイル名を固定にしていないため、消さないとどんどん古いのがたまるから。

それでは実行してみます。

>$ npm run build
~
DONE  Build complete. The dist directory is ready to be deployed.
 INFO  Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html

[ Start Deploy ]
Connecting: kawadev.net
Connected: kawadev.net
Zipping Deploy Command:
 > tar -czvf ./deploy.tgz --exclude=deploy.tgz --ignore-failed-read --directory=dist/ .
Uploading Deploy Files:
upload file from local path: deploy.tgz to remote path: /home/hoge/public_html/calc-tax-insurance
done uploading.
Unzip Zipfile:
 > cd /home/hoge/public_html/calc-tax-insurance && tar -xzvf deploy.tgz && rm /home/hoge/public_html/calc-tax-insurance/deploy.tgz
Local cleanup:
 > rm deploy.tgz
Deployed: 3043ms
Closed: kawadev.net

正常にデプロイできました。簡単ですね!といいたいのですが、最初はうまく動作しませんでした。。まぁ、デプロイ先の環境に問題があったのですが、SSHの接続はOKなのですがファイルがアップロードされないという状況に陥ってました。

SSH接続までは正常なのにファイルがアップロードされない場合

状況は以下

  • SSHの接続は問題ない(SSH接続があるとLineに通知が来るようにしてある)
  • /var/log/secureにもログイン成功のログは記録されている
  • Uploading Deploy Files:でとまる
  • もちろんデプロイ先のディレクトリをみてもファイルがアップロードされている形跡はない

パスが間違ってるのかと思い、何度も確認しましたが問題ない。パーミッションも777にしてみましたがダメ。
いったん初心にもどり、scpコマンドで直接アップしてみることにします。

$ scp -v -P 22 -i /workspace/ssh/id_rsa /workspace/calc-tax-insurance/dist/index.html hoge@kawadev.net:/home/hoge/public_html/calc-tax-insurance

~ (SSHの接続などのログが流れます)
debug1: Sending environment.
debug1: Sending env LANG = en_US.UTF-8
debug1: Sending command: scp -v -t /home/hoge/public_html/calc-tax-insurance

うーむ、かならずここで止まります。やはりファイルのアップロードでフリーズしてる模様。
scpではまったことないのになぁと思い、試しにほかのサーバ(開発用のサーバ)に実行してみたところ問題なく動作します。問題はデプロイ先サーバにあるのは確実です。

開発用のサーバとデプロイ先サーバの違いは、SSH接続時にLINE Notifyへの通知をするかしないかです。
試しにLINEへの通知をOffにしてみるとビンゴ! コマンドラインでもssh-webpack-pluginでも動作しました。
「scp 失敗 止まる」とかでGoogle先生に聞いてみるとある方の記事を見つけました。なんでも、SSH接続時にコンソールに何か出力していたりすると失敗するようです。

これでした、curlでLINE NotifyのAPIをキックした結果を出力してました!

{"status":200,"message":"ok"}

Windows側にNodeをインストールしている場合

Windows10では2018年4月ごろのアップデートにて、OpenSSHクライアントが自動でインストールされます(私の環境は入ってました)。同時にscpも使えるようになってますね。いつのまに。。。

ということは、ssh-webpack-pluginそのまま使えるんじゃね?ということで試してみました。

vue.config.js

module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      const SshWebpackPlugin = require('ssh-webpack-plugin')
      config.plugins.push(new SshWebpackPlugin({
        host: 'kawadev.net',
        port: 22,
        username: 'hoge',
        privateKey: require('fs').readFileSync('C:\\ssh\\id_rsa'),
        from: 'dist/',
        to: '/home/hoge/public_html/calc-tax-insurance',
        cover: false,
        zip: false
      }))
    }
  }
}

こつは、zip: falseです。Windowsではtarコマンド使えないのでエラーになります。圧縮せずに送信すればOKです。

これでrsyncのプラグインは使う理由がなくなりました。Linux・Windows両方が同じ手順でデプロイできます。

なるべくダウンタイムを短くしたい場合

デプロイ先のファイル全消し→ 圧縮ファイル展開。Windows版であれば、デプロイ先全消し→アップロードでは少なからずダウンタイムが発生します。特にWindows側はファイル1つずつコピーするのでファイル数が多くなればなるほどダウンタイム多くなります。

ssh-webpack-pluginには以下のオプションがあります。

項目 説明
after string or array デプロイ前にリモートで実行するコマンド
before string or array デプロイ後にリモートで実行するコマンド

こちらを利用して、アップロードまですべて終わったらディレクトリのmvで入れ替える方法とかもできます。シンボリックリンクでの切り替えとかにするともっと早いですね。(シンボリックを使う場合はApacheの設定変更が必要になるケースもあります)

ディレクトリのmvを使う方法は以下のようにします。

  1. 公開先とは別の場所にアップロード
  2. 公開ディレクトリを別の場所へmv
  3. アップロードしたディレクトリをmv

設定はこんな感じ
vue.config.js

module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      const SshWebpackPlugin = require('ssh-webpack-plugin')
      config.plugins.push(new SshWebpackPlugin({
        ~,
        to: '/home/hoge/public_html/deploy',
        before: 'mkdir /home/hoge/public_html/deploy',
        after: [
          'mv -f /home/hoge/public_html/calc-tax-insurance /home/hoge/calc-tax-insurance-backup',
          'mv /home/hoge/public_html/deploy /home/hoge/public_html/calc-tax-insurance'
        ]
      }))
    }
  }
}

まとめ

あまり複雑に考えず、個人開発で簡単にすませるならこのようなやり方でも全然いけますね。以上でした。