在本文中,我们将使用基于分布式存储的 Kubernetes 部署来构建一个 IRIS 的高可用配置,而不使用“传统的”IRIS Mirror。 这种部署将能够容忍与基础架构相关的故障,如节点、存储和可用区故障。 所描述的方法可以大大降低部署的复杂性,代价是 RTO的略微延长。
镜像是一种用于高可用性、灾难恢复、数据库备份和 OLAP 解决方案的 InterSystems 技术。
文档。
在本文中,我们将使用基于分布式存储的 Kubernetes 部署来构建一个 IRIS 的高可用配置,而不使用“传统的”IRIS Mirror。 这种部署将能够容忍与基础架构相关的故障,如节点、存储和可用区故障。 所描述的方法可以大大降低部署的复杂性,代价是 RTO的略微延长。
最近在多家现场都遇到了备机长时间宕机导致镜像日志写满磁盘的问题。在这里我将对这个问题发生的原因、发生后的处理、和如何预防这类问题发生进行一些讨论。
问题的发生一般始于一些原因导致的主机(如,01)宕机,进而触发镜像的主备切换。切换后备机(如,02)成为主机,并无缝接管业务。由于业务不受影响,如果不注意监控环境的话,很可能现场技术人员长时间都注意不到镜像的备机(01)是宕机状态。
备机长时间宕机会导致如下问题:
1. 这种情况下如果主机(02)再次遇到问题宕机,镜像将无法发挥其高可用性,无法保持业务稳定运行。
2. 主机(02)产生的镜像日志将无法同步到备机(01)。未同步的日志将一直被保存在主机(02)上不被删除。长此以往镜像日志磁盘将被写满,同样导致主机(02)宕机。
问题发现时切记不要手动从文件夹直接删除主机(02)上的镜像日志。未同步的日志一旦手动删除,镜像将无法自动同步,需要重做主备镜像。
问题发现时如果主机(02)还未宕机,此时尝试解决备机(01)问题,启动备机(01),等待镜像自动同步即可。同步完成之后镜像日志将可以被定时任务定时清除。如果遇到较为复杂的情况,现场请第一时间联系您的软件供应商,软件供应商将协同系联软件全球响应中心一起来解决您遇到的具体问题。
为了避免以上的问题发生,现场运维需要对镜像的状态和磁盘的状态配置监控。
我们最近发布了一份关于在镜像环境中使用报告节点(完整的“异步报告镜像成员”)的新白皮书。越来越多的客户正在研究这种机制,将其作为一种快速、简单的方法来设置保持最新的生产数据副本,但可以用于分析查询或重型报告工作负载,而不影响源系统。 请在此处阅读白皮书。
与往常一样,我们很想听听您对如何在组织中使用此镜像选项的反馈,以及您是否对我们如何提高其效率有想法。
在维护IRIS的镜像前,管理员需要清楚的了解以下一些概念:
切换模式在镜像监视器里被翻译成”故障转移模式“。 有两种模式:
通常情况,生产环境的镜像是安装了arbiter(仲裁者)的。Mirror启动时,在还没有连接上arbiter的时候,自动进入Agent-Controlled模式。而后当两台机器,主机,备机都连通了Arbiter,会保持在这个模式。
满足上面的条件,就进入arbiter controlled mode。而如果主备的任一方,失去了和arbiter的连接,或者备用侧丢了active, 开始尝试连接另一方,退回到agent-controlled模式。
Mirror Member Journal Transfer and Dejournaling Status. 请注意,这里面有两个概念:一个是Mirror成员的状态,一个是Journal传输和Dejournaling的状态。下面的图中是3个字段: STATUS, Journal传输,Dejournaling.

STATUS
镜像成员的状态。 正常工作状态
对于同步成员,是Primary(主), Backup(备机)。
对于异步成员,正常状态是Connected(已连接)
In Trouble : 如果主机In Trouble, 是失去了到backup的连接。备机收到主机的同步数据是要返回证实(Ack)消息的。一旦出现问题,主机无法收到备机的Ack, 主机就会把备机标为"In trouble", 从此再也不会向备机发同步数据。
Transition: 暂时状态,进程正在查看一个成员的状态,很快会转换到一个稳定状态。 如果在mirror配置的member中发现了primary,本机会进入Synchronizing状态,否则自己会尝试进入primary状态。
Sychronizing: 从Primary接收journal,同步数据库。
Journal Transfer是主机向其他成员发送Journal文件。而Dejournal是把Journal文件读入数据库。 对于backup或者asycn成员,Journal Transfer状态表示镜像成员是否有来自主数据库的最新日志数据,如果没有,则表示日志传输的落后程度,Dejournaling表示从主数据库收到的所有日志数据是否已经被dejournaled(应用到成员的镜像数据库),如果没有,则表示dejournaling的落后程度。
上图中显示的是正常的状态,其中主机 Journal Transfer 和 Dejournaling 都是N/A, 表示不适用。
对于其他成员,我们分开看:
Journal Transfer状态
Active: backup的正常状态。说明backup从primary收到了最新的journal。注意哪怕是Dejournal状态只是“x秒落后“,而不是"被捕获",Journal Transfer状态也可以是Active,只要是从主机收到了最新的Journal更新。
Caught up(被捕获) : 备机被捕获状态,说明备机从主机收到了最新的journal数据,但主机没有在等待备机的证实消息。 这通常是一个暂时的过程,当备机在连接主机的时候会出现。 异步成员,因为不需要向主机发证实,所以正常的状态就是“被捕获”
If the Primary Failover Member does not receive an acknowledgment from the Backup every Heartbeat Interval period, it demotes the Backup system from Active status to Catch-Up mode.
time behind (多少秒落后)
Disconnected on time(断开): 在一个时间点上这个成员和primary断开了。
Dejournaling状态
正常状态下的图;

备机Backup MirrorB, Journal Transfer是Active, Dejournaling是Caught up, 异步机器MirrorDR的Journal Transfer状态和Dejournaling状态都是Caught up. 表示它们收到了最新的journal数据,并且也都把最新的global修改写入了自己的数据库。
Mirror的核心是自动切换。Backup接替主机的工作有两个前提:1. 备机在同步(Active) 状态, 2. 主机不能正常工作。在这两个前提下,我们来看看自动切换的触发条件,涉及主机,备机,仲裁机之间的通信,
自动切换触发条件
Primary要求Backup接替。这种情况,主机会发生一个请求消息给备机, 要求备机接替。
备机收到arbiter的请求,报告失去了到主机的连接。
仲裁机要求是和外部系统以及应用服务器部署在一个网段的。如果仲裁机无法联络主机,可以认为其他的应用系统和服务器也无法连接主机。有可能主机宕机, 也有可能主机还在正常工作,但外界已经无法联络它了, 这时候也是需要备机接手的。
这时备机也要再去核实一下,是不是能联络到主机。如果能联络到, 备机会发请求让主机Down。如果不能, 说明主机要么死了, 要么失联了, 备机先接手,等联络上再让对方force down.
从主机的ISCAgent收到消息,报告Primary已经down or hung.
在agent-controlled的情况。 primary的服务器还活着。备机主动去问主机的agent, 一旦agent报告主机死了, 那备机就可以上位了。
管理员应该了解mirror涉及的那些进程。当出现故障时,这些进程名字,或者称为User, 经常会出现在message log记录的故障描述中。
On Primary Failover Member(主机)

我们来一个个的看看这些进程:
On Backup Member/Async member(备机)

