midPointで構築する「冗長構成」実践編
首先
根据系统要求的程度不同,支持程度也会有所不同,但在进行任何系统的实际运行时,确保一定可用性是必不可少的。在这里,我们将实践介绍过的在”第3天的midPoint系统构建示例”中提到的冗余配置。
此外,本次我们将进行使用Docker进行环境搭建。只要有Docker执行环境,就可以轻松运行,所以希望您能基于运行的实例加深理解。

・https://github.com/openstandia/midpoint-dockerfiles
冗长结构的概要
本次构建的冗余结构如下图所示。
通过这种结构,midPoint服务器的处理将被负载均衡,并且即使其中一个节点停止,处理仍可在剩余的节点上继续。屏幕处理利用”负载均衡器”和”共享数据库”来确保可用性,任务处理利用midPoint内部的调度库”Quartz”,并结合”共享数据库”和”节点间通信(REST)功能(※)”来确保可用性。
对于远程节点进行存活监测,对远程节点进行调度的注册和删除,以及强制终止在远程节点上运行的任务。
なお、「共有データベース」の可用性については、SPOF(Single Point Of Failure)とならないように、何らかの方式で可用性が確保されているものとします。実際には、クラウドのマネージドサービスDB(Amazon RDSなど)を利用したり、Active/Standby構成を組むなどの対処を行う必要があります。

次に、構成するDockerコンテナは、以下の表のようになります。
(外部ポート:コンテナポート)役割利用イメージ補足LBコンテナmp-ha-example-lb443:443
80:80ロードバランサー(SSLアクセレータ)fuww/alpine-nginx-stickySSLを復号し、構成されるIGAコンテナへロードバランシング(スティッキーセッション)するように構成IGAコンテナ#1mp-ha-example-iga18080:8080midPoint本体(ノードA)evolveum/midpoint:4.0.1公式midPointのDockerイメージに、あらかじめ準備したmidPoint初期設定をインポートするように構成IGAコンテナ#2mp-ha-example-iga28081:8080midPoint本体(ノードB)〃〃DBコンテナmp-ha-example-db4mp5432:5432midPointデータベースpostgres:10.6公式PostgreSQLのDockerイメージに、midPointデータベース(DDL)を構築するように構成
然后,构成该内容的项目如下表所示。
https://iga.example.com/
administrator/5ecr3t我们将根据上述构成内容进行构建。
构建冗长的结构
前述を踏まえまして、実際に冗長構成を構築していきます。
前提条件
只需要一个选项来用中文转述以下内容:无论是什么操作系统,前提条件是要安装了”Docker CE”(※1)和”Docker Compose”(※2)。
※2「Docker Compose」は、複数のコンテナから成るサービスを構築、実行する手順を自動的にし、管理を容易にするツールです。
如果你已经有Docker的运行环境了,可以跳过这部分。如果你还没有,请参考以下网站进行安装。
-
- Install Docker CE
- Install Docker Compose
Dockerは、さまざまなOSに対応しているので、任意の環境にインストールしてください。また、必要に応じて、DockerのHTTP/HTTPSプロキシの通し方について記載された「HTTP/HTTPS proxy」を参照し、設定を行ってください。
另外,由于本文作者在CentOS 7上进行了确认,因此在以下内容中将以CentOS 7为基础进行描述,读者也可以根据自己的执行环境进行操作。
設定確認
構築手順に入る前に、設定マニュアルとDockerビルドファイルの設定箇所を確認してみましょう。
設定マニュアルの確認
在MidPoint的手册中,有关于设置方法的解释。
- Clustering / high availability setup
要約すると、以下の2STEPの作業が発生します。
STEP1: 共有データベースの設定をする
STEP2: クラスタリング設定をする(クラスタモードをオンにし、クラスタ設定項目を設定する)

また、3.9以前のノード間通信は、`JMX`方式を採用していましたが、セキュリティの懸念により、4.0から`REST`方式が追加されました。4.0でも`JMX`方式の設定も可能ですが、非推奨となっていますので、`REST`方式を選択してください。
また、クラスタリング設定で使用するパラメーターとその説明を以下に記載しておきます。
true
を指定します。 -Dmidpoint.nodeId〇ノード識別子を指定します。 -Dmidpoint.nodeIdSource ノード識別子のソースを指定します。Autoscaling可能な環境などで、明示的なノードIDが定義できないケースに利用します。 -Dmidpoint.hostname ローカルホスト名を指定します。指定しない場合、OSのホスト名が使用されます。通常、この情報を指定する必要はありません。-Dmidpoint.httpPort ローカルHTTPポートを指定します。指定しない場合、8080ポートが使用されます。通常、この情報を指定する必要はありません。-Dmidpoint.url ノード間通信用のURLを指定します。指定しない場合、https://LOCAL_HOSTNAME
:8080/midpointが使用されます。通常、この情報を指定する必要はありません。确认设置位置
マニュアル通りに、/config.xmlに設定することも可能ですが、今回提供するDockerビルドファイルには、以下のように設定しています。
共享数据库的设置
-
- https://github.com/openstandia/midpoint-dockerfiles/blob/mp-ha-example4qiita/mp-ha-example/docker-compose.yml#L78-L88
- https://github.com/openstandia/midpoint-dockerfiles/blob/mp-ha-example4qiita/mp-ha-example/docker-compose.yml#L116-L126

