Goに入門してRedis+PostgresなアプリをHerokuにデプロイするまで

お久しぶりです。Goの門を叩いてみました

Goはマスコットの謎生物が可愛いですね。Gopherというらしいです。
どこかで見たことあると思ったら、くまのプ●さんにゴーファーというそのままなキャラクターがいましたね。

他の言語とはソースコードの構成やら書き方やら結構違っていて馴染めなかったのですが、なんとかことはじめの記事くらいは書ける程度にはなれたので、備忘録として残します。

Goの環境構築から、Ginという軽量フレームワークでRedisとPostgreSQLを使ったデモアプリを作って、Herokuにデプロイするところまでの備忘録です。 少しでもGoに入門する人の助けになれば幸いです。

なぜGoなのか

なんとなくと言ってしまえばそれまでなのですが、 最近あまり新しいことに挑戦してないなー と思ったのがきっかけです。
というのと、今まで触れたことない言語に触れてみたい欲が湧いたので、Goを選んでみました。

Goについて調べているうちに、並列・スケールしやすいとか動作が速いとか良さそうなキーワードが手に入ったり、 ライブラリ管理つらい とか嫌ーな情報を入手しつつ、な入門です。

まだGoのコードや構成の良し悪しの判断がつかないひよっ子ですので、誤った解釈・もっと良い方法などがございましたらご教授いただけると幸いです。

参考資料

この記事を書くに当たりこれらの情報を参考にさせていただきました。ありがとうございます。

A Tour of Go

Golang周辺のツールをいろいろ使いながらGin Web FrameworkでAPIを作る

takasing/golang-gin-api

GoアプリをHerokuにデプロイする

Deploying a Golang web apps to Heroku

herokuでセッションの保存先をRedisにする

Ubuntu環境に、Redisをインストールして、使ってみる

Go言語での構造体実装パターン

便利メソッド(util)系を Static Method としてまとめる

デモ、今回の構成

まずGoでハマったのが、 ファイル構成よくわからん。

