tail my trail

作るのも使うのも、結局は、人なのだ

iPhone 6Sが出たのでiPhone6 + MVNO に乗り換えてみた

iPhone 6S / 6S plus が2015年9月25日に発売されました。 2年前にSoftbankで購入したiPhone 5Sの2年分割払 + 月月割 が10月で切れるので、次をどうするか悩んだのですが、タイトルのとおり、このタイミングでiPhone 6 + MVNO への切替を試してみました。 試算上、端末代含めた今後2年間の出費を半額程度に減らすことができました。

  • 以下、10月時点の情報です。
  • 当ブログの情報は個人的に調べて試算したものなので間違いがあるかもしれません。これによる一斉の責任は負いかねます。
  • もし数値や認識に間違いなど見つかったらご指摘頂けますと幸いです。

iPhone 6 + MVNOにした背景

今までSoftbankの機種変のレールにそのまま乗っかり、2年分割払いで購入して月々割引 & 旧端末下取のパターンでiPhone 4S, 5Sと使ってきました。 Facetime オーディオやLINE電話などを使うことが多いので通話料はあまり取られないのですが、機種の分割払いも含めて月々7,000円前後かかっていました。年間 84,000円。 こうしてみると、結構かかってる。 今年は子どもも生まれ、色々と支出も増える年になったので、出来るだけ余計な出費は抑えたい。 仮にこの固定費を半額程度に抑えられたら、ルンバでも買って時間捻出できるなと思ったのがきっかけでした。

iPhone 6Sのアップデート内容を見ると、1200万/500万画素のカメラ、4Kビデオ、3D touchなどそこそこアップデートはありましたが、会社で検証端末は手に入るので、私物として確保したいと強く思えるような、ビビッと来るものがなかったので、現状キープか型落ちのiPhone 6を調達するで良いかと思い始めました。

また、ここ1〜2年で急激に盛り上がりを魅せているMVNOも試してみたい思いがあったので、まずはフラットに検討してみようと思ったのが背景です。

料金比較

ということで、支出がどの程度になるか、幾つかのパターンでシミュレーションしてみました。

前提

自分の生活と用途を考えて、以下の制約をおきました。

月々の通信量3GB以内

自宅や職場にいる時はWifiにつないでいるので、月々の通信量は2GB程度に収まっています。 なので、3GB程度のプランで十分そうでした。

音声通話

Facetime オーディオやLINE電話などを使うことが多いですが、緊急時などいざというときに音声電話はほしいです。また、電話番号変わると各調整が非常に手間なので、キャリア変えるにしてもMNPして電話番号はキープしておきたいです。

iPhoneを購入する場合ストレージは16GB

今までは "大は小を兼ねる" という言葉に従い、面倒な容量切り詰め作にかかる時間を買う目的で64GBを使ってきました。ただ、この4年間の運用で不要なアプリやデータが貯まり散らかる一方だし、写真などのデータは端末に入れておく理由は全くなく、共有フォトストリームやGoogleフォトなどクラウドサービスに放り込んでおいたほうが利便性は高いので、仮に機種を返るなら不要物を根こそぎ消す前提で16GBを候補にしました。 あと、自分は手が大きい方ではなく画面が大きいと辛いので、Plusではなく小さい方を候補に選びました。

MVNO使う場合、購入するiPhone6docomo

SIMフリー版でもいいんですが、値段が大きく変わってくるので。10月時点で値段に3〜4万程度の開きがありました。 MVNOの多くはdocomo SIMなので、選択肢の多いほうが望ましいと考えました。

Apple iPhone 6 16GB シルバー 【docomo 白ロム】MG482J

Apple iPhone 6 16GB シルバー 【docomo 白ロム】MG482J

候補

ということで、上がった候補が以下。

利用中のキャリアが、"実質0円で機種変できるキャンペーン" をやっていたので、比較のため候補にあげてみました。 docomo乗換に関しては、代理店がキャッシュバックキャンペーンをやっていたりするので、もう少し調べれば大手キャリアに分のある選択肢も出てくるのかもしれません。10月時点では、iPhone6も台数限定で一括0円キャンペーン + キャッシュバックなどやっていたようです。 ただ、複雑怪奇な契約体型から脱したい思いもあったし、キャッシュバックやっている店舗を調べるのも足を運ぶのも時間がかかり過ぎる印象があり、"台数限定" に焦るのもバカバカしかったので、そういったものは対象外としました。

