16
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

midPoint by OpenStandiaAdvent Calendar 2019

Day 19

midPointで構築する「冗長構成」実践編

Last updated at Posted at 2019-12-18

はじめに

 システム要求の度合により手厚さは変わりますが、どんなシステムも実運用をする上で、一定の可用性の確保は必要不可欠です。ここでは「3日目 midPointで構築するシステムの構成例」でご紹介した冗長構成を実践していきます。
 また、今回はDockerを利用した環境構築を行っていきます。Docker実行環境があれば簡単に動かすことができますので、動くものベースに理解を深めていただければと思います。

:information_source: GitHubに公開しています
本稿で紹介するDocker ビルドファイルは、以下のGitHubに公開しています。ぜひこちらも合わせてご参照ください。
https://github.com/openstandia/midpoint-dockerfiles

冗長構成の概要

 今回構築する冗長構成は、以下の図のようになります。
 この構成により、midPointサーバーの処理が負荷分散され、また、片方のノードが停止した場合でも、残りのノードで処理が継続可能となります。画面処理は「ロードバランサー」+「共有データベース」を活用して可用性が確保され、タスク処理はmidPointに内包するスケジューリングライブラリ「Quartz」+「共有データベース」+「ノード間通信(REST)機能(※)」を活用して可用性が確保されています。

※ リモートノードに対する死活監視、リモートノードへのスケジューリング登録および削除、リモートノードで実行中のタスクの強制終了を行います

 なお、「共有データベース」の可用性については、SPOF(Single Point Of Failure)とならないように、何らかの方式で可用性が確保されているものとします。実際には、クラウドのマネージドサービスDB(Amazon RDSなど)を利用したり、Active/Standby構成を組むなどの対処を行う必要があります。

image.png

 次に、構成するDockerコンテナは、以下の表のようになります。

コンテナ名 コンテナ名(物理名) ポート
(外部ポート:コンテナポート)
役割 利用イメージ 補足
LBコンテナ mp-ha-example-lb 443:443
80:80
ロードバランサー(SSLアクセレータ) fuww/alpine-nginx-sticky SSLを復号し、構成されるIGAコンテナへロードバランシング(スティッキーセッション)するように構成
IGAコンテナ#1 mp-ha-example-iga1 8080:8080 midPoint本体(ノードA) evolveum/midpoint:4.0.1 公式midPointのDockerイメージに、あらかじめ準備したmidPoint初期設定をインポートするように構成
IGAコンテナ#2 mp-ha-example-iga2 8081:8080 midPoint本体(ノードB)
DBコンテナ mp-ha-example-db4mp 5432:5432 midPointデータベース postgres:10.6 公式PostgreSQLのDockerイメージに、midPointデータベース(DDL)を構築するように構成

 そして、構成するコンテンツは、以下の表のようになります。

コンテンツ名 URL ID/PASSWORD
midPoint管理コンソール https://iga.example.com/ administrator/5ecr3t

以上、この構成内容で構築していきます。

冗長構成の構築

 前述を踏まえまして、実際に冗長構成を構築していきます。

前提条件

 実行環境(OS)は問いませんが、「Docker CE」(※1)および「Docker Compose」(※2)がインストールされていることが、前提条件となります。

※1「Docker CE」は、コンテナ型の仮想化環境を提供するオープンソースソフトウェアのコミュニティー版である「Docker Community Edition」の略称です。
※2「Docker Compose」は、複数のコンテナから成るサービスを構築、実行する手順を自動的にし、管理を容易にするツールです。

 既に、Dockerの実行環境をお持ちの方は、この部分は読み飛ばして構いません。お持ちでない方は、以下のサイトを参考にインストールしてください。

 Dockerは、さまざまなOSに対応しているので、任意の環境にインストールしてください。また、必要に応じて、DockerのHTTP/HTTPSプロキシの通し方について記載された「HTTP/HTTPS proxy」を参照し、設定を行ってください。

 なお、筆者はCentOS 7で確認を行ったため、これ以降CentOS 7をベースにして記載しますが、読者の実行環境に読み替えて実施することも可能です。

