| @技術/プログラミング

docker-on-ecs.png

ブログを Docker 化して AWS ECS で運用するようにした。

なぜ Docker 化したか

  • 仕事で Docker を使う機会が増え知見がたまってきた
  • 仕事では Production 投入はできていないので個人ブログで Production 投入して知見を得ておきたかった

どうやったか

ローカルセットアップ編

  • Dockerfile & docker-compose.yml を作成した
    • Alpine Linux を使ってなるべくイメージを小さくする
  • Gem::LoadError 問題
    • Lokka の Gemfile には動的な読み込みを行っている部分があるため、 Dockerfile で単純に COPY するだけでは Gem::LoadError になってしまう。
      • Lokka のプラグインは Gem 化されておらずリポジトリ内に含める形式
      • プラグイン側で必要な gem はプラグイン内に Gemfile を配置して宣言する形式
      • Lokka 本体の Gemfile には Dir["public/plugin/lokka-*/Gemfile"].each {|path| eval(open(path) {|f| f.read }) } のようなコードがあって強引に eval で内容を取り込んでいる
    • 対策
      • Gemfile.docker を用意する
      • Gemfile.docker を生成するためのシェルスクリプトを用意して実行する
      • Dockerfile の COPY は以下のようにする
      • COPY Gemfile.docker /app/Gemfile
  • 他、 MySQL のコンテナを追加して手元でアプリが起動するところまでは確認済み

Production セットアップ編

  • ECR にリポジトリを作成し image を push (公式のチュートリアル通りにやればできる)
  • ECS にサービスやターゲットグループ、タスクの作成なども指示通りに行う
    • 土台となる EC2 インスタンスは手動で作るのではなく、 ECS の画面でポチポチやると勝手に作られる
    • 詳細コンテナ設定でエントリポイントを入力する欄に、 Dockerfile と同じように文字列で書いていたらコンテナが起動せずハマった
      • カンマ区切りで書かないといけないらしい
      • puma を起動したかったら bundle exec puma ではなく、 bundle,exec,puma というように書かないといけない スクリーンショット 2017-08-19 10.50.09.png
  • 諸々設定を済ませたらロードバランサー( ALB )を EC2 のパネルで作成してターゲットに Docker コンテナが動いている EC2 インスタンスを指定する
    • ECS の用語やサービス構成に慣れるのに時間がかかるが、歯を食いしばってがんばるしかない
  • DB に関しては RDS を使うことにした
    • 稼働中の VPS サーバーで mysqldump -ufoo -p db_name | mysql -ubar -p db_name -h foo.bar.ap-northeast-1.rds.amazonaws.com みたいな感じで雑に流し込んで移行する
  • ECS は VPC でしか使えないので、 VPC に慣れてない人は VPC に慣れるところから頑張るしかない
  • セキュリティグループの設定なども必要になるので頑張って下さい
  • Nginx を利用しないので SSL の復号を ALB で行う必要がある
    • ACM で無料で証明書を発行できるのを知らず、 Let's Encrypt の証明書を取り込んで使う
  • ここまでで一旦公開

運用して気づいた問題点

  • サイトが 503 や 504 になる
    • Docker コンテナがすぐ死ぬ
    • ALB から切り離されることしばしば
    • VPS 時代は Nginx に静的ファイルの配信をまかせていたが、 Nginx を挟まなくなったので puma が担当することになりアプリの負荷が高まったのではと推察
      • CloudFront を挟んでいい感じにキャッシュしてもらい、静的ファイルの配信は CloudFront にまかせることに

CloudFront 導入編

  • ALB で使っているのとは別に SSL 証明書を取得する必要がある
    • CloudFront <-> ALB 間の通信を HTTPS で行うため
    • Route53 で ALB に割り当てている A レコードをサブドメイン付きの別のものに変更
    • ALB 用にはワイルドカード証明書を使う(無料で証明書取得できる ACM 最高)
    • Let's Encrypt の証明書を使うのはやめ、ルートドメインの証明書も ACM で取得して CloudFront に設定
  • 動的コンテンツ( HTML など)はキャッシュしないようにしないといけない
    スクリーンショット 2017-08-19 11.32.04.png
    • 当初、設定がうまくいっておらず、以下のような問題が発生
      • POST, PUT, DELETE できない
      • Cookie が origin に転送されずセッションが維持できない
      • クエリストリングが無視されてしまい、ページ検索などができない

所感

  • 体感的にサイトの読み込みがチョッパヤになった
  • CloudFront 導入したが、まだ 503 にはなる
    • そもそもインスタンスを良いやつに変えないとダメなのかもしれない
    • タスク数を増やしてクラスタリングするなどいろいろ試してみる
      • クラスタリングするためには Cookie セッションではなく Redis や Memcached などをセッションストレージに使う必要が出てくる…
  • Deploy だるい問題
    • cap deploy しなくなり、イメージをビルドして push する感じになる
      • Alpine Linux でもそこそこイメージサイズはでかくなるので貧弱な回線では docker push にめっちゃ時間かかる
    • ECS 側でもサービスを更新するなどの作業が発生
      • Blue / Green Deployment できるがポチポチ作業が発生するのがだるい
      • Rails を運用する場合は migration なども発生するのでうまいことやる必要あり
    • git commit しなくても作りかけのコードの状態で docker-compose build してしまいがちになり、リポジトリのコードと動いてるコンテナイメージの間に差分が発生してしまいそう
      • ちゃんと CircleCI などを導入してイメージのビルドとプッシュは CI サービスでやる、というような運用にしないと破綻しそう
  • 手順書問題
    • こんな風にブログを書いて雑な手順書を作成するようではいけない。 Terraform 化しないと破滅する。
  • Lokka は CMS for Cloud です
    • git push heroku master するだけで使えることが売りの Lokka を AWS のガチな構成で運用するという皮肉
  • お金高い
    • 毎月 3000 円くらいかかる感じになりそう。 VPS は年払いで 16000 円くらいなのでだいぶ高い。払えなくなったら VPS に戻しそう。

謝辞

r7kamura さんの amakan Docker 化の一連の記事と Classmethod 社の ECS 関連の記事には大変お世話になりました。

| @技術/プログラミング

むかしやってた映画のブログ( WordPress )のデータをこのブログに取り込もうとしたら、 mysqldump しといたデータが文字化けして読めなかった。 映画ブログのデータベースの文字コードは utf-8 にしてたけど、 Dreamhost の MySQL サーバーの defualt-character-set がおそらく latin1 になっていたと考えられ、 mysqldump するときに utf8 のデータが latin1 として export されてしまい文字化けを起こしていたっぽい。

vim で開いて e ++enc=utf8 してみたり、 nkr で変換しようと試みたり、ググって出てきた Perl のスクリプトで変換しようと試してみたけど直らなかった。この変換は MySQL 自身にやらせないとダメなのではと思い、以下を試したがダメ。

  • [x] latin1 で DB 作成し latin1 で import 、 utf8 で export
  • [x] latin1 で DB 作成し utf8 で import 、 utf8 で export

結局、 Stack Overflow にあった以下の方法で解決できた。

一旦文字化けしたまま utf-8 の文字列として import し、以下の SQL を流す。

UPDATE [table_name] SET [column_name] = CONVERT(BINARY CONVERT([column_name] USING latin1) USING utf8);

やっぱり MySQL で化けたデータは MySQL で直すしかないみたいだった。

| @労働

Kaizen Chat とは

Kaizen Platform 内でユーザー同士がコミュニケーションを取ることができるサービス。

  • Kaizen Platform のユーザー
    • カスタマー
      Kaizen Platform と契約し、 A/B テストツールや Growth Hacker によるサイト改善のデザイン案を募集
    • Growth Hacker
      募集に応じてカスタマーサイトのデザインを改善するデザイン案を投稿
    • Kaizen Platform 社員
      カスタマーと Growth Hacker の間の調整役

Kaizen Platform 内のユーザーが外部のツールや電話を利用して行っていた伝言ゲーム的なコミュニケーションを置き換えて、直接コミュニケーションを取ってもらうようになることが目標。

サーバーサイド 2.5 人、フロントエンド 2 人で 2 ヶ月くらいで作った。

構成

数多く存在しているマイクロサービスの中の一つとして実装。

フロントエンド

  • React
  • 多分他にもバズワード的な仕組み・フレームワークを使ってる(後で調べて書きます)

バックエンド

  • Ruby 2.3
  • Rails 5
  • MySQL 5.7
  • Sidekiq
  • Redis
  • Pusher (SaaS)

Rails 5 で開発

Rails 5 は Release Candidate だったが利用することに。

リリース直後に Rails 5 出て将来的に Rails 4 から Rails 5 へのアップデートにリソースを割くの避けたかった。(別の Rails アプリを 3 から 4 に上げたときには大変だった…)

Rails 5 で開発して困ったこと

あんまりないが強いて挙げるとすれば以下。

  • 個人的に好きで前職の頃から使ってた gem ( モデル層のバリデーションのテスト書くのが楽になる accept_values_for ) が Rails 5 対応してなかったので Pull Request 送ったりなど
  • ActiveResorces など Rails 5 対応が遅かった gem もあったが github 直接参照して対応版がリリースされたら rubygems.org を見るように変えるなど(普通です)
  • 社内の Rails プロジェクトで共通して使ってる gem を Rails 5 対応させるなど

WebSocket は Pusher (SaaS) を利用

Pusher を使った。 ActionCable 使って自前実装するのは考えなかった。

  • Rails 5 なら ActionCable では????
    • 急なアクセス増などを考えて SaaS を使うことに
    • WebSocket に関してはインフラのことを考えなくてよくなる
    • 無理に自前実装せず、少々金がかかったとしても、外のリソースを利用できるときは SaaS を使う(社風)
      大人の事情で使えない、とかがないのがよい
  • フロントエンド側も Pusher から SDK が提供されており楽できた(はず)

Pusher との通信の詳細

  • CRUD のうち Read だけ Pusher 経由で行う
  • Pusher は Create/Update/Delete も担えるけど Rails アプリと Pusher とクライアントの間のデータの流れを一方向にしたかった

Kaizen Chat Pusher

感想

Sidekiq 速い

  • 社内で初めて Sidekiq を導入したけど速かった
  • Thread で並行処理をするのでスレッドセーフな作りにしないといけない(利用 gem 含む)
  • capistrano-sidekiq が sidekiq 本体と機能かぶってるところがあるのがちょっと残念

マイクロサービス間の通信が課題

  • マイクロサービス間でなるべく疎結合になるように、相手のサービス側の DB を直接参照しないように頑張る(気合い)
  • スピードが遅くなってしまうところはキャッシュする

BFF 的な考え方必要

  • ActiveModelSerializers 使ってると Serializer が乱立して収集付かなくなる
  • Frontend から必要なフィールド渡してもらってそれをシュバッと返すおシャンティな API にしていきたい

今後

  • モバイルクライアントの開発
    チャットなのに携帯電話で返信できないとかダメですよね…
  • 通知の充実
    チャットなのに通知こなくて気がつけないとかダメですよね…
  • 画像ハンドリングの充実
    負荷やジョブとの戦いに突入します。

Kaizen はチャットの会社じゃないので、自分たちがチャットの機能を作ることはどんな意味があるのか、ということを考えながら機能追加していきたいですね。

なお Kaizen Chat は Kaizen Platform にアカウントをお持ちの方でしたらどなた様でも無料でご利用いただけますのでもしよかったら触ってみてください。僕にファンレターを送ることも出来ます。

また Kaizen Platform, Inc. は本日( 2016-09-08 )から開催されている RubyKaigi 2016 にブースを出しておりますので CTO (リクルート時代に chouseisan 作ってた)やエンジニアと話してみたい人、 Kaizen Platform ロゴ入り手ぬぐいが欲しい人、僕のアイコンのステッカーが欲しい人はお気軽にお越しください。

| @技術/プログラミング

九州新幹線

関わっているサイトの Rails のバージョンが 3.2.20 から 4.1.8 に上がった。自分は割と傍観していて他の人が主にバージョンアップしてたんだけど、いくつかはまりポイントがあって自分も Pull Request 送ったりしたのでやったことを書いときます。

1. session に注意

Rails 4 から Flash メッセージ(ログインしましたとか)を格納する session のオブジェクトが普通の Hash になってる。 Rails 3 ではこれは FlashHash とかいうやつ。

Rails 3 から Rails 4 へのアップグレードで一旦 Rails 4 を出してやっぱりやめて Rails 3 に戻したりとか、ロードバランサーに Rails 3 と Rails 4 で動くサーバーを混ぜてリクエスト捌いたりするとまずいことになる。