MVNO業者は、どこもプランが横並びなので、メジャーなIIJmio (みおふぉん) をサンプルにおきました。

  1. 現状維持: iPhone 5S (64GB) + Softbank (ホワイトプラン + パケットし放題フラット for 4G LTE)
  2. 機種/プラン変更: iPhone 6S (16GB) + Softbank (スマ放題ライト + データ定額パック5GB)
  3. docomoMNP: iPhone 6S (16GB) + docomo (カケホーダイライトプラン + データMパック5GB)
  4. MVNOMNP: iPhone 6S (16GB) + MVNO (3GB程度通信 + 音声通話)
  5. MVNOMNP: iPhone 6 (16GB) + MVNO (3GB程度通信 + 音声通話)

見積もってみると

あくまで試算ですが、こうしてみると結構な開きが出ました。

型落ちのiPhone6で、MVNOで運用するとさすがに安いです。新しい機種を買うにも関わらず、現状stayと比べると先2年の総出費が倍以上変わります。

面白いのが、Softbankで機種変をしてもしなくてもTotalの出費はほとんど変わらないという点です。

1.現状維持 2.機種変更 3. docomoMNP 4. MVNOMNP 5. MVNOMNP
音声通話基本料 ¥1,008 ¥1,836 ¥1,836 ¥756 ¥756
データ通信基本料 ¥6,156 ¥5,400 ¥5,400 ¥972 ¥972
インターネット接続サービス ¥324 ¥324 ¥324 ¥0 ¥0
端末代 24ヶ月分割 ¥0 ¥3,900 ¥3,888 (※1) ¥3,906 (※1) ¥2,354
機種変サポート(24ヶ月間) ¥0 -¥2,835 -¥3,456 ¥0 ¥0
MNP特典 (※2) -¥450 ¥0 ¥0
特典 (12ヶ月間) ¥0 ¥0 -¥1,350 ¥0 ¥0
下取り ¥0 -¥1,065 (※3) -¥625 (※3) -¥625 (※3) -¥625 (※3) -¥625
1年目 月々支払額 ¥7,488 ¥7,560 ¥5,567 ¥5,009 ¥3,329
2年目 月々支払額 ¥7,488 ¥7,560 ¥6,917 ¥5,009 ¥3,329
2年間 総支払額 ¥179,712 ¥181,440 ¥149,808 ¥120,216 ¥79,900

ということで、10月中旬〜11月中旬の2年契約更新期間 (違約金が発生しない1ヶ月) のタイミングで、プラン5の 型落ちiPhone6 + MVNO に切り替えることにしました。

iPhone調達とMVNO切替

iPhoneAmazonで購入しました。 今はさらに値下がりしているようですが、私が購入した時は上記見積もりの¥56,500で購入することが出来ました。 赤シム引いてしまうと後で悲しい思いをするので白ロム保証を明記しているところを選ぶと良いと思います。 iPhoneは注文して2日後に届きました。

MVNOはいくつか比較検討した結果、IIJmioのみおふぉんを選びました。理由は通信実績です。 MVNOは競争が激しく、特に新興はキャンペーンを打ち出して料金も割引されることがあります。 ユーザが少ないうちは通信も快適でメリットが多いのですが、ユーザが増えた途端速度が落ちたなんてこともあるようです。 あくまで10月までの実績ベースですが、IIJmioは比較的通信が安定しており、ユーザ増加にあわせて増強対応も進められているようでした。

参考にしたのはこちらのサイトです。

格安SIM(MVNO)の速度比較 - androidlover.net

ちなみに、解約違反金は音声通話を入れている場合は¥0ではないですが、月々その額は減っていき、1年経つとそれ以降は違約金はかからなくなるそうです。

IIJmioAmazonで購入しました。こちらは注文して翌日にMNP用の書類が届きました。

IIJ IIJmio SIM 音声通話 パック みおふぉん IM-B043

IIJ IIJmio SIM 音声通話 パック みおふぉん IM-B043

MNPの場合は書類のみ届きます。キャンペーンコードが入ってるので、IIJmioのWebサイトで申請を進めると、数日後にSIMカードが届きます。

なお、手続き前にキャリアからMNP予約番号を取得しておくこと。softbankMNP予約番号はMySoftbankから申請できず、店頭か電話での申し込みになります。

SIMカードが届いたら開通手続きを行えば、1時間程度で新しいSIMが使えるようになります。 全て自宅で行えるのでありがたいですね。

揃ったものがこちら。 SIMは当然docomo SIMでした。 f:id:uorat:20151115172528j:plain f:id:uorat:20151115172520j:plain

64GB→16GBへの移行とiPhone5S売却

データの整理は思った以上に大変でした。

4SでiPhoneを使い出して以来、ずっと64GBで運用してきたので不要なアプリや写真、聞かない音楽が貯まり散らかりまくっていたので。 16GBといえども、iOSメタデータが乗ってくるので、実際に使える領域は10GB程度と予め割りきっておくと良いです。 私は音楽の取捨選択に最も苦労しました。

移行が無事完了したら、あとは旧端末iPhone5Sの売却です。 私は周囲の買取評判が比較的良かったゲオのSmarketを利用しました。

スマートフォンの買取ならゲオのSmarket | iPhone・Androidなど高価買取

申し込みをすると、数日後段ボール箱が届くので、手順通り初期化とアクティベートを行ってから箱に収納し送り返します。 数日後査定結果がメールで送られてくるので、同意すればその額が銀行口座に振り込まれます。 この時のために2年前iPhoneの箱は捨てずにとっておいたので、傷はありながらも条件は少しは良くなるのではと期待。結果、私は¥17,500程度で買い取ってもらえました。 iPhone6の購入価格が ¥56,500 だったので、実質手に入れたので、実質 ¥39,000 程度で手に入れたことになります。

移行してみて

もうすぐ一ヶ月程度経ちますが、不自由なく快適に過ごしています。

通信状況はちゃんと計測していないですが、私の生活範囲では体感むしろ向上した気がします。 ランニングコストも大幅に削減できたし、複雑怪奇な料金プラン/契約から脱することもできました。

節約バンザイ。自由バンザイ。

AWS CLI のprofileを簡単に切り替える

意外と知らない人がちらほらいたので、書き留めておく。

AWSAPIとIAMについて

基本的にAWSの全てのサービス / リソースの操作はAPIによって行われます。 (Management Consoleの操作も内部的には全て同じAPIアクセス) S3への画像のuploadから、EC2 Instanceの増設/設定変更から、CloudWatchの参照、Support Caseの起票まで、あらゆる操作をAPIで行うことができるので、 単純なWeb Applicationだけでなく、オペレーション自動化など多種多様な用途で活用することになり、IAM User/Access Keyもあわせて複数用意することが多いと思います。

環境やサービスによってAWSのアカウント自体を分ける運用も多いと思いますが、そうなると扱うKeyの数は益々増えていきます。

AWS CLI

何かAWSのリソースを使ってものづくりをする際に、デバッグなどで AWS CLI を使うことは多いと思います。 なんたってワンライナーで手軽にAPI叩けますからね。 蛇足ですが、S3 のオブジェクト一覧見る時なんか、Management Console見るよりも、AWS CLI叩くことのほうが私は多いです。そのほうが速い。

本題

だいぶ引き伸ばしましたが、本題です。 幾つものUser, Keyを併用して使っている時に、ユーザを切り替えて権限確認、動作確認したい場合の方法です。 至ってシンプル。 --profile オプションを使いましょう。 以下、Helpより引用。

$ aws help
      
      ( ...omitted ...)

       --profile (string)

       Use a specific profile from your credential file.

       ...


$ aws configure help

      ( ...omitted ...)

SYNOPSIS
          aws configure [--profile profile-name]

      ...

aws configure コマンドで --profile *profile-name* を付与して設定を進めると、指定した名前で別の設定を加えることができます。 default (無名) は消えず別の内容で登録され、他サブコマンド実行時に同様に --profile *profile-name* オプションを付与するだけでアカウントを切り替えできます。

以下設定例。