設定確認

 構築手順に入る前に、設定マニュアルとDockerビルドファイルの設定箇所を確認してみましょう。

設定マニュアルの確認

 midPointのマニュアルに、セットアップ方法が解説されています。

 要約すると、以下の2STEPの作業が発生します。
 STEP1: 共有データベースの設定をする
 STEP2: クラスタリング設定をする(クラスタモードをオンにし、クラスタ設定項目を設定する)

:information_source: POINT
共有データベースの例として記載がある`PostgreSQL`の他、`MySQL`、`Oracle`、`SQLServer`など、主要データベースに対応しています。
また、3.9以前のノード間通信は、`JMX`方式を採用していましたが、セキュリティの懸念により、4.0から`REST`方式が追加されました。4.0でも`JMX`方式の設定も可能ですが、非推奨となっていますので、`REST`方式を選択してください。

また、クラスタリング設定で使用するパラメーターとその説明を以下に記載しておきます。

パラメーター 必須 説明 備考
-Dmidpoint.taskManager.clustered タスクのクラスターモードを指定します。ONにするには、trueを指定します。  
-Dmidpoint.nodeId ノード識別子を指定します。  
-Dmidpoint.nodeIdSource   ノード識別子のソースを指定します。Autoscaling可能な環境などで、明示的なノードIDが定義できないケースに利用します。  
-Dmidpoint.hostname   ローカルホスト名を指定します。指定しない場合、OSのホスト名が使用されます。 通常、この情報を指定する必要はありません。
-Dmidpoint.httpPort   ローカルHTTPポートを指定します。指定しない場合、8080ポートが使用されます。 通常、この情報を指定する必要はありません。
-Dmidpoint.url   ノード間通信用のURLを指定します。指定しない場合、https://LOCAL_HOSTNAME:8080/midpointが使用されます。 通常、この情報を指定する必要はありません。

設定箇所の確認

 マニュアル通りに、<MP_HOME>/config.xmlに設定することも可能ですが、今回提供するDockerビルドファイルには、以下のように設定しています。

共有データベースの設定

:information_source: POINT
オフィシャルDockerイメージは、環境変数`REPO_***`を指定することでデータベース設定が可能です。
また、Dockerを利用されない方は、以下のように起動オプションで設定できます。
https://github.com/Evolveum/midpoint-docker/blob/575ba7120dca8e3801175540a42ee2894d20e31a/container_files/usr-local-bin/start-midpoint.sh#L26-L41

クラスタリングの設定

:information_source: POINT
オフィシャルDockerイメージは、環境変数`MP_JAVA_OPTS`の中に、クラスタ設定項目を指定することで、クラスタリングの設定が可能です。

構築手順

 まずは、このDockerホストとなるCentOSへFQDNでアクセスできるように、クライアントマシンのhostsファイルに設定を追加します。

hosts
xxx.xxx.xxx.xxx iga.example.com

※「xxx.xxx.xxx.xxx」の部分はCentOSのIPアドレスです。

 次に、今回公開したDockerビルドファイルを任意の場所にダウンロードし、解凍します。

ダウンロード・解凍
$ wget https://github.com/openstandia/midpoint-dockerfiles/archive/mp-ha-example4qiita.zip
$ unzip mp-ha-example4qiita.zip

 そして、Dockerイメージのビルド、およびDockerコンテナの起動をします。

ビルド・起動
$ cd midpoint-dockerfiles/mp-ha-example/
$ docker-compose up -d --build
~ (省略) ~
Creating mp-ha-example-db4mp ... done
Creating mp-ha-example-lb    ... done
Creating ha-example-iga2     ... done
Creating ha-example-iga1     ... done

 最後に、以下の通り、コンテナが起動していることを確認します。

起動確認
$ docker-compose ps
       Name                      Command                 State                       Ports                
----------------------------------------------------------------------------------------------------------
ha-example-iga1       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8080->8080/tcp               
ha-example-iga2       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8081->8080/tcp               
mp-ha-example-db4mp   docker-entrypoint.sh postgres   Up             0.0.0.0:5432->5432/tcp               
mp-ha-example-lb      nginx -g daemon off;            Up             0.0.0.0:443->443/tcp,                
                                                                     0.0.0.0:80->80/tcp 

 これで、midPointの冗長構成の構築が完了です。

 補足になりますが、Dockerコンテナを停止する場合には、以下のコマンドを実行します。

停止
$ docker-compose down
Stopping mp-ha-example_iga2 ... done
Stopping mp-ha-example_iga1 ... done
Stopping mp-ha-example-lb    ... done
Stopping mp-ha-example-db4mp ... done
Removing mp-ha-example_iga2 ... done
Removing mp-ha-example_iga1 ... done
Removing mp-ha-example-lb    ... done
Removing mp-ha-example-db4mp ... done
Removing network mp-ha-example_default

冗長構成の動作確認

 それでは、冗長構成の動作を確認していきましょう。今回は、以下の2つのシナリオで確認します。

  • [1] 画面処理の可用性
     スティッキーセッションでロードバランシングされた側のノードがダウンした場合でも、残りのノードにロードバランシングされ、画面処理が継続されることを確認します。ただし、セッションは共有されていないので、再度ログインを求められます。
  • [2] タスク処理の可用性
     タスクが実行中のノードがダウンした場合でも、残りのノードでタスクが自動リカバリーされ、タスク処理が継続されることを確認します。

[1] 画面処理の可用性

 はじめに、画面処理の可用性の確認を行います。

 まずは、midPoint管理コンソールにアクセスし、administratorユーザーでログインします。
 ブラウザの開発者ツール等で、ロードバランサーのCookieの値(iga_lb_id)を見ると、どちらのノードにロードバランシングされているか確認できます。ここでは、ノードAにロードバランシングされています。

ログイン画面(ノードAにロードバランシングされている)
image.png

 次に、ユーザーの登録を行います。
 ユーザー一覧で現在のユーザー登録状態を確認し、「新規ユーザー登録画面」に遷移します。一旦、最低限の名前のみ入力したものでユーザーを登録し、新しくユーザーが登録されたことを確認します。

ユーザー一覧画面(新規ユーザー登録前)
image.png
新規ユーザー登録画面(新規ユーザー登録入力)
image.png
ユーザー一覧画面(新規ユーザー登録後)
image.png

このタイミングで、ノードAで障害が発生したと仮定して、ノードAのコンテナを強制終了します。

強制終了・起動確認
$ docker-compose kill iga1
Killing ha-example-iga1 ... done
$ docker-compose ps
       Name                      Command                 State                      Ports                
---------------------------------------------------------------------------------------------------------
ha-example-iga1       /usr/local/bin/startup.sh       Exit 137                                           
ha-example-iga2       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8081->8080/tcp              
mp-ha-example-db4mp   docker-entrypoint.sh postgres   Up             0.0.0.0:5432->5432/tcp              
mp-ha-example-lb      nginx -g daemon off;            Up             0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp 

 画面に戻り、ユーザー一覧の検索を行います。
 エラー画面に遷移することなく、ノードBにロードバランシングされますが、セッションは共有されてないので、再度、ログインを求められ、ホーム画面に戻ってしまいます。しかし、先ほど登録したユーザーデータは共有されているので、画面処理が継続可能であることが確認できます。なお、もともとノードBにロードバランシングされていたユーザーは、スティッキーセッションですので、ログインも求められることなく、画面処理が継続可能です。

ユーザー一覧画面(再検索)
image.png
ログイン画面(ノードBにロードバランシングされている)
image.png
ホーム画面(再ログイン後はホーム画面に戻ってしまう)
image.png
ユーザー一覧画面(データが共有されている)
image.png

そして、強制終了したノードAが復旧したと仮定して、ノードAのコンテナを起動します。

起動・起動確認
$ docker-compose start iga1
Starting ha-example-iga1 ... done
$ docker-compose ps
       Name                      Command                 State                       Ports                
----------------------------------------------------------------------------------------------------------
ha-example-iga1       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8080->8080/tcp               
ha-example-iga2       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8081->8080/tcp               
mp-ha-example-db4mp   docker-entrypoint.sh postgres   Up             0.0.0.0:5432->5432/tcp               
mp-ha-example-lb      nginx -g daemon off;            Up             0.0.0.0:443->443/tcp,0.0.0.0:80->80/tcp  

 これで、両方のノードにロードバランシング(スティッキーセッション)が再開されます。

