使用 GitLab 持续交付 InterSystems 解决方案 – 第 9 部分:容器架构
在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:
- Git 101
- Git 流程(开发流程)
- GitLab 安装
- GitLab 工作流
- 持续交付
- GitLab 安装和配置
- GitLab CI/CD
- 为何使用容器?
- 容器基础架构
- 使用容器的 CD
- 使用 ICM 的 CD
- 容器架构
在本文中,我们将讨论如何构建并部署您自己的容器。
Durable %SYS
由于容器是临时的,它们不应存储任何应用程序数据。 持久化 %SYS 功能让我们能够实现这一点 – 将设置、配置、%SYS 数据等存储到主机卷上,即:
- iris.cpf 文件。
- /csp 目录,包含 Web 网关配置和日志文件。
- /httpd/httpd.conf 文件,实例的私有 Web 服务器的配置文件。
- /mgr 目录,包含以下内容:
- IRISSYS 系统数据库,包括 IRIS.DAT 和 iris.lck 文件、流目录,以及包含 IRISTEMP、IRISAUDIT、IRIS 和 USER 系统数据库的 iristemp、irisaudit、iris 和 user 目录。
- 写入镜像日志文件,IRIS.WIJ。
- /journal 目录,包含日志文件。
- /temp 目录,用于存储临时文件。
- 日志文件,包括 messages.log、journal.log 和 SystemMonitor.log。
容器架构
另一方面,我们需要将应用程序代码存储在我们的容器内,以便在需要时进行升级。
这一切使我们实现了如下所示的架构:

为了在构建过程中实现这一点,我们至少需要创建一个额外的数据库(用于存储应用程序代码)并将其映射到我们的应用程序命名空间。 在我的示例中,我将使用 USER 命名空间来保存应用程序数据,因为它已经存在且是持久化的。
安装程序
基于上述内容,我们的安装程序需要执行以下任务:
- 创建 APP 命名空间/数据库
- 将代码加载到 APP 命名空间
- 将我们的应用程序类映射到 USER 命名空间
- 完成所有其他安装(在本例中,我创建了 CSP Web 应用和 REST 应用)
Class MyApp.Hooks.Local
{
Parameter Namespace = "APP";
/// See generated code in zsetup+1^MyApp.Hooks.Local.1
XData Install [ XMLNamespace = INSTALLER ]
{
<Manifest>
<Log Text="Creating namespace ${Namespace}" Level="0"/>
<Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="" Data="IRISTEMP">
<Configuration>
<Database Name="${Namespace}" Dir="/usr/irissys/mgr/${Namespace}" Create="yes" MountRequired="true" Resource="%DB_${Namespace}" PublicPermissions="RW" MountAtStartup="true"/>
</Configuration>
<Import File="${Dir}Form" Recurse="1" Flags="cdk" IgnoreErrors="1" />
</Namespace>
<Log Text="End Creating namespace ${Namespace}" Level="0"/>
<Log Text="Mapping to USER" Level="0"/>
<Namespace Name="USER" Create="no" Code="USER" Data="USER" Ensemble="0">
<Configuration>
<Log Text="Mapping Form package to USER namespace" Level="0"/>
<ClassMapping From="${Namespace}" Package="Form"/>
<RoutineMapping From="${Namespace}" Routines="Form" />
</Configuration>
<CSPApplication Url="/" Directory="${Dir}client" AuthenticationMethods="64" IsNamespaceDefault="false" Grant="%ALL" Recurse="1" />
</Namespace>
</Manifest>
}
/// This is a method generator whose code is generated by XGL.
/// Main setup method
/// set vars("Namespace")="TEMP3"
/// do ##class(MyApp.Hooks.Global).setup(.vars)
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 0, pInstaller As %Installer.Installer) As %Status [ CodeMode = objectgenerator, Internal ]
{
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "Install")
}
/// Entry point
ClassMethod onAfter() As %Status
{
try {
write "START INSTALLER",!
set vars("Namespace") = ..#Namespace
set vars("Dir") = ..getDir()
set sc = ..setup(.vars)
write !,$System.Status.GetErrorText(sc),!
set sc = ..createWebApp()
} catch ex {
set sc = ex.AsStatus()
write !,$System.Status.GetErrorText(sc),!
}
quit sc
}
/// Modify web app REST
ClassMethod createWebApp(appName As %String = "/forms") As %Status
{
set:$e(appName)'="/" appName = "/" _ appName
#dim sc As %Status = $$$OK
new $namespace
set $namespace = "%SYS"
if '##class(Security.Applications).Exists(appName) {
set props("AutheEnabled") = $$$AutheUnauthenticated
set props("NameSpace") = "USER"
set props("IsNameSpaceDefault") = $$$NO
set props("DispatchClass") = "Form.REST.Main"
set props("MatchRoles")=":" _ $$$AllRoleName
set sc = ##class(Security.Applications).Create(appName, .props)
}
quit sc
}
ClassMethod getDir() [ CodeMode = expression ]
{
##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR"))
}
}
为了创建非持久化数据库,我会使用 /usr/irissys/mgr 的一个子目录,它不是持久的。 请注意,调用 ##class(%File).ManagerDirectory() 会返回持久化目录的路径,而不是内部容器目录的路径。
持续交付配置
查看第 7 部分以了解完整信息,但我们需要做的就是在现有配置中添加这两行(加粗)代码。
run image:
stage: run
environment:
name: $CI_COMMIT_REF_NAME
url: http://$CI_COMMIT_REF_SLUG.docker.eduard.win/index.html
tags:
- test
script:
- docker run -d
--expose 52773
--volume /InterSystems/durable/$CI_COMMIT_REF_SLUG:/data
--env ISC_DATA_DIRECTORY=/data/sys
--env VIRTUAL_HOST=$CI_COMMIT_REF_SLUG.docker.eduard.win
--name iris-$CI_COMMIT_REF_NAME
docker.eduard.win/test/docker:$CI_COMMIT_REF_NAME
--log $ISC_PACKAGE_INSTALLDIR/mgr/messages.logvolume 参数将主机目录挂载到容器中,ISC_DATA_DIRECTORY 变量告诉 InterSystems IRIS 使用哪个目录。 引用文档内容:
当您使用这些选项运行 InterSystems IRIS 容器时,会发生以下情况:
指定的外部卷被挂载。 如果由 ISC_DATA_DIRECTORY 环境变量指定的持久化 %SYS 目录(上述示例中的 iconfig/)已经存在且包含持久化 %SYS 数据,则实例的所有内部指针都会重置到该目录,实例使用其中包含的数据。 如果 ISC_DATA_DIRECTORY 环境变量指定的持久化 %SYS 目录已经存在但不包含持久化 %SYS 数据,则不会复制数据,实例使用容器内安装树中的数据运行,这意味着实例特定的数据不是持久的。 因此,您可能需要在脚本中包含对此条件的检查,然后再运行容器。 如果 ISC_DATA_DIRECTORY 指定的持久化 %SYS 目录不存在:
指定的持久化 %SYS 目录已创建。 将持久化 %SYS 目录内容中列出的目录和文件从其安装位置复制到持久化 %SYS 目录(原始文件仍留在原位)。 实例的所有内部指针都会重置到持久化 %SYS 目录,实例使用其中包含的数据。
更新
当应用程序演进并发布新版本(容器)时,有时您可能需要运行一些代码。 这可能是预编译/后编译挂钩、架构迁移、单元测试,但归根结底,您需要运行任意代码。 这就是为什么您需要一个管理应用程序的框架。 在之前的文章中,我概述了这种框架的基本结构,但它当然可以大幅度扩展以满足特定应用程序的需求。
结论
创建容器化应用程序需要一些思考,但 InterSystems IRIS 提供了几个功能,可以让此流程更加轻松。