$ aws configure --profile bob
AWS Access Key ID [AKIA************hoge]:
AWS Secret Access Key [****************hOgE]:
Default region name [ap-northeast-1]:
Default output format [None]:

設定内容は $HOME/.aws/config, credentials ファイルに INI形式で保存されます。 中身を見ると以下のように、default (無名) はそのままに、上のprofile-name で指定した情報が追記されています。

$ cat .aws/config
[profile default]
region = ap-northeast-1

[profile bob]
region = ap-northeast-1

$ cat .aws/credentials
[default]
aws_access_key_id = AKIA************DMKA
aws_secret_access_key = ************************************hoge

[bob]
aws_access_key_id = AKIA************hoge
aws_secret_access_key = ************************************hOgE

切替を試してみましょう。 defaultはrootアカウントのKey, S3の任意のbucketのみ権限があるようなIAM Userをbobとしてprofile登録しています。

# root権限でbucket一覧確認
$ aws s3 ls
2015-10-15 19:28:40 fuga
2015-10-15 19:29:17 hogehoge

# bobユーザでfuga bucketの中身をls
$ aws s3 ls s3://fuga/ --profile bob
2015-10-15 19:44:10      14152 test.png

# hogehoge bucketにアクセス (権限なし)
$ aws s3 ls s3://hogehoge --profile bob

A client error (AccessDenied) occurred when calling the ListObjects operation: Access Denied

終わりに

jq コマンド、補完入力設定と同じくらい必須のTIPsだと思います。 環境変数にcredential書く手もありますが、その場合profileオプションで切り替えられず少々不便になるので、こちらのほうがオススメです。

AWS CLIは鬼のようにできることが多いのと、たまにManagement Consoleではできない操作なんかもあったりするので、 移動中などでちまちま暇つぶしがてらリファレンス呼んでみると面白いです。 aws — AWS CLI 1.8.12 documentation

Ansible入門&ハンズオン資料を公開しました

少し時間が経ってしまいましたが、 先日Ansible入門というイベント でAnsibleの説明とハンズオンをする機会を頂きました。 資料は SlideShare に公開しているので、これからAnsible始めたいという方はご参考にしていただければ幸いです。 資料の後半にハンズオン用の題材があり、Playbookの例はGithubに公開しています。

概要

Ansibleに興味がある未経験者〜使い始めた or 別の構成管理は使ったことがあるくらいのAnsible初心者をターゲットとし、受講後自走できるようにという思いで作りました 資料自体は、「構成管理とは」「なぜやるのか」という導入から始まり、Ansibleの世界観や登場人物をざっくりと説明しています。 読めばキーワードがざっと入って脳内インデックスができ、一時間半程度のハンズオンを経ればある程度勘所はつかめるようになるので、あとは公式ドキュメントなど見ながら自主学習してね、というスタンスの資料です。 ハンズオンは計3つです。

  1. 基礎編
    1. yumモジュールでパッケージインストール
    2. templateモジュールでバナーファイル作成
    3. yumモジュールでNginxインストール、serviceモジュールでNginx自動起動 (serviceモジュール実行時のdry-run時のエラーは無視する)
    4. copyモジュールで静的ページを配置
  2. adhoc編
    • ansibleコマンドでadhocにshellモジュールを実行する
  3. 応用編
    1. shellモジュールを活用してEC2 instanceにスワップ領域を追加する
      • register使って冪等性ちゃんと担保すること
      • スワップファイル作成は fallocateコマンドで
      • mkswap, swaponはもちろん、自動マウントも忘れずに
    2. yumモジュールでパッケージインストール
      • epelリポジトリ指定
      • rpmパッケージ指定 (これも冪等性ちゃんと担保するように注意)
    3. デプロイ
      • gitモジュールでデプロイ
      • Nginxの設定ファイルをtemplateモジュールで生成
      • デプロイないしは設定ファイルに変更があったらnotify使ってでNginxを再起動する

※ "冪等性ちゃんと担保する": 要するに、dry-run モード ansible-playbook --check で実行してchangedにならないようにする。

