PROBLEM

  • Elixirをさわりはじめてしばらく経つけどふかく理解した気になれない
  • Phoenixやほかのフレームワークに頼られないケースが出てきたとき自由な発想ができるようになっておきたい
  • 巷でいわれているSLA 99.9999999% などの実際がどうなのか腹落ちしてない

-

SOLUTION

というわけで、LYSE本を読むことにした。Elixirに関係ありそうな箇所を選定している。

  • 今回は分散システム Erlang Port Mapper Daemon (EPMD) についてかんがえる。今回の章がSLAにもっとも関係しているといえそうである。ほかの言語・フレームワークが「分散コンピューティングの落とし穴」「CAP定理」にどのように対応している比較すると、Erlangの特徴がより見えてくるだろう。

分散システムEPMDの特徴

前提

  • 分散システムによるFault toleranceについて
    • ソフトウェアの稼働状況と対ハードウェア障害リスク
      • マシン1台
        • リスク対策できない
      • マシン複数台
        • アプリケーションが正しく構築されない場合、リスク対策できない
  • 「分散コンピューティングの落とし穴」へのErlangの対応
    • ネットワークは信頼できる
      • Erlangの対応
        • 非同期通信モード(リンクやモニタ)により、メッセージを送信に正常な場合に必ず返信するように設計
        • ただし、ノード間でリンクやモニタを張った際にネットワーク障害起きた場合、リンクやモニタが一斉にトリガーされシステムに予期しない負荷をかけることになる
    • レイテンシはゼロである
      • Erlangの対応
        • タイムアウト、リンク、モニタ、非同期パターンにより遅延を想定し設計
    • 帯域幅は無限である
      • Erlangの対応
        • 大きなメッセージを送らない
    • ネットワークはセキュアである
      • Erlangの対応
        • Erlangはネットワークの安全性を確認しないため
          • 異なるデータセンター間で自動的にクラスタ化しない
          • あるいは、SSLに切り替える
          • 安全なチャンネル越しにトンネルする
          • ノード間の通信プロトコルを再実装する
    • ネットワーク構成は変化せず一定である
      • Erlangの対応
        • アプリケーションでネットワーク構成(トポロジー)を管理しない
    • 管理者は1人である
      • Erlangの対応
        • デバッグツールによる個別障害対応
        • ノード監視ツールによるシステム運用状況の共有
        • 実装プロトコルやAPIのバージョン管理
    • 転送コストはゼロである
      • Erlangの対応
        • Erlangはほかのノードに渡されるメッセージを圧縮しないため
          • 送るメッセージを小さくする
          • あるいは、独自の通信レイヤを実装する
    • ネットワークは均質である
      • Erlangの対応
        • Erlangノードと同じプロトコル形式にして通信
          • Cノード
          • BERT
          • BERT-RPC
  • 障害(ノードの応答不能)への対応
    • 下記の中から原因を特定するが確実には対応できない
      • ハードウェア障害
      • アプリケーションクラッシュ
      • ネットワーク分断
        • 輻輳
        • 遮断
    • ゾンビ化するということ
      • ネットワーク分断が起きている間アプリケーションが生きていた場合
        • 当該ノードで保持していたデータがクラスタ間で保持していたデータと整合性がとれず、欠損扱いになる(一貫性の欠如)
        • 当該ノードからレスポンスが返ってこないため生きているか死んでいるかわからない(可用性の欠如)
    • CAP(Consistency, Availability, Partition Tolerance)定理
      • ノード間において、同時に下記3つの要素を保証することはできない
        • 一貫性 Consistency
          • すべてのデータ読み込みにおいて、最新の書き込みデータもしくはエラーのどちらかを受け取れること
        • 可用性 Availability
          • システム要求に応答できること(SPOFがない)
        • 分断耐性 Partition tolerance
          • ネットワーク分断時でもシステムを継続して運用できること
      • 組合せ、採用条件、採用ケース
        • CA
          • 採用条件
            • ネットワークが絶対に落ちない場合
            • ネットワークが1つの塊として動作している場合
            • データの読み書きが多い場合
          • 採用ケース
            • RDBMS
            • NFS
        • AP
          • 採用条件
            • ネットワーク分断が起きやすい場合
            • SPOFがない場合
            • ネットワーク分断時データ変更不可だと問題がある場合
            • 結果整合性を適用可能な場合
              • 時間ベース
              • 個別にコンフリクト解消
              • 全ノードによる合意
                • 合意割合による調整
                • 問い合わせ対象による調整
          • 採用ケース
            • Amazon SimpleDB
            • Apache Cassandra
            • DNS
            • HTTPキャッシュ
        • CP
          • 採用条件
            • ネットワーク分断が起きやすい場合
            • SPOFがある場合
            • ネットワーク分断時データ変更不可でも問題ない場合
          • 採用ケース
            • Mnesia
            • Apache HBase