Mirror Masht, Mirror Arbiter不再重复解释,我们看看其他进程是干什么的。
Mirror JrnRead: Mirror Journal从Primary发送到backup是先写到硬盘的。 JrnRead进程把收到的journal同步读到内存里,然后才进行下一步,Dejournal的工作。
Mirror Dejour: backup机器的dejournal job进程。它把从Primary收到的journal中记录的global改变(set and kill)保存到本机的镜像数据库。
Mirror Prefetch: 这个稍微有点难懂。当收到的journal修改中包括了使用当前backup的journal中已有的内容时,比如收到了一个修改:set ^A=^B+1, 而^B当前存在backup里, Prefetch进程会把^B从硬盘拿到内存,以加快dejournal的速度。
Mirror Backup: two-way channel, 把收到的primary的journal写到backup的mirror journal,并且返回证实(ACK)
这里我省略了在DR上的进程,如果有兴趣,请自己查看文档。
根据不同的场景,查看Mirror的状态有以下几种途径

如果您只是要简单的获得Mirror成员的状态,最直接的方法是使用^Mirror程序。 我们先看看在IRIS Terminal下^MIRROR的执行。
%SYS>do ^MIRROR
1) Mirror Status
2) Mirror Management
3) Mirror Configuration
Option? 1
1) List mirrored databases
2) Display mirror status of this node
3) Display journal file info
4) Status Monitor
Option? 4
Status of Mirror MIRRORTEST at 08:09:24 on 05/19/2023
Arbiter Connection Status:
Arbiter Address: arbiter|2188
Failover Mode: Agent Controlled
Connection Status: This member is not connected to the arbiter
Journal Transfer
Member Name+Type Status Latency Dejournal Latency
-------------------------- --------- --------------- --------------
MIRRORA
Failover Primary N/A N/A
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval <60> D
Database display is now on
Status of Mirror MIRRORTEST at 08:09:29 on 05/19/2023
Arbiter Connection Status:
Arbiter Address: arbiter|2188
Failover Mode: Agent Controlled
Connection Status: This member is not connected to the arbiter
Journal Transfer
Member Name+Type Status Latency Dejournal Latency
-------------------------- --------- --------------- --------------
MIRRORA
Failover Primary N/A N/A
Mirror Databases:
Record To
Name Directory path Status Dejournal
------------- ----------------------------------- ----------- -----------
TEST /isc/mirrorA/TESTDB/ Normal N/A
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval <60>
在操作系统中执行^MIRROR
您可以把以下的代码写入您的脚本语言,查看mirror的状态
irisowner@mirrorA:~$ iris session iris -U "%sys" "Monitor^MIRROR"
Status of Mirror MIRRORTEST at 02:57:08 on 06/13/2023
Arbiter Connection Status:
Arbiter Address: arbiter|2188
Failover Mode: Arbiter Controlled
Connection Status: Both failover members are connected to the arbiter
Journal Transfer
Member Name+Type Status Latency Dejournal Latency
-------------------------- --------- --------------- --------------
MIRRORA
Failover Primary N/A N/A
MIRRORB
Failover Backup Active Caught up
MIRRORDR
Disaster Recovery Connected Caught up Caught up
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval <60>q
Doneirisowner@mirrorA:~$
或者更简单的,只查看本机的mirror成员状态:
irisowner@mirrorA:~$ iris session iris -U "%sys" "LocalMirrorStatus^MIRROR"
This instance is a Failover member
Status for mirror MIRRORTEST is "Primary"
Current mirror file #2 ends at 681224
Min trans file #2 min trans index: 680744
irisowner@mirrorA:~$
如果您熟悉ObjectScript, 也可以使用$SYSTEM.Mirror类的各个method来查看:
irisowner@mirrorB:~$ echo "write \$SYSTEM.Mirror.GetMemberStatus(),! halt" |iris session iris -U "%sys"
Node: mirrorB, Instance: IRIS
%SYS>
Backup
irisowner@mirrorB:~$
如果您要查看更多的内容,您可以更多的使用%SYSTEM.Mirror类的其他方法,比如%SYSTEM.Mirror.GetFailoverMemberStatus(.pri,.alt), $SYSTEM.Mirror.ArbiterState()等等。
如果您从第3方的工具查询mirror成员的状态,还有一个简单的方案,就是调用%SYS命名空间的存储过程。下图是从iris管理门户调用的截图,你可以使用任何SQL客户端调用。