Playbookの基礎を抑えている内容となっていると思うので、大体の勘所がつかめ、すぐに業務自動化に励めると思います。 Ansibleが素晴らしいのは、エージェントレスでアーキテクチャも記法もシンプルなので、季節の環境を汚すことなくスモールスタートでねじ込める点です。 さくっと運用便利ツールや監視エージェントをばらまく、脆弱性対応するくらいのカジュアルな使い方から運転できるのは大きいですね。 私は去年まで1年ちょっとChefに使っていましたが、Ansibleのこのシンプルさはとても好きです。

SlideShare

ハンズオンの回答例

uorat/ansible-handson · GitHub

感想

当日は台風の影響もあり、申し込み数ほどに参加者は集まらなかったのですが、参加頂いた方は皆熱心に臨んでくださったので救われましたw "収穫が多かった、タメになった" という感想を幾つか頂きました。 小さいイベントですが、せっかく来て頂いた方には何かを持ち帰ってアクションに繋げられるようにしたいと思ったことと、この資料も今回に閉じず社内勉強会などで使いまわしたいという思いがあり、それなりに考えてコンテンツ作ったので、こういう声は純粋に嬉しいですし、今後の励みになります。 ご協力頂いた皆さま、ありがとうございました。

なお、ハンズオンのgit module使ってデプロイするというお題の中で、 @yteraoka さんの ansible-tutorial - Github を参照させていただきました。

以下のgithub.ioですね。ServerspecでCI回すところまで含まれていて素晴らしいTutorialです。 是非こちらも参考になさって下さい。

Ansible チュートリアル | Ansible Tutorial in Japanese

あと、この本もおすすめです。

入門Ansible

入門Ansible

naoyaさんのChef入門と同じくらいのボリューム感ですね。

入門Chef Solo - Infrastructure as Code

入門Chef Solo - Infrastructure as Code

Socket.io with Websocket の SSL/TLS 対応

※ (2016/9/19 追記) Nginx 使った対応方法も記載しているので、あわせて参考にして下さい。


昨今のサービスにおいて、暗号化はもはや必須の流れである。 GoogleFacebookなど主要なサービスはずいぶん前からHTTPS通信を標準としているし、HTTPS化対応しているサイトはSEO的にも優遇されるようになる という方針が出ていたりする。

前記事でSocket.IO + Redis PubSubを用いたリアルタイムメッセージ配信の仕組みをまとめたが、このままWebSocketを利用すると当然インターネット上を平文のテキストが流れてしまう。 また、チャット機能を呼び出す親元のWebページがHTTPSで提供されているものであれば、Mixed Content でブラウザによっては暗号化されていないWebSocket通信をブロックされることもあるだろう。 後からSSL/TLS対応を入れると往々にしてハマるので、ハナから対応しておくに越したことはない。

ということで、Socket.IOを用いたWebSocket通信をSSL/TLS対応させる。 以下、プログラム側での対応方法を記したが、NginxをWebSocket Proxyとして利用 できるので、NginxでSSL Terminationさせる手もある。

ポイント

f:id:uorat:20150830185442p:plain

  • ブラウザによってはWebSocket通信が利用できないことがあるため、Socket.IOでは、クライアントに適した通信プロトコルを自動選択する仕組みが実装されている。
  • セッションを張り続けて通信を行うWebSocketを使う以上、ボトルネックになりがちなロードバランサーを介することは推奨できない。
    • ロードバランサは最初の接続確立のみ利用し、WebSocket通信はクライアントからサーバに対して直刺しさせる

例えばAWSで運用している場合、SSL TerminationをELBに任せることが多いと思うが、WebSocket通信をELBを介さず行うため、サーバサイド (Socket.js on Node.js) で暗号化させる必要がある。

実装

サーバサイド

サーバに事前に配置したサーバ証明書、中間CA証明書、秘密鍵のセットをロードさせて起動することで、SSL対応が可能。 この対応で、Listenしたポートでhttps, wss通信が可能となる。

var fs = reqquire('fs');
var io = require('socket.io').listen(3000, {
    key : fs.readFileSync('/etc/pki/tls/private/your.domain.com.key').toString(),
    cert: fs.readFileSync('/etc/pki/tls/certs/your.domain.com.crt').toString(),
    ca: fs.readFileSync('/etc/pki/tls/certs/your.domain.com.cer').toString(),
    'log level':1
});