Rails 4 のサーバーで session 出来た人が次にリクエストしたときに Rails 3 に当たるとログイン後とかに session に残っているメッセージを消そうとする処理とかで NoMethodError が発生して落ちてしまう。しかもたちが悪いことに Rack 層で死んでしまったりするから皆さんよく使ってると思われる ExceptionNotification とかで気づくことが出来ない。これはつらい。

対処法としては Hash クラスをオープンしてモンキーパッチするというのがある。こういうの。

↑のだと #alert とか #notice が呼ばれたときにエラーになるので自分は以下のようにした。

# NOTE Rails 4 と Rails 3 を混ぜて使うと Hash#sweep が見つからなくてエラーに
# なるようなのでモンキーパッチします。
# 参照: http://jasonneylon.wordpress.com/2014/08/27/rails-4-flashhash-upgrade-gotcha/

class Hash
  def now
    Rails.logger.warn "Stubbing now during upgrade"
    {}
  end

  def keep
    # stub keep for upgrade purposes
    Rails.logger.warn "Stubbing keep during upgrade"
  end

  def sweep
    # stub sweep for upgrade purposes
    Rails.logger.warn "Stubbing sweep during upgrade"
  end

  def alert
    Rails.logger.warn "Stubbing alert during upgrade"
    self[:alert]
  end

  def notice
    Rails.logger.warn "Stubbing notice during upgrade"
    self[:notice]
  end
end

ただこれもパーフェクトではなくて、何もしないように上書きしているだけなのでログイン後のメッセージとか削除後のメッセージが消せなくなったりする。それでも 500 エラーになるよりかはましなのでどうしても Rails 3 と Rails 4 を混ぜて投入したいみたいときなんかは有効。

2. 絵文字に注意

Rails 3 の頃は ActiveRecord が絵文字を DB に保存することが基本的になかった。ユーザーが POST してきたフォームの中に絵文字が含まれてたら絵文字のところでテキストをぶった切って DB に保存するような挙動だった。しかし Rails 4 からは ActiveRecord は絵文字を素通りさせるようになってしまったので困ったことになる。

絵文字を DB に保存するためには、 MySQL の場合は DB のテキストエンコーディングを utf8mb4 というやつにしてないといけない。ただの utf8 だと保存時に Mysql2::Error: Incorrect string value というエラーが出て DB に保存できない。emojimmy のような gem を使えば utf8mb4 でない DB でも使えるけど、 stores_emoji_characters :column_name を忘れずにモデルに定義しないといけない。たとえば購入時に購入した製品のスナップショットを注文テーブルに取るような DB 設計だと、製品テーブルのカラムは stores_emoji_characters してたとしても注文テーブルのカラムを stores_emoji_characters し忘れていて死亡、というような悲劇が起こり得る。

いまはスマートフォンの時代で、ユーザーが入力してくるフィールドには必ず絵文字が含まれると思っておいた方がいい。スマートフォンをメインで使ってる人たちは開発者が想定しないようなフィールド(名前の敬称とか)に平気で絵文字を使ってくる。下手すりゃ住所や名前にも絵文字を入れて送ってくるかも知れない。アスキー文字しか受け付けないようなフィールドは JavaScript やサーバーサイドでバリデーション行ってると思うけど、マルチバイト文字列を受け付けるフィールドの場合はせいぜい長さくらいしかチェックしてないと思う。チェックを入れて絵文字を弾くことも可能だけど、スマートフォンの時代の流れに反しているしユーザーを失うことになりそう。これから新規でサービスを作ってデータベースに MySQL を使う場合はエンコーディングは utf8mb4 にしておいた方がいい。

他にも script/rails が bin/rails に変わってること忘れてて rails runner なバッチ処理が動いてなかったとか、 paranoia.gem の Rails 4 対応バージョンで物理削除のときに呼び出すメソッド名が変わっててはまったとかいろいろあったけど大きなところは上の session と絵文字だった。開発環境で使ってるときには気がつかず本番に出すまで気がつきにくいという意味で非常にやっかいな現象だと思う。

これから Rails 4 に上げる皆さんは頑張ってください。応援しています。

| @労働

この記事は 闇アドベントカレンダー、 22 日目の記事です。何書こうか迷って担当日に書けなかったので三日ほど遅れてしまったけど書きます。