如果是从iris里执行,
%SYS>do ##class(%ResultSet).RunQuery("SYS.Mirror","MemberStatusList")
Member Name:Current Role:Current Status:Journal Transfer Latency:Dejournal Latency:Journal Transfer Latency:Dejournal Latency:Display Type:Display Status:
MDCHCNDBSL1.HICGRP.COM/STAGE:Primary:Active:N/A:N/A:N/A:N/A:Failover:Primary:
MDCHCNDBSL2.HICGRP.COM/STAGE:Backup:Active:Active:Caught up:Active:Caught up:Failover:Backup:
CDCHCNDRSL.HICGRP.COM/STAGE:Async:Async:Caught up:Caught up:Caught up:Caught up:Disaster Recovery:Connected:
如果使用监控工具,您可以通过SNMP获得Mirror的状态,下面是最新的ISC-IRIS.mib中有关Mirror得指标部分。
.4.1.12 = irisMirrorTab | Table of current Mirror Members status and information
-- .4.1.12.1 = irisMirrorRow | Conceptual row for Mirror status and metrics | INDEX = irisSysIndex, irisMirrorIndex
-- .4.1.12.1.1 = irisMirrorIndex | unique index for each Mirror Member | INTEGER
-- .4.1.12.1.2 = irisMirrorName | Name of the mirror this system is a member of | STRING
-- .4.1.12.1.3 = irisMirrorMember | Mirror member name | STRING
-- .4.1.12.1.4 = irisMirrorRole | "Primary", "Backup", or "Async". | STRING
-- .4.1.12.1.5 = irisMirrorStatus | "Active" or "Activate". | STRING
-- .4.1.12.1.6 = irisMirrorJrnLatency | Mirror journal latency "Caught up", "Catchup", or "N/A". | STRING
-- .4.1.12.1.7 = irisMirrorDBLatency | Mirror database latency "Caught up", "Catchup", or "N/A". | STRING
通常情况下, 维护人员是通过mirror的日志和警告来获得Mirror状态,Mirror成员之间的连接情况,而不必须定时的用命令或者调用存储过程来查看。
Cache'和IRIS的日志和警告保存在两个文件: console.log/messages.log和alert.log, 其中alert.log中记录了console.log/messages.log中级别为2,3的记录, 并必须实时发送给管理员。有关这部分内容,请参考在线文档,或者我的帖子:
我们来看看在日志中有哪些mirror的记录:
Becoming primary mirror server
系统固有的通知消息, level =2。当一个iris实例从备机变成了主机,此信息会写到此实例的alert.log, 同时发送给管理员。 可以查看这个链接。
在Mirror切换时,管理员除了从刚刚接手的机器中收到Becoming primary mirror server的通知。如果原来的主机没有宕机或者从宕机中恢复,它也会将引起切换的故障从alert.log发送给管理员,是一个level2, 或者level3的记录。
Arbiter connection lost
level =2 , 自动发送给管理员。 当主机和arbiter失去连接后,在主机上会出现此警告。此时在备机上会出现“Switched from Arbiter Controlled to Agent Controlled failover on request from primary”的提示,是个level0的信息。
MirrorServer: Connection to xxxx(backup) terminatedMirrorServer: Connection to MIRRORDR (async member) terminated
当主机和备机(backup)失去连接,在主机上会出现level2的警告。 而和异步成员丢失连接,主机会出现level1的消息。尽管level1的消息不能自动通知管理员,但这时如果同时监控该异步成员的alert.log, 通常会有level2的警告消息发出,能提醒管理员检查MIRRORDR这个镜像成员的状态。
举例说明:如果在MirrorDR中操作系统重启,IRIS启动后会出现这样的level2的警告:“Previous system shutdown was abnormal, ^SHUTDOWN forced down”
Async member for MirrorSetName started but failed to connect to primary
level =2 , 自动发送给管理员
其他更多的关于Mirror出错的level2, 也就是警告记录, 比如:
这不是个完整的列表,实际环境中会出现各种各样的告警通知。读懂这些通知,需要管理员了解镜像的原理,架构,以及上面介绍的镜像状态和进程的功能。
除此之外,绝大多数的level2日志的同时,会有更多的level0,level1的有关mirror变化的记录。这些内容不需要通知管理员,只是用于分析问题。 如图,下面是在一个messages.log里一个iris从备机变成主机的过程。
06/13/23-07:16:25:472 (2189) 0 [Generic.Event] MirrorClient: Switched from Arbiter Controlled to Agent Controlled failover on request from primary
06/13/23-07:16:26:274 (2189) 1 [Generic.Event] MirrorClient: Mirror_Client: Primary closed down, last # read = 504
06/13/23-07:16:26:301 (2189) 0 [Generic.Event] MirrorClient: Backup waiting for old Dejournal Reader (pid: 2190, job #31) to exit
06/13/23-07:16:27:394 (2189) 0 [Generic.Event] MirrorClient: Set status for MIRRORTEST to Transition
06/13/23-07:16:28:477 (1996) 0 [Utility.Event] [SYSTEM MONITOR] Mirror status changed. Member type = Failover, Status = Transition
06/13/23-07:16:30:261 (2177) 0 [Utility.Event] Returning to restart, old primary reported: "DOWN
06/13/23-07:16:31:524 (11721) 0 [Utility.Event] Applying journal data for mirror "MIRRORTEST" starting at 1538184 in file #2(/isc/mirrorB/mgr/journal/MIRROR-MIRRORTEST-20230613.001)
06/13/23-07:16:31:804 (2177) 0 [Utility.Event] Manager initialized for MIRRORTEST
06/13/23-07:16:31:986 (2177) 0 [Utility.Event] MIRRORA reports it is DOWN, becoming primary mirror server
06/13/23-07:16:32:381 (2177) 0 [Generic.Event] INTERSYSTEMS IRIS JOURNALING SYSTEM MESSAGE
Journaling switched to: /isc/mirrorB/mgr/journal/MIRROR-MIRRORTEST-20230613.002
06/13/23-07:16:32:426 (2177) 0 [Utility.Event] Scanning /isc/mirrorB/mgr/journal/MIRROR-MIRRORTEST-20230613.001
06/13/23-07:16:32:479 (2177) 0 [Utility.Event] No open transactions to roll back
06/13/23-07:16:32:485 (2177) 0 [Generic.Event] MirrorServer: New primary activating databases which are current as of 1538184 (0x00177888) in mirror journal file #2
06/13/23-07:16:32:488 (2177) 0 [Generic.Event] Changed database /isc/mirrorB/TESTDB/ (SFN 5) to read-write due to becoming primary.
06/13/23-07:16:32:924 (2177) 0 [Utility.Event] Initializing Interoperability during mirror initialization
06/13/23-07:16:32:930 (2177) 2 [Utility.Event] Becoming primary mirror server
更多的有关mirror监控和排除的问题, 请各位留言。 谢谢
++ 更新:2018 年 8 月 1 日
使用内置于 Caché 数据库镜像的 InterSystems 虚拟 IP (VIP) 地址有一定的局限性。特别是,它只能在镜像成员驻留在同一网络子网时使用。当使用多个数据中心时,由于增加了网络复杂性( 此处有更详细的讨论),网络子网通常不会“延伸”到物理数据中心之外。出于类似的原因,当数据库托管在云端时,虚拟 IP 通常无法使用。
负载均衡器(物理或虚拟)等网络流量管理设备可用于实现相同级别的透明度,为客户端应用程序或设备提供单一地址。网络流量管理器自动将客户端重定向到当前镜像主服务器的真实 IP 地址。自动化旨在满足灾难后 HA 故障转移和 DR 升级的需求。
题外话:我刚刚翻译了InterSystems专家Bob Binstock的Caché Mirroring 101:简要指南和常见问题解答。 尽管题目是Caché Mirror 101, 而且是写于2016年,但因为讲解的都是Mirror的基本原理,所以在大量使用IRIS的今天也完全适用。
前面的3篇文章,包括了配置Mirror的各个方面。如果您照着操作,现在已经有了一个工作的mirror环境,并加入了您的数据库。然而,还没完,这篇我来讨论一下后面的工作,首先的问题是:
Mirror不复制什么
简单说,Caché/IRIS镜像是数据库复制(Database Replication)。在Caché/IRIS里什么是数据库?也就是Cache.dat和iris.dat文件。数据库的修改日志,也就是journal,从主机被传送到其他镜像成员。而除此之外的内容,需要维护人员来分别的个个处理, 解决这些内容在各个镜像成员间的拷贝。需要很多的计划和细心。
系统数据库, 包括IRISSYS, IRISTEMP, IRISLIB等等, 这些Caché/IRIS本身的数据库不应该被加入Mirror,在大多数Caché/IRIS版本里也都设置成不可以加入入MIRROR。
例外的HealthCare产品, HSSYS需要做Mirror, HSCustom可以做Mirror, 而HSLIB不可以Mirror
我们可以把问题转换成下面的题目:
命名空间是应用开发的概念,它使用数据库。命名空间定义了3种映射关系:Package Mapping, Routing Mapping, Global Mapping。这样在一个命名空间可以使用多个数据库的内容。
通常情况下,用户会在主机创建命名空间的同时,创建一个新的带有mirror属性的数据库,然后会在其他mirror成员中手工一个个的创建命名空间,加入镜像的数据库。之后,管理员无需考虑更多的操作。
然而,对命名空间的修改,比如要添加或者删除命名空间的某些mapping,这偶尔会需要,尤其是应用迭代和系统扩容的情况下,那么,管理员/实施人员,必须清楚Mirror无法同步这个修改,您必须手工同步修改到其他机器去。
如果配置的mapping比较多, 我建议使用Manifest来操作。Mainfest是一个xml的文本,用来安装或者修改Caché/IRIS的配置,你可以参考在线文档: Using a Manifest, 或者社区文章使用Manifest。
这里给一个配置mapping的例子:
<Manifest>
<Namespace Name="FHIRNS">
<Configuration>
<Database Name="FHIRNS"/>
<GlobalMapping Global="%SYS" From="IRISSYS"/>
<GlobalMapping Global="OAuth2.*" From="HSSYS"/>
<GlobalMapping Global="SchemaMap.*" From="HSLIB"/>
<ClassMapping Package="HS" From="HSLIB"/>
<ClassMapping Package="HS.Local" From="HSCUSTOM"/>
<ClassMapping Package="HSMOD" From="HSLIB"/>
<ClassMapping Package="SchemaMap" From="HSLIB"/>
<RoutineMapping Routines="HS.*" From="HSLIB"/>
<RoutineMapping Routines="HSMOD.*" Type="INC" From="HSLIB"/>
<RoutineMapping Routines="HSMOD.*" From="HSLIB"/>
<RoutineMapping Routines="SchemaMap.*" From="HSLIB"/>
</Configuration>
</Namespace>
</Manifest>
如果是资深的Caché维护工程师,懂得如果修改CPF文件并在不重启实例的情况下应用修改后的内容,可以考虑把主机上的CPF中的mapping部分复制粘贴到其他机器。如果您没有这方面的经验,我不建议这种方式。
另外,在IRIS 2022后的版本中有了一个新工具,Configuration Merge。 文档在这里。可惜只有最新版的IRIS或者Health Connect 用户有的用。
数据库的内容会通过Journal从主机同步到其他成员,但修改不会,一般会遇到的是压缩和截断。
由于某种错误操作,某个数据库,会扩展到不正常的大,而当错误修正后,用户可能需要对该数据库进行压缩和截断,以释放被错误占用的空闲的磁盘空间。
由于除主机外,其他镜像成员的数据库都是只读的,这个操作的顺序应该是这样:
在主机A执行压缩和截断
切换到备机B, 再次执行压缩和截断。
异步成员DR。 一种方案是吧DR提升到备机。这时当前的备机A会将为灾备,然后再切换DR为主机,再进行压缩和截断。
还有一个选择,就是重新配置DR上的这个数据库,这需要从主机到DR的数据库备份和恢复。
从最常用的内存的配置,Service的配置, 用户,权限,资源的配置等等。它们都不会被MIRROR同步。如果您在MIRROR主机里做了修改了缩表的大小,或者启动了一个,比如TELNET服务, 您需要人工在其他机器上做相同操作。
像上面的mapping配置一样,这里还是建议使用Manifest人工同步IRIS得修改。注意的是,Mainfest不保证能支持所有的配置。比如在Caché的版本下, 比如您在主机上启动了TELNET服务, Manifest没有相应的标签<TAG>。这种情况下, 如果您熟悉ObjectScript语言,可以把ObjectScript实现加入执行Manifest的方法,比如说:
ClassMethod main(){
//执行Manifest修改命名空间
Set pVars("Namespace")="MYNAMESPACE"
$$$ThrowOnError(..ModifyNamespace(.pVars))
//启动IRIS的TELNET服务
set properties("Enabled")=1 // 有効
set sts=##class(Security.Services).Modify("%Service_Telnet",.properties)
}
当然,如果您缺乏开发实施的知识,在用户界面上一个个机器的操作是最省心的办法。
问题是,打开一个服务,修改一个配置参数操作都很简单,但是如果要添加大量的用户和权限怎么办?
用Manifest管理是一个办法。但根本上,如果您经常有大量的用户管理的工作,其实使用Kerberos或者LDAP管理用户身份认证和授权的工作, 在有多个镜像成员的情况下,尤其的合适。 关于这部分内容,请参考在线文档:Authentication and Authorization
在主机上创建的定时任务, 您需要人工在其他机器上做相同操作。这里有2个步骤:
在主机上创建新任务的时候,要选择”应如何为镜像运行任务“。 这是个下拉菜单,选项有*”仅在主镜像成员上运行“,“仅在非主镜像成员上运行“ ,“在任何镜像成员上运行"。*
选择的出发点是:非主镜像成员的数据库是只读的。因此,比如一个Ensemble的镜像配置中, 删除Ensemble消息的定时任务, 一定是”仅在主镜像成员上运行“。
把新的定时任务从主机同步到其他成员。
如果是一个或者少量几个TASK, 那么手工在其他各个镜像成员上添加是最简单直接的做法。而如果是有很长 的任务列表,尤其在配置Mirror得时候可以需要同步一个长长的列表时, 您可以考虑从主机导出Task到其 他机器导入,我只知道使用ObjectScript命令的方法, 使用%SYS.Task.ExportTask()和 %SYS.Task.ImportTasks()。 文档在这里。
主机上配置的Web Applicaiton 也要同步到其他镜像成员。如果要同步的Web Application比较多,推荐的方式依然是Manifest, 下面是一个例子。
<Manifest>
<Namespace Name="flagger">
<CSPApplication CSPZENEnabled="1" LoginClass="/csp/flagger/Flagger.LoginAuth.cls" CustomErrorPage="/csperror.csp" ChangePasswordPage="/csp/flagger/ChangePassword.cls" AutoCompile="1" Url="/csp/flagger" IsNamespaceDefault="1" InboundWebServicesEnabled="1" Recurse="1" AuthenticationMethods="64" DefaultTimeout="900" Directory="${CSPDIR}flagger" UseSessionCookie="2" CookiePath="/csp/flagger/" ServeFiles="1" ServeFilesTimeout="3600"/>
</Namespace>
</Manifest>
麻烦的是不同的版本Caché/IRIS使用的标签上会略有不同,要稍微仔细的查看一下您的版本的文档。
如果您对ZPM, 现在称为IPM熟悉的话, 用ZPM做同步也是个好选择。关于zpm, 您可以参考这个帖子zpm介绍。提醒一下的是,程序因为是存在数据库里面的,如果该数据库是被镜像的,您其实不需要用ZPM把程序代码拷贝到其他镜像成员。
一般用到的有SQL Gateway和External Language Gateway,它们分别用于连接其他的数据库和使用其他语音的代码包。
SQL Gateway
记录保存在%SYS命名空间的*%Library.sys_SQLConnection*数据表里。简单的方法是使用工具把表记录导入导出。
External Language Gateway(外部语言网关)
新版的IRIS系统内嵌了外部语言服务器,包括%Python Server, %Java Server, %Dotnet Server等。如果您使用的是默认配置,各个镜像成员是一致的,无需操心。如果只是IP端口的修改,手工同步一下也很容易,毕竟工作量有限,只是您需要清楚的记得,这个也是不被Mirror自动同步的。
我把文件分为两类, 一类是“固定文件”,包括一下几个部分,
这类文件上传到主机的时候, 也必须上传到其他镜像成员,这是个简单的操作,别忘了就行。
麻烦的是流文件。在ObjectScript里如果使用了%Stream.FileBinary, %Stream.FileCharacter等类,那么数据不是保存到Cache.Dat或者IRIS.data, 而是保存在和.Dat同目录的一个stream的子目录下,而这个目录是不会被镜像同步的。 而且,因为这是实时数据,你也不可能手工的把它拷来拷去。
如果您的应用里用到了文件流,我任务您需要一个文件服务器保证流文件在各个各个镜像成员间的同步。
对于Ensemble和Health Connect用户,您需要阅读这部分在线文档: Production Considerations for Mirroring , 简单总结一下:
上面的这些并不是完整的内容,尽管在大多少情况下这些内容差不多够了。如果您想要确保Mirror的主机的工作内容完全同步到了备机和DR, 请仔细阅读在线文档的这一部分:Mirror Configuration Guidelines
另外,对于各种需要人工同步的内容的操作,还建议阅读在线文档:Server Migration。
如果是最新的IRIS用户,请参考在线文档:Deploy Mirrors Using Configuration Merge
Caché 镜像是一种可靠、廉价且易于实施的高可用性和灾难恢复解决方案,适用于基于 Caché 和 Ensemble 的应用程序。镜像在广泛的计划内和计划外中断情况下提供自动故障转移,应用程序恢复时间通常限制在几秒钟内。逻辑数据复制消除了存储作为单点故障和数据损坏的根源。升级可以在很少或没有停机时间的情况下执行。
但是,部署 Caché 镜像确实需要大量规划,并且涉及许多不同的过程。与任何其他关键基础设施组件一样,操作镜像需要持续监控和维护。
您可以通过两种方式使用本文:作为常见问题列表,或作为理解和评估镜像、规划镜像、配置镜像和操作镜像的简要顺序指南。每个答案都包含指向每个主题的详细讨论以及每个任务的分步过程的链接。
当您准备好开始规划镜像部署时,您的起点应该始终是Caché 高可用性指南“镜像”一章的镜像架构和规划部分。
今天从官网下载了IRISHealth-2023.1.0.229.0-lnxrh9x64版本的数据库安装包,在RHEL9.0上进行安装配置测试,遇到一个问题,趁热记录下来。
测试环境及软件版本:
操作系统——Red Hat Enterprise Linux release 9.0 (Plow)
数据库——IRISHealth-2023.1.0.229.0-lnxrh9x64
测试创建镜像时,配置好虚拟IP,点击保存。
保存后发现数据库没有变成主成员状态,一直是等待的状态,如下图所示:
这时查看控制台日志发现有大量报错,如下图所示:
从控制台日志看出,此时矛头指向了virtualIP.sh,经过查找发现这个脚本位置在安装目录下的bin目录中。
这里只是创建了MIRROR的配置,然后出现了问题,大概可以猜到可能是添加VIP的方法有什么问题,开启脚本的DEBUG,测试运行添加VIP的方法。
果然,发现了一处报错,如下图所示:
打开脚本文件,查看第588行代码。
研究发现RHEL9.0中,ID=`id | grep uid= | awk -F"=" '{print $2}' | awk -F"(" '{print $1}'`,$ID返回是空,不是数字,所以报错了,修改脚本,注释掉其中六行代码,并关闭DEBUG,如下:
保存脚本后,再次执行添加IP方法测试,IP可以正常添加。PS:不要忘记把测试添加的IP移除掉。
我们客户的一个共同需求是配置 HealthShare HealthConnect 和 IRIS的高可用性模式。
市场上的其他集成引擎通常被宣传为具有“高可用性”配置,但事实并非如此。通常,这些解决方案与外部数据库一起使用,因此,如果这些数据库未配置为高可用性,当发生数据库崩溃或与它的连接丢失时,整个集成工具将变得不可用。
对于 InterSystems 解决方案,这个问题不存在,因为数据库是工具本身的一部分和核心。 InterSystems 如何解决高可用性问题?深奥的配置会把我们拖入异化和疯狂的漩涡?不!在 InterSystems,我们倾听并处理了您的投诉(正如我们一直努力做的那样 ;)),并且我们已将镜像功能提供给所有用户和开发人员。
镜像如何工作?这个概念本身非常简单。如您所知,IRIS 和 HealthShare 都使用一个日志系统,该系统记录每个实例的数据库上的所有更新操作。这个日志系统是后来帮助我们在崩溃后恢复实例而不会丢失数据的系统。好吧,这些日志文件在镜像中配置的实例之间发送,允许并保持镜像中配置的实例永久更新。
让我们简要解释一下在 Mirror 中配置的系统架构是什么样的:
在生产环境中IRIS通常以故障转移集群的形态被部署,而集群中各故障转移成员的镜像状态将决定该集群在故障发生时是否能够顺利切换保障业务不中断。因此,成员状态通常也是运维团队需要巡检或监控的目标。
尽管IRIS内部API提供了丰富的集群配置、成员状态监控等一系列接口,但除Portal上的镜像监视器外,并没有特定的接口便于从外部系统访问(如进行企业级监控集成时),也没有整合好的监控接口可用与获取镜像的健康状态。但在IRIS上开发一个REST接口暴露镜像状态数据并不困难,如下所示:
在Cache 2018之前的版本中,数据库的高可用是通过第三方HA软件保障的,Cache数据库在2018以后及IRIS支持MIRROR技术,通过MIRROR可以保障数据库的高可用及数据的冗余,那么在新版本中,第三方HA软件与MIRROR是否可以同时使用以实现更高的数据库可用性?使用起来有哪些需要注意的?本文重点介绍探讨上述两个问题。
为得出正确结论,我们搭建了如下实验环境:
我们采用3个服务器节点A、B、C分别部署IRIS 2021.1数据库,其中A节点、B节点部署第三方HA软件组成数据库高可用主备集群(本例中,采用的是基于POWER平台的PowerHA),该集群中定义A节点为主节点,B节点为备用节点,HA集群的共享资源组存放在共享SAN存储上,通过HA,生成HA集群的对外服务IP,即我们通常说的Service ip,保证在生产节点发生网络故障、主机故障、以及操作系统故障、手动切换等情况下,IRIS服务、共享资源组、以及HA的Service IP可自动切换至另外一台服务器,保障IRIS高可用,经测试,HA集群内部节点间服务切换时间约为30秒。
在虚拟化环境中使用镜像,构成镜像的InterSystems IRIS实例被安装在虚拟主机上,创造了一个混合的高可用性解决方案,将镜像的优点与虚拟化的优点结合起来。镜像通过自动故障切换对计划内或计划外的故障提供即时响应,而虚拟化HA软件在计划外的机器或操作系统故障后自动重新启动承载镜像成员的虚拟机。这允许失败的成员迅速重新加入镜像,充当备份(或在必要时作为主机)。
当镜像被配置在虚拟化环境中时,请参考以下建议:
为了将自动故障转移扩展到尽可能广泛的故障情况,InterSystems建议你为每个镜像配置一个仲裁机。
要充当仲裁者,系统必须有一个正在运行的ISCAgent进程。由于ISCAgent是与InterSystems IRIS一起安装的,任何承载一个或多个InterSystems IRIS实例的系统都符合这一要求,可以被配置为仲裁者而无需进一步准备;但是,承载一个或多个故障转移或DR异步镜像成员的系统不应该被配置为该镜像的仲裁者。
没有托管InterSystems IRIS实例的系统可以通过安装Arbiter方式的作为仲裁者。请从InterSystems公司下载适合你的仲裁者系统平台的ISCAgent安装包,然后,安装ISCAgent。
注意:Arbiter的版本要和InterSystems IRIS安装版本保持一致。
在Windows系统上,只需执行安装文件,例如ISCAgent-2020.1.0.540.0-win_x64.exe。
我们所有的工作环境都是Ensemble 2017.2。但我们最近将迁移到IRIS for Health 2021.1版本。这是一个复杂的过程,但经过仔细考虑,我们找到了实现这一目标的方法。
我们有一个开发服务器和两个生产服务器的镜像,采用Failover模式。我们有40多个名称空间在使用中,有些有HL7集成,有些有Soap服务、Rest服务、文件处理......什么都有点。我们需要确定向IRIS的迁移不会出现问题,最重要的是,我们需要不惜一切代价避免服务中断。因此,我们必须做的第一件事是建立一个计划。
各位好,
你曾建立过一个镜像环境吗?它是否有一个私有网络、虚拟IP地址和SSL配置? 在做了几次之后,我意识到这是一个漫长的过程,而且需要很多手动操作来生成证书和配置每个IRIS实例。 对于经常要做这件事的人来说,这是一个痛苦的过程。
例如,质量保证团队可能需要为每个新的应用程序版本创建一个新的镜像环境来测试。支持团队可能需要创建一个镜像环境来重现一个复杂的问题。
我们肯定需要工具来快速创建这些镜像环境。
在这篇文章中,我们将用如下环境创建一个镜像样例:

乍一看,它似乎有点复杂,看起来需要大量的代码,但不要担心。 在OpenExchange上有一些库,可以轻松地执行大多数操作。
本文的目的是提供一个例子,说明如何根据你的需要调整这个过程,但在安全问题上,它不是一个最佳实践指南。 现在,让我们来创建我们的样本。
PKI-script: 公钥基础设施(PKI)是一个与IRIS集成的功能,它允许你生成一个自签名的证书并拥有你的授权服务器。在伟大的Pete Greskoff的文章之后,PKI-script的目标是以编程方式执行所有操作,避免在管理门户中进行任何手动操作。 该库包括用于镜像的实用方法。 然而,如果你已经有了证书,你可以用它们来代替PKI-Script。
config-api: 这个库将被用来配置IRIS。它从1.1.0版本开始支持镜像配置。我们将不对如何使用这个库进行详细描述。 这里 已经有一组文章。简而言之,config-api将被用来创建IRIS模板配置文件(JSON格式)并轻松加载。
ZPM.
Docker.
你可以在iris-mirroring-samples repository上找到所有必要的资源文件。
克隆现有的资源库:
git clone https://github.com/lscalese/iris-mirroring-samples
cd iris-mirroring-samples
如果你喜欢从头开始创建一个样本,而不是克隆资源库,只需创建一个带有子目录的新目录: backup 和 config-files. 下载 irissession.sh :
mkdir -p iris-mirroring-samples/backup iris-mirroring-samples/config-files
cd iris-mirroring-samples
wget -O session.sh https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh
为了避免以后出现 "权限拒绝 "的问题,我们需要创建irisowner组,irisowner用户,并将备份目录的组改为irisowner。
sudo useradd --uid 51773 --user-group irisowner
sudo groupmod --gid 51773 irisowner
sudo chgrp irisowner ./backup
这个目录将被用作卷,在与其他节点建立第一个镜像成员后共享数据库备份。
镜像在IRIS社区版中不可用。
如果你还没有有效的IRIS容器许可证,请用你的账户连接到
全球响应中心(WRC)。
点击 "Actions" --> "SW distribtion", 然后点击 "Evaluations" 按钮并选择"Evaluation License"; 填写表单。
把你的许可证文件iris.key复制到这个目录。
###登录Intersystems 容器注册中心(Containers Registry)
为了方便起见,我们使用Intersystems Containers Registry(ICR)来提取docker镜像。如果你不知道你的docker登录名/密码,只要用你的WRC账户连接到SSO.UI.User.ApplicationTokens.cls 就可以检索到你的ICR Token。
docker login -u="YourWRCLogin" -p="YourICRToken" containers.intersystems.com
myappdata数据库和global我们现在并没有真正创建myappdata数据库,而是准备一个配置,在docker构建时创建它。
为此,我们只是用JSON格式创建一个简单的文件。
config-api库将被用来在IRIS实例中加载它。
为此,我们只是用JSON格式创建一个简单的文件。
config-api库将被用来在IRIS实例中加载它。
创建文件config-files/simple-config.json
{
"Defaults":{
"DBDATADIR" : "${MGRDIR}myappdata/",
"DBDATANAME" : "MYAPPDATA"
},
"SYS.Databases":{
"${DBDATADIR}" : {}
},
"Databases":{
"${DBDATANAME}" : {
"Directory" : "${DBDATADIR}"
}
},
"MapGlobals":{
"USER": [{
"Name" : "demo.*",
"Database" : "${DBDATANAME}"
}]
},
"Security.Services" : {
"%Service_Mirror" : { /* Enable the mirror service on this instance */
"Enabled" : true
}
}
}
这个配置文件允许你用默认设置创建一个新的数据库,并在USER命名空间做global映射demo.*。
关于config-api 配置文件功能的更多信息请参考相关的文章 或github页
Docker文件是基于现有的 docker模板的,但我们需要做一些修改,以创建一个工作目录,安装使用虚拟IP的工具,安装ZPM等等。
我们的IRIS image对每个镜像成员都是一样的。镜像将根据其角色The mirroring will be set up on the container starting with the correct configuration depending on its role (第一成员,故障转移备份成员,或读写报告成员) 在容器上以正确的配置开始设置。 请看下面Dockerfile上的注释:
ARG IMAGE=containers.intersystems.com/intersystems/iris:2021.1.0.215.0
# 不需要从WRC下载image。它将在构建时从ICR中提取。
FROM $IMAGE
USER root
#
COPY session.sh /
COPY iris.key /usr/irissys/mgr/iris.key
# /opt/demo 将是我们的工作目录,用于存储我们的配置文件和其他安装文件。
# 安装iputils-arping 以得到一个arping 命令。这在配置虚拟IP时会用到。
# 下载最新ZPM 版本 (ZPM 仅包含在社区版中)。
RUN mkdir /opt/demo && \
chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/demo && \
chmod 666 /usr/irissys/mgr/iris.key && \
apt-get update && apt-get install iputils-arping && \
wget -O /opt/demo/zpm.xml https://pm.community.intersystems.com/packages/zpm/latest/installer
USER ${ISC_PACKAGE_MGRUSER}
WORKDIR /opt/demo
# 设置默认镜像(Default Mirror)角色为主控(master)
# 它将在运行时被重写在docker-compose文件上(第一个实例的主文件、备份和报告)。
ARG IRIS_MIRROR_ROLE=master
# 将config-files目录的内容复制到/opt/demo。
# 目前我们只创建了一个simple-config来设置我们的数据库和global映射。
# 在本文的后面,我们将添加其他配置文件来设置镜像。
ADD config-files .
SHELL [ "/session.sh" ]
# 安装 ZPM
# 用 ZPM 安装config-api 和 pki-script
# 用config-api加载simple-config.json文件,用以:
# - 创建"myappdata" 数据库,
# - 为 "myappdata "数据库中global "demo.*"在命名空间 "USER "中添加一个global映射。
# 基本上,安装ObjectScript应用程序的入口在这里。
# 对于这个例子,我们将加载simple-config.json来创建一个简单的数据库和一个global映射。
RUN \
Do $SYSTEM.OBJ.Load("/opt/demo/zpm.xml", "ck") \
zpm "install config-api" \
zpm "install pki-script" \
Set sc = ##class(Api.Config.Services.Loader).Load("/opt/demo/simple-config.json")
# 复制镜像初始化脚本。
COPY init_mirror.sh /
# 执行一个启动后的脚本,配置镜像。
# init_mirror.sh的内容将在本文后面描述。
CMD ["-a", "/init_mirror.sh"]
Docker文件已经准备好了;我们可以制作image了。:
docker build --no-cache --tag mirror-demo:latest .
这个image将会运行 将被用于运行主节点、备份节点和报告节点。
config-api库允许配置一个镜像,所以我们必须为第一个镜像成员创建一个专门的配置文件config-files/mirror-master.json
为方便起见,注释直接位于JSON中。你可以下载 没有注释的mirror-master.json. 所有的IP地址将通过Docker-compose文件分配给每个节点。
{
"Defaults":{ /* Section contains all variables */
"MirrorName" : "Demo", /* The name of our mirror */
"ArbiterNode" : "172.16.238.10|2188", /* IP Address and port of the arbiter node */
"VirtualAddress" : "172.16.238.100/24", /* Virtual IP Address */
"VirtualAddressInterface" : "eth0", /* Network interface used for the Virtual IP Address. */
"MirrorAddress" : "172.16.220.20", /* IP Address of this node in the private mirror network */
"AgentAddress" : "172.16.238.20", /* IP Address of this node (Agent is installed on the same machine) */
"SystemName" : "master", /* This instance name in the mirror */
"DBDir" : "${MGRDIR}myappdata/", /* Database directory to add to the Demo mirror */
"DBName" : "MYAPPDATA" /* Database name in the mirror */
},
"SYS.MirrorMaster" : {
"${MirrorName}" : {
"Config" : {
"Name" : "${MirrorName}",
"SystemName" : "${SystemName}",
"UseSSL" : true,
"ArbiterNode" : "${ArbiterNode}",
"VirtualAddress" : "${VirtualAddress}",
"VirtualAddressInterface" : "${VirtualAddressInterface}",
"MirrorAddress": "${MirrorAddress}",
"AgentAddress": "${AgentAddress}"
},
"Databases" : [{ /* List of databases to add to the mirror */
"Directory" : "${DBDir}",
"MirrorDBName" : "${DBName}"
}],
"SSLInfo" : { /* SSL Configuration, certificates are generated by PKI */
"CAFile" : "/usr/irissys/mgr/CAServer/CA_Server.cer",
"CertificateFile" : "/usr/irissys/mgr/master_client.cer",
"PrivateKeyFile" : "/usr/irissys/mgr/master_client.key",
"PrivateKeyPassword" : "",
"PrivateKeyType" : "2"
}
}
}
}
创建一个配置文件,故障转移备份成员config-files/mirror-backup.json.
它看起来像第一个成员。
{
"Defaults":{ /* Section contains all variables */
"MirrorName" : "Demo", /* Mirror to join */
"AgentAddress" : "172.16.238.20", /* Agent IP Address of the first mirror member */
"SystemName" : "backup", /* This instance name in the mirror */
"PrimaryInstanceName" : "IRIS", /* IRIS Instance name of the first mirror member */
"VirtualAddressInterface" : "eth0", /* Network interface used for the Virtual IP Address. */
"DBDir" : "${MGRDIR}myappdata/", /* DB in mirror */
"MirrorAddress" : "172.16.220.30" /* IP Address of this node in the private mirror network */
},
"SYS.MirrorFailOver" : {
"${MirrorName}" : {
"Config": {
"Name" : "${MirrorName}",
"SystemName" : "${SystemName}",
"InstanceName" : "${PrimaryInstanceName}",
"AgentAddress" : "${AgentAddress}",
"AgentPort" : "2188",
"AsyncMember" : false,
"AsyncMemberType" : ""
},
"Databases" : [{
"Directory" : "${DBDir}"
}],
"LocalInfo" : {
"VirtualAddressInterface" : "${VirtualAddressInterface}",
"MirrorAddress": "${MirrorAddress}"
},
"SSLInfo" : {
"CAFile" : "/usr/irissys/mgr/CA_Server.cer",
"CertificateFile" : "/usr/irissys/mgr/backup_client.cer",
"PrivateKeyFile" : "/usr/irissys/mgr/backup_client.key",
"PrivateKeyPassword" : "",
"PrivateKeyType" : "2"
}
}
}
}
它与故障转移配置文件非常相似。 不同之处在于AsyncMember、AsyncMemberType和MirrorAddress的值。
创建文件./config-files/mirror-report.json:
{
"Defaults":{
"MirrorName" : "Demo",
"AgentAddress" : "172.16.238.20",
"SystemName" : "report",
"PrimaryInstanceName" : "IRIS",
"VirtualAddressInterface" : "eth0",
"DBDir" : "${MGRDIR}myappdata/",
"MirrorAddress" : "172.16.220.40"
},
"SYS.MirrorFailOver" : {
"${MirrorName}" : {
"Config": {
"Name" : "${MirrorName}",
"SystemName" : "${SystemName}",
"InstanceName" : "${PrimaryInstanceName}",
"AgentAddress" : "${AgentAddress}",
"AgentPort" : "2188",
"AsyncMember" : true,
"AsyncMemberType" : "rw"
},
"Databases" : [{
"Directory" : "${DBDir}"
}],
"LocalInfo" : {
"VirtualAddressInterface" : "${VirtualAddressInterface}",
"MirrorAddress": "${MirrorAddress}"
},
"SSLInfo" : {
"CAFile" : "/usr/irissys/mgr/CA_Server.cer",
"CertificateFile" : "/usr/irissys/mgr/report_client.cer",
"PrivateKeyFile" : "/usr/irissys/mgr/report_client.key",
"PrivateKeyPassword" : "",
"PrivateKeyType" : "2"
}
}
}
}
所有的配置文件都准备好了!
我们的Dockerfile的最后一行是CMD ["-a", "/init_mirror.sh"]。 现在我们要写这个脚本来生成证书,并用相关的配置文件来设置每个IRIS节点。
正如你在这个脚本中看到的那样,生成证书的代码非常简单:
Do ##class(lscalese.pki.Utils).MirrorMaster(,"",,,,"backup,report") -主控节点。
它配置了PKI服务器,PKI客户端,请求证书;等待验证,获得证书,自动接受验证节点的进一步请求,持续5分钟。自动接受的请求只限于故障转移备份主机 和 报告异步成员主机。Do ##class(lscalese.pki.Utils).MirrorBackup("${PKISERVER}","") -备份节点和报告节点。
配置 PKI 客户端,请求证书,等待验证,获得证书。#!/bin/bash
# 用来测试镜像的数据库
DATABASE=/usr/irissys/mgr/myappdata
# 目录包含由主控节点备份的myappdata,以便在其他节点上恢复。
BACKUP_FOLDER=/opt/backup
# 主控节点的json config-api格式的镜像配置文件。
MASTER_CONFIG=/opt/demo/mirror-master.json
# json config-api格式的镜像配置文件,用于故障转移备份节点。
BACKUP_CONFIG=/opt/demo/mirror-backup.json
# 报告异步节点的json config-api格式的镜像配置文件。
REPORT_CONFIG=/opt/demo/mirror-report.json
# 镜像名字...
MIRROR_NAME=DEMO
# 镜像成员清单
MIRROR_MEMBERS=BACKUP,REPORT
# PKI服务器主机:端口(PKI服务器安装在主实例上)。
PKISERVER=master:52773
# 在主控节点上操作。
# 在这个实例上配置公钥基础设施服务器,并生成证书以配置使用SSL的镜像。
# 请参见文章https://community.intersystems.com/post/creating-ssl-enabled-mirror-intersystems-iris-using-public-key-infrastructure-pki。
# 和相关的工具https://openexchange.intersystems.com/package/PKI-Script。
# 使用config-api加载镜像配置与/opt/demo/simple-config.json文件。
# 启动一个作业,自动接受其他名为 "备份 (backup)"和 "报告 (report)"的成员加入镜像(避免在门户管理中进行手动验证,最大延迟为600秒)。
master() {
rm -rf $BACKUP_FOLDER/IRIS.DAT
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS <<- END
Do ##class(lscalese.pki.Utils).MirrorMaster(,"")
Set sc = ##class(Api.Config.Services.Loader).Load("${MASTER_CONFIG}")
Set ^log.mirrorconfig(\$i(^log.mirrorconfig)) = \$SYSTEM.Status.GetOneErrorText(sc)
Job ##class(Api.Config.Services.SYS.MirrorMaster).AuthorizeNewMembers("${MIRROR_MEMBERS}","${MIRROR_NAME}",600)
Hang 2
Halt
END
}
# 由主控节点运行,对myappdata 数据库镜像备份
make_backup() {
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).DismountDatabase(\"${DATABASE}\")"
md5sum ${DATABASE}/IRIS.DAT
cp ${DATABASE}/IRIS.DAT ${BACKUP_FOLDER}/IRIS.TMP
mv ${BACKUP_FOLDER}/IRIS.TMP ${BACKUP_FOLDER}/IRIS.DAT
# 备份被储存在容器外
# chmod 777 可避免我们需要从主机删除这个文件时碰到拒绝权限问题
chmod 777 ${BACKUP_FOLDER}/IRIS.DAT
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).MountDatabase(\"${DATABASE}\")"
}
# 恢复镜像数据库 "myappdata"。 这个恢复过程是在故障转移 "备份 "节点和 "报告 "节点上进行的。
restore_backup() {
sleep 5
while [ ! -f $BACKUP_FOLDER/IRIS.DAT ]; do sleep 1; done
sleep 2
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).DismountDatabase(\"${DATABASE}\")"
cp $BACKUP_FOLDER/IRIS.DAT $DATABASE/IRIS.DAT
md5sum $DATABASE/IRIS.DAT
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).MountDatabase(\"${DATABASE}\")"
}
# 配置“备份”成员
# - 配置公钥基础设施客户端以安装证书并使用镜像的SSL。
# - 如果这个实例是备份,加载配置文件/opt/demo/mirror-backup.json,或
# 如果这个实例是报告(即异步读写镜像节点),加载配置文件/opt/demo/mirror-report.json
other_node() {
sleep 5
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS <<- END
Do ##class(lscalese.pki.Utils).MirrorBackup("${PKISERVER}","")
Set sc = ##class(Api.Config.Services.Loader).Load("$1")
Halt
END
}
if [ "$IRIS_MIRROR_ROLE" == "master" ]
then
master
make_backup
elif [ "$IRIS_MIRROR_ROLE" == "backup" ]
then
restore_backup
other_node $BACKUP_CONFIG
else
restore_backup
other_node $REPORT_CONFIG
fi
exit 0
我们有四个容器可以启动。一个Docker-compose文件是一个完美的文件,可以协调我们的样本。
version: '3.7'
services:
arbiter:
image: containers.intersystems.com/intersystems/arbiter:2021.1.0.215.0
init: true
container_name: mirror-demo-arbiter
command:
- /usr/local/etc/irissys/startISCAgent.sh 2188
networks:
app_net:
ipv4_address: 172.16.238.10
extra_hosts:
- "master:172.16.238.20"
- "backup:172.16.238.30"
- "report:172.16.238.40"
cap_add:
- NET_ADMIN
master:
build: .
image: mirror-demo
container_name: mirror-demo-master
networks:
app_net:
ipv4_address: 172.16.238.20
mirror_net:
ipv4_address: 172.16.220.20
environment:
- IRIS_MIRROR_ROLE=master
ports:
- 81:52773
volumes:
- ./backup:/opt/backup
hostname: master
extra_hosts:
- "backup:172.16.238.30"
- "report:172.16.238.40"
cap_add:
- NET_ADMIN
backup:
image: mirror-demo
container_name: mirror-demo-backup
networks:
app_net:
ipv4_address: 172.16.238.30
mirror_net:
ipv4_address: 172.16.220.30
ports:
- 82:52773
environment:
- IRIS_MIRROR_ROLE=backup
volumes:
- ./backup:/opt/backup
hostname: backup
extra_hosts:
- "master:172.16.238.20"
- "report:172.16.238.40"
cap_add:
- NET_ADMIN
report:
image: mirror-demo
container_name: mirror-demo-report
networks:
app_net:
ipv4_address: 172.16.238.40
mirror_net:
ipv4_address: 172.16.220.40
ports:
- 83:52773
environment:
- IRIS_MIRROR_ROLE=report
volumes:
- ./backup:/opt/backup
hostname: report
extra_hosts:
- "master:172.16.238.20"
- "report:172.16.238.40"
cap_add:
- NET_ADMIN
networks:
app_net:
ipam:
driver: default
config:
- subnet: "172.16.238.0/24"
# 镜像私有网络
mirror_net:
ipam:
driver: default
config:
- subnet: "172.16.220.0/24"
docker-compose up
等待每个实例有一个良好的镜像状态:
Primary.Backup.Connected.这需要一些时间,因为系统在获取虚拟IP方面有一些问题。
将进行许多尝试,并在messages.log中写上AddVirtualAddress failed。
最后,你应该在docker日志中看到这些信息:
mirror-demo-master | 01/09/22-11:02:08:227 (684) 1 [Utility.Event] Becoming primary mirror server
...
mirror-demo-backup | 01/09/22-11:03:06:398 (801) 0 [Utility.Event] Found MASTER as primary, becoming backup
...
mirror-demo-report | 01/09/22-11:03:10:745 (736) 0 [Generic.Event] MirrorClient: Connected to primary: MASTER (ver 4)
你也可以直接通过门户网站 http://localhost:81/csp/sys/utilhome.csp 检查镜像状态。