また、Dockerを利用されない方は、以下のように起動オプションで設定できます。
https://github.com/Evolveum/midpoint-docker/blob/575ba7120dca8e3801175540a42ee2894d20e31a/container_files/usr-local-bin/start-midpoint.sh#L26-L41
クラスタリングの設定
-
- https://github.com/openstandia/midpoint-dockerfiles/blob/mp-ha-example4qiita/mp-ha-example/docker-compose.yml#L93
https://github.com/openstandia/midpoint-dockerfiles/blob/mp-ha-example4qiita/mp-ha-example/docker-compose.yml#L131
POINT
オフィシャルDockerイメージは、環境変数MP_JAVA_OPTSの中に、クラスタ設定項目を指定することで、クラスタリングの設定が可能です。
创建程序
首先,在作为Docker主机的CentOS上,通过FQDN进行访问,需要在客户端机器的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
冗长构成的操作确认 de
それでは、冗長構成の動作を確認していきましょう。今回は、以下の2つのシナリオで確認します。
[1] 画面処理の可用性
スティッキーセッションでロードバランシングされた側のノードがダウンした場合でも、残りのノードにロードバランシングされ、画面処理が継続されることを確認します。ただし、セッションは共有されていないので、再度ログインを求められます。
[2] タスク処理の可用性
タスクが実行中のノードがダウンした場合でも、残りのノードでタスクが自動リカバリーされ、タスク処理が継続されることを確認します。
[1]画面处理的可用性
はじめに、画面処理の可用性の確認を行います。
まずは、midPoint管理コンソールにアクセスし、administratorユーザーでログインします。
ブラウザの開発者ツール等で、ロードバランサーのCookieの値(iga_lb_id)を見ると、どちらのノードにロードバランシングされているか確認できます。ここでは、ノードAにロードバランシングされています。

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

このタイミングで、ノード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にロードバランシングされていたユーザーは、スティッキーセッションですので、ログインも求められることなく、画面処理が継続可能です。

そして、強制終了したノード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管理控制台,并使用管理员用户登录。转到”服务器任务”界面,在节点详细信息中,如果节点A和节点B的状态为”正在执行”,则表示任务可以进行分布式执行。然后,我们准备了一个测试任务,所以请转到”任务详情”。

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

在此任务结束之前,假设在节点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上执行。

そして、強制終了したノード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正在运行,并且任务的分布式执行重新开始。

なお、今回はタスクの異常終了時に自動リカバリーするシナリオで解説しましたが、実装するタスクによっては、エラー内容や影響範囲を把握せず闇雲に自動リカバリーしたくないケースもあるかと思います。スケジューリング設定の中で少し触れましたが、タスクが実行中に異常停止した場合の動作や、両ノードがダウンしていてスケジュール時刻に起動できなかった場合の動作は、運用要件に合わせて変更可能です。ここでは詳細な解説は割愛しますが、ぜひ、midPointのマニュアルを読みながら、このDocker環境で試して、理解を深めてみてください。
最后
以上で、2つのmidPointサーバーが冗長化されていることを確認できました。今回はmidPointのマニュアルをベースに構築を行ったのですが、アレンジでセッションの共有も行うことが可能です。midPointは「Spring Boot」をベースに構築されていますので、「Spring Session」を利用することで、MemcacheやRedisなどのインメモリデータストアにセッションが共有され、より理想的な冗長構成となります。ロードバランシングの切り替わりタイミングでログイン画面さえも表示させたくないといった高いシステム要求の場合には、障害ポイントが増えることにも注意しながら、採用を検討してみてください。
请给出一个选项。
群集/高可用性设置
※ 这是官方集群设置手册
GitHub Evolveum/midpoint-docker – demo(clustering)
※ 这是Evolveum/midpoint-docker的官方Docker集群演示(请注意,它是使用不推荐的”JMX”方式构建的,仅供参考)
任务管理器
※ 介绍了任务的操作方法。
