ここから本文です

Sensu、Uchiwa、Redis、RabbitMQを本番利用に向けて冗長化する

6/30(金) 8:10配信

@IT

●Sensuの監視環境を冗長化するには

 今回は、Sensuを本番(プロダクション)環境で使うための冗長化について解説します。「sensu-server」「Redis」「RabbitMQ」をそれぞれ2系統ずつ構築することで、耐障害性を高めます。ここでは、Sensu、Redis、RabbitMQをインストールしたホストを2台用意し、SensuとRabbitMQはアクティブ-アクティブ、Redisはマスター(Master)-スレーブ(Slave)として構成します。

Uchiwaに2台のsensu-apiが登録されていることが確認できる

●Redisのレプリケーションを構成する

 今回の構成では、Redisはマスター-スレーブ形式で冗長化されて、データがレプリケーションされるようになっています。スレーブはマスターから非同期でデータをコピーしており、マスターの“完全なコピー”となります。

 今回は、Sensuの公式ドキュメントに従って、2台のRedisでマスター-スレーブを構成します。

 まずは、マスター側(ここでは「sensu-one.hico.io」)の設定を行います。マスター側では複数のsensu-serverから接続できるように、ローカルホスト以外からの接続も受け入れるように設定します。併せて、接続時にはパスワード認証を行うようにします(「your_redis_password」は適宜ご自身の環境に置き換えてください)。

・マスター(sensu-one.hico.io)の設定(bash)
――――――
# ローカルホスト以外からの接続を受け入れる
$ sudo sed -i 's/^bind 127\.0\.0\.1$/# bind 127.0.0.1/g' /etc/redis/redis.conf
# パスワード認証を有効化(your_redis_passwordは適宜置き換えてください)
$ sudo sed -i 's/^# requirepass foobared$/requirepass your_redis_password/g' /etc/redis/redis.conf
# Redisのサービス(デーモン)を起動
$ sudo /etc/init.d/redis-server start
――――――

 次に、スレーブ側(ここでは「sensu-two.hico.io」)の設定を行います。マスターと同様にローカルホスト以外からの接続の受け入れと、パスワード認証の設定を行います。自身が“マスターのスレーブであること”と、マスターのパスワードを設定すれば、スレーブとして動作します。

・スレーブ(sensu-two.hico.io)の設定(bash)
――――――
# ローカルホスト以外からの接続を受け入れる
$ sudo sed -i 's/^bind 127\.0\.0\.1$/# bind 127.0.0.1/g' /etc/redis/redis.conf
# パスワード認証を有効化(your_redis_passwordは適宜置き換えてください)
$ sudo sed -i 's/^# requirepass foobared$/requirepass your_redis_password/g' /etc/redis/redis.conf
# Master(sensu-one.hico.io)のレプリケーションを設定
$ sudo sed -i 's/^# slaveof <masterip> <masterport>$/slaveof sensu-one.hico.io 6379/g' /etc/redis/redis.conf
# Masterのパスワード認証を設定(requirepassと対応)
$ sudo sed -i 's/^# masterauth <master-password>$/masterauth your_redis_password/g' /etc/redis/redis.conf
# Redisのサービス(デーモン)を起動
$ sudo /etc/init.d/redis-server start
――――――

 最後に、レプリケーションが構成されたことを「redis-cli」コマンドで確認します。マスターがスレーブに接続していること、スレーブがマスターに接続していることが確認できます。

・「redis-cli」コマンドでレプリケーション構成を確認する(bash)
――――――
# Masterでconnected_slaves:1であることを確認
$ redis-cli -h sensu-one.hico.io -a your_redis_password INFO | grep -A 2 role
role:master
connected_slaves:1
slave0:ip=150.95.150.153,port=6379,state=online,offset=603,lag=0
# Slaveでmaster_link_status:upであることを確認
$ redis-cli -h sensu-two.hico.io -a your_redis_password INFO | grep -A 3 role
role:slave
master_host:sensu-one.hico.io
master_port:6379
master_link_status:up
――――――

 ここまでの設定で、Redisのレプリケーション(マスターとスレーブのデータ同期)が設定できました。

 ただ、この構成ではマスター側に障害が発生したときの「スレーブの昇格(フェイルオーバー)」まではサポートされていません。公式の監視およびフェイルオーバー用ツールとしては「Sentinel」が提供されています。SensuでもSentinelをサポートしているので、興味のある方は試してみてください。

 他にも、「Consul」などのサービスディスカバリ系ツールを使っている事例もあります。筆者の職場ではConsulを使っており、マスターの監視に失敗すると、スレーブでスクリプトを実行してマスターに昇格させるといった運用を行っています。ConsulはDNS(Domain Name System)の機能を持っており、常にマスターのIPアドレスを引けるようになっているので便利です。

●RabbitMQのクラスタリングとミラーリングを構成する

 今回の構成では、RabbitMQはアクティブ-アクティブの冗長構成をとり、キューの内容がミラーリングされるようになっています。クラスタリングすることでバーチャルホストやユーザーが共有され、ミラーリングすることでキューの内容が同期されます。

 RabbitMQをクラスタリングし、クラスタ内部でキューのミラーリングを設定するという流れになります。

 今回はSensuのドキュメントに従って、2台のRabbitMQでクラスタリングとミラーリングを構成します。

 まずは、1台目(ここでは「sensu-one.hico.io」)の設定を行います。RabbitMQでクラスタリングを構成するには、「Erlang Cookie」の値が同じである必要があります(詳細はClustering Guideの「How Nodes Authenticate to Each Other:the Erlang Cookie」をご覧ください)。

 ここでは、例として「cookiemonster」に設定していますが、ハッシュ値などのランダムな文字列の方がよいと思います。クラスタリングするためには、一度RabbitMQのバーチャルホストやユーザーなどの情報をリセットする必要があります。

・1台目のRedisの設定(bash)
――――――
# クラスタリングのためにErlangのCookie値を統一
$ echo coookiemonster | sudo tee /var/lib/rabbitmq/.erlang.cookie
$ sudo /etc/init.d/rabbitmq-server restart
# クラスタリングのためにRabbitMQをリセット
$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl reset
$ sudo rabbitmqctl start_app
――――――

 次に、2台目(ここでは「sensu-two.hico.io」)の設定を行います。1台目と同様に「Erlang Cookie」の設定を行った後、1台目のクラスタに参加します。

 「join_cluster」の引数の「@sensu-one」の箇所は、名前解決ができる必要があります。DNSにレコードを登録するか、「/etc/hosts」に記載してください(詳細はClustering Guideの「Hostname Resolution Requirements」をご覧ください)。

・2台目のRedisの設定(bash)
――――――
# クラスタリングのためにErlangのCookie値を統一
$ echo coookiemonster | sudo tee /var/lib/rabbitmq/.erlang.cookie
$ sudo /etc/init.d/rabbitmq-server restart
# sensu-one.hico.ioのクラスタに参加する
$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl join_cluster rabbit@sensu-one
$ sudo rabbitmqctl start_app
――――――

 最後に、Sensu用のバーチャルホストとユーザーを作成し、権限とミラーリングを設定します。「ha-sensu」という名前のポリシーを作成し、「results」と「keepalives」のキューをミラーリングします。「ha-mode」と「ha-sync-mode」の設定内容は下記の通りです。

□□
・"ha-mode": "all":クラスタ内の全ノードでミラーリングする(新しいノードが参加したときも含む)
・"ha-sync-mode": "automatic":新しいノードが参加したとき、操作をブロックして自動でミラーリングする
□□

・Sensu用のバーチャルホストとユーザーを作成し、権限とミラーリングを設定する(bash)
――――――
# クラスタリングできていることを確認
$ sudo rabbitmqctl cluster_status
# Sensu用のバーチャルホストとユーザーを作成し権限を設定
$ sudo rabbitmqctl add_vhost /sensu
$ sudo rabbitmqctl add_user sensu secret
$ sudo rabbitmqctl set_permissions -p /sensu sensu ".*" ".*" ".*"
# バーチャルホストのミラーリングを設定
$ sudo rabbitmqctl set_policy ha-sensu "^(results$|keepalives$)" '{"ha-mode":"all", "ha-sync-mode":"automatic"}' -p /sensu
――――――


 これで、片方のRabbitMQに障害が発生しても、メッセージング処理を継続できるようになりました。さらに可用性を向上させたい場合や、sensu-clientの台数が増えた場合などでも、クラスタのノードを増やすことで対応できるようになります。

●Sensuを冗長化する

 Sensuでは、sensu-serverとsensu-apiを冗長化します。Sensuの場合は冗長化する2台で設定は共通なので、とてもシンプルです。

 Redisの設定では、IPアドレスをマスターのものに変更し、パスワードを追加します。RabbitMQの設定では、クラスタリングしているノードの情報を配列で設定します(ランダムに1台のノードを選択し、障害が発生した場合は自動で次のノードが選択されます)。作業が完了したら、sensu-server、sensu-api、sensu-clientを再起動して設定を反映します。