当然、証明書のドメインとWebSocket接続時にクライアントから指定するドメインは揃える必要がある。 ワイルドカードで証明書を作成していれば、例えば hostname.domain.com で接続させれば良い。

DNSにホスト名で名前解決できるようにA recordを登録しておく必要あり

クライアントサイド

以下のとおり、https で指定する。 "wss"ではない。 前に述べたとおり、handshake時にどのプロトコル (WebSocket / xhr-polling) で対話するかを採択するが、その通信は httpないしはhttpsで行われる。 handshakeが成功すると、wss または xhr-poolling over TLS で接続確立される。

<script src="https://hostname.domain.com:3000/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('https://hostname.domain.com:3000');

    ... your logic ...
</script>

ソース (※ 2016/5/15 追)

※ローカルのメモを転記しておく。

Socket.IOのdocumentaionにはSSL/TLS対応に関する記載が見当たらなかったが、ソース読んでみると明解。

Socket.IO

Socket.IO のコードを読んでみると、 `require('socket.io').listen した時に options.key があれば https#CreateServer 呼びだすロジックになっている。 ちょっと古いが、socket.io (v0.9.13) /lib/socket.io.js l63 - l66 抜粋

if (options && options.key)
  server = require('https').createServer(options);
else
  server = require('http').createServer();

Node.js - https.createServer

Official Documentationにあるとおり、 https.createServer の options は tls.createServer と同様で、以下のフィールドでSSL/TLS通信に必要なフィールドを指定できる。

https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener

終わりに

以上、Socket.IOでのWebSocket通信のSSL対応の一例としてプログラム側での対応方法を記したが、NginxをWebSocket Proxyとして利用 できるので、NginxでSSL Terminationさせるという方法もある。 手っ取り早く試すなら上記でも良いが、既にNginxを投入済みのサイトであればNginx WebSocket Proxyを用いても良いかも。

※ (2016/9/19 追記) Nginx 使った対応方法も記載しているので、あわせて参考にして下さい。

実践Node.js プログラミング (Programmer's SELECTION)

実践Node.js プログラミング (Programmer's SELECTION)

Socket.io + Redis PubSubでリアルタイムメッセージ配信

とあるサービスに「チャット機能」を追加しようという話になり、急ピッチで仕組みを用意することになった。 仕様/要件はふわっとしているものの、 2週間後にはリリースというケツは決まっている。 はてさてどうしたものかとその瞬間は思ったものだが、無事仕組みとして載せられたので、備忘記しておく。

リアルタイムチャット機能

要件は以下。

  • クライアントはWebブラウザとネイティブアプリ (iOS, Android)
  • 視聴者に軽量なテキストメッセージをbroadcastする
  • メッセージの永続化必須
  • 可用性/負荷分散も考慮する

例えば、動画を視聴しているとして、その動画の横に、自分含む視聴者のコメントが流れてくる、 ようなものを想像してもらえれば良い。

Socket.IO

「クライアント - サーバ間のリアルタイムなメッセージのやり取り」ということで、

という方針は早々に決まった。自動的にサーバは Node.js に決定。 ブラウザからのアクセスがあるため、xhr-polling / WebSocket をSocket.IO が両方サポートしていることは有難かったし、WebSocket通信を数行で始められる手軽さも魅力的だった。 ケツが2週間後なので実装コストがなるべく少なく済み、ビジネスロジックに集中できる方法を採択する必要があったため。

Socket.IOのポイントをまとめると、

  • WebSocket, xhr-polling をサポート、クライアントの動作環境に応じていずれかを自動採択する
  • "1 : 1" や "1 : N" のようなBasicな特定双方向通信、一斉配信 (broadcast)、接続単位を論理的に分離する 名前空間/namespace や 部屋/room 機能 など様々な配信方法をサポート

インストールは npm で一発 。 実装イメージは以下。

server side

var io = require('socket.io').listen(3000);
io.sockets.on('connection', function (socket) {
  
  ... your logic ...
  
});

client side

<script src="http://your.domain.com:3000/socket.io/socket.io.js"></script>
<script>
  var socket = io('http://your.domain.com:3000');
  socket.on('connect', function() {

    ... your logic ...

  });

  ... and so on...
</script>

ネイティブアプリ用のクライアントライブラリもある。 Socket.IOのGithub を見たところ、 C++ の実装もあるようだ。

Redis PubSub

Socket.IO on Node.js なWeb/Appサーバに対して、各クライアントはWebSocket通信で接続する。 接続数増と耐障害性を担保するためWeb/Appサーバの冗長化を考えた時に、一斉配信するメッセージを複数台のサーバにどのように伝達させるかで一瞬悩んだ。 所謂Publish / Subscribe ... ということで思い出したのがRedis。 "危険なほどのスピード" で有名なオンメモリKey-Value データストアで、扱えるデータ構造がMemcached と比べて豊富、ディスクへの永続化もあり人気のKVSだが、このRedis、PubSub も提供している。

1. 購読者 (subscriber) 、任意のkeyをsubscribeする

$ redis-cli
127.0.0.1:6379> SUBSCRIBE hoge
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "hoge"
3) (integer) 1

2. (別のターミナルで) 出版者 (publisher) 、1で指定したkeyでメッセージをpublishする

$ redis-cli
127.0.0.1:6379> PUBLISH hoge "hello pubsub"
(integer) 1

3. 購読者 (subscriber) の端末に、2で配信されたメッセージが表示される

$ redis-cli
127.0.0.1:6379> SUBSCRIBE hoge
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "hoge"
3) (integer) 1
1) "message"       ←追加
2) "hoge"          ←追加
3) "hello pubsub"  ←追加

Socket.IO - Redis の連携は容易。Socket.IOのバージョンによりインストール方法が若干変わる点に注意。

  • Socket.IO v0.9 系まで
    • Socket.IO 内に同梱されているため、特に追加インストールは必要なし。
    • 呼出は require('redis')
  • Socket.IO v1.0 以降
    • 外部ライブラリとして切り離された socket.io-redis をインストール
    • 呼出は require('socket.io-redis')

ちなみに、AWSのElasticCacheはRedisをサポートしており、Read Replica & Multi-AZも対応している。

PubSubはReplica Nodeに対しても有効で、PubSub sessionに関しても負荷対策が可能。

  1. Node.jsからReplica Nodeに対してSubscribe sessionを張っておく
  2. コメント投稿はMaster Nodeに対してPublish
  3. Read Replicaに対してSubscribeしている全購読者に配信される

WebSocketとELB

Socket.IO on Node.js サーバを冗長化させた場合の振り分け方も一工夫必要だった。 通常のHTTP通信と異なるため。注意点は以下の2点。

  • Cient - Server間の通信は通常のHTTP通信と異なりWebSocketで接続持続される
  • Socket.ioによるhandshake処理は必ず同一サーバに接続させなければならない

例えば、ロードバランサーを上段に挟む場合、全てのセッションがロードバランサーに対して張られることとなる。 通常のHTTPと異なりWebSocketのセッションを張り続けるという特性上、接続を集約させるロードバランサーボトルネックになる危険がある。 また、ロードバランサーによっては、Socket.IO/WebSocketの間に立てない場合がある。例えば、AWSのELB。

AWSのSolution Architectも、ELBは最初のセッション確立時にのみ利用してdispatchしてWebSocketさせるのが良いと言及している。

教えに従い、接続情報を返すAPIをクライアント側で呼出してから、サーバに対して直接WebSocket通信させる方式とした。

システム構成

行き着いたシステム構成は以下。

主機能がApache / PHPで動くWebアプリケーションのため、APIは既存資産を流用して実装。 Apahce/PHP を Node.js/Socket.IOと同梱させAPIを提供するようにした。 同梱させた理由は、接続情報管理の実装を省き自分のホスト名を返すようにしたかったため。 本来は、APIサーバは切り離して、ステータス含めて接続情報を管理するようにしたほうが望ましいと思う。

f:id:uorat:20150830185442p:plain

次回は、WebSocket接続のSSL/TLS対応をまとめようと思う。

Redis入門 インメモリKVSによる高速データ管理

Redis入門 インメモリKVSによる高速データ管理

Amazon Web Services クラウドデザインパターン設計ガイド 改訂版

Amazon Web Services クラウドデザインパターン設計ガイド 改訂版

Amazon Web Services パターン別構築・運用ガイド

Amazon Web Services パターン別構築・運用ガイド