在Docker-compose中,我们对端口81、82和83进行了映射,使其可以访问每个管理门户。 这就是所有实例的默认登录名和密码:
检查镜像监视器( mirror monitor) (在管理门户management portal中;这是默认用户名和密码): http://localhost:81/csp/sys/op/%25CSP.UI.Portal.Mirror.Monitor.zen
验证镜像设置: http://localhost:81/csp/sys/mgr/%25CSP.UI.Portal.Mirror.EditFailover.zen?$NAMESPACE=%25SYS

我们可以通过简单地设置一个以demo.开始的global来启动一个测试。
记住,我们已经在命名空间USER上配置了一个global映射demo.*。
在主服务器上打开一个终端会话:
docker exec -it mirror-demo-master irissession iris
Set ^demo.test = $zdt($h,3,1)
检查备份节点上的数据是否可用:
docker exec -it mirror-demo-backup irissession iris
Write ^demo.test
检查报告节点上的数据是否可用:
docker exec -it mirror-demo-report irissession iris
Write ^demo.test
好了! 我们已经准备好了一个镜像环境,完全以自动化方式/编程方式创建。 为了更完整一些,我们应该在网络网关和IRIS之间添加一个带https和加密的网络网关,但我们将把它留给下一篇文章。
如果你决定创建自己的脚本,希望这篇文章对你有用。
本文内容的灵感来自于以下内容:
在本帖中,我将展示使用_外部备份_来备份 Caché 的策略,以及与基于快照的解决方案集成的示例。 如今,大多数解决方案部署在基于 VMware 的 Linux 上,因此许多帖子都以展示解决方案如何集成 VMware 快照技术为例。
Caché 安装后即包含 Caché 在线备份,可提供不间断的 Caché 数据库备份。 但随着系统规模的扩大,您应该考虑更高效的备份解决方案。 集成了快照技术的_外部备份_是推荐的系统(包括 Caché 数据库)备份解决方案。
外部备份的在线文档包含了全部详细信息。 一个关键考虑事项是:
“为确保快照的完整性,Caché 提供了在创建快照时冻结数据库写操作的方法。 在创建快照期间,只冻结对数据库文件的物理写入,从而允许用户进程继续在内存中不间断地执行更新。”
还需要注意的是,虚拟化系统上的部分快照过程会导致正在备份的虚拟机短暂暂停,这段时间通常称为关闭时间。 该时间通常不到一秒,因此不会被用户注意到,也不会影响系统运行,但在某些情况下,关闭时间可能较长。 如果关闭时间长于 Caché 数据库镜像的 QoS 超时时间,那么备份节点将认为主节点出现故障,并将进行故障转移。 在本帖的后面部分,我将说明在需要对镜像 QoS 超时时间进行更改时如何查看关闭时间。
本文提供了一个参考架构,作为示例说明基于 InterSystems Technologies(适用于 Caché、Ensemble、HealthShare、TrakCare 以及相关的嵌入式技术,例如 DeepSee、iKnow、Zen 和 Zen Mojo)提供的强大性能和高可用性应用。
Azure 有两种用于创建和管理资源的不同部署模型:Azure Classic 和 Azure Resource Manager。本文中的详细信息基于 Azure Resource Manager (ARM) 模型。