Goで書いたWebサーバを本番(VPS)で動かす
最近、趣味で書くWebアプリは、いわゆるSPAで、バックエンドのJSONを返すAPIにはGo、フロントエンドは仕事で使うための検証・学習を兼ねて適当なJavaScriptのフレームワークを選択している。
この記事は、Goで書いたWebサーバを本番(自分の場合、OSがCentOSのVPS)でどうやって動かすかという内容。
プロセス管理ツール
プロセスを複数立ち上げたり、プロセスが死んだ時に再び起動したりしてほしかったので、プロセス管理ツールを検討した。 色々調べたところ、circusが以下の点でよさそうだった。
- プロセスまわりで癖のある(forkできないなど)Goと組み合わせて使うプロせス管理ツールとして実例を多く見かけた
- 設定で記述する内容が他のプロセス管理ツールと比べて少ない(ような気がする)
- (fdを受けとるようにアプリケーションコードを変更しないといけないのがちょっと微妙だけど)プロセス管理のプロセスがMaster-WorkerモデルのMasterプロセスを兼ねることになるのでプロセスの関係がシンプルになる
- hotdeployを実現できる
circusそのものの振る舞いの説明は、catatsuyさんの Golang_ads_deliver // Speaker DeckGolang_ads_deliver // Speaker Deck が分かりやすかった。
インストールはドキュメント通りpipから。 (refs: Installing Circus — Circus 0.12.2 documentation )
circusのdaemon自体のマネージは、upstartやsystemdを使う。ドキュメントに設定例が載っているので(refs:
Deployment — Circus 0.12.2 documentation )、参考にしつつ自分の事情に合わせて修正した。
(自分しか使わない趣味のアプリでプロセス管理ツールを使う必要があるか微妙な気もしてたんですが、herokuゆとりになってしまってここ最近まともにそういうことをやっていなかったのと、普通に楽しいのでいいかな、と)
設定ファイルの記述例
[circus] statsd = 1 [watcher:webapp] working_dir = /usr/local/foobar cmd = ./current args = --fd $(circus.sockets.web) numprocesses = 3 use_sockets = True copy_env = True stdout_stream.class = FileStream stdout_stream.filename = /var/log/foobar_stdout.log stdout_stream.refresh_time = 0.3 stdout_stream.max_bytes = 1073741824 stdout_stream.backup_count = 2 stderr_stream.class = FileStream stderr_stream.filename = /var/log/foobar_stderr.log stderr_stream.refresh_time = 0.3 stderr_stream.max_bytes = 1073741824 stderr_stream.backup_count = 2 [socket:web] host = 0.0.0.0 port = 8000
サーバー構成
HTML, CSS, JavaScript等の静的ファイルはGoで書いたサーバの前段にリバースプロキシ(nginx)を置いて配信している。
JSONを返すAPIのパスにリクエストがきたら、後ろのGoで書いたサーバにまわす。
serverディレクティブにunix domain socketに指定しようとしたけど、自分しか使わないアプリだしパフォーマンスはそこまで求めていないのと、ちょっと面倒そうだったのでローカルのポートを指定するようにした。その代わり、TCPのコネクションを張るコストを少しは意識してkeepaliveディレクティブを指定している。
upstream backend { server 127.0.0.1:8000; keepalive: 16; }
8/12に追記:
unix domain socketにするのそんなに面倒じゃなかった。(refs: Configuration — Circus 0.12.2 documentation )
circusの[socket:web]
のセクションを以下のようにするだけ。
pathを指定すれば、勝手にfamilyはAF_UNIXとして扱ってくれるらしいけど、念のため明示しておく。
[socket:web] path = /var/run/foobar.sock family = AF_UNIX
デプロイ
デプロイは、シンプルなデプロイツールのfabricを使って、手元のPCでCentOS用にビルドしたバイナリの転送&リモートでcircusctlを実行をするだけ。
本番(VPS)のほうでアプリケーションの実行環境をあれこれ用意する必要がないのでだいぶ楽。
いま動いているバイナリが何者かわからなくなると困るので、ここ( golang - ビルドする際にバージョン情報を埋め込む - Qiita )を参考にバージョン情報を埋め込んでいる。godepで依存管理しているので、アプリケーションのリビジョン番号が分かれば、依存しているライブラリのバージョン(リビジョン番号)も分かる。