Erlangが提供する道具

  • ノードとEPMD
    • 特徴
      • ノードとはErlang VMのインスタンスのこと
      • 各ノードはEPMDに接続されている
        • 新しいノードは自動的にErlangクラスタに接続され、各ノードに接続される
      • 接続されているノードでも完全に独立している
        • 各ノードが固有に保持しているもの
          • プロセスレジストリ
          • ETSテーブル
          • 読み込んだモジュール
      • EPMDはErlangクラスタの一部として、各コンピューター上で稼働する
      • EPMDは名前サーバとして機能する
    • Pros
      • Fault tolerance
    • Cons
      • 1ノードにつき1エフェメラルポートが必要になるため、Scalingに制限がある
        • 対処として、ノードのグループを小さなクラスタに分割
  • シリアライズ、デシリアライズ
  • マルチプロセス
  • ネットワークの障害監視

Erlangクラスタを設定する

  • ノードの名前解決
    • 長い名前
      • aaa.bbb.cccのような完全修飾ドメイン名
      • DNSリゾルバによって解決
      • erl -name LongName
    • 短い名前
      • ピリオドがないホスト名
      • ホストファイルやDNSエントリによって解決
      • erl -sname ShortName
    • 注意点
      • 1台のコンピューター上でErlangノードを設定する際は通常短い名前をつかう
      • 長い名前と短い名前、双方でのやり取りはできない
  • 関数
    • net_kernel:connect_node/1 - ノード接続
    • ノード名参照
      • node/0 - 現在のノード名を参照
      • nodes/0 - 接続ノード参照
    • リンクとモニタ
      • link - ノードをまたいだリンク
      • erlang:monitor/2 - ノードをまたいだモニタ(ネットワーク分断時に一斉に活性化し負荷が増加する可能性あり)
      • erlang:monitor_node/2 - ノードを指定したモニタ
    • 遠隔操作
      • spawn/2
      • spawn/4
      • spawn_link/2
      • spawn_link/4
  • クラスタのUIDトークンとしてのCookie
    • 関数
      • erl -sname ShortName -setcookie CookieName
      • erlang:get_cookei/0
      • erlang:set_cookei/2
  • 隠しノード
    • クラスタを介してノード接続すると、クラスタ間で予期しないメッセージ通信がおこなわれる可能性があるため、それを防止する策としてクラスタを介さず接続可能な隠しノード機能がある
    • 関数
      • erlang:send(Dest, Message, [noconnect]) - クラスタを介さずにノードに接続
      • erl -sname ShortName -hidden - クラスタを介さずに隠しノードに接続
      • nodes(hidden) - 隠しノードを参照
  • ポート範囲指定
    • EPMDのポート番号は 4369
    • 設定方法
      • erl -name LongName -kernel inet_dist_listen_min 9100 -kernel inet_dist_listen_max 9115
      • erl -name LongName -config ports
        • ports.config: [{kernel, [{inet_dist_listen_min, 9100}, {inet_dist_listen_max, 9115}]}].
  • 分散用モジュール
    • net_kernel
      • start([Name, Type, HeartbeatInMilliseconds]) - インスタンスを一時的にノード化
      • set_net_ticktime(Milliseconds) - Ticktime(ハートビートを4倍した時間、ノードの死亡判定時間)を設定変更
    • global - プロセスレジストリの代替
      • register_name(Name, Pid) - gloabl登録し名前変更
      • unregister_name(Name, Pid) - globalから名前解除
      • re_register_name(Name, Pid) - 参照先の喪失を防ぎながらglobal登録し名前変更
      • whereis_name(Name) - PID探索
      • send(Name, Message) - メッセージ送信
      • random_exit_name/3 - ランダムにプロセスをkillする
      • random_notify_name/3 - 2つのプロセスのうちいかすプロセスをランダムに1つ選び、globalから解除されるプロセスに {global_name_conflict, Name} というメッセージを送信
      • notify_all_name/3 - 指定したPIDプロセスをglobalから解除し、 {global_name_conflict, Name, OtherPid} というメッセージを送信、コンフリクト解消を促す
    • rpc
      • call(Node, Module, Function, Args[, Timout])
      • async_call(Node, Mod, Fun, Args[, Timout])
      • yield(AsyncPid)
      • nb_yield(AsyncPid[, Timeout]) - ポーリング、ロングポーリング
      • cast(Node, Mod, Fun, Args) - 返値なしのコール
      • multicall(Nodes, Mod, Fun, Args) - 複数ノードへのコール
      • eval_everywhere(Nodes, Mod, Fun, Args) - 複数ノードへの命令(コールとほぼ同じ)

-

以上 :droplet: