タケユー・ウェブ日報

Ruby on Rails や Flutter といったWeb・モバイルアプリ技術を武器にお客様のビジネス立ち上げを支援する、タケユー・ウェブ株式会社の技術ブログです。

Amazon Auroraメモ

クライアント様が使いたいということで調べたことメモ。

aws.amazon.com

費用について

  • Auroraは最低プランでもメモリ15GBぐらいのプランになるので、現在開発サーバで使っているm3.mediumに比べると3倍ほど。
  • 同じr3.largeだと2割ほど高いです。

MySQL Single-AZ

db.m3.medium $0.120/h \10,368/month

db.r3.large $0.285/h \24,624/month

MySQL Multi-AZ

db.m3.medium $0.240/h \20,736/month

db.r3.large $0.570/h \49,248/month

Aurora

db.r3.large $0.35/h \30,240/month

Aurora + リードレプリカ1個

db.r3.large $0.70/h \60,480/month

※Auroraは障害時にリードレプリカのうち1つがマスタになるのでこれでMulti-AZと同じフェイルオーバー運用が可能

Auroraのよいところ

速度はまだ正確にはわからないので、速い、というのを信用することにして、運用面のメリットが大きそうです。

これは少人数のチームでは特に大きなメリットになりそうです。

リードレプリカなしでも障害に強い

  • Auroraは常に三箇所のDCで6重のバックアップがあり信頼性が高い
  • 落ちても自動復旧(ただし最大15分のダウンタイムは発生する模様)

リードレプリカが強力

  • MySQLのリードレプリカと違い、遅延がほぼない
  • リードレプリカによるマスタの速度への影響は小さい。Multi-AZは同期レプリケーションのため速度の低下があった。
  • リードレプリカは平常時は読み出し専用DBとして使うことでDB負荷を分散できて高速化が可能
  • フェイルオーバー。マスターの障害時にレプリカがマスタに昇格することで、Multi-AZと同様にすぐに復旧。データ損失なし。

https://aws.amazon.com/jp/rds/aurora/faqs/#high-availability-and-replication