・/etc/sensu/conf.d/redis.json
――――――
{
 "redis": {
  "host": "163.44.169.160",      // RedisマスターのIPアドレスに変更
  "port": 6379,
  "password": "your_redis_password"  // Redisのパスワードを追加
 }
}
――――――

・/etc/sensu/conf.d/rabbitmq.json
――――――
{
 "rabbitmq": [          // ハッシュ形式から配列形式に変更
  {
   "host": "163.44.169.160",  // RabbitMQ 1台目のIPアドレス
   "port": 5672,
   "vhost": "/sensu",
   "user": "sensu",
   "password": "secret"
  },
  {
   "host": "150.95.150.153",  // RabbitMQ 2台目のIPアドレス
   "port": 5672,
   "vhost": "/sensu",
   "user": "sensu",
   "password": "secret"
  }
 ]
}
――――――

 sensu-serverは追加の設定をせずに、ノードを追加するだけで自動でスケールアウトできるように設計されています。

 sensu-serverは主に3つのタスクを持っており、各タスクはクラスタ内のノードの1台で実行されます。

□□
・check_request_publisher:sensu-clientに対してCheck(監視)の実行を依頼します
・client_monitor:新しいsensu-clientの「client_registry」や「keepalive」の監視を行います
・check_result_monitor:sensu-clientからの監視結果を受け取り、イベントを発火します
□□

 各sensu-serverが担当しているタスクは、「Info API」で簡単に確認できます。試しにsensu-serverを1台停止すると、もう片方のsensu-serverにフェイルオーバーしました。

・各sensu-serverのタスクを確認する(bash)
――――――
# sensu-serverが両系でタスクを分担している
$ curl sensu-two.hico.io:4567/info | jq -r '.servers[] | (.hostname, .tasks)'
sensu-one.hico.io
[
 "check_request_publisher"
]
sensu-two.hico.io
[
 "client_monitor",
 "check_result_monitor"
]
# 片系のsensu-serverを停止させる
$ ssh sensu-two.hico.io 'sudo systemctl stop sensu-server'
# もう片系のsensu-serverにフェイルオーバーした
$ curl sensu-one.hico.io:4567/info | jq -r '.servers[] | (.hostname, .tasks)'
sensu-one.hico.io
[
 "client_monitor",
 "check_result_monitor",
 "check_request_publisher"
]
――――――

●Uchiwaを冗長化する

 最後に、冗長化した複数のsensu-apiに接続するように、Uchiwaの設定を変更します。"sensu"の配列で同じ"name"で登録することで、自動的にフェイルオーバーや負荷分散を行うようになります。

・/etc/sensu/uchiwa.json
――――――
{
 "sensu": [
  {
   "name": "sensu.hico.io",  // 冗長化しているsenus-apiでnameをそろえる
   "host": "163.44.169.160",
   "port": 4567
  },
  {
   "name": "sensu.hico.io",  // 冗長化しているsenus-apiでnameをそろえる
   "host": "150.95.150.153",
   "port": 4567
  }
 ],
 "uchiwa": {
  "host": "0.0.0.0",
  "port": 3000
 }
}
――――――

 Uchiwaの「Datacenters」メニューで「sensu.hico.io」を確認すると、2台のsensu-apiが登録されていることが確認できます。

●終わりに

 今回は、Sensu、Uchiwa、Redis、RabbitMQの冗長化構成について説明しました。Sensuは依存するミドルウェアも含めてアーキテクチャが複雑ですが、それぞれのソフトウェアは簡単にスケールアウトできるので、冗長化や可用性の向上という点ではメリットが大きいです。本稿の内容が、Sensuの導入の助けになれば幸いです。

 次回はヤフーの渡邉貴志氏より、大規模なプライベートクラウド環境におけるSensuの運用ノウハウを紹介していただきます。お楽しみに。

●筆者紹介

堀内 晨彦(ほりうち あきひこ)

生まれも育ちも香川県。香川大学大学院 情報科出身。学生時代は研究の傍ら、勉強会やコミュニティー活動に積極的に参加し、「Sensu Deep Talks」などを主催。2016年4月からは上京し、ICT企業でベアメタルクラウドの開発に従事。趣味は料理とカメラ。

Webサイト:https://hico-horiuchi.github.io/

最終更新:6/30(金) 8:10
@IT