如何以自动化方式/编程方式创建一个镜像环境
各位好,
你曾建立过一个镜像环境吗?它是否有一个私有网络、虚拟IP地址和SSL配置?
在做了几次之后,我意识到这是一个漫长的过程,而且需要很多手动操作来生成证书和配置每个IRIS实例。
对于经常要做这件事的人来说,这是一个痛苦的过程。
例如,质量保证团队可能需要为每个新的应用程序版本创建一个新的镜像环境来测试。支持团队可能需要创建一个镜像环境来重现一个复杂的问题。
我们肯定需要工具来快速创建这些镜像环境。
在这篇文章中,我们将用如下环境创建一个镜像样例:
- 仲裁机
- 主服务器
- 故障切换备份成员
- 读写报告异步成员
- 节点间日志转移的SSL配置
- 镜像环境中的私有网络
- 虚拟IP地址
- 镜像数据库
乍一看,它似乎有点复杂,看起来需要大量的代码,但不要担心。
在OpenExchange上有一些库,可以轻松地执行大多数操作。
本文的目的是提供一个例子,说明如何根据你的需要调整这个过程,但在安全问题上,它不是一个最佳实践指南。
现在,让我们来创建我们的样本。
工具和库
-
PKI-script: 公钥基础设施(PKI)是一个与IRIS集成的功能,它允许你生成一个自签名的证书并拥有你的授权服务器。在伟大的Pete Greskoff的文章之后,PKI-script的目标是以编程方式执行所有操作,避免在管理门户中进行任何手动操作。 该库包括用于镜像的实用方法。 然而,如果你已经有了证书,你可以用它们来代替PKI-Script。
-
config-api: 这个库将被用来配置IRIS。它从1.1.0版本开始支持镜像配置。我们将不对如何使用这个库进行详细描述。 这里 已经有一组文章。简而言之,config-api将被用来创建IRIS模板配置文件(JSON格式)并轻松加载。
-
ZPM.
- Docker.
Github 页
你可以在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社区版中不可用。
如果你还没有有效的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文件是基于现有的 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"]
制作 IRIS image
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"
}
}
}
}
配置IRIS节点并生成证书
所有的配置文件都准备好了!
我们的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 文件
我们有四个容器可以启动。一个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进行了映射,使其可以访问每个管理门户。
这就是所有实例的默认登录名和密码:
- Master http://localhost:81/csp/sys/utilhome.csp
- Failover backup member http://localhost:82/csp/sys/utilhome.csp
- Read-Write report async member http://localhost:83/csp/sys/utilhome.csp
测试
检查镜像监视器( 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和加密的网络网关,但我们将把它留给下一篇文章。
如果你决定创建自己的脚本,希望这篇文章对你有用。
来源
本文内容的灵感来自于以下内容: