Go言語でWebアプリを書くときにオートリロードどうするといいの問題

Go言語を書く際、成果物がシングルバイナリになるのは便利です。deployするときや他人に使ってもらうときに、それだけ渡せば使ってもらえるので。cliツールやapiサーバーを書くときにはこの方式で困っていなかったのですが、いわゆるWebアプリをGo言語で書くときのベストプラックティスが分からなかったのでエントリにしておきます。

前提

  • Go言語側は重厚なフレームワークは特に使わない
    • net/httphtml/templateといった標準ライブラリを使う
  • フロント側はVue.js

シングルバイナリを作るまでの過程

以下の過程をMakefileに書いてmake buildとやってシングルバイナリを作っていました。

  • webpackでJavaScript関係をbundle.jsという感じで一つのファイルにまとめる
  • go-assets-builderを使って、index.htmlbundle.jsを一つのasset(asset.goみたいなのができる)にまとめる
  • 全てgoのファイルになったので、go buildでシングルバイナリに固める

シングルバイナリがすぐできるならば問題ないですが、自然言語処理を内部で使っているアプリケーションのため、成果物が70Mb以上のサイズになります(kagomeを使わせてもらっています)。htmlやjsをちょっと変えて確認したいだけなのに、make buildで20秒くらいかかるのはなかなかストレスですね。なんとかしたい。ちなみにファイルに変更があったら自動でmake buildするには、reflexなどのファイル変更を監視する系のツールを使うといいというのを教えてもらいました。

解決案1: ローカルと本番で処理を分ける

ローカルではindex.htmlbundle.jsを呼ばれる度に静的ファイルを読み込むということをすれば、テンプレートが書き換えられる度にシングルバイナリを作りなおす必要はなくなります。本番環境ではさきほどまでに述べた方法で全部goのファイルにしてシングルバイナリを作ります。

問題自体は解決しますが、localと本番で処理を分岐するのは考えることも増えるし、コードも増えるのでちょっとイマイチですね...。

解決案2: 静的ファイルはNginxで返すようにして、アプリケーションサーバーはAPIサーバーの役割に徹する

自分はこれを選択しました。SPAになっているので、index.htmlbundle.jsはNginxで返します。Vue.jsから叩かれるエンドポイントはgoがAPIサーバーっぽく処理させます。Nginxの設定ファイルだとこんな感じ。

upstream local-backend {
    server localhost:7778; # goのアプリケーションサーバーです
}

server {
    listen 7777 default_server;
    proxy_connect_timeout 600;
    proxy_read_timeout    600;
    proxy_send_timeout    600;

    location / {
        root /www/app;
    }
    location /api {
        proxy_pass http://local-backend;
    }
}

手元でNginxを立てるのは面倒なので、その場合はwebpackの機能を使います。webpack.config.jsに以下のように書くと、特定のpath以下だとapiサーバーにリクエストをproxyしてくれます。webpack便利やん。

module.exports = {
  entry: './src/js/app',
  output: {
    path: __dirname + '/static',
    filename: 'bundle.js'
  },
  ...,
  devServer: {
    host: 'localhost',
    port: 7777,
    contentBase: __dirname + '/static',
    proxy: {
      '/api': {
        target: 'http://localhost:7778'
      }
    }
  }
};

これでhtmlやjsを書き換えただけなのにシングルバイナリ作りが始まってなかなか修正された様子が見れない、といったことがなくなりました。やったー。全体の様子はこの辺です。Webサイト自体はこちらです。

Goの変数でテンプレートを出し分けなどをやりたい場合、素のhtmlでない場合が多いのでこのパターンは使えないというのが悩ましいですね。

まとめ

Go言語でWebアプリケーションを書くときにオートリロードをするときに困ったこと、自分なりの解決策を書いてみました。もっといい方法がある!!という方は是非教えてください。

みんなのGo言語[現場で使える実践テクニック]

みんなのGo言語[現場で使える実践テクニック]

  • 作者: 松木雅幸,mattn,藤原俊一郎,中島大一,牧大輔,鈴木健太
  • 出版社/メーカー: 技術評論社
  • 発売日: 2016/09/09
  • メディア: Kindle版
  • この商品を含むブログを見る