パスを縛るな、好きなディレクトリで開発させろや(#゚Д゚)ゴルァ!!
…すみません好き勝手な構成にした愚鈍めをお許し下さい…プロジェクト作り直しますので…

という感じでした。
自プロジェクト内にある別ファイルをどうインポートするのか がわからず全部1ファイルに書いてしまおうかという悪夢を見ました。
この辺は参考記事さまのデモアプリ(takasing/golang-gin-api)のコードを見て理解しました。

ということで今回の構成です。

Macローカルに色々インストールするのは嫌なので、Vagrant+AnsibleでVM環境を作って、その内にGoやその他もろもろを追加します。
自前で作った部分はごく僅かです。各ツール・ライブラリを公開してくださっている皆様に感謝です。

欲張りすぎず簡単すぎない程度に、DBアクセス、認証、セッション管理、CSRF対策あたりをやってみようと思います。
セッション管理にはRedisを使ってみます。複数台構成にするときに面倒にならないよう早めにセッション共有を突っ込んでおく狙いです。 ライブリロードに使用しているginとフレームワークのginは別物なので要注意です。

今回の記事で作るものはこちらのリポジトリにて公開しています。記事と合わせてご覧ください。
(無料プランなので接続に時間がかかる場合があります。 Goが遅いわけではなくHerokuが原因です。 ご留意ください)。

リポジトリ: Leko/godemo
動作デモ(Heroku): go-demo

※ここから先の内容はデモリポジトリを使う前提で進めます。何をしているかの詳細は各ファイルを御覧ください。

環境を整える

Vagrantfileとprovisioningフォルダが入っているので、さくっと環境構築を済ませます。

Vagrant+AnsibleでGo環境を構築し、せっかくなのでAWS SDK for Goを試してみる。さまの記事をベースにしつつ、Vagrantの設定、構成・プロビジョニング周りを色々自分好みに改造してます。
Ansibleのインストール等は上記の記事をご覧ください。

$ vagrant -v
Vagrant 1.7.2

$ ansible --version
ansible 1.9.2

$ git --version
git version 2.4.5

$ git clone git@github.com:Leko/godemo.git
$ cd godemo
$ bower install
$ vagrant up --provision

Ansibleのプロビジョニングが走り、エラーが出なければおそらくOKです。
VM内にログインしてGoのアプリを起動してみます。

$ vagrant ssh
$ vagrant@precise64:~$ cd go/src/godemo
$ vagrant@precise64:~/go/src/godemo$ go get
$ vagrant@precise64:~/go/src/godemo$ go get github.com/codegangsta/gin
$ vagrant@precise64:~/go/src/godemo$ gin -p 8080

/ping

http://localhost:3000/pingにアクセスし、上記画面になれば動作確認OKです。
以下の内容はこのデモアプリの解説になります。

プロジェクトを作成する

ファイル構成はこのようにします。

ローカル側は~/work/godemo(任意のパスでOKです)、VM側は/home/vagrant/go/src/godemoとします。
VagrantのSynced folderで紐付けを行っています。

godemo/
├── Godeps            # ライブラリの依存性定義とソースコード
├── assets            # フロントエンドのファイル(gulpを使ってビルド)
│   ├── dist    
│   ├── scss    
│   └── vendor  
├── controller        # コントローラ
│   └── users.go
├── database          # データベース接続(PostgreSQL, Redisのユーティリティ)
│   ├── postgres.go
│   └── redis.go
├── model             # モデル
│   └── user.go
├── session           # Redisを使用したセッション管理
│   └── session.go
├── templates         # テンプレートファイル
│   ├── footer.tpl    # 共通フッタ(jsの読み込みなど)
│   ├── header.tpl    # 共通ヘッダ(metaタグ, CSSの読み込み, グローバルメニューなど)
│   ├── index.tpl
│   └── user_form.tpl
├── Procfile          # Heroku用
└── main.go           # メインファイル

※Goの構成やHerokuと直接関係のないファイルは除外しています。

Herokuにデプロイする(準備)

Godepというツールを用いてライブラリの管理を行います。 くわしくはHerokuのマニュアルを御覧ください。

Getting Started with Go on Heroku | Heroku Dev Center

ここでの疑問は、できあがったGodeps/_workspaceというディレクトリはコミットする必要があるのか? ignoreして大丈夫なのか? です。
マニュアルを読んでみてもignoreの話が出てこないので、試してみたところ_workspaceはignoreしたらデプロイできませんでした。コミット必須なようです。

HerokuでRedisを使用するにはRedis Cloudというアドオンが良さそうでした。ということで採用。インストールのコマンドは次の章にまとめて掲載しています。

RedistoreでRedis CloudのURLを扱う際は、NewRediStoreだと「too many colons」のエラーが出ます。代わりに

  • github.com/garyburd/redigo/redisgithub.com/soveran/redisurlをインポート
  • soveran/redisurlを使用して、Redis.Connを入手(デモのコード参照)
  • それを返す関数を作成し、Redis.NewPoolに渡す(デモのコード参照)
  • NewRediStoreの代わりにNewRediStoreWithPoolを使用してインスタンスを生成

redisurlはRedis.Connを返し、NewRediStoreWithPoolはredis.Poolを受け取る想定なので、変換に一手間かかりました。 かなりゴリ押し感が出てしまっているので、良い対処法をご存知の方がいらっしゃいましたらご教授いただけると幸いです。

この件はこちらの記事(Stackoverflow)が参考になりました。
使っているライブラリが違うなど、直接の原因や対策が出てこず苦戦しました。まだ人口の少ない言語なので仕方ないかと思います。

Go – JSON-RPC – "too many colons"

Golang Redis error on heroku

Herokuにデプロイする

ここまでくればおなじみのコマンドです。
Goは独自のビルドパックを使用する必要があるのでheroku createコマンドの後ろに--buildpackオプションを渡しています。

$ heroku create --buildpack https://github.com/kr/heroku-buildpack-go.git
$ heroku addons:create rediscloud:30
$ heroku addons:create heroku-postgresql:hobby-dev
$ git push heroku master
$ heroku open

動作確認

$ heroku open

登録/ログイン/パスワードのハッシュ化/ログアウトあたりが確認できればOKかと思います。

所感

Goを触っていて思ったことの雑記です。

型を制するものがGoを制す? ?

配列を制すものがPHPを制す、関数を制すものがjsを制す、
とか他の言語で適当な標語を掲げたりしていますが、Goはtypeキーワードを使った片付け周りが柔軟かつ高機能なので、そこがキーになりそうだなと感じました。
現状はいまいち理解できていないので構造体しか使えてませんが、理解をしっかり深めていきたいです。

型付け+コンパイルやっぱり素敵

C, Java, スクリプト言語と入っているので、プログラミングを始めた頃は型付け+コンパイルがお友達でした。
スクリプト言語のゆるふわさは楽で良いのですが、他の人が書いたコードはじっくり読まないと扱いにくいし破綻しやすいという感じがしています。

例えばPHPならタイプヒンティングで引数の型は縛れますが戻り値の型は矯正できません。PHP 7でできるようになりますが、 PHP 5で書かれたものを7にリプレースする体力はあるのだろうか、新規なら別に7で書けばいいけど言語の選択肢がある新規プロジェクトならわざわざPHP選択したくないよ。 といった感じです。

言語構造がシンプル

構文やリテラルがそこまで多くなく、機能もそこまで多いわけではないので、入門にさほど時間が要りませんでした。
型の情報も相まって、入門直後でもライブラリを使っていて分からないことは直接ソースを読めば理解できるくらいにはシンプルだなと感じました。

ドキュメント生成ツールを公式が提供

GoDocを公式が提供していることもあり、各ライブラリがきちんとAPIドキュメントを公開しているのが好印象です。 ライブラリが作られGodocも増えて洗練されて、と開発者ありきのコミュニティの下地をきちんと整えられているなーと思います。 片付け言語なこともありコメントで補わなきゃならない情報が少なく、ドキュメントと実装の型、引数が異なることもないので、ドキュメントの質も高いと思います。

Gopherの方々はレベルの高い印象

入門者におすすめされる言語ではない、まだメジャーではないなどが理由なのかもしれませんが、情報が少ない分クオリティが高いなと感じています。 もちろんすべてがそう、とは言えませんがクオリティが総じて高いと言っても良いんじゃないかなと思います。

一方ぺちぺちする言語とかどこぞの宝石とか移植性キングとかクソい情報ばかり散見されるなと思ってます 自分がレベルを下げる要因にならないよう気をつけて参ります。

Goも人口が増えて入門者が増えればどんどんクオリティの低い情報であふれると思いますが、今ならStackoverflowを調べればだいたいいい情報が出てきます。

起動がとにかく速い

サーバの起動が一瞬なのがとても良いです。
はじめてGoのサーバ立てたときに思わず 「は?」 と口に出してしまいました。

Railsで開発していると、サーバの起動にローカルだと20秒くらいかかることがあって。Spring使っても初回起動はとにかく遅いので、(何か設定間違えてるのかもしれませんが)何をするにもいちいち遅いのが個人的に嫌でした。 一方Goはサーバの起動に1秒かかりません。感覚的にはサーバ起動のコマンドを入力し、Enterキーから指を離した頃には起動している、くらいです。

とはいえ、マイクロフレームワークと重量洗車Railsを比較するのは不平等だと思いますが、
入門ついでにRevelというGoのフルスタックフレームワークも試してみましたが、こちらも同様にサーバの起動が速いです。 サーバの再起動がこれだけ速ければデプロイ時の懸念事項も減ってリリースしやすいアプリが作れるのかなー、と感じました。

ビルド時に不使用の変数、パッケージを検出してくれる

開発時にはやや不便ですが、その不便さを差し置いても素晴らしいと思います。
エラーで通らないのは当たり前で、使ってない依存性までエラーにしてくれるなんて素敵過ぎる。

Goのルールになれるまでは面倒だなーと思ってましたが使ってないパッケージのimportが入っていると後々のコードがどんどん汚れていきそうなので、ファイルを保存する時点で最低限の体裁を保てるあたり素晴らしいと思います。
まぁそれでも保存前にビルドせずコミットする人とか居そうですけど

パッケージ管理周りは安定してない

ソースコードだけでパッケージ管理する 、という謳い文句はすごいと思いますし、ローカルで開発する文には本当にそうでびっくりしています。
設定ファイルを書かなくて良いしコマンドもgo getだけなのでさくさくとライブラリを追加できます。

ソースコードから依存性を抽出する ことと前述の 使用していないパッケージをimportしているとエラー があるので、クリーンな依存性管理だと言えると思います。

ただHerokuにデプロイしたり、といった可搬性とバージョン管理を考えるとGodepとか色々と対応策は出ているようですが、バージョンを固定するにはライブラリのソースコードをリポジトリに入れて管理しなきゃいけない状況はちょっとなぁ、、、と思います。 その点に関しては他のよくあるパッケージ管理のようにGitのTag+semverな管理に寄せてもよいんじゃないかな、と思います。

まとめ

ということで入門記事でした。
入門直後はちょっとこの言語無理だわ…と思っていましたが、慣れてくると結構いい感じだなと思います。よくあるスクリプト言語とは別物 と割り切って入門するといいかもしれません。

Tour of Goで読み流してしまっているところとか結構あるのでGoの勉強してきます。では。