2011 年の 10 月から FANIC という音楽配信サービスの開発に携わっていたのだけど、サービスを成長させることができず、 2013 年の 8 月にサービス終了した。

サービスが死ぬのは技術者がクソだということだけではないと思う。市場とか外部環境に左右されるし、企画とか売り方がダメなことの方が多いと思う。しかし現実に自分はプログラマーとして FANIC というサービスの死に荷担してしまった。弔いになるか分からないけど、 FANIC で何がよくて何が良くなかったのかを書いてみたいと思う。

FANIC とは

FANIC は主にアマチュアのミュージシャンをターゲットにしたホームページ作成&音楽販売サービスで、アーティストは自分の公式ホームページを簡単に作ることができ、楽曲をアップロードして試聴公開したり、リスナーに販売することが出来た。月額利用料は 315 円で、曲が売れたときに手数料 15.75 % が従量課金される仕組みだった。 2012 年の 1 月に公開して、 2013 年の 8 月 31 日にサービス終了した。

FANIC に携わることになった経緯

ペ社に入社したのは 2 年前の 10 月だった。前の職場がどうしようもないブラック企業だったことは前に書いた。

会社の開発環境が地獄のレガシー環境だったので面談で社長に Subversion やめて Git 使いたいと言ったら、「会社が気に入らないならさっさと辞めろ」と言われたので、何とか反省して心を入れ替えたふりをしながら職探しをしていたところ、 Rails 、 Node.js 、 Redis 、 MongoDB 、 Nginx 、 AWS みたいな福岡の求人にしては珍しくナウいキーワードで募集していたのがペ社の Dazaifu Project だった。

プロジェクトの紹介ページが何となく面白そうだったのと Rails で仕事できそうだったので応募した。小さなチーム、大きな仕事を読んだり、RubyKaigi 2010 に行ったこともあって、DHH や RubyKaigi で登壇していた人達みたいにとにかく Ruby で仕事したいという思いが強かった。前の職場でも Ruby を使いたかったけど ColdFusion や Flex など旬を終えたプロプライエタリなテクノロジーばかりを触らないといけなくてとにかくつらく、わらをもすがる思いで求人応募フォームを送信した。

8 月に応募して 9 月に内定が出て 1 ヶ月後にペ社に入社した。入社するまで配属先を知らなかったんだけど、 Dazaifu Project の物販系と音楽系の二つのサービスのうち音楽系の方に配属されることになった。それが FANIC だった。

FANIC で良かったこと

一つのプロジェクトに集中できる

受託の会社とかだと受託案件があって、案件ごとにメンバーがアサインされるから同時並行的に二つか三つを掛け持ちで担当するということがあるけど、自社サービスの会社では基本的に一つのサービス(案件)にかかりっきりで仕事をすることができた。これがとても良かった。午前中に A 社の対応をして午後から B 社、夕方からまた A 社のタスクに取りかかって 20 時から終電まで C 社対応、みたいな複数の仕事の切り換えがない。タスクの切り換えにはオーバーヘッドが発生するから、一つのことにかかりきりになれる自社サービスの仕事はとてもやりやすかった。加えてプログラミング言語はずっとやりたいと希望していた Ruby だったので言うことなしだった。会社に行くのが楽しかった。

ナウでヤングなアーキテクチャー

求人に上に書いたようなナウいキーワードを混ぜていたのは先輩の @linyows さんで、FANIC はインフラは AWS を利用し、データベースに MongoDB 、音楽や画像は S3 に保存して、WAV や AIFF でアップロードされた音楽ファイルを試聴用の MP3 に変換するバッチ処理や、画像を S3 から取り出すリアルタイムリサイズサーバーは Node.js で実装してあった。フロントエンドのアプリケーションは Rails で構築されていて、自分は主に顧客管理とか決済部分なんかを開発した。