[2] タスク処理の可用性

 続きまして、タスク処理の可用性を確認します。
 midPoint管理コンソールにアクセスし、administratorユーザーでログインします。「サーバータスク画面」に遷移し、ノードの明細に、ノードAとノードBのステータスが実行中になっていれば、タスクの分散実行が可能な状態です。そして、テスト用タスクを用意してありますので、「タスク詳細」に遷移します。

タスク画面(両ノードで分散実行が可能な状態)
image.png

 このテスト用タスクは、ロギングしながら30秒間スリープするだけの処理が書かれているのですが、このタスクをスケジューリングします。ここでは、確認がしやすいように60秒間隔の繰り返しタスクで設定し、何かしらの障害でタスクのスレッドが意図せず終了した場合にはリスタートするよう設定します。
 また、タスクが実行された場合、「実行ステータス」に、実行ノードが表示されます。ここでは、ノードAとなっています。

タスク詳細画面(スケジューリング設定)
image.png
タスク詳細画面(タスクがノードAで実行中)
image.png

 このタスクが終了する前に、ノードAで障害が発生したと仮定して、ノードAを強制終了します。

強制終了・起動確認
$ docker-compose kill iga1
Killing ha-example-iga1 ... done
$ docker-compose ps
       Name                      Command                 State                       Ports                
----------------------------------------------------------------------------------------------------------
ha-example-iga1       /usr/local/bin/startup.sh       Exit 137                                           
ha-example-iga2       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8081->8080/tcp              
mp-ha-example-db4mp   docker-entrypoint.sh postgres   Up             0.0.0.0:5432->5432/tcp              
mp-ha-example-lb      nginx -g daemon off;            Up             0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp 

 サーバーログ確認すると、ノードAで実行中のテスト用タスクが途中で終了しますが、ノードBタスクが自動リカバリーされていることが確認できます。

ログ確認
$ docker-compose logs -f
ha-example-iga1 | 2019-12-15 06:55:51,869 [MODEL] [midPointScheduler_Worker-7] INFO (com.evolveum.midpoint.expression): ==== doing test-task ==== Start ←'★ノードAでテスト用タスクが開始'
ha-example-iga1 | 2019-12-15 06:55:52,870 [MODEL] [midPointScheduler_Worker-7] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====.
ha-example-iga1 | 2019-12-15 06:55:53,871 [MODEL] [midPointScheduler_Worker-7] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====..
ha-example-iga1 | 2019-12-15 06:55:54,871 [MODEL] [midPointScheduler_Worker-7] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====...
~ (省略) ~
ha-example-iga1 | 2019-12-15 06:56:04,878 [MODEL] [midPointScheduler_Worker-7] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====.........+...
ha-example-iga1 exited with code 137 ←'★テスト用タスク処理の途中でノードAが強制終了された'
ha-example-iga2 | 2019-12-15 06:56:24,319 [] [QuartzScheduler_midPointScheduler-NodeB_ClusterManager] INFO (org.quartz.impl.jdbcjobstore.JobStoreTX): ClusterManager: detected 1 failed or restarted instances.
ha-example-iga2 | 2019-12-15 06:56:24,320 [] [QuartzScheduler_midPointScheduler-NodeB_ClusterManager] INFO (org.quartz.impl.jdbcjobstore.JobStoreTX): ClusterManager: Scanning for instance "NodeA" s failed in-progress jobs. ←'★ノードAでタスクが異常終了したことを検知'
ha-example-iga2 | 2019-12-15 06:56:24,344 [] [QuartzScheduler_midPointScheduler-NodeB_ClusterManager] INFO (org.quartz.impl.jdbcjobstore.JobStoreTX): ClusterManager: ......Deleted 1 complete triggers(s).
ha-example-iga2 | 2019-12-15 06:56:24,344 [] [QuartzScheduler_midPointScheduler-NodeB_ClusterManager] INFO (org.quartz.impl.jdbcjobstore.JobStoreTX): ClusterManager: ......Scheduled 1 recoverable job(s) for recovery.
ha-example-iga2 | 2019-12-15 06:56:24,415 [] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.task.quartzimpl.execution.JobExecutor): Recovering resilient task Task(id:1576390139067-0-1, name:test-task, oid:task0000-0000-0000-0000-00000test) ←'★ノードBでテスト用タスクを自動リカバリ'
ha-example-iga2 | 2019-12-15 06:56:25,217 [MODEL] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.expression): ==== doing test-task ==== Start ←'★ノードBでテスト用タスクが開始'
ha-example-iga2 | 2019-12-15 06:56:26,352 [MODEL] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====.
ha-example-iga2 | 2019-12-15 06:56:27,353 [MODEL] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====..
ha-example-iga2 | 2019-12-15 06:56:28,354 [MODEL] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====...
~ (省略) ~
ha-example-iga2 | 2019-12-15 06:56:55,387 [MODEL] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.expression): ==== doing test-task ====.........+.........+.........+
ha-example-iga2 | 2019-12-15 06:56:55,387 [MODEL] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.expression): ==== doing test-task ==== End ←'★ノードBでテスト用タスクが終了'
ha-example-iga2 | 2019-12-15 06:56:55,389 [] [midPointScheduler_Worker-5] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Executed script on the pipeline

