"Wakame"で始めるクラウドコントロール

第2回クラウド上でサーバ構成を管理するための考え方と仕掛け

構成管理ミドルウェアはシステム管理者となる

複数サーバを管理するシステム管理の経験をお持ちの方は、構成管理のためのツールを日常的に利用しているかと思います。自作の物である場合もあるでしょうし、商用・無償を含む多くのパッケージもすでに存在しています。構成管理のツールとして単純な例は、makeとsshを組み合わせたものがあげられると思います。サーバのホスト名のリストを用意しておき、それらに対してssh接続し、必要なコマンドを順番に走らせることができれば十分に作業の効率化に貢献します。

前回Wakameがターゲットとしているクラウド型ホスティングでは、APIによるサーバ・仮想マシンの管理ができることで作業の自動化可能な範囲が飛躍的に広がったということを説明しました。

これまで比較的変化が少なかったサーバ構成に、ダイナミズムが加わりました。そのため、新たな課題が表面化することになります。

例えばAmazon EC2で自動化を行おうとした場合、課題が大きく2つあります。

1.作業の同期化・非同期化をコントロールしなければならない
  • 複数の仮想マシンに対する並列的な処理と、手続きが重要な部分では直列的な処理ができること
  • Amazon EC2のWeb APIは受動的な呼び出ししかなく、処理も非同期で行われるため、適宜Web APIにアクセスして確認できること
2.アプリケーションの設定値を動的に変更しなければならない
  • 仮想マシンへのアクセスに必要な情報(IPアドレス・ホスト名など)が動的に決まるものに対応できること

これらの点を考慮しつつとなれば、先に述べたmakeとsshの組み合わせだけで実現したいとは思わないでしょう。少なくともダイナミックな構成管理というものには、最低限必要とされる汎用的な処理がありそうです。

Wakameは、これらの従来のホスティング環境には無かった課題を乗り越えるためのフレームワークを整備し、手順を自動化することを目指しています。言わば、あたかも「システム管理者」として機能するのが理想です。

クラウド型ホスティング上での構成管理に求められるもの

Amazon EC2ではWeb APIへアクセスし、仮想マシンの調達を行います。

この時、仮想マシンが使える状態になるまでの時間にはばらつきが出ることもしばしばです。5分以内で完了する場合もあれば、運悪くAmazonのデータセンタ内の負荷が高い場合10分以上かかってしまう場合もあります。起動しようとするマシンイメージファイルのサイズにも相関があるようです。

ここで重要なのは、処理が非同期で実行されているために、いつ終わるか分からないということです。

処理の同期化・非同期化のコントロール

Amazon EC2のWeb APIには仮想マシンの状態を取得するものが用意されています。しかし現時点のWeb APIでは受動的な呼び出ししかサポートされておらず、状態の変化を能動的に通知してくる仕組みはありません。

そのため、処理が終わったことを受けて次の手順に移るためには、都度Web APIを通じて状態の変化を監視し、仮想マシンの状態が起動完了を示すまで待つというような工夫が必要になります。

実際には、さらにそこからOSが起動するのでsshなどのアプリケーションレベルのアクセスが可能になるにはさらに時間がかかります。Web APIを通じた状態の監視の他に、アプリケーションレベルの通信が可能な状態であるかも監視することになります。

この例から分かるように、クラウド型ホスティングの管理では処理の同期化と非同期化が要求されます。同期的な処理を実行していくモデルだけの構成管理ツールでは手順を自動化しづらいケースが存在することが分かっていただけると思います。

並列処理による効率化と、直列処理による確実化

Amazon EC2上ではWeb APIの呼び出しだけで仮想マシンを数十台?数百台確保し、大規模にサービスを開始することも可能です。

複数台のマシンでサービスを実現する場合、Webサーバやデータベースサーバ、バッチサーバなどと処理や役割に応じて小グループ化することが多いと思われます。

そして、役割を持ったグループ間には依存関係があり、立ち上げや終了の作業には順番があります。アプリケーションサーバが動くためにはデータベースサーバが必要ですし、ロードバランサが動作するためにはアプリケーションサーバの数とIPアドレスが決まっていないといけないのです。

図1のような構成の場合、ロードバランサの設定の完了までの面倒を見ようとすると、下の段に属するアプリケーションサーバや静的コンテンツ用のWebサーバのIPアドレスが決定されないことには、ロードバランサの起動が先に完了したとしてもリクエストの振り分けの設定まで終わらせることができません。

図1 LB-Web-APPサーバの構成図
図1 LB-Web-APPサーバの構成図

Amazon EC2では、仮想マシンに対するIPアドレスが動的に割り当てられます。IPアドレスが割り当てられるタイミングは、仮想マシンの調達を要求してすぐではなく、実際にOSが起動できる状態になってからです。したがって、IPアドレスを知ろうとすると、その仮想マシンがきちんと起動してくるまで待つ必要があります。

サーバ群の起動・サービスの開始までを自動化しようとするケースを想定してみましょう。仮想マシンの台数が大量にある場合、作業手順が同じサーバに対しては並列に接続し、同じコマンドの呼び出しを行うことで全体の作業時間を短縮することができます。一度に並列処理をする中に、先ほどのIPアドレスを知るというような、直列処理が求められるのです。

設定の変更と反映

さて、ここまでで作業の並列処理と直列処理を織り交ぜて、手順として効率的かつ確実に実行する必要があることが把握できたかと思います。

次は、そうした手順を実行していく中で、各サーバの設定を変更する必要が出てきます。仮想マシンが追加されると、その仮想マシン上で動くサーバを利用するように隣接するサーバの設定を変更する必要があります。つまり、サーバの構成図から隣接するサーバに対して、設定ファイルを動的に書き換え、その設定ファイルを読み直すようサーバに通知しなければなりません。動的にサーバ台数を変化させられる様サーバ群を構成するためには、台数が変化しゆく部分の設定内容を更新するだけでなく、その機能に依存する部分がある場合、別のサーバで稼働中のサービスに対してもきちんと反映できるよう手順を踏む必要があります。

次のページでは、これら直列・並列的な処理、サーバ間をまたぐ設定内容の変更を可能にするWakameの概要についてお話しします。

Wakameのアーキテクチャ概要

Wakameは、クラウド型ホスティングの構成管理ツールとしての従来のやり方に加えて、以下の機能をフレームワークとして提供できるようRubyForgeにて開発が続けられています。

  • 非同期型ワークフローのサポート
  • 作業の並列化

本節ではそうした機能を踏まえ、Wakameの概要を見ていくことにします。

Master-Agent構成を取る

Wakameは、MasterとAgentを構成要素に持ち1対Nの構成になります。内部の通信プロトコルには、AMQPという標準化されたメッセージキューのプロトコルを利用しています。

実際の通信トポロジとしては、RabbitMQというAMQP実装を中心としたスター形を取っており、Master、Agentそれぞれが必要に応じてAMQP上のチャンネルを開き相互に通信を行っています。

Agentは各仮想マシンに1つずつ配置されます。こうした構成は米国Engine Yard社からリリースされているnaniteやVertebraのデザインを参考にしています。

図2 RabbitMQでつながるMasterとAgent
図2 RabbitMQでつながるMasterとAgent

Agentの役割は、立ち上がったノード上でMasterから受けた指令の処理とノードの監視を行うことです。

受け付ける指令は以下のようなものになります。

  • Apacheなどのデーモン用スタートアップスクリプトを呼び出せ
  • 監視を開始せよ、終了せよ

Masterの役割は、全てのAgentの把握、手順のAgentへの指示、起動すべきサービスの指定、サービス全体に関わる状態の管理など多岐に渡ります。

Amazon EC2に特化した部分ですと、新しい仮想マシンを立ち上げるためのWeb APIへのアクセスなどもMasterの役割となっています。Masterは管理用コマンド(wakameadm)を通じてユーザからの命令も随時受け付け、その命令を元にサービスの開始終了処理を行うことができます。

補足:
現在のバージョンにはMasterに待機系を持たせる機能がありませんが、将来のバージョンでサポートする予定です。

サービスの定義を記述する

システムの設計者は、Masterに対してサービスの構成図を伝える必要があります。そして、Masterはその構成図を元にサービス全体の管理を行います。

現在のバージョンでは、Rubyのソースコードとして内容を定義するようになっています。

図3 Resource、Cluster、Instanceの相関関係
図3 Resource、Cluster、Instanceの相関関係

Resource

Resourceとは、Apache, MySQLなどの単体のサービスの定義をするためのデータ構造です。定義内容には、起動・終了方法を含む処理手順も含まれます。

リスト1静的コンテンツを配信するApacheプロセスを起動・終了させる定義クラスの例/p>

class Apache_WWW < Wakame::Service::Resource

  # Apacheの起動処理
  def start(svc, action)
    cond = ConditionalWait.new { |cond|
      cond.wait_event(Wakame::Event::ServiceOnline) { |event|
        event.instance_id == svc.instance_id
      }
    }
# PidFileを元にしたプロセスモニタリングの開始をAgentに指示
    request = action.actor_request(svc.agent.agent_id,
                                   '/service_monitor/register', svc.instance_id, :pidfile, '/var/run/apache2-www.pid').request
    # 起動スクリプトの呼び出しをAgentに指示
    request = action.actor_request(svc.agent.agent_id,
                                   '/daemon/start', "apache_www", 'init.d/apache2-www').request

    # Event::ServiceOnlineがやってくるまで待つ
    cond.wait
  end

# Apacheの終了処理
  def stop(svc, action)
    cond = ConditionalWait.new { |cond|
      cond.wait_event(Wakame::Event::ServiceOffline) { |event|
        event.instance_id == svc.instance_id
      }
    }

# 終了スクリプトの呼び出しAgentに指示
    request = action.actor_request(svc.agent.agent_id,
                                   '/daemon/stop', 'apache_www', 'init.d/apache2-www').request

    #  Event::ServiceOfflineがやってくるまで待つ
    cond.wait

    # プロセスモニタリングの停止をAgentに指示
    request = action.actor_request(svc.agent.agent_id,
                                   '/service_monitor/unregister', svc.instance_id ).request
     
  end
end

Cluster

サービス全体の定義を行うデータ構造で、Resourceの組み合わせとして表されます。また、各Resourceの依存関係情報も保持しています。

リスト2 Webでよくある構造でサービスを組み合わせたClusterの定義例
class WebCluster < Wakame::Service::ServiceCluster
  def initialize(master)
     super(master) { |c|
       # サービスの定義を追加
       c.add_resource(Apache_WWW.new)
       c.add_resource(Apache_APP.new)
       c.add_resource(Apache_LB.new)
       c.add_resource(MySQL_Master.new)
      
       # 依存関係の指定
       c.set_dependency(Apache_WWW, Apache_LB)
       c.set_dependency(Apache_APP, Apache_LB)
       c.set_dependency(MySQL_Master, Apache_APP)
     }
  end
end

Instance

ResourceおよびClusterは、あくまでも論理的な依存関係を示すもので、動作する実体ではありません。Resourceが実体化されると、Instanceと呼び、Webサーバが実際に何台の仮想マシンで動作しているのかを示すようになります。

作業を並列処理するための仕組み

ここまでで、MasterはAgentに対しアプリケーションのコントロールする指示を出せる仕組みがそろいました。あとは、指示を出す並列処理をどう実現するかが肝となります。

以下の図は、Masterの中で並列的に手続き処理が行われている様子を図示したものです。

図4 EventとActionの関係図
図4 EventとActionの関係図

Event

Masterは、このInstanceで発生している全ての出来事をEventという形で管理しています。次に述べるActionから、通知して欲しいイベントをあらかじめEventへ申請しておけば、Eventは該当するイベントが発生すると希望者のイベントキューへ配信するようになります。

Eventの例としては、以下のようなものがあります。

  • Clusterの状態が変わったとき(ClusterStatusChanged)
  • サービスが起動/終了したとき(ServiceOnline/ServiceOffline)
  • Agentが認識されたとき(AgentMonitored/AgentUnmonitored)
  • Agentが数秒ごとに定時連絡をしてきたとき(AgentPong)

Action

Wakameでは、システム管理者が行う作業をActionという形で記述することができます。Masterにはツリー構造状に連鎖したActionを手順として保持させます。

Action内部は基本的に直列処理となっており、直列で手順を書いていく中で、並列処理にしたい部分は、さらにActionを発生させることで実現することができます。また、Actionは自分に必要なイベントをEventに申請し、そのイベントの状態を監視することで、しかるべき状態になるまで待つような処理を入れることができます。

このようにして、ActionはInstance全体の調和をとりながら記述していくことが可能になっています。

作業の呼び出し方

Actionを起動する方法はいくつかあります。

  1. wakameadmコマンドによるMasterへのメッセージ送信
  2. Masterの内部に設定されたスケジューラ
  3. Agentから報告されたメッセージ

最初(1)のコマンドは、WakameのGetting Startedにも記載されているように、いつでも好きなタイミングでActionを起動する方法です。wakameadmコマンドが理解できるオプションを指定することで、任意のActionを起動できます。

次に(2)のスケジューラですが、内部的にはTimedSequenceと呼ばれているものです。時間を指定し、Clusterで示される各Instanceの数を指定するだけで、その時間になるとInstanceの数の帳尻を合わせるために、しかるべきActionを起動するものです。Proactive Scaling(計画的なスケールアウト)はこの機能で実現します。

最後に(3)ですが、Agentは仮想マシンの状態を定期的にMasterへ報告しており、この報告内容をきっかけに任意のActionを起動するという方法です。実は計画的なスケールアウトとは別に、WakameにはReactive Scaling(負荷の増加などをきっかけにしたスケールアウト)を実現するために必要な仕組みが備わっています。

次回予告

次回は、Webサーバをスケールアウトする設定方法を解説し、wakameadmコマンドからこうしたActionを実際に呼び出して動作を確認してみたいと思います。

おすすめ記事

記事・ニュース一覧