データ容量が事実上無制限

  • RDS MySQLだと容量の変更は時間がかかるしMulti-AZじゃないと長時間のダウンタイムが発生
  • Aurora データ量に応じて自動で拡張、しかも最大64TB
  • 大量のデータのためにテーブルを分割するシャーディングというテクニックがあるが実装が大変、Auroraはこれを不要にする(という触れ込み

RDS MySQLからの移行がかんたん

MySQLのスナップショットから移行できたりします。

Auroraの残念なところ

  • ローカルの開発環境で同じものを使えない
  • staging環境は本番適用前の最終確認の意味あいがあるので、本番と同様の構成にしたいものの、最低料金が高め
  • VPCが必須。VPC以外から使うならVPCDNS解決とDNSホスト名の設定を「はい」にした上で、Auroraのpublicアクセスを許可にする必要がある。セキュリティグループの設定もゆるめる。

アプリケーションからの接続

その他参考リンク

【AWS】Aurora に絵文字『utf8mb4』をINSERTできるか試してみた | Developers.IO

スナップショットを使用してRDSをMySQLからAuroraへ移行する | Developers.IO

Amazon Aurora/MariaDB/MySQLの違いを整理してみる #reinvent | Developers.IO

Amazon RDS for Aurora の費用について « サーバーワークス エンジニアブログ

オーロラは雲の上 — RDBのScalabilityとAvailability | Developers.IO

Amazon Aurora で使用する VPC を作成する方法 - Amazon Relational Database Service

少額減価償却資産の特例 freeeでの記帳メモ

毎回どうやったっけな?ってなるのでメモ。

前提

29万円のノートパソコンを購入した。

仕訳

購入日

貸方 工具器具備品 29万円 / 借方 現金 29万円

固定資産

[決算] > [固定資産台帳] から登録

取得金額 29万円

勘定科目 工具器具備品

償却方法 少額償却

クラウド会計ソフトfreeeレビュー:親切すぎ? UIが変わった「freee」で確定申告書を作成してみた (3/4) - ITmedia エンタープライズ

reeeでは、「固定資産の登録」画面で償却方法を「少額償却」とすることで、上記の特例に沿った形での登録が可能だ。この方法を選択すると最終的に固定資産台帳の摘要に「措法28の2」と表示される。

Action Cable + Flux + React でリアルタイムチャットデモを作ってみた

成果物

デモ(Heroku) http://fluxchat.takeyu-web.com/

ソースコード takeyuweb/rails-fluxchat-example · GitHub

キーワード

  • Rails 4.2
  • ActionCable
  • ES6
  • React
  • Flux (Alt)

Action Cable?

まだコード全部は読んでないのですが、WebSocketを使ったリアルタイム通信を手軽に実装するためのサーバ・クライアントフレームワーク実装だと感じました。(違ってたらご指摘下さい!)

Railsらしく規約に従ってサーバ・クライアント側で所定のクラスを作っておけば、実際の接続処理や接続の監視・再接続、受け取ったデータのハンドリングなど細かいことはActionCableが良い感じにしてくれるっぽい。

なおAction CableのサーバはこれまでのWebサーバ(rails sなやつとか)とは別に立ち上げる必要があります。(もちろんポートも空けます)

# cable/config.ru
require ::File.expand_path('../../config/environment',  __FILE__)
Rails.application.eager_load!

require 'action_cable/process/logging'

run ActionCable.server

で28080番ポートでWebSocketサーバを起動(※ポートは決まってないのでなんでもいいです。ここでは公式のサンプルに合わせました)

$ bundle exec puma -p 28080 cable/config.ru

HTTPサーバはもちろん別

$ bundle exec rails s

なおRails5の新機能ではありますがRails4でも使えます。

gem 'rails', '4.2.4'
gem 'actioncable', github: 'rails/actioncable'
gem 'puma'

ポイント

FluxとWebScoket(ActionCable)

Fluxについてまだ勉強不足なので理解違いがあるかもしれませんが、図の「Web API Utils」にあてはめて実装してみました。

https://raw.githubusercontent.com/facebook/flux/master/docs/img/flux-diagram-white-background.png

facebook/flux · GitHub

送信

View (React Component) -> Action Creator -> Channel (ActionCable Subscription)

受信

Channel (ActionCable Subscription) -> Action Creator -> Dispatcher -> Store -> View (React Component)

Action Cable

認証

WebSocketではCookieが使えるので、署名済みCookieを使うのが楽です。

Action Cableで送信するデータ

公式のサンプルアプリではRails5の新しいRendererでアクション外で部分テンプレートを処理しHTMLを送りつける感じですが、Action Cable自体はなんでも送れるので、クライアント側でそれにあわせた処理をすれば良いです。

今回のサンプルでは単に送り側(Ruby/Rails)でモデルインスタンス.to_jsonして、受け取り側(JavaScript/ActionCable.Subscription)でJSON.parseしてみました。

コントローラやバックグラウンド処理からクライアントに通知を送る

ActionCable.server.broadcastであとはよしなに。時間がかかることもあるので、Active Jobと組み合わせて非同期で実行するのがベター。

class MessageRelayJob < ApplicationJob
  def perform(message)
    if message.to.present?
      # 宛先が指定されている場合はそこと自分に送る
      ActionCable.server.broadcast "chat_activity:#{message.uuid}", message.to_json
      ActionCable.server.broadcast "chat_activity:#{message.to}", message.to_json
    else
      # そうでなければ全体へ
      ActionCable.server.broadcast 'chat_activity', message.to_json
    end
  end
end

実際には全体へのブロードキャストは宛先が死ぬほど増えそうなので、チャットであればチャットルームIDなどで絞り込む必要がありそう。

楽しい

そんな感じでとりあえずやってみたAction CableとFluxですがなかなか楽しいです。

実運用ではコネクション張りっぱなしによるリソースの消費がどんな具合かとか、負荷テストはどうやるの?とか、サーバは何使う?とか、経路上のたとえばELBがどうなっているかとか、Android標準ブラウザの対応が4.4以降で割と非対応端末多そうな問題とかいろいろ考えることはありますが、ぜひ使っていきたいと思っています。

気になるセキュリティ

未調査。

セキュリティ回りで参考になりそうな記事メモ。

IPA セキュア・プログラミング講座:Webアプリケーション編

Webアプリ開発者のためのHTML5セキュリティ入門

Herokuで作るWebSocketアプリケーション

WebSocket通信のメリットを考える - フレクトのHeroku Lab

近年のJavaScriptプログラミングでは処理全体をdocument#readyのクロージャで括るのが一般的ですが、その場合仮にXSS脆弱性があったとしても外部から接続済みのWebSocketインスタンスにアクセスすることは不可能です。

DHHのサンプルや今回のデモチャットではwindow.App.cable.connection.webSocketでWebSocketインスタンスがとれてしまうので、そのあたり取り回しを考えた方がよいかも。

OpsWorks で bundle install の without オプションを指定して不要なgemがインストールされないようにする

デフォルトで--without development testが指定されているようになっていますが、僕の場合他にいくつか環境を作っていたので、その対応として。

OpsWorks customize.rbでbuilt-in cookbooksの設定値を変更するど同様にして

deploy/attributes/customize.rb

normal[:opsworks][:rails][:ignore_bundler_groups] = ['test', 'development', 'newenv']

のように設定する。

僕の場合springがリリースディレクトリごとにたくさん起動してメモリ足んなくなって発覚しました。

Aws::S3::Clientでリージョンをまたぐコピーをするときは、コピー先のリージョンを明示してやる。

たとえばダウンロード販売のコンテンツを置く場合など

  • 長期間ホストするファイルの容量が大きい
  • 頻繁なリージョン間転送(EC2など)は発生せず、インターネットへの転送がメイン
  • そこまでダウンロード速度を要求しない

こんなとき、S3バケットを価格の安いリージョンに作るのがよいです。

今回、東京リージョンのS3にアップロードされたオブジェクトの一部をシンガポールリージョンにコピーする必要があったのでそのメモです。

サンプルコード

確認環境

リージョンをまたがないとき

s3 = Aws::S3::Client.new
resp = s3.copy_object(
    copy_source: ['src-bucket', 'path/to/src'].join('/'),
    bucket: 'dst-bucket',
    key: 'path/to/dest')

リージョンをまたぐとき

Aws::S3::Client.newのコンストラクタで、コピー先のリージョンを明示してやらないとAWS::S3::Errors::PermanentRedirectが発生します。

コピー元のリージョンではだめです。

s3 = Aws::S3::Client.new('ap-southeast-1') # シンガポールリージョン
resp = s3.copy_object(
    copy_source: ['src-bucket', 'path/to/src'].join('/'),
    bucket: 'dst-bucket',
    key: 'path/to/dest')