※ ポイントとなる行に、★印付きで解説を記載しています

 このとき、「タスク画面」よりノードの状態を確認すると、ノードAオフとなっており、以降のタスクはノードBで実行されます。

タスク画面(ノードBの片肺運転状態)
image.png

そして、強制終了したノードAが復旧したと仮定して、ノードAのコンテナを起動します。

起動・起動確認
$ docker-compose start iga1
Starting ha-example-iga1 ... done
$ docker-compose ps
       Name                      Command                 State                       Ports                
----------------------------------------------------------------------------------------------------------
ha-example-iga1       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8080->8080/tcp               
ha-example-iga2       /usr/local/bin/startup.sh       Up (healthy)   0.0.0.0:8081->8080/tcp               
mp-ha-example-db4mp   docker-entrypoint.sh postgres   Up             0.0.0.0:5432->5432/tcp               
mp-ha-example-lb      nginx -g daemon off;            Up             0.0.0.0:443->443/tcp,0.0.0.0:80->80/tcp     

 再度「タスク画面」よりノードの状態を確認すると、ノードB実行中となり、タスクの分散実行が再開されます。

タスク画面(両ノードで分散実行が可能な状態)
image.png

 なお、今回はタスクの異常終了時に自動リカバリーするシナリオで解説しましたが、実装するタスクによっては、エラー内容や影響範囲を把握せず闇雲に自動リカバリーしたくないケースもあるかと思います。スケジューリング設定の中で少し触れましたが、タスクが実行中に異常停止した場合の動作や、両ノードがダウンしていてスケジュール時刻に起動できなかった場合の動作は、運用要件に合わせて変更可能です。ここでは詳細な解説は割愛しますが、ぜひ、midPointのマニュアルを読みながら、このDocker環境で試して、理解を深めてみてください。

おわりに

 以上で、2つのmidPointサーバーが冗長化されていることを確認できました。今回はmidPointのマニュアルをベースに構築を行ったのですが、アレンジでセッションの共有も行うことが可能です。midPointは「Spring Boot」をベースに構築されていますので、「Spring Session」を利用することで、MemcacheやRedisなどのインメモリデータストアにセッションが共有され、より理想的な冗長構成となります。ロードバランシングの切り替わりタイミングでログイン画面さえも表示させたくないといった高いシステム要求の場合には、障害ポイントが増えることにも注意しながら、採用を検討してみてください。


参考

Clustering / high availability setup
※ 公式のクラスタリング設定のマニュアルです

GitHub Evolveum/midpoint-docker - demo(clustering)
※ 公式のDockerのクラスタリングデモです(非推奨の「JMX」方式で構築されているので参考までに)

Task Manager
※タスクの動作について解説されています。

mark.png

16
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?