はじめに
この文書では、普段iOSアプリエンジニアをやっているような人がWebサービスを挫折せずに作りきって公開するまでについての学習手順を、下記の視点で書いています
- iOSアプリを作ったことがある
- Gitを日々使っている
- Webサービスのためのプログラミングもなんとなく知っている
- HTTPリクエストのGETとPOSTの違いについて説明できる
- htmlとcssの役割を理解している
なのでプログラミングしたことがないという人は対象としていないものの、特にiOSアプリの知識がないと読めないほどのことは書いてないという感じです。むしろそんなにiOSアプリの事書いてないからそこに期待はしないで欲しいかもしれません。タイトルは釣りです。
そもそもなぜRailsを選んだかというと、普段iOSアプリ開発で利用するcocoapodsがそもそもRubyGems(以下gem)的にライブラリを管理してくれることもあり、その使い方で迷わないということと、知り合いにRailsを使う人が多いということが自分にとっては大きな理由でした。
他に検討した言語/プラットフォームとしてはScala+Play Frameworkを検討していて、その理由はSwiftのOSSで使われるEitherなパターンがScalaのPlay Frameworkで頻繁に使われているのでPlay使ってみたいなとは思ってはいました。
しかし、Scalaはコンパイルが遅いという話を聞いてそれはキツイな、と。また、ライブラリ管理のエコシステムがどうなってるのかイマイチ分からなかったというのが選ばなかった理由です。
その他、この文書の前提条件としては
- 負荷分散のことは考えていない
- 冗長構成については考えていない
という感じです。
ちなみに、エンジニアアウトプットスコアランキング - Stargazerというウェブサービスを公開しました。Stargazerでは、GitHubやQiita、SlideshareのLike数などのスコアを合計し、それぞれのサービスで公開したリポジトリや記事を自動でまとめます。
続いて、学習と開発自体の順序に沿って文章をまとめています。
学習と開発の順序
自分は次のような順序で進めました
- RubyとRuby on Railsについて知る
- Ruby on Railsについての参考になりそうな本を探す
- 作りたいものがどんなものかをKeynoteで企画書をつくる
- Railsで実際に作りたいものを作る
- バッチ処理
- デプロイ
- βテストをお願いする
- NginxやUnicornなどのWeb/アプリケーションサーバ関連
RubyとRuby on Railsについて知る
ドットインストール
学習の初期段階でドットインストールRuby on Rails 4入門を利用しました。これを最初にやったことが一番良かった点だと思ってます。
一つの動画がだいたい3分以内なので、一度眺めてそこから再生しなおして同じように手を動かす、動かなければもう一度見直すを繰り返して土日で全部見終わってコードも書き終わる分量でした。
ドットインストールを見ていて思ったのは
- どの手続が必須でどの手続がオプショナルなのかモヤモヤする時がある
- 自分のWebアプリではこれは必須なの?と思う
- メソッド呼び出しのカッコ()を省略している部分があるのでRubyでメソッドの呼び出しを書いてるのかおまじない的なDSLを書いてるのか...という気分になる
基本的に全部を説明することはムリだろうからとか、きっとこれはRubyの良さだろうとか色々割り切って動画を見て最後までやり切るのに意味を持つほうが良いとは思います。
他に自分が気にしながらやったことは
- RubyMineなど動画にない開発環境を使ってはいけない
- コーディングミスで進めなくなった場合にまったく同じだとイチからやり直しやすい
- 理解できない箇所は見なおしてもいいが、後で理解できるはずと思い込み進む勇気
- RubyやRailsについて気に入らないところも出てくるが、割り切って進める
土日あれば余裕でこなせると思いますし、もしここで本当にRuby on Railsで作りたくない/作れないなと思うようであれば、土日に勉強したなって気分で思い切って他の手段を探したほうが良い気がします。それぐらい楽な気分で進めてみればいいかなと。
参考URL
Ruby on Rails チュートリアル
良い評判を聞くのでやってみようと始めてみました。
第一章は環境構築やGitの話があってわりとゼロからの開発という雰囲気。おそらく何かしらWebサービス開発に携わったことがあればスキップできると思います。
ただ、自分の場合第三章やってるあたりで先が長いなと思うようになって挫折しました。自分にはあのチュートリアルが合わなかった。
参考URL
Ruby on Railsについての参考になりそうな本を探す
買った本はだいたいこんな感じ
基本的には本屋でRubyとRails関連の本は目を通して、買おうかどうか迷って結局買ったのがパーフェクトRubyとパーフェクトRuby on Rails。迷わず買ったのがポケットリファレンス。
ポケットリファレンス(逆引き)系はパラパラめくるだけで楽しいので買って損はなかったと思います。
ただパーフェクト系は買う必要はなかったかもしれない。ひと通り読み終わった後、開発途中わからないことがあって調べてみても大抵自分の疑問は解決しない。自分には合わなかった。
初めてのRubyについては2008年に発売された本なので内容の古さはあるものの読み物として読みやすいなという感じでした。Rubyを触ったことがないプログラマがRubyとの距離を縮めるための一冊という感じかもしれません。
作りたいものがどんなものかをKeynoteで企画書をつくる
Railsでの開発がだいたいイメージができてくると実際に手を動かしたくなるんですが、ここですぐにコードを書かずに企画書を作ってみました。
経験上、手を動かすとすぐに最低限のやりたいことは出来てしまってそれで満足してしまう事が多いのであえてすぐには手を動かさないようにし、人に見せられる最低限のレベルの企画書を作り複数の友人とご飯を食べに行った時にその企画書を見せるようにしました。
- でも企画書を見せることを目的としない(すぐに見せない
- 最近自分が作りたいものがあるという話をして相手の興味がありそうなら見せる
ちょっと相手の立場想像して欲しいんですが、ご飯を食べに行って急に企画書みせられる体験ってのは遠慮したいですよね。ただ、話のノリで好奇心が湧いた内容について企画書見せるんならアリじゃないかと思いました。
企画書の作り方としては社外閲覧注意!無料で見られるすごい企画書まとめにあるnanapiの企画書を主に参考にしました。
ちなみに、企画書はiPadで見せるようにしました。5ページくらいです。そのときの見せた相手の反応はどうだったかというと、それほどいい反応ではありませんでした。みなさん、期待してはいけません。ご飯を食べに行ってるときにiPadで企画書を見せてもいい反応になることは期待せず、それでがっかりしないようにするのが良いと思いますね。
参考URL
Railsで実際に作りたいものを作る
こっからは実際に作っていく際に取捨選択をしたことについて書いています
フロントエンド用のフレームワークとしてBootstrapを使う
フロントエンド用のCSSフレームワークとしてとりあえずTwitter社のBootstrapを使うのが手っ取り早く実装できるし変なオリジナリティを発揮する必要もなくていいです。特にUI用のコンポーネントはBootstrapにあるものから選ぶと楽。さらにレスポンシブな見た目に対応できるのもいいですね。
他のCSSフレームワークについてもFoundationを検討しましたがこのときは違いがそれほど分からなかったのでBootstrapを選びました。
やって失敗だったこと
- 色々なサイトが配布しているBootstrapのテーマを変える
- 気に入るテーマがあっても細かな部分が気に入らなくてカスタマイズしてると無駄に時間がすぎるしテーマと合わなくなってしまった
やらなかったこと
- SASS, SCSSについては手を付けない
- これは今必要ないと判断して手を付けませんでした
- CSSをベタに書いています
- 最初はCSSベタに書いてましたが徐々にSCSSにしていきました
- CoffeeScriptも使わなかった
- Railsの標準なのかCoffeeScriptファイルが生成されることもあるが学習コストを考えると今やりたくなかったので使わないことに
- テンプレートエンジンはERBのままにして、SlimやHamlを使わなかった
- ERBだとタグの閉じ忘れが頻発するが、学習コストとしてはERBが圧倒的に低いのでERBのまま
- 今後SlimやHamlを検討すればいいかなと
参考URL
RubyMineを使う
ドットインストールの動画の延長のノリでコードを書いていましたが、開発環境としてRubyMineを使うことにしました。プロジェクトのインポートは楽にできるので途中からRubyMineを使ってもよし。
RubyMine使ってよかったなと思うのは
- Xcodeなどのようにメソッドの定義元へのジャンプがある
- Railsのコードもすぐに読める
- gemのコードもすぐに読める
- cssのclassからcssへもジャンプできる
- DB用のブラウザがあり簡単にデータの確認ができる
- 作成画面ごとのMVCの切り替えが楽
- UsersControllerのindexメソッドに対応するhtmlテンプレートにジャンプなどが楽
- テンプレートが見つからなければ作成できる
- htmlに記述しているcssのclass名からcssファイルにジャンプできる
- 代入だけして使ってない変数があれば気づける
普段Xcodeを使っている人がRubyMineを使っていて気になったことは次の通りです
- Xcodeのように画面分割はできる(上の図)
- Xcodeにある「ショートカットで左エディタのメソッドのジャンプ先として右エディタを選択する」ということが出来ない
- 定義元からジャンプしていき、元のコードに戻る機能が無いようで近いのはカーソルのバッファ位置を戻す機能になる
- ウインドウのエディタは1つに固定しないとタブが増えるので1つに固定できる(上の図)
- 対応するMVCでジャンプできるんだからタブで切り替える概念は相反している気がする
参考URL
他サービスとの連携について
私はcocoapodsでライブラリをガンガン突っ込むのはリスクを増やしすぎている気がしてかなり慎重なんですが、Webサービスへのログインに他SNSなどのOAuthを利用するなら、OmniAuthのgemはかなり便利で、Railsを利用して良かったと思える点でも有りました。
例えば、GitHubログインをやりたいならOmniAuthとOmniAuth GitHubを利用すればOAuth連携はかなり楽を出来ます。QiitaもOmniAuth Qiitaがあり同じく実装は簡単です。
参考URL
Rubyのコーディングスタイルガイド
自分の慣れてない言語を利用する際は、慣れている人たちが使っているコーディングスタイルのガイドラインを見るとその言語の特徴が分かっていい。自分はcookpad社のRubyスタイルガイドを参考にしました。
どんな事が書いてあるかというと次のような感じ
[SHOULD] if !condition の代わりに unless condition と書くこと。
[SHOULD] while !condition の代わりに until condition と書くこと。
[SHOULD] unless に対して else を使ってはならない。
Rubyではunless、untilがあるので条件式の否定のための"!"をなるべく使わない方がRubyらしくなり読みやすい。Rubyのコードを読むときはRubyらしくコードが書かれているほうがロジックに集中できるからというのが大きいんでしょう。
[MUST] メソッド呼び出しの括弧は、省略を許可された場合を除いて、省略してはならない。
[MUST] 引数なしのメソッド呼び出しは括弧を省略すること。
Rubyはメソッド呼び出しのカッコについても言及してあり、ある程度コードを書いていると納得できますね。
参考URL
バッチ処理
非同期処理
非同期処理はアカウント登録後などにバックグラウンドで通信をする場合に使う必要がありました。アカウント登録がすぐに終わって後ろでは時間のかかる他サービスへの通信処理が動くようなイメージです。
例えるような話ではないですが、iOSアプリ開発の場合dispatch系の関数を使うなりして非同期処理を実行しコールバックによりその完了後に何かする、ということはかなり簡単なんですが、Webアプリではそういうわけにもいかないわけですよね。
非同期処理を動作させるためには処理する内容をジョブとし、永続的な保存をする領域にキューとして保存し、それを実行する必要があります。
おおまかに説明すると次のような感じなると思います
- 非同期実行したい処理を実装(ジョブ)
- キューへのジョブ登録部分実装(エンキュー)
- キューの保存先としてKVSかDBに保存することになる
- キューからジョブを実行するワーカーを指定する
それを踏まえて非同期処理を簡単にできるGemは3つぐらいあるんですよね
- Resque
- Redisに保存
- Sidekiq
- Redisに保存
- delayed_job
- DBに保存
しかし、そもそもRails4.2からActiveJobという非同期処理のインターフェースを抽象化して上記の非同期処理をまとめたものもあるらしく、どれを選んでもインターフェースはそれほど変わらないようです。
ただ、Resqueで充分だったのと最初の何も分かってない状態から抽象化されたインターフェースを触るより、Resqueそのまま使うほうがトラブル時になんとかなりそうだったためResqueを選択しました。
Resqueは最新のv2系ではなくドキュメントの多いv1系を選びました。v1系はキューを処理するためのrakeのオプションがかなりダサい感じがするのだけが難点です。
もちろん、非同期処理といってもキューを使わないのであればリクエストを受けて実行されるRailsの処理からLinuxコマンドを実行すればいけるんでしょうがそれだと再実行できないこともありキューを使うことにしました。
参考URL
デプロイ
Dockerを使う
DockerとはDocker社が開発するOSSのコンテナ管理ソフトウェアで、アプリケーションサーバやミドルウェアを運用しやすくするものです。
なぜDockerを使うのか
例えばサーバーやローカル環境にPostgreSQLを入れたりする際、いくらでもインストールする方法があるために複数のPostgreSQLをインストールしてしまったりします。
そうすると、このWebサービスで使ってるのはどのPostgreSQLなのか、のような気持ちが悪い思いをしたくないのでいつでも捨てられれるDockerコンテナを使う方法が自分には合っていると考えました。
他の理由としては、比較が難しいところですが、Webサービス開発にはないiOSアプリ開発での良さというのは開発環境がよっぽどのことがない限り固定されているところですよね。なのでiOSアプリを日々作っている人がDockerを用いるのはかなり良い手段だと思います(逆にプログラミングが初めてでWebサービスを作ってみたい人には絶対オススメできません)。
ただ、Dockerを使うこともいい面ばかりではなく、1プロセスを1コンテナで実行することを原則としていてRailsアプリを動かす場合は制約を強く感じてしまいます。しかし、役割に応じてコンテナを分けると思って使い方を変えるほうが楽ですし、私はそうしました。
Dockerコンテナを役割によって分ける
具体的にはRailsアプリとして一つのDockerコンテナに役割を割り振ったほうが自然な場合があります。私はRailsを実行しているコンテナ上でcronとか非同期処理などを1コンテナで動作させるたかったので、supervisorというプロセス管理のOSSを使いました。
基本的にDockerコンテナのプロセスはrootで実行されることを想定してあるようですが、supervisorで各プロセスの実行ユーザの指定をしています。
Docker Language Stackについて
DockerにはDocker Hub RegistryというDockerイメージを配布するリポジトリサイトが有り、Language Stackという各種開発言語用の公式Dockerイメージがあります。
Language StackによってあらかじめRubyの最新版やRailsの最新版がすでにインストールされた状態で使うことができますが、このDocker Language Stackはcronが実行されないように変更されていたりもするため、使っていて不満があればLanguage Stackを使わず、これまた公式のubuntuイメージを使いRubyやRailsをDockerファイルで記述しDocker Buildでインストールするほうが手っ取り早い場合があります。
具体的にRubyのLanguage Stackでcronを動かした際にエラーメッセージは下記
Cannot make/remove an entry for the specified session
対処法はいろいろググると出てきますが、どれも解決できなかったので自分はubuntuイメージを使っています。
買った本
Docker関連で買った本は次の通りです
Dockerについて日本語の専門書籍が無かったのでとりあえずあるものを買ってみました。
書籍Dockerエキスパート要請読本については、よくある「ムック本タイプで色々な開発者が書いているパターン」なので、内容として入門者向け内容もそれぞれの書き手が含めていてある程度Dockerを知っていると冗長な気がしました。
具体的には、P29でDockerファイルの各コマンドについて説明しているのにまたP63で他の著者が同じことを説明している、とかです。
ただ、kubernetesとかDocker Composeについて本の書籍で知りたければいいかもしれないという感じ。自分はDocker Composeを最初は使おうかと思いましたがDockerコンテナが4つくらいなら必要ないかと思って使ってません。
Dockerコンテナ運用
雑誌WEB+DB PRESS Vol.86のDocker特集では、「ローカルのRails環境までDocker運用してしまうのはしんどい」的なことが書いていて参考にさせてもらいました。
具体的には本番サーバーでのDockerは次のようなコンテナ構成にしているものの
- Redis
- PostgreSQL
- Supervisor(Unicorn/Rails, cron, Resque)
- nginx
ローカルの開発環境では利用するコンテナは2つだけ
- Redis
- PostgreSQL
ローカルのRailsはWEBrickを使うのでWebサーバとしてのnginxは利用しないのと、ローカルでもRailsのDockerコンテナに対してリモートでコードを触ることで気軽さが失われるデメリットのほうが大きいのでRailsのコンテナを使いませんでした。
MacOSXではkitematicというDocker用のGUIツールがあるのでこれを使っています。
- コマンドラインを知らなくても設定を見ているとDockerはコマンドでこういうことができるんだなと分かる
- ポートや共有されているディレクトリなど設定されている内容が見やすい
Dockerの勉強会に参加してみる
勉強会にも参加してみました。
渋谷のSansan社でDockerのハンズオン的な社内勉強会をするというので参加させてもらいました。一番効果的だったのは勉強会に参加する直前にローカル環境にKitematicを入れて触っていて事前準備していたことですね。
すごく私個人に依存する話ですが、自分が発表しない勉強会に参加することで得られることはそれほどないため、目的を絞っていくと効果が大きいかなと思います。
具体的には学習効率を上げるためとかそういうのは期待せず、勉強会までに何かをやるということ実際にやってる人に質問するということが重要かなと。
もくもく会に参加してみる
もくもく会にも参加してみました。
Wantedly社がゴールデンウィークにCTOとコード書きたい人Wanted!という募集を出していたので参加して機能を実装した後、成果発表タイムでWebサービスを見せて反応を聞く、ということをしてみました。
正直自分の家のほうが作業は捗るんですが、もくもく会というのは成果を見せて反応を伺う良い機会と思います。一日Wantedly社で作業しましたが、全員で昼飯を食べに店を探しに行って、GWだから開いている店が少なかったんでぶらぶら目黒を歩いて世間話をするのが予想外に楽しかったですね。
もくもく会で自分の作ってるものをお披露目しようとする人はいると思うんですが、意見を聞くのは積極的にやったほうがいいです。ほっといても意見を言ってくれるなんて絶対ない。細かくアドバイスを求めるようなこちらの姿勢でやっと意見を言ってもらえるかなというのが実感です。
アプリケーションサーバのDocker運用について
- RailsのappルートにDockerfileを置いてRailsのプロジェクトと共にgit管理
- Dockerfileにbundle installする手順を書く
- DockerfileのEntrypointでsupervisorを実行するようにする
- supervisorの実行時にrailsを起動させる
- supervisorの実行時にcronを起動させる
- supervisorの実行時に非同期処理をするWorkerプロセスを起動させる
- コードを変更してデプロイしたい場合
- GitHubからpull/fetch merge
- Dockerfileをビルド
という風にしています。
Dockerのセキュリティについて
どこかの記事で公式のPostgreSQLイメージは設定ファイルのアクセス制限がスッカスカと書かれていましたがそこまでではありません
- パスワード指定すればhost all all 0.0.0.0/0 md5になります
- 他ホストからパスワード必須になる設定です
- 逆にローカルではパスワード必須じゃないですが
- SSHをコンテナに立ててない状態でDockerコンテナへ侵入されるってことはそもそもrootが取られていますのでローカルパスワードを必須にしてもセキュリティ的にはほぼ無意味に感じます
- ローカルのパスワードを必須にしたいということはSSHをDBのコンテナに立てたいのかもしれませんがそれはよくないと感じます
- Railsの実行について
- Dockerファイルで任意のユーザを作成し
- supervisorの指定で任意のユーザでRailsを実行することにしました
DockerのパフォーマンスやDockerについて
IBMがslideshareで公開しているKVM and docker LXC Benchmarking with OpenStackにある画像がそもそもDockerとは何かを含めて理解しやすいかなと思います。
ハイパーバイザ型の仮想化は仮想マシン(VM)を利用していて、ゲストOSを実行する必要がありハードウェアのエミュレーションから行うのでコンテナ型よりもオーバーヘッドがありますよと。そしてコンテナ型のためのOSSを集めてREST APIとDockerfileで扱いやすくしたのがDockerですよというのがざっくり分かりますね。
参考URL
βテストをお願いする
友人にβテストをお願いする
一般公開する前には使ってもらいたくなるのが心情、友人にお願いしてアカウント登録をしてもらいました。友人が少なければ仕事のお客さんに使ってもらうのもいいですよね。実際に目の前でお願いするほうがドキドキが少なくだいたい気軽に承諾してもらえます。
やっぱりここでも重要なのは相手が興味を持つかどうかで、お願いする前には興味の度合いを伺うことが重要だと思います。興味が無い状態で使ってもらってもフィードバック貰えないですよね。
βテストでは自分で立てた仮説を検証することが目的でもあるので、利用状況を観察することにしていました
- 利用してくれると思った機能を利用してくれない
- 面倒なのかもしれないので自動化を検討する
- 機能の説明をしてヒアリング
iOSアプリの場合はインストールしてもらわないといけないのがWebサービスだとURLを教えるだけでいいので気軽ですね。
ぜんぜん違う人に見せる
ちゃんと動くレベルになっているのなら、友人ではないまったく立場の違う人に見せてみるのもいいです。意外に褒められる。なんなら動いているだけで褒められる。利用用途とは違った意見も言ってくれるかもしれない。なんで違う人に見せるかというと直前でリリースしたくなくなる病に襲われることもあるから、承認欲求でそれを避けられます。
お金をもらえるわけではないのに何かを世の中にリリースしたい場合というのは
- 脆弱性があってそれで晒しあげられるんじゃないだろうか
- 誰かの役に立つんだろうか
と思ってしまうのではないでしょうか。そういう時はもう承認欲求に寄り添いたい。普段はしょうもないと思っている他人の承認欲求ですがこういうときは承認欲求を動機として前に進むと楽です。
Nginx UnicornなどのWebサーバ、アプリケーションサーバ関連
リリース前の負荷テストはapache benchを使うのが手っ取り早くて、その負荷テストの数値などからWeb/アプリケーションサーバ関連を試すのが良いと思います。
apache benchで負荷テストをしていて気づいたのは、WEBrickでも同時接続数250ほどではNginx+Uniconrと比較してそれほど性能が劣っているとは感じられないところです(もしかしてテスト方法が悪いのかもしれませんが)。
なのでWEBrickでもとりあえずいいかな、と思っていたんですがNginxのメリットととしてはRailsをデプロイ中にメンテンナンスモードとして動作させて適切なステータスコードを返す事ができるっていうのは大きなメリットですね。それでNginx使うならUnicornなどのアプリケーションサーバを使えばいいと思うので導入しました。
参考URL
おわりに
もしかしたら誰かの役に立つかもしれないので細かいことをグダグダと書いてしまいました。
私が公開したエンジニアアウトプットスコアランキング - StargazerというWebサービスについて補足しておくと、GitHubやQiita、SlideshareのLike数などのスコアを合計していて、合計数に応じてランキング化していますが、各サービスのスコアが高いからといってそれがそのまま技術者としてのレベルが高い、ということになるかというとそれは分かりません。
アウトプットのスコアが多いことで誰かの役に立っているという事実はとても素晴らしいことなのですが、世の中へのアウトプットをあまりしなくても誠実に仕事に取り組んで自然に素晴らしい実績を残してきた人も多いでしょう。
ただ、各サービスでのスコアをまとめて様々な角度から他人と比較することが、自分自身のモチベーションを高めることになり、技術的アウトプットが増えることになれば、さらに世の中良くなっていくんじゃないかということは思っていたりもします。