ペ社では通常、インフラを担当する専任のチームがサーバーの面倒を見る。しかし Dazaifu Project はスモールスタートを掲げていたので、インフラも @linyows さんが一人で構築していた。今自分が配属されているサービスは結構でかくて、サーバー管理はインフラチームが担当し基本的に自分たちで設定を変えたりサーバーを構築したりすることはない。ちょっと Nginx の設定を書き換えるのにも申請書出してハンコリレーしたりしないといけない。おかげで凡ミスで 500 エラーみたいなことにはなりにくい仕組みになってるんだけど、リリースのスピードを上げにくい。そういう状況からすると、 FANIC でミドルウェアのバージョンを上げるのなんかも部署間の調整が必要なくさくっと出来たのはとても良かった。加えてデータベースが MongoDB だったため、データ構造の変更が柔軟に出来たのも良かった。

社内でいち早く CoffeeScript 使ってみたほか、ペアプログラミングもやってみた。かなり疲れるけど集中して取り組めるし、プログラマー間で仕様を共有できるのでこれは良いものだと思った。 FANIC のおかげでずいぶん成長させてもらったと思っている。

FANIC でつらかったこと

FANIC ですべてが良い方向に向かっていたかと言えば、決してそうではなかった。技術的にもプロジェクト運営的にも、苦しい部分が多々あった。

MongoDB で金の計算

技術的にはまず MongoDB で金の計算をするのがつらかった。

決済部分の開発で MongoDB の Embedded Document でかなり苦しめられた。契約 Collection の中に Embedded Document として請求と入金があるという構造だったけど、契約日と入金日が月をまたいでいて、一つの契約 Document 内にある Embedded Document をそれぞれ別の月の入金と請求とする必要があり、かなり苦しい感じだった。前受金の概念とかも Document 指向のデータベースで対処するのは地獄的な感じがした。Document の中に何でもスポスポーと Embedded Document を突っ込んでいけるのは便利なんだけど、親 Document と Embeded Documet を別の文脈で使おうとするときに困難が生じると思った。

MongoDB を大規模に利用している CA 社も JOIN ができないので金関係だけは MySQL でやっているという話を聞いたことがある。金の計算をするときに JOIN の代わりに Map Reduce する必要があったけど、自分が開発に利用していた頃の Mongoid (MongoDB 用の ActiveRecord 的なやつ)は Map Reduce に対応しておらず、 Rails のモデル内に Map Reduce 用のクエリ(MongoDB のクエリは JavaScript で書かないといけないので Rails のモデル内にヒアドキュメントで JS が書いてあるという地獄っぽい感じになる)を書いたりした。Map Reduce しないのであれば二重 Loop でグルグル処理を回さなければならず、毎度これを Ruby にやらせるのはパフォーマンス的にしんどかった。総じて MongoDB は大量のデータを集計したりするのには向いてないかなーという印象を持った。それぞれを独立したドキュメントとして利用する機会が多いタイプのアプリケーションには向いてる気がする。設定情報の保存とかブログ記事の保存とか。金系のデータが沢山入っていてそれを月ごとに集計してどうのこうのとかやるときがつらい。

Mongoid のため便利 gem が使えない

Rails エコシステムの中で MongoDB は少数派だから、様々な便利 gem が ActiveRecord に依存していて使えないことがあったのも困った。たとえば ActiveAdmin が ActiveRecord に依存しているために利用することができず、顧客管理を一から実装しなければならなかった。なければ作る or Pull Request 出して取り込ませる、というようなマッチョマンじゃないと Rails でレールから外れるのは厳しいと感じた。

リーンスタートアップできていなかった

プロジェクト運営的には、チームの目標とか優先順序の付け方とかがハッキリせず、行き当たりばったりな開発・運用になっていたのがつらかった。

2012 年の春、『リーンスタートアップ』が流行ってた。みんな本買って読んでて、社内で antipop さんによる講義とかもあった。でもリーンスタートアップは本当に難しくて、誰でもリーンスタートアップ読めば新規事業で成功できるわけではないと思う。サービスの企画立案者が有能なだけでは不十分で、起業家に加えて、極めて優れたエンジニアが付帯していないと厳しいと思う。自分たちに必要だったのはリーンスタートアップを読むことではなかった気がする。全然そのレベルに到達していないという感じだった。

思い込み・お問い合わせベース開発

リーンスタートアップには、仮説の検証をものすごい速度で行ってフィードバックループを回さないといけないと書いてあるけど、そもそも自分たちには仮説の検証方法が存在していなかった。本に書いてあるとおりに様々な仮説を素早く検証していくには、企画担当者がやりたいと思ったことを一週間くらいで実装して次々に仮説を検証していかないといけない。A/B テストをやるにしても、A/B テストをやるための仕組み作りが大変だと思う。オープンソースで使えるものに Cookpad の Chanko とかあるけど、 MongoDB がネックになって利用できなかった。自分たちでそういう仕組みを作るにはかなり多くの時間を作らなければならなかったと思う。結局思いつきやお客さんからのお問い合わせベースで開発する機能が決定されて実装されていった。お問い合わせが結構あったから 1 ヶ月以上かけて実装してみたのに全く使われない機能とか、これは絶対に行けるはずと自分が思い込みで提案して実装したのに全く使われない機能とかあって、全然良くなかった。

ちなみに↓のスライドではてなブログのリーンな開発風景が紹介されてるけど、情報収集のところで紹介されてる手法を真似しようとするとかなり大変だと思う。雑魚いエンジニアしかいないとここまでやるのは難しい。

技術的に正しいことをしようとこだわりすぎて中途半端になっていた

入社した頃、テストを書くという習慣がプロジェクトになかった。プロジェクトに加入した初期の頃から自分はなるべくテストを書こうとしていて、テストを書く習慣を定着させたつもりだったけど、これも良かったのか悪かったのか分からない。テストを書くとどうしても時間がかかってしまう。 TDD BootCamp Fukuoka Vol.2 できしだなおきさんが言っていたんだけど、テストコードがあるとプロダクトコードのメンテナンスに加えてテストコードのメンテナンスも必要になる。そうすると俊敏に動くということが難しくなる。自分はテストを書くことに時間をかけすぎてしまってサービス開発のスピードを鈍らせていたような気がする。

テストを書いたりリファクタリングをしたりしないのは正しい行いではないと思うけど、会社に雇われて給料をもらっている以上、サービスを成長させることがエンジニア云々以前にサラリーマンとして大事だと思う(『情熱プログラマー』とかにも書いてある)。特にプロジェクトの最初の段階では、技術的な理想を追求するよりも、不完全でも良いのではやく製品を投入して一つでも多くの仮説を検証して学びを得ることの方が重要なのではないかと思った(Minimum Viable Product)。

自分のプロダクト感を持てていなかった

サービスに対して、「自分たちのプロダクトだ」という感覚が希薄だったと思う。そもそも hitode909 さんのスライドみたいにドッグフード食べてなかった。自分の FANIC 上のページは検証用に作ったインターネット上のゴミみたいなページだった。

みんなビラ配りとかオフィス外での宣伝活動とかに消極的だったし、このプロジェクトがぽしゃったら失職する、みたいな危機感が希薄だった。自分は机に座ってプログラミングできればそれでいいみたいな感じがあった。口ばっかりで手が動いてなかった感がある。

地元の野外フェスとかに行ってビラ配るとかオフラインでの宣伝が必要だったのかもと思う。終わるって決まったとき、やりきった感がなかった。これでうまくいかないなら仕方がない、と思えるところまでやれてなかった。

エンジニアであってもプロジェクトの当事者意識を強く持たなければならないのだと思う。いまいくら自分たちが稼いでいて、あといくら稼ぐと黒字になるのかとか、どのくらいのスピードで成長していかなければならないのかとか。

チームの雰囲気が良くなかった

一番の苦しかったのはこれだと思う。

いま思い返してみると、 1 年半の間でチームのメンバーが揃って食事をしたのは 1 回くらいしかなかった。お互いへの理解が欠如していたような気がする。雰囲気悪かったからサービスが崩壊したのか、サービスがうまくいっていないからギスギスした感じになったのかは分からない。ただ、雰囲気の良くないチームが作る製品が良いものになることは基本的にないと思う。

FANIC を離れたあとも、会社を挙げて永和システムマネジメント社の講習を受けてスクラムに取り組んだりしてる。しかし結局どんなプラクティスを導入しようとも、チーム内でコミュニケーションが取れていないと技術的に見過ごすことの出来ない問題に気づけず非常に大きな手戻りが発生したり、仕様上の大どんでん返しがやってきたりする。技術的に足りない部分があったとしても、コミュニケーションが機能していたらカバーできたのではないかと思う。

結論

結論としてはチームで寿司を食べたりするしかないと思う。銀しゃりのまばゆい輝きが、うにやいくらの神々しい光が、鋭利な刃物のように光るサバのにぎりが、闇を照らしてくれる。

上にぎり 1.5 人前


この記事は闇 Advent Calendar 2013 - Adventar 22 日目の記事でした。 23 日目は hurutoriya さんで、今日の担当は @udzura さんです。

| @技術/プログラミング

公開している Lokka の DB (MySQL) のデータを、手もとの Lokka の DB (SQLite) に取り込むときに毎回やり方を忘れるので書いておきます。

今回は MySQL to Sqlite converter という gist のコメント欄に書いてある以下の方法を試した。

sudo gem install sequel
sudo gem install sqlite3
sudo gem install mysql
rbenv rehash
sequel mysql://user:password@host/database -C sqlite://db.sqlite

これでデータを MySQL から SQLite に変換できた。Lokka をローカルで起動してアクセスしてみるが記事が表示されない。DB の中にはちゃんとデータが入ってるし、管理画面から記事一覧を表示するとちゃんと記事が表示される。どうも Post.published[] を返すみたい。

手でクエリを投げてみると

sqlite> select * from entries order by created_at desc limit 1;
             id = 960
        user_id = 1
    category_id = 5
           slug = eye-pro-is-shit
          title = Eye-Fi Pro を買ったけどクソだった
           body = [hitode909さんの日記](http://hitode909.hatenablog.com/entry/2013/05/24/093749 "hitode909の日記")...
           type = Post
          draft = 0
     created_at = 2013-10-06 07:28:00.000000
     updated_at = 2013-10-06 13:11:23.000000
frozen_tag_list =
         markup = redcarpet

draft = 0 なので表示されそうなもんなんだけど、よくよく調べてみると SQLite には Boolean 型がなくて、DataMapper は SQLite に Boolean 型のデータを保存するときは t/f の文字列で保存するみたい。これは盲点だった。

結局 draft = 0draft = 'f' に変えてあげればすべての記事を表示できるようになった。

| @技術/プログラミング

Lokka がすごく遅い

EC2 の micro で Lokka を運用してたけど、Unicorn が CPU 100% 近く使う状態が続き、リクエスト送ってレスポンスが返ってくるまでに 30 秒近くかかる状態になってて、AWS での運用を諦めざるを得なかった。さくら VPS に戻したところ、CPU 使用率もロードアベレージも落ち着いた。しかしレスポンスは遅くて、HTML が返ってくるまでに 5 秒くらいかかってる。

原因調べた

Newrelic を入れて調べてみた。Application が一番遅い。MySQL も遅いけど気になるレベルではないみたいだった。

  • EC2 に Application
  • さくら VPS に MySQL

という構成で運用していたのが遅い原因だったかも。App も DB も同じサーバーに置いたら Newrelic 上で Database が遅いと表示されなくなった。つまり Application をどうにかするしかない。

やろうとしてること

Lokka で動いててもそんなに遅くないページもある。komagata さんのブログは遅くない(heroku に置いてあるっぽいので heroku 側でキャッシュとかいろいろやってあるのもあると思う)。自分のブログに関してはテンプレートで最近の過去数ヶ月の月ごとの記事数表示したりタグクラウド出したりしてるところが遅そう。なので重い処理のところをフラグメントキャッシュしたい。

試したこと

sinatra-cache

導入できて動いた。勝手にページキャッシュする。フラグメントキャッシュできるけど、キャッシュのキーが URL のディレクトリベースのため、効率が悪い。

padrino-cache

導入できなかった。Padrino::Routing に依存してるっぽくて素の Sinatra で使いづらい。

落っこちてた gist (Simple fragment caching in sinatra)

これも Sinatra が前提。view から使うフラグメントキャッシュ専用ヘルパーメソッド。なんか Sinatra が内部的に使ってるインスタンス変数を上書きするというやり方みたい そのままでは Lokka で使えず <- イマココ。

最後のやつが一番導入に近いところまで来てるっぽいけど、断片の部分だけ haml のコードを実行させて結果を取得させる、というところがなかなか難しい。Rails の ActionController::Caching のコードを見て参考にしようとしてみたけどちょっとよく分からなかった。

実は最近、仕事で Ruby 書かないおじさんになってしまったので週末に Ruby のコード見てもすんなり頭に入ってこない。ダメだなぁ。