#持续集成

0 关注者 · 23 帖子

在软件工程中,持续集成 (CI) 是一天多次将所有开发者的工作副本合并到共享主线上的实践。

文章 Michael Lei · 九月 27, 2024 11m read

欢迎来到我的 CI/CD 系列的下一个章节,我们将探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。

今天,我们来谈谈互操作性。

问题

当您有一个有效的互操作性生产时,您有两个独立的流程:一个是处理消息的可以正常运行的生产流程,另一个是更新代码、生产配置和系统默认设置的 CI/CD 流程。

显然,CI/CD 流程会影响互操作性。 但问题是:

  • 更新期间究竟发生了什么?
  • 我们需要做些什么以在更新期间尽可能缩短或消除生产停机时间?

术语

  • 业务主机 (BH) – 互操作性生产的一个可配置元素:业务服务 (BS)、业务流程(BP、BPL)或业务操作 (BO)。
  • 业务主机作业 (Job) – 运行业务主机代码并由互操作性生产管理的 InterSystems IRIS 作业。
  • 生产 – 业务主机的互联集合。
  • 系统默认设置 (SDS) – 特定于安装 InterSystems IRIS 的环境的值。
  • 有效消息 – 当前正在由某个业务主机作业处理的请求。 一个业务主机作业最多只能有一条有效消息。 没有有效消息的业务主机作业处于空闲状态。

发生了什么?

我们从生产生命周期开始。

生产启动

首先,可以启动生产。 每个命名空间只能同时运行一个生产,通常而言(除非您真正知道自己在做什么以及为什么这样做),每个命名空间内只能运行一个生产。 不推荐在一个命名空间中于两个或多个不同的生产之间来回切换。 启动生产会启动生产中定义的所有已启用的业务主机。 某些业务主机启动失败不会影响生产启动。

提示:

  • 可以从系统管理门户或者调用以下代码来启动生产:##class(Ens.Director).StartProduction("ProductionName")
  • 通过实现 OnStart 方法可以在生产启动时(在启动任何业务主机作业之前)执行任意代码
  • 生产启动是一个可审核事件。 您始终可以在审核日志中查看是谁在何时启动的生产。

生产更新

在生产启动后,Ens.Director 会持续监视正在运行的生产。 生产存在两种状态:目标状态,在生产类和系统默认设置中定义;以及_运行状态_ – 当前运行的作业及其创建时应用的设置。 如果所需状态与当前状态相同,则一切正常;但如果有差异,就可以(也应该)更新生产。 通常,您会在系统管理门户的“生产配置”页面上看到一个红色的 Update 按钮。

更新生产意味着尝试使当前生产状态与目标生产状态匹配。

当您运行 ##class(Ens.Director).UpdateProduction(timeout=10, force=0) 以更新生产时,它会为每个业务主机执行以下操作:

  1. 将有效设置与生产/SDS/类设置进行比较
  2. 当且仅当 (1) 显示不匹配时,业务主机会被标记为过时并需要更新。

为每个业务主机运行此操作后,UpdateProduction 会构建一组更改:

  • 要停止的业务主机
  • 要启动的业务主机
  • 要更新的生产设置

然后,应用这些更改。

通过这种方式,“更新”设置而不更改任何内容不会导致生产停机。

提示:

  • 可以从系统管理门户或者调用以下代码来更新生产:##class(Ens.Director).UpdateProduction(timeout=10, force=0)
  • 默认的系统管理门户更新超时为 10 秒。 如果您知道处理消息需要更长时间,请调用 Ens.Director:UpdateProduction 并设置更长的超时。
  • 更新超时是一个生产设置,您可以将其更改为更大的值。 此设置适用于系统管理门户。

代码更新

UpdateProduction 不会使用过时的代码更新 BH。 这是一种以安全为导向的行为,但如果您想要在底层代码更改时自动更新所有正在运行的 BH,请按以下步骤操作:

首先,按以下方式加载和编译:

do $system.OBJ.LoadDir(dir, "", .err, 1, .load)
do $system.OBJ.CompileList(load, "curk", .errCompile, .listCompiled)

现在,listCompiled 将包含由于 u 标志而实际编译的所有条目(使用 git 差异来尽可能减小加载集)。 使用此 listCompiled 获取所有已编译类的 $lb:

set classList = ""
set class = $o(listCompiled(""))
while class'="" { 
  set classList = classList _ $lb($p(class, ".", 1, *-1))
  set class=$o(listCompiled(class))
}

然后,计算需要重启的 BH 的列表:

SELECT %DLIST(Name) bhList
FROM Ens_Config.Item 
WHERE 1=1
  AND Enabled = 1
  AND Production = :production
  AND ClassName %INLIST :classList

最后,在获取 bhList 后,停止并启动受影响的主机:

for stop = 1, 0 {
  for i=1:1:$ll(bhList) {
    set host = $lg(bhList, i)
    set sc = ##class(Ens.Director).TempStopConfigItem(host, stop, 0)
  }
  set sc = ##class(Ens.Director).UpdateProduction()
}

生产停止

生产可以停止,这意味着向所有业务主机作业发送关闭请求(如果存在,则在它们处理完有效消息后安全地关闭)。

提示:

  • 可以从系统管理门户或者调用以下代码来停止生产:##class(Ens.Director).StopProduction(timeout=10, force=0)
  • 默认的系统管理门户停止超时为 120 秒。 如果您知道处理消息需要更长时间,请调用 Ens.Director:StopProduction 并设置更长的超时。
  • 关闭超时是一个生产设置。 您可以将其更改为更大的值。 此设置适用于系统管理门户。
  • 通过实现 OnStop 方法可以在生产停止时执行任意代码
  • 生产停止是一个可审核事件,您始终可以在审核日志中查看是谁在何时停止的生产。

重要的一点是,生产是业务主机的总和:

  • 启动生产意味着启动所有已启用的业务主机。
  • 停止生产意味着停止所有正在运行的业务主机。
  • 更新生产意味着计算出过时的业务主机的子集,因此首先停止它们,然后立即重新启动。 此外,新增的业务主机只会启动,从生产中删除的业务主机只会停止。

这会将我们带到业务主机的生命周期。

业务主机启动

业务主机由相同的业务主机作业组成(根据池大小设置的值)。 启动业务主机意味着启动所有业务主机作业。 它们会并行启动。

单个业务主机作业的启动方式如下:

  1. 互操作性作业是一个将成为业务主机作业的新进程。
  2. 新进程注册为互操作性作业。
  3. 业务主机代码和适配器代码加载到进程内存中。
  4. 与业务主机和适配器相关的设置加载到内存中。 优先级顺序如下: a. 生产设置(覆盖系统默认设置和类设置)。 b. 系统默认设置(覆盖类设置)。 c. 类设置。
  5. 作业准备就绪并开始接受消息。

在完成 (4) 后,作业无法更改设置或代码,因此当您导入新的/相同的代码和新的/相同的系统默认设置时,它不会影响当前正在运行的互操作性作业。

业务主机停止

停止业务主机作业意味着:

  1. 互操作性命令作业停止接受更多消息/输入。
  2. 如果存在有效消息,业务主机作业具有一定的超时秒数来处理该消息(完成消息会结束 BO 的 OnMessage 方法,BS 的 OnProcessInput,BPL BP 的状态 S<int> 方法以及 BP 的 On* 方法)。
  3. 如果在超时前有效消息未被处理且 force=0,则生产更新会在该业务主机上失败(您会在 SMP 中看到一个红色的 Update 按钮)。
  4. 如果此列表中的任何一项为真,则表示停止成功:
    • 没有有效消息
    • 有效消息在 timeout 之前处理完
    • 有效消息在超时之前未处理完但 force=1
  5. 作业的互操作性取消注册并停止。

业务主机更新

业务主机更新意味着停止当前运行的业务主机作业并启动新作业。

业务规则、路由规则和 DTL

所有业务主机会在新版本的业务规则、路由规则和 DTL 可用时立即开始使用它们。 在这种情况下,不需要重启业务主机。

离线更新

不过,有时生产更新需要单个业务主机停机。

规则取决于新代码

考虑接下来的情况。 您有一个当前的路由规则 X,它根据任意标准将消息路由到业务流程 A 或 B。 在新提交中,您同时添加:

  • 业务流程 C
  • 新版本的路由规则 X,可以将消息路由到 A、B 或 C。

在此场景下,您不能先加载规则,然后再更新生产。 原因在于,新编译的规则会立即开始将消息路由到业务流程 C,而 InterSystems IRIS 可能尚未编译该规则,或者互操作性尚未更新以供使用。 在这种情况下,您需要禁用包含路由规则的业务主机,更新代码,更新生产,然后再次启用业务主机。

注:

  • 如果您使用生产部署文件更新生产,它会自动禁用/启用所有受影响的 BH。
  • 对于 InProc 调用的主机,编译会使调用方持有的特定主机的缓存失效。

业务主机之间的依赖关系

业务主机之间的依赖关系至关重要。 假设您有业务流程 A 和 B,其中 A 向 B 发送消息。 在新提交中,您同时添加:

  • 新版本的流程 A,可以在向 B 发送的请求中设置新属性 X
  • 新版本的流程 B,可以处理新属性 X

在此场景中,我们必须首先更新流程 B,然后更新流程 A。 您可通过以下两种方式之一完成此操作:

  • 在更新期间禁用业务主机
  • 将更新拆分为两步:首先,仅更新流程 B,然后在单独的更新中开始从流程 A 向流程 B 发送消息。

这个主题一个更具挑战性的变体是,新版本的流程 A 和流程 B 与旧版本不兼容,这需要业务主机停机。

队列

如果您知道更新后某个业务主机将无法处理旧消息,则需要确保在更新前该业务主机队列为空。 为此,请禁用所有向该业务主机发送消息的业务主机,并等到其队列变空。

BPL 业务流程中的状态更改

首先,简单介绍一下 BPL BP 的运作方式。 在您编译 BPL BP 后,会在与完整 BPL 类同名的软件包中创建两个类:

  • Thread1 类包含方法 S1、S2、... SN,对应于 BPL 中的活动
  • Context 类包含所有上下文变量,以及 BPL 将执行的下一个状态(即 S5

此外,BPL 类是持久类,可以存储当前正在处理的请求。

BPL 的工作方式是在 Thread 类中执行 S 方法并相应地更新 BPL 类表、Context 表和 Thread1 表,其中一条“正在处理”的消息是 BPL 表中的一行。 请求处理完后,BPL 会删除 BPL、ContextThread 条目。 由于 BPL BP 是异步的,通过在 S 调用之间保存信息并在不同请求之间切换,一个 BPL 作业可同时处理多个请求。 例如,BPL 处理一个请求,直至其到达 sync 活动 – 等待来自 BO 的回答。 它会将当前上下文保存到磁盘中,同时将 %NextState 属性(位于 Thread1 类中)设置为响应活动 S 方法,并在 BO 回答前继续处理其他请求。 BO 回答后,BPL 会将上下文加载到内存中,并执行与 %NextState 属性中保存的状态对应的方法。

现在,当我们更新 BPL 时会发生什么? 首先,我们需要检查是否至少满足以下两个条件之一:

  • 更新期间,上下文表为空,这意味着没有正在处理的有效消息。
  • 新状态与旧状态相同,或者新状态是在旧状态之后添加的。

如果至少满足一个条件,我们就可以开始更新。 要么没有需要更新后 BPL 处理的更新前请求,要么状态是在结束时添加的,这意味着旧请求也可以进入其中(假设更新前请求与更新后 BPL 活动和处理兼容)。

但是,如果您有正在处理的有效请求,而 BPL 更改了状态顺序,该怎么办? 理想情况下,如果您可以等待,则禁用 BPL 调用方并等待队列为空。 验证上下文表是否也为空。 请记住,队列只显示未处理的请求,而上下文表存储正在处理的请求,因此您可能会遇到一个非常繁忙的 BPL 显示零队列大小的情况,这是正常的。 之后,禁用 BPL,执行更新并启用所有之前禁用的业务主机。

如果无法实现(通常是在 BPL 非常长的情况下,例如,我记得我更新过一个花了大约一周时间处理请求的 BPL,或者更新窗口太短),请使用 BPL 版本控制

或者,您也可以编写一个更新脚本。 在此更新脚本中,将旧的后续状态映射到新的后续状态并在 Thread1 表上运行,以便更新的 BPL 可以处理旧请求。 当然,在更新期间必须禁用 BPL。 也就是说,这是一种极为罕见的情况,通常您不必这样做,但如果您需要这样做,方法就是如此。

结论

为了将底层代码更改后实现生产所需的操作数降到最低,互操作性实现了一种复杂的算法。 每次 SDS 更新时,调用具有安全超时的 UpdateProduction。 对于每次代码更新,您都需要决定一种更新策略。

通过使用 git 差异减少编译的代码量有助于缩短编译时间,但利用自身“更新”代码并重新编译它或使用相同的值“更新”设置不会触发或要求进行生产更新。

更新和编译业务规则、路由规则和 DTL 可使它们在未进行生产更新的情况下立即可用。

最后,生产更新是一项安全操作,通常不需要停机。

链接

在本文撰写期间,@James MacKeith@Dmitry Zasypkin@Regilo Regilio Guedes de Souza 提供了宝贵的帮助,作者对此深表感谢。

0
0 117
文章 Michael Lei · 九月 27, 2024 6m read

在这一系列文章中,我想向大家介绍并探讨使用 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.log

volume 参数将主机目录挂载到容器中,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 提供了几个功能,可以让此流程更加轻松。

0
0 97
文章 Michael Lei · 九月 27, 2024 9m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD
  • 为何使用容器?
  • 容器基础架构
  • 使用容器的 CD
  • 使用 ICM 的 CD

在本文中,我们将使用 InterSystems Cloud Manager 构建持续交付。 ICM 是一个面向基于 InterSystems IRIS 的应用程序的云配置和部署解决方案。 它允许您定义所需部署配置,ICM 会自动提供这些配置。 有关详情,请参阅 ICM 概述

工作流

在我们的持续交付配置中,我们将完成以下任务:

  • 将代码推送到 GitLab 仓库
  • 构建 docker 镜像
  • 将镜像发布到 docker 注册表
  • 在测试服务器上测试镜像
  • 如果测试通过,则在生产服务器上部署

也可以用示意图形式表示此流程:

如您所见,基本操作是相同的,不过我们将使用 ICM 而不是手动方式来管理 Docker 容器。

ICM 配置

在我们开始升级容器之前,需要对它们进行配置。 因此,我们需要定义 defaults.jsondefinitions.json 来描述我们的架构。 我将为 LIVE 服务器提供这 2 个文件,TEST 服务器的 definitions 是相同的,defaults 只在 TagSystemMode 值上有所不同。

defaults.json:

{
    "Provider": "GCP",
    "Label": "gsdemo2",
    "Tag": "LIVE","SystemMode": "LIVE",
    "DataVolumeSize": "10",
    "SSHUser": "sample",
    "SSHPublicKey": "/icmdata/ssh/insecure.pub",
    "SSHPrivateKey": "/icmdata/ssh/insecure",
    "DockerImage": "eduard93/icmdemo:master",
    "DockerUsername": "eduard93",
    "DockerPassword": "...",
    "TLSKeyDir": "/icmdata/tls",
    "Credentials": "/icmdata/gcp.json",
    "Project": "elebedyu-test",
    "MachineType": "n1-standard-1",
    "Region": "us-east1",
    "Zone": "us-east1-b",
    "Image": "rhel-cloud/rhel-7-v20170719",
    "ISCPassword": "SYS",
    "Mirror": "false"
}

definitions.json

[
    {
    "Role": "DM",
    "Count": "1",
    "ISCLicense": "/icmdata/iris.key"
    }
]

在 ICM 容器内部,/icmdata 文件夹从主机挂载,并且:

  • TEST 服务器的定义放置在 /icmdata/test 文件夹中
  • LIVE 服务器的定义放置在 /icmdata/live 文件夹中

获取所有必需的密钥后:

keygenSSH.sh /icmdata/ssh
keygenTLS.sh /icmdata/tls

并将必需的文件放置在 /icmdata 中:

  • iris.key
  • gcp.json(用于部署到 Google Cloud Platform)

调用 ICM 来配置您的实例:

cd /icmdata/test
icm provision
icm run
cd /icmdata/live
icm provision
icm run

这将为每个 TEST 和 LIVE 服务器配置一个独立的 InterSystems IRIS 实例。

有关更详细的指南,请参阅 ICM 概述

构建

首先,我们需要构建镜像。

我们的代码通常存储在仓库中,CD 配置位于 gitlab-ci.yml 中,但为了提高安全性,我们会在构建服务器上存储几个服务器特定的文件。

iris.key

许可证密钥。 或者,它可以在容器构建过程中下载,而不是存储在服务器上。 将密钥存储在仓库中非常不安全。

pwd.txt

包含默认密码的文件。 将这类文件存储在仓库中也非常不安全。 此外,如果您在单独的服务器上托管生产环境,它可能有不同的默认密码。

load_ci_icm.script

初始脚本,它执行以下任务:

  • 加载安装程序
  • 安装程序执行应用程序初始化
  • 加载代码
set dir = ##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR"))
do ##class(%SYSTEM.OBJ).Load(dir _ "Installer/Global.cls","cdk")
do ##class(Installer.Global).init()
halt

请注意,第一行有意留空。

与之前的示例相比,有几个不同之处。 首先,我们没有启用操作系统身份验证,因为 ICM 会与容器交互而不是直接与 GitLab 交互。 其次,我使用安装程序清单来初始化我们的应用程序,以展示不同的初始化方式。 有关安装程序的更多信息,请参阅这篇文章。 最后,我们将在 Docker Hub 中以私有仓库的形式发布我们的镜像。

 

Installer/Global.cls

我们的安装程序清单如下所示:

<Manifest>
    <Log Text="Creating namespace ${Namespace}" Level="0"/>
    <Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="" Data="IRISTEMP">
        <Configuration>
            <Database Name="${Namespace}" Dir="${MGRDIR}/${Namespace}" Create="yes" MountRequired="true" Resource="%DB_${Namespace}" PublicPermissions="RW" MountAtStartup="true"/>
        </Configuration>

        <Import File="${Dir}MyApp" Recurse="1" Flags="cdk" IgnoreErrors="1" />
    </Namespace>

    <Log Text="Mapping to USER" Level="0"/>
    <Namespace Name="USER" Create="no" Code="USER" Data="USER" Ensemble="0">
        <Configuration>
            <Log Text="Mapping MyApp package to USER namespace" Level="0"/>
            <ClassMapping From="${Namespace}" Package="MyApp"/>
        </Configuration>

        <CSPApplication  Url="/"      Directory="${Dir}client" AuthenticationMethods="64" IsNamespaceDefault="false" Grant="%ALL"  />
        <CSPApplication  Url="/myApp" Directory="${Dir}"       AuthenticationMethods="64" IsNamespaceDefault="false" Grant="%ALL"  />
    </Namespace>
</Manifest>

它实现了以下更改:

  1. 创建应用程序命名空间。
  2. 创建应用程序代码数据库(数据将存储在 USER 数据库中)。
  3. 将代码加载到应用程序代码数据库中。
  4. 将 MyApp 软件包映射到 USER 命名空间。
  5. 创建 2 个 Web 应用程序:分别用于 HTML 和 REST。

gitlab-ci.yml

现在,继续持续交付配置:

build image:
  stage: build
  tags:
    - master
  script:
    - cp -r /InterSystems/mount ci
    - cd ci
    - echo 'SuperUser' | cat - pwd.txt load_ci_icm.script > temp.txt
    - mv temp.txt load_ci.script
    - cd ..
    - docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t eduard93/icmdemo:$CI_COMMIT_REF_NAME .

这里会执行哪些操作?

首先,由于 docker build 只能访问基础构建目录(在我们的示例中为仓库根目录)的子目录,我们需要将我们的“秘密”目录(其中包含 iris.keypwd.txtload_ci_icm.script)复制到克隆的仓库中。

接下来,首次终端访问需要用户名/密码,因此我们会将这些信息添加到 load_ci.script 中(这也是 load_ci.script 开头一行留空的原因)。

最后,我们会构建 docker 镜像并适当地为其添加标签:eduard93/icmdemo:$CI_COMMIT_REF_NAME

其中,$CI_COMMIT_REF_NAME 是当前分支的名称。 请注意,镜像标签的第一部分应与 GitLab 中的项目名称相同,这样才能在 GitLab 的“注册表”标签页中看到它(“注册表”标签页中提供了关于添加标签的说明)。

Dockerfile

构建 docker 镜像是通过 Dockerfile 完成的,具体如下:

FROM intersystems/iris:2018.1.1-released

ENV SRC_DIR=/tmp/src
ENV CI_DIR=$SRC_DIR/ci
ENV CI_PROJECT_DIR=$SRC_DIR

COPY ./ $SRC_DIR

RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/ \
 && cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/ \
 && $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt \
 && iris start $ISC_PACKAGE_INSTANCENAME \
 && irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script \
 && iris stop $ISC_PACKAGE_INSTANCENAME quietly

我们从基本的 iris 容器开始。

首先,我们将仓库(和“秘密”目录)复制到容器中。

接下来,我们将许可证密钥复制到 mgr 目录中。

然后,我们将密码更改为 pwd.txt 中的值。 请注意,此操作会删除 pwd.txt。

之后,实例启动并执行 load_ci.script。

最后,iris 实例停止。

请注意,我使用的是 GitLab Shell 执行器,而不是 Docker 执行器。 当您需要从镜像内部提取某些内容时,将使用 Docker 执行器,例如在 Java 容器中构建 Android 应用程序并且只需要一个 apk 时。 在我们的示例中,我们需要整个容器,因此需要使用 Shell 执行器。 因此,我们通过 GitLab Shell 执行器运行 Docker 命令。

发布

现在,我们将镜像发布到 Docker Hub

publish image:
  stage: publish
  tags:
    - master
  script:
    - docker login -u eduard93 -p ${DOCKERPASSWORD}
    - docker push eduard93/icmdemo:$CI_COMMIT_REF_NAME

注意 ${DOCKERPASSWORD} 变量,它是 GitLab 的秘密变量。 我们可以在“GitLab > 项目 > 设置 > CI/CD > 变量”中添加它们:

作业日志中也不包含密码值:

Running with gitlab-runner 10.6.0 (a3543a27)
  on icm 82634fd1
Using Shell executor...
Running on docker...
Fetching changes...
Removing ci/
HEAD is now at 8e24591 Add deploy to LIVE
Checking out 8e245910 as master...Skipping Git submodules setup$ docker login -u eduard93 -p ${DOCKERPASSWORD}
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded
$ docker push eduard93/icmdemo:$CI_COMMIT_REF_NAME
The push refers to repository [docker.io/eduard93/icmdemo]
master: digest: sha256:d1612811c11154e77c84f0c08a564a3edeb7ddbbd9b7acb80754fda97f95d101 size: 2620
Job succeeded

在 Docker Hub 中,我们可以看到新镜像:

 

运行

我们有了镜像,接下来在我们的测试服务器上运行它。 脚本如下。

run image:
  stage: run
  environment:
    name: $CI_COMMIT_REF_NAME
  tags:
    - master
  script:
    - docker exec icm sh -c "cd /icmdata/test && icm upgrade -image eduard93/icmdemo:$CI_COMMIT_REF_NAME"

使用 ICM,我们只需要运行一个命令 (icm upgrade) 即可升级现有部署。 我们通过运行“docker exec icm sh -c”来调用它,这会在 icm 容器内执行指定的命令。首先,我们进入 /icmdata/test,这里定义了我们为 TEST 服务器准备的 ICM 部署定义。 之后,我们调用 icm upgrade 将当前存在的容器替换为新容器。

测试

我们来运行一些测试。

test image:
  stage: test
  tags:
    - master
  script:
    - docker exec icm sh -c "cd /icmdata/test && icm session -namespace USER -command 'do \$classmethod(\"%UnitTest.Manager\",\"RunTest\",\"MyApp/Tests\",\"/nodelete\")' | tee /dev/stderr | grep 'All PASSED' && exit 0 || exit 1"

同样,我们在 icm 容器内执行一条命令。 icm 会话在一个已部署的节点上执行命令。 该命令运行单元测试。 之后,它将所有输出传输到屏幕,同时也传输给 grep 以查找单元测试结果,并成功退出进程或以出错方式退出。

部署

生产服务器上的部署与测试服务器上的部署完全相同,只是为 LIVE 部署定义使用了另一个目录。 如果测试失败,此阶段将不会被执行。

deploy image:
  stage: deploy
  environment:
    name: $CI_COMMIT_REF_NAME
  tags:
    - master
  script:
    - docker exec icm sh -c "cd /icmdata/live && icm upgrade -image eduard93/icmdemo:$CI_COMMIT_REF_NAME"

结论

ICM 为您提供了一种简单直观的方式来配置云基础架构并在它上面部署服务,帮助您立即进入云,无需大规模开发或重新工具化。 基础架构即代码 (IaC) 和容器化部署的优势使您可以轻松地在公共云平台(如 Google、Amazon 和 Azure)或者在您的私有 VMware vSphere 云上部署基于 InterSystems IRIS 的应用程序。 定义您的需求,发出几条命令,ICM 会完成其余工作。
即使您已经在使用云基础架构、容器或两者,ICM 也能通过自动执行大量原本需要手动完成的步骤,极大地减少配置和部署应用程序所需的时间和精力。
0
0 87
文章 Michael Lei · 九月 27, 2024 9m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD
  • 为何使用容器
  • 容器基础架构
  • 使用容器的 CD

第一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。

第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程,并介绍了持续交付。

第三篇文章中,我们介绍了 GitLab 安装和配置以及将环境连接到 GitLab

第四篇文章中,我们编写了 CD 配置。

第五篇文章中,我们讨论了容器以及使用容器的方式(和原因)。

第六篇文章中,我们将探讨运行包含容器的持续交付管道所需的主要组件以及这些组件如何协同运行。

在这篇文章中,我们将构建上一篇文章中探讨的持续交付配置。

工作流

在持续交付配置中,我们会:

  • 将代码推送到 GitLab 仓库
  • 构建 docker 镜像
  • 进行测试
  • 将镜像发布到 docker 注册表
  • 将旧容器换为注册表中的新版本

也可以用示意图形式表示此流程:

我们开始吧。

构建

首先,我们需要构建镜像。

我们的代码通常存储在仓库中,CD 配置位于 gitlab-ci.yml 中,但为了提高安全性,我们会在构建服务器上存储几个服务器特定的文件。

GitLab.xml

包含 CD 挂钩代码。 该代码是在上一篇文章中开发的,并在 GitHub 上提供。 这是一个小型库,用于加载代码、运行各种挂钩以及测试代码。 作为更好的替代方案,您可以使用 git 子模块将此项目或类似项目包含到您的仓库中。 最好选择子模块,因为子模块更容易保持最新状态。 另一个替代方案是在 GitLab 上为版本添加标签,并使用 ADD 命令加载这些版本。

iris.key

许可证密钥。 或者,它可以在容器构建过程中下载,而不是存储在服务器上。 将密钥存储在仓库中非常不安全。

pwd.txt

包含默认密码的文件。 将这类文件存储在仓库中也非常不安全。 此外,如果您在单独的服务器上托管生产环境,它可能有不同的默认密码。

load_ci.script

初始脚本,它执行以下任务:

  • 实现操作系统身份验证
  • 加载 GitLab.xml
  • 初始化 GitLab 实用工具设置
  • 加载代码

set sc = ##Class(Security.System).Get("SYSTEM",.Properties) write:('sc) $System.Status.GetErrorText(sc) set AutheEnabled = Properties("AutheEnabled") set AutheEnabled = $zb(+AutheEnabled,16,7) set Properties("AutheEnabled") = AutheEnabled set sc = ##Class(Security.System).Modify("SYSTEM",.Properties) write:('sc) $System.Status.GetErrorText(sc) zn "USER" do ##class(%SYSTEM.OBJ).Load(##class(%File).ManagerDirectory() _ "GitLab.xml","cdk") do ##class(isc.git.Settings).setSetting("hooks", "MyApp/Hooks/") do ##class(isc.git.Settings).setSetting("tests", "MyApp/Tests/") do ##class(isc.git.GitLab).load() halt

请注意,第一行有意留空。

由于某些设置可以是服务器特定的,脚本不会存储在仓库中,而是单独存储。 如果此初始挂钩始终相同,将其存储在仓库中即可。

gitlab-ci.yml

现在,继续持续交付配置:

构建镜像:
  stage: build
  tags:
    - test
  script:
    - cp -r /InterSystems/mount ci
    - cd ci
    - echo 'SuperUser' | cat - pwd.txt load_ci.script > temp.txt
    - mv temp.txt load_ci.script
    - cd ..
    - docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t docker.domain.com/test/docker:$CI_COMMIT_REF_NAME .

这里会执行哪些操作?

首先,由于 docker build 只能访问基础构建目录的子目录(本例中是仓库根目录),我们需要将“秘密”目录(包含 GitLab.xmliris.keypwd.txt 和 load_ci.script 的目录)复制到克隆的仓库中。

接下来,首次终端访问需要用户名/密码,因此我们会将这些信息添加到 load_ci.script 中(这也是 load_ci.script 开头一行留空的原因)。

最后,我们会构建 docker 镜像并适当地为其添加标签:docker.domain.com/test/docker:$CI_COMMIT_REF_NAME

其中,$CI_COMMIT_REF_NAME 是当前分支的名称。 请注意,镜像标签的第一部分应与 GitLab 中的项目名称相同,这样才能在 GitLab 的“注册表”标签页中看到它(“注册表”标签页中提供了关于添加标签的说明)。

Dockerfile

构建 docker 镜像是通过 Dockerfile 完成的,具体如下:

FROM docker.intersystems.com/intersystems/iris:2018.1.1.611.0

ENV SRC_DIR=/tmp/src ENV CI_DIR=$SRC_DIR/ci ENV CI_PROJECT_DIR=$SRC_DIR

COPY ./ $SRC_DIR

RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/
&& cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/
&& $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt
&& iris start $ISC_PACKAGE_INSTANCENAME
&& irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script
&& iris stop $ISC_PACKAGE_INSTANCENAME quietly

我们从基本的 iris 容器开始。

首先,我们将仓库(和“秘密”目录)复制到容器中。

接下来,我们将许可证密钥和 GitLab.xml 复制到 mgr 目录中。

然后,我们将密码更改为 pwd.txt 中的值。 请注意,此操作会删除 pwd.txt。

之后,实例启动并执行 load_ci.script。

最后,iris 实例停止。

以下是作业日志(部分日志,跳过了加载/编译日志):

Running with gitlab-runner 10.6.0 (a3543a27)
  on docker 7b21e0c4
Using Shell executor...
Running on docker...
Fetching changes...
Removing ci/
Removing temp.txt
HEAD is now at 5ef9904 Build load_ci.script
From http://gitlab.eduard.win/test/docker
   5ef9904..9753a8d  master     -> origin/master
Checking out 9753a8db as master...
Skipping Git submodules setup
$ cp -r /InterSystems/mount ci
$ cd ci
$ echo 'SuperUser' | cat - pwd.txt load_ci.script > temp.txt
$ mv temp.txt load_ci.script
$ cd ..
$ docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t docker.eduard.win/test/docker:$CI_COMMIT_REF_NAME .
Sending build context to Docker daemon  401.4kB

Step 1/6 : FROM docker.intersystems.com/intersystems/iris:2018.1.1.611.0 ---> cd2e53e7f850 Step 2/6 : ENV SRC_DIR=/tmp/src ---> Using cache ---> 68ba1cb00aff Step 3/6 : ENV CI_DIR=$SRC_DIR/ci ---> Using cache ---> 6784c34a9ee6 Step 4/6 : ENV CI_PROJECT_DIR=$SRC_DIR ---> Using cache ---> 3757fa88a28a Step 5/6 : COPY ./ $SRC_DIR ---> 5515e13741b0 Step 6/6 : RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/ && cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/ && $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt && iris start $ISC_PACKAGE_INSTANCENAME && irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script && iris stop $ISC_PACKAGE_INSTANCENAME quietly ---> Running in 86526183cf7c . Waited 1 seconds for InterSystems IRIS to start This copy of InterSystems IRIS has been licensed for use exclusively by: ISC Internal Container Sharding Copyright (c) 1986-2018 by InterSystems Corporation Any other use is a violation of your license agreement

%SYS> 1

%SYS> Using 'iris.cpf' configuration file

This copy of InterSystems IRIS has been licensed for use exclusively by: ISC Internal Container Sharding Copyright (c) 1986-2018 by InterSystems Corporation Any other use is a violation of your license agreement

1 alert(s) during startup. See messages.log for details. Starting IRIS

Node: 39702b122ab6, Instance: IRIS

Username: Password:

Load started on 04/06/2018 17:38:21 Loading file /usr/irissys/mgr/GitLab.xml as xml Load finished successfully.

USER>

USER>

[2018-04-06 17:38:22.017] Running init hooks: before

[2018-04-06 17:38:22.017] Importing hooks dir /tmp/src/MyApp/Hooks/

[2018-04-06 17:38:22.374] Executing hook class: MyApp.Hooks.Global

[2018-04-06 17:38:22.375] Executing hook class: MyApp.Hooks.Local

[2018-04-06 17:38:22.375] Importing dir /tmp/src/

Loading file /tmp/src/MyApp/Tests/TestSuite.cls as udl

Compilation started on 04/06/2018 17:38:22 with qualifiers 'c' Compilation finished successfully in 0.194s.

Load finished successfully.

[2018-04-06 17:38:22.876] Running init hooks: after

[2018-04-06 17:38:22.878] Executing hook class: MyApp.Hooks.Local

[2018-04-06 17:38:22.921] Executing hook class: MyApp.Hooks.Global Removing intermediate container 39702b122ab6 ---> dea6b2123165 [Warning] One or more build-args [CI_PROJECT_DIR] were not consumed Successfully built dea6b2123165 Successfully tagged docker.domain.com/test/docker:master Job succeeded

请注意,我使用的是 GitLab Shell 执行器,而不是 Docker 执行器。 当您需要从镜像内部提取某些内容时,将使用 Docker 执行器,例如在 Java 容器中构建 Android 应用程序并且只需要一个 apk 时。 在我们的示例中,我们需要整个容器,因此需要使用 Shell 执行器。 因此,我们通过 GitLab Shell 执行器运行 Docker 命令。

 

运行

我们构建了镜像,接下来要运行镜像。如果是功能分支,我们销毁旧容器并启动新容器即可。 如果是环境,我们可以运行临时容器,在测试成功的情况下,可以替换环境容器(此内容留给读者作为练习)。

脚本如下。

destroy old:
  stage: destroy
  tags:
    - test
  script:
    - docker stop iris-$CI_COMMIT_REF_NAME || true
    - docker rm -f iris-$CI_COMMIT_REF_NAME || true

此脚本会销毁当前运行的容器,并且始终都会成功(默认情况下,如果 docker 尝试停止/移除不存在的容器,则会失败)。

接下来,我们启动新镜像并将其注册为环境。 Nginx 容器 使用 VIRTUAL_HOST 环境变量和 expose 指令(用于了解要代理的端口)自动代理请求。

run image:
  stage: run
  environment:
    name: $CI_COMMIT_REF_NAME
    url: http://$CI_COMMIT_REF_SLUG. docker.domain.com/index.html
  tags:
    - test
  script:
    - docker run -d
      --expose 52773
      --env VIRTUAL_HOST=$CI_COMMIT_REF_SLUG.docker.eduard.win
      --name iris-$CI_COMMIT_REF_NAME
      docker.domain.com/test/docker:$CI_COMMIT_REF_NAME
      --log $ISC_PACKAGE_INSTALLDIR/mgr/messages.log

 

测试

我们来运行一些测试。

test image:
  stage: test
  tags:
    - test
  script:
    - docker exec iris-$CI_COMMIT_REF_NAME irissession iris -U USER "##class(isc.git.GitLab).test()"

发布

最后,我们将镜像发布到注册表中

publish image:
  stage: publish
  tags:
    - test
  script:
    - docker login docker.domain.com -u dev -p 123
    - docker push docker.domain.com/test/docker:$CI_COMMIT_REF_NAME

可以使用 GitLab 秘密变量传递用户名/密码。

现在,我们可以在 GitLab 中看到该镜像:

其他开发者可以从注册表中拉取该镜像。 “环境”标签页上提供了我们所有的环境,可以轻松进行浏览:

 

结论

在这一系列文章中,我介绍了持续交付的常规方式。 这是一个涉及面非常广的话题,您应将这一系列文章视为方法集合,而不是确定性内容。 如果您想自动构建、测试和交付应用程序,可以选择持续交付(一般情况)和 GitLab(特殊情况)。 利用持续交付和容器,您可以根据需要自定义工作流。

链接

后续内容

就是这些! 希望我已介绍了持续交付和容器的基础知识。

但还有一些主题我没有介绍(可能后面会介绍),特别是关于容器的内容:

  • 数据可以在容器外持久保持,以下是相关文档
  • kubernetes 等编排平台
  • InterSystems Cloud Manager
  • 环境管理 – 创建临时环境以进行测试,在功能分支合并后移除旧环境
  • Docker compose 可以实现多容器部署
  • 缩减 docker 镜像大小并缩短构建时间
  • ...
0
0 110
文章 Michael Lei · 九月 27, 2024 2m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD
  • 为何使用容器?
  • 容器基础架构
  • 使用容器的 GitLab CI/CD

第一篇文章中,我们介绍了 Git 基础知识,以及为什么对 Git 概念的高层次理解对于现代软件开发如此重要,以及如何使用 Git 开发软件。

第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程和持续交付。

第三篇文章中,我们介绍了 GitLab 的安装和配置以及如何将环境连接到 GitLab。

第四篇文章中,我们编写了 CD 配置。

第五篇文章中,我们讨论了容器与如何(以及为什么)使用它们。

在本文中,我们将探讨运行使用容器的持续交付管道所需的主要组件,以及它们如何协同工作。

我们的配置如下所示:

在这里,我们可以看到三个主要阶段的分离:

  • 构建
  • 传送
  • 运行

构建

在之前的部分中,构建通常是增量式 – 我们计算当前环境与当前代码库之间的差异,并修改我们的环境以与代码库相对应。 使用容器时,每次构建都是完整构建。 构建的结果是一个可以通过依赖关系在任何地方运行的镜像。

传送

在我们的镜像构建并通过测试后,它会被上传到注册表 – 用于托管 docker 镜像的专用服务器。 在那里,它可以替换具有相同标签的旧镜像。 例如,由于对 master 分支的新提交,我们构建了新镜像 (project/version:master),如果测试通过,我们可以用相同标签的新镜像替换注册表中的旧镜像,这样,所有拉取 project/version:master 的人都会获得新版本。

运行

最后,我们的镜像部署完成。 CI 解决方案(如 GitLab)可以控制此流程,也可以由专门的编排器控制,但目的都一样 – 执行一些镜像,定期检查健康状态,如果有新镜像版本可用就进行更新。

查看 docker 网络研讨会以了解这些不同阶段的解释。

或者,从提交的角度来看:<

在我们的交付配置中,我们会:

  • 将代码推送到 GitLab 仓库
  • 构建 docker 镜像
  • 测试该镜像
  • 将镜像发布到我们的 docker 注册表
  • 用新版本替换注册表中的旧容器

为此,我们需要:

  • Docker
  • Docker 注册表
  • 注册域(可选但推荐)
  • GUI 工具(可选)

 

Docker

首先,我们需要在某个地方运行 docker。 我建议从一台运行主流 Linux 发行版(如 Ubuntu、RHEL 或 Suse)的服务器开始。 不要使用面向云的发行版(如 CoreOS、RancherOS 等) - 它们并不适合初学者。 别忘了将存储驱动程序切换为 devicemapper。 

如果我们在讨论大规模部署,那么使用像 Kubernetes、Rancher 或 Swarm 这样的容器编排工具可以自动执行大多数任务,但我们不会探讨它们(至少在这一部分中)。

 

Docker 注册表

这是我们需要运行的第一个容器,它是一个无状态、可扩缩的服务器端应用程序,作用是存储和分发 Docker 镜像。
您应当在想要实现以下目的时使用注册表:

  •  严格控制您的镜像存储位置
  •  完全掌控您的镜像分发管道
  •  将镜像存储和分发紧密整合到您的内部开发工作流中

此处提供了注册表的文档

连接注册表和 GitLab

注:GitLab 包含内置注册表。 您可以运行它而不使用外部注册表。 请阅读本段中链接的 GitLab 文档。

要将您的注册表连接到 GitLab,您需要运行支持 HTTPS的注册表 – 我使用 Let's Encrypt 获取证书,并按照此 Gist 获取证书并将其传递到容器中。 在确认注册表可以通过 HTTPS 访问后(您可以从浏览器检查),请按照这些说明将注册表连接到 GitLab。根据您的需要和 GitLab 安装情况,这些说明会有所不同。就我而言,配置是将注册表证书和密钥(正确命名且具有正确的权限)添加到 /etc/gitlab/ssl 并将以下行添加到 /etc/gitlab/gitlab.rb 中:

registry_external_url 'https://docker.domain.com'
gitlab_rails['registry_api_url'] = "https://docker.domain.com"

重新配置 GitLab 后,我可以看到一个新的注册表标签页,其中提供了如何正确为新构建的镜像添加标签以便它们会出现在这里的信息。

 

在我们的持续交付配置中,我们将为每个分支自动构建一个镜像,如果镜像通过测试,那么我们会将其发布到注册表并自动运行,因此我们的应用程序将在所有“状态”下自动可用,例如,我们可以访问:

  • 几个功能分支,网址为 <featureName>.docker.domain.com
  • 测试版本,网址为 master.docker.domain.com
  • 试生产版本,网址为 preprod.docker.domain.com
  • 生产版本,网址为 prod.docker.domain.com

为此,我们需要一个域名并添加一条通配符 DNS 记录,将 *.docker.domain.com 指向 docker.domain.com 的 IP 地址。 另一种选项是使用不同的端口。

Nginx 代理

由于我们有几个功能分支,需要将子域自动重定向到正确的容器。 为此,我们可以使用 Nginx 作为反向代理。 此处提供了一份指南

GUI 工具

要开始使用容器,您可以使用命令行或 GUI 界面之一。 有许多可用工具,例如:

  • Rancher
  • MicroBadger
  • Portainer
  • Simple Docker UI
  • ...

它们允许您从 GUI 而不是 CLI 创建和管理容器。 下面是 Rancher 的外观:

 

GitLab Runner

与之前相同,要在其他服务器上执行脚本,我们需要安装 GitLab 运行程序。 我在第三篇文章中探讨过这一点。

请注意,您需要使用 Shell 执行器而不是 Docker 执行器。 当您需要从镜像内部提取某些内容时,将使用 Docker 执行器,例如在 Java 容器中构建 Android 应用程序并且只需要一个 apk 时。 在我们的示例中,我们需要整个容器,因此需要使用 Shell 执行器。

 

结论

开始运行容器十分容易,并且有许多工具可供选择。

使用容器的持续交付与常规的持续交付配置在以下几个方面有所不同:

  • 依赖关系的需求在构建时得到满足,并且在镜像构建后无需考虑依赖关系。
  • 可重现性 – 您可以通过本地运行相同的容器轻松重现任何现有环境。
  • 速度 – 由于容器仅包含您显式添加的内容,它们的构建速度更快,更重要的是,它们仅需构建一次并在需要时使用。
  • 效率 – 同上,与虚拟机等相比,容器产生的开销更少。
  • 可扩缩性 – 使用编排工具,您可以根据工作负载自动扩缩应用程序,并且仅消耗当前所需的资源。

后续内容

在下一篇文章中,我们将创建一个利用 InterSystems IRIS Docker 容器的 CD 配置。

0
0 148
文章 Michael Lei · 九月 27, 2024 4m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD
  • 为何使用容器
  • 使用容器的 GitLab CI/CD

第一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。

第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程,并介绍了持续交付。

第三篇文章中,我们介绍了 GitLab 安装和配置以及将环境连接到 GitLab

第四篇文章中,我们编写了 CD 配置。

在这篇文章中,我们将介绍容器以及使用容器的方法(和原因)。

这篇文章假设读者熟悉 docker 和容器的概念。 如果您想了解容器镜像,请查阅 @Luca Ravazzolo 撰写的文章。

优势

使用容器有诸多优势:

  • 可移植性
  • 效率
  • 隔离
  • 轻量
  • 不可变性

下面我们来具体介绍这些优势。

可移植性

容器将应用程序与运行所需的一切(如配置文件和依赖项)打包在一起。 这样一来,您可以在您本地台式机、物理服务器、虚拟服务器、测试、暂存、生产环境以及公共云或私有云等不同环境中轻松、可靠地运行应用程序。

可移植性的另一个优势是,在您构建 Docker 镜像并确认它可以正常运行后,它便可以在任何运行 docker 的位置(目前是 Windows、Linux 和 MacOS 服务器)运行。

效率

实际上,您只需要运行应用程序进程,而不需要运行所有系统软件等。 容器恰好可以满足这一需求 – 容器仅会运行您明确需要的进程,而不运行其他进程。 由于容器不需要单独的操作系统,占用的资源较少。 虚拟机的大小通常为几千兆字节,而容器的大小通常只有几百兆字节,因此在一台服务器上运行的容器数量要比虚拟机多得多。 由于容器对底层硬件的利用率更高,您需要的硬件更少,从而降低了裸机成本和数据中心成本。

隔离

容器会将应用程序与其他一切隔离开,同时多个容器可以在同一台服务器上运行,它们可以完全彼此独立。 容器之间的任何交互都应明确声明。 如果一个容器发生故障,它不会影响其他容器,并且可以快速重新启动。 得益于这种隔离机制,安全性也得到了保障。 例如,利用裸机上的 Web 服务器漏洞可能会让攻击者访问整个服务器,但如果使用的是容器,攻击者只能访问 Web 服务器容器。

轻量

由于容器不需要单独的操作系统,只需几秒钟即可启动、停止或重新启动,从而可以加快所有相关开发流程并缩短生产时间。 您可以更快地开始工作,并且无需花费任何时间进行配置。

不可变性

不可变基础架构由不可变组件组成,每次部署时都会替换这些组件,而不是就地更新。 这些组件通过每次部署构建一次的通用镜像启动,可以进行测试和验证。不可变性减少了不一致情况,并且可以轻松地在应用程序的不同状态之间进行复制和移动。 详细了解不可变性

新可能

利用上文提到的所有优势,我们可以通过全新的方式管理基础架构和工作流。

编排

裸机或虚拟机环境存在一个问题 – 它们具有个体性,这一问题会在使用过程中带来许多意外,而这些意外通常不尽如人意。 解决这一问题的方法是基础架构即代码 – 利用 DevOps 团队为源代码使用的版本控制方法,采用描述性模型管理基础架构。

通过基础架构即代码,无论环境的起始状态如何,部署命令始终会将目标环境设为相同的配置。 要实现这一点,可自动配置现有目标,也可以丢弃现有目标并重新创建全新的环境。

因此,利用基础架构即代码,团队会更改环境描述并对配置模型进行版本控制,而配置模型通常采用 JSON 等归档完好的代码格式。 发布管道会执行该模型,以配置目标环境。 如果团队需要进行更改,则可以编辑源代码,而不是目标环境。

借助容器,这一切都可以实现,并且实施起来简单得多。 关闭容器并启动一个新容器只需几秒钟,而配置新虚拟机则需要几分钟。 我甚至都没有提到将服务器回滚到空白状态。

扩缩

基于之前的描述,您可能认为基础架构即代码本身是静态的。 并不是这样,因为编排工具也可以根据当前工作负载提供横向扩缩(进行更多相同的配置)。 您仅应运行目前需要的容器,并相应地扩缩应用程序。 这样还可以降低成本。

结论

容器可以简化开发流程。 消除了环境之间的不一致,可以更轻松地进行测试和调试。 可以通过编排构建可扩缩应用程序。可以轻松实现部署或回滚到不可变历史的任意点。

组织希望在更高层级上处理任务,其中所有上述问题均已得到解决,并且我们发现调度程序和编排程序以自动方式为我们处理更多的任务。

后续内容

在下一篇文章中,我们将探讨使用容器进行配置,并创建利用 InterSystems IRIS Docker 容器的 CD 配置。

0
0 100
文章 Michael Lei · 九月 27, 2024 8m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD

第一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。

第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程,并介绍了持续交付。

第三篇文章中,我们介绍了 GitLab 安装和配置以及将环境连接到 GitLab

在这篇文章中,我们将介绍编写 CD 配置。

计划

环境

首先,我们需要多个环境以及与之对应的分支:

环境分支交付有权提交的角色有权合并的角色
测试master自动开发者、所有者开发者、所有者
预生产preprod自动所有者
生产prod半自动(按下按钮进行交付)

所有者

开发周期

作为示例,我们将使用 GitLab 流程开发一个新功能,并使用 GitLab CD 进行交付。

  1. 在功能分支中开发功能。
  2. 对功能分支进行审查并将其合并到 master 分支中。
  3. 一段时间(合并了多个功能)后,将 master 分支合并到 preprod 分支中
  4. 一段时间(用户测试等)后,将 preprod 分支合并到 prod 分支中

具体如下图所示(我用草图标出了我们需要为 CD 开发的部分):

  1. 开发和测试
    • 开发者将新功能的代码提交到单独的功能分支中
    • 功能稳定后,开发者将功能分支合并到 master 分支中
    • 来自 master 分支的代码被交付到测试环境,在其中进行加载和测试
  2. 交付到预生产环境
    • 开发者创建从 master 分支到 preprod 分支的合并请求
    • 仓库所有者在一段时间后批准合并请求
    • 来自 preprod 分支的代码被交付到预生产环境
  3. 交付到生产环境
    • 开发者创建从 preprod 分支到 prod 分支的合并请求
    • 仓库所有者在一段时间后批准合并请求
    • 仓库所有者按下“部署”按钮
    • 来自 prod 分支的代码被交付到生产环境

也可以用示意图形式表示此流程:

应用程序

应用程序由两部分组成:

  • 在 InterSystems 平台上开发的 REST API
  • 客户端 JavaScript web 应用程序

阶段

通过上面的计划,我们可以确定需要在持续交付配置中定义的阶段:

  • 加载 – 将服务器端代码导入 InterSystems IRIS
  • 测试 – 测试客户端和服务器代码
  • 封装 – 构建客户端代码
  • 部署 – 使用 Web 服务器“发布”客户端代码

以下是它在 gitlab-ci.yml 配置文件中的样式:

stages:
  - load
  - test
  - package
  - deploy

脚本

加载

下面我们来定义脚本。 脚本文档。 我们先来定义用于加载服务器端代码的脚本 load server

load server:
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test
  stage: load
  script: csession IRIS "##class(isc.git.GitLab).load()"

脚本会执行哪些操作?

  • load server 是脚本名称
  • 接下来,我们来描述此脚本运行的环境
  • only: master – 告知 GitLab 此脚本仅应在向 master 分支进行提交时运行
  • tags: test 指定此脚本仅应在具有 test 标签的运行程序上运行
  • stage 指定脚本的阶段
  • script 定义要执行的代码 在本例中,我们从 isc.git.GitLab 类调用类方法 load

重要说明

对于 InterSystems IRIS,请将 csession 替换为 iris session

对于 Windows,请使用:irisdb -s ../mgr -U TEST "##class(isc.git.GitLab).load()

现在,我们来编写相应的 isc.git.GitLab 类。 此类中的所有入口点如下所示:

ClassMethod method()
{
    try {
        // code
        halt
    } catch ex {
        write !,$System.Status.GetErrorText(ex.AsStatus()),!
        do $system.Process.Terminate(, 1)
    }
}

请注意,可以通过两种方式结束此方法:

  • 停止当前进程 – 在 GitLab 中注册为成功完成
  • 调用  $system.Process.Terminate – 异常终止进程,GitLab 将此情况注册为错误

因此,加载代码如下:

/// Do a full load
/// do ##class(isc.git.GitLab).load()
ClassMethod load()
{
    try {
        set dir = ..getDir()
        do ..log("Importing dir " _ dir)
        do $system.OBJ.ImportDir(dir, ..getExtWildcard(), "c", .errors, 1)
        throw:$get(errors,0)'=0 ##class(%Exception.General).%New("Load error")
    halt
} catch ex {
    write !,$System.Status.GetErrorText(ex.AsStatus()),!
    do $system.Process.Terminate(, 1)
}

}

调用了两个实用方法:

  • getExtWildcard – 获取相关文件扩展名列表
  • getDir – 获取仓库目录

如何获取目录?

执行脚本时,GitLab 会先指定很多环境变量。 其中一个环境变量是 CI_PROJECT_DIR – 克隆仓库以及运行作业位置的完整路径。 我们可以通过 getDir 方法轻松获取:

ClassMethod getDir() [ CodeMode = expression ]
{
##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR"))
}


测试

以下是测试脚本:

load test:
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test
  stage: test
  script: csession IRIS "##class(isc.git.GitLab).test()"
  artifacts:
    paths:
      - tests.html

有哪些更改? 当然是名称和脚本代码,但还添加了工件。 工件是作业成功完成后附加到作业的文件和目录列表。 本例中,测试完成后,我们可以生成重定向到测试结果的 HTML 页面,并使其可以通过 GitLab 访问。

请注意,加载阶段有很多复制粘贴的内容 – 环境是相同的,脚本部分(例如环境)可以单独标记并附加到脚本。 我们来定义测试环境:

.env_test: &env_test
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test

现在,我们的脚本如下:

load test:
  <<: *env_test
  script: csession IRIS "##class(isc.git.GitLab).test()"
  artifacts:
    paths:
      - tests.html

接下来,我们使用 UnitTest 框架执行测试。

/// do ##class(isc.git.GitLab).test()
ClassMethod test()
{
    try {
        set tests = ##class(isc.git.Settings).getSetting("tests")
        if (tests'="") {
            set dir = ..getDir()
            set ^UnitTestRoot = dir
        $$$TOE(sc, ##class(%UnitTest.Manager).RunTest(tests, "/nodelete"))
        $$$TOE(sc, ..writeTestHTML())
        throw:'..isLastTestOk() ##class(%Exception.General).%New("Tests error")
    }
    halt
} catch ex {
    do ..logException(ex)
    do $system.Process.Terminate(, 1)
}

}

本例中,测试设置是相对于存储单元测试的仓库根目录的路径。 如果此处为空,则跳过测试。 writeTestHTML 方法用于输出重定向到测试结果的 html:

ClassMethod writeTestHTML()
{
    set text = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "html").Data.Read()
    set text = $replace(text, "!!!", ..getURL())
set file = ##class(%Stream.FileCharacter).%New()
set name = ..getDir() _  "tests.html"
do file.LinkToFile(name)
do file.Write(text)
quit file.%Save()

}

ClassMethod getURL() { set url = ##class(isc.git.Settings).getSetting("url") set url = url _ $system.CSP.GetDefaultApp("%SYS") set url = url_"/%25UnitTest.Portal.Indices.cls?Index="_ $g(^UnitTest.Result, 1) _ "&$NAMESPACE=" _ $zconvert($namespace,"O","URL") quit url }

ClassMethod isLastTestOk() As %Boolean { set in = ##class(%UnitTest.Result.TestInstance).%OpenId(^UnitTest.Result) for i=1:1:in.TestSuites.Count() { #dim suite As %UnitTest.Result.TestSuite set suite = in.TestSuites.GetAt(i) return:suite.Status=0 $$$NO } quit $$$YES }

XData html { <html lang="en-US"> <head> <meta charset="UTF-8"/> <meta http-equiv="refresh" content="0; url=!!!"/> <script type="text/javascript"> window.location.href = "!!!" </script> </head> <body> If you are not redirected automatically, follow this <a href='!!!'>link to tests</a>. </body> </html> }

封装

我们的客户端是一个简单的 HTML 页面:

<html>
<head>
<script type="text/javascript">
function initializePage() {
  var xhr = new XMLHttpRequest();
  var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/version";
  xhr.open("GET", url, true);
  xhr.send();
  xhr.onloadend = function (data) {
    document.getElementById("version").innerHTML = "Version: " + this.response;
  };

var xhr = new XMLHttpRequest(); var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/author"; xhr.open("GET", url, true); xhr.send(); xhr.onloadend = function (data) { document.getElementById("author").innerHTML = "Author: " + this.response; }; } </script> </head> <body onload="initializePage()"> <div id = "version"></div> <div id = "author"></div> </body> </html>

要进行构建,需要将 ${CI_ENVIRONMENT_URL} 替换为其值。 当然,实际应用程序可能需要 npm,但此处仅为了举例说明。 脚本如下:

package client:
  <<: *env_test
  stage: package
  script: envsubst < client/index.html > index.html
  artifacts:
    paths:
      - index.html

部署

最后,我们将 index.html 部署到 Web 服务器根目录,以部署客户端。

deploy client:
  <<: *env_test
  stage: deploy
  script: cp -f index.html /var/www/html/index.html

就是这些!

多个环境

如果您需要在多个环境中执行相同(相似)的脚本,应该如何操作? 脚本部分也可以是标签,因此下面给出了在测试和预生产环境中加载代码的示例配置:

stages:
  - load
  - test

.env_test: &env_test environment: name: test url: http://test.hostname.com only: - master tags: - test

.env_preprod: &env_preprod environment: name: preprod url: http://preprod.hostname.com only: - preprod tags: - preprod

.script_load: &script_load stage: load script: csession IRIS "##class(isc.git.GitLab).loadDiff()"

load test: <<: *env_test <<: *script_load

load preprod: <<: *env_preprod <<: *script_load

通过这种方式,我们便无需复制粘贴代码。

有关完整的 CD 配置,请参阅此处。 该配置遵循在测试、预生产和生产环境之间移动代码的原始计划。

结论

可以将持续交付配置为自动执行任何所需的开发工作流。

链接

后续内容

在下一篇文章中,我们将创建利用 InterSystems IRIS Docker 容器的 CD 配置。

0
0 104
文章 Michael Lei · 九月 26, 2024 4m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD

第一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。

第二篇文章中,我们介绍了 GitLab 工作流 – 一个完整的软件生命周期流程,并介绍了持续交付。

在这篇文章中,我们将探讨:

  • GitLab 安装和配置
  • 将环境连接到 GitLab

GitLab 安装

我们将在本地安装 GitLab。 可以通过多种方式安装 GitLab – 通过源代码、软件包安装,以及在容器中安装。 我不会在这里详细介绍所有步骤,请参阅相关指南。 但仍需要注意几点:

前提条件:

  • 单独的服务器 – 由于 GitLab 属于 Web 应用程序,并且需要占用大量资源,最好在单独的服务器上运行
  • Linux
  • (可选,但强烈建议采用)域 – 运行页面和保护整个安装时需要

配置

首先,您可能需要发送包含通知的电子邮件

接下来,建议安装 Pages。 正如我们在上一篇文章中所讨论的 – 可以将脚本中的工件上传到 GitLab。 用户可以下载这些工件,但能够直接在浏览器中打开工件将非常有用,为此我们需要使用页面。

需要使用页面的原因:

由于 html 页面会在加载时重定向,可以使用 html 页面将用户重定向到我们需要的位置。 例如,下列代码生成的 html 页面会将用户重定向到上次执行的单元测试(生成 html 时):

ClassMethod writeTestHTML()
{
  set text = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "html").Data.Read()
  set text = $replace(text, "!!!", ..getURL())

set file = ##class(%Stream.FileCharacter).%New() set name = "tests.html" do file.LinkToFile(name) do file.Write(text) quit file.%Save() }

ClassMethod getURL() { set url = "http://host:57772" set url = url _ $system.CSP.GetDefaultApp("%SYS") set url = url_"/%25UnitTest.Portal.Indices.cls?Index="_ $g(^UnitTest.Result, 1) _ "&$NAMESPACE=" _ $zconvert($namespace,"O","URL") quit url }

XData html { <html lang="en-US"> <head> <meta charset="UTF-8"/> <meta http-equiv="refresh" content="0; url=!!!"/> <script type="text/javascript">window.location.href = "!!!"</script> </head> <body> If you are not redirected automatically, follow this <a href='!!!'>link to tests</a>. </body> </html> }

我使用页面时遇到了错误(浏览工件时出现 502 错误) - 解决方法

 

将环境连接到 GitLab

要运行 CD 脚本,需要使用环境,即配置好的用于运行应用程序的服务器。 假设有一个安装了 InterSystems 产品(例如 InterSystems IRIS,但也可以使用 Caché 和 Ensemble)的 Linux 服务器,可以通过以下步骤将环境连接到 GitLab:

  1. 安装 GitLab 运行程序
  2. 在 GitLab 中注册运行程序
  3. 允许运行程序调用 InterSystems IRIS
有关安装 GitLab 运行程序的

重要说明 - 安装 GitLab 运行程序后请勿克隆服务器。否则,结果将不可预测,并且大多数情况下的结果都不尽如人意。

在 GitLab 中注册运行程序

运行初始程序后:

sudo gitlab-runner register

您会看到多个提示,大部分步骤都非常简单,但有几步比较复杂:

请输入用于此运行程序的 gitlab-ci 令牌

有多个令牌可用:

  • 一个令牌用于整个系统(在管理设置中提供)
  • 一个令牌用于每个项目(在项目设置中提供)

由于您连接运行程序是为特定项目运行 CD,需要指定此项目的令牌。

请输入此运行程序的 gitlab-ci 标签(以逗号分隔):

在 CD 配置中,您可以筛选针对具体标签运行的脚本。 因此,在最简单的情况下,请指定一个将作为环境名称的标签。

请输入执行器:ssh、docker+machine、docker-ssh+machine、kubernetes、docker、parallels、virtualbox、docker-ssh、shell:
docker

如果您使用的是没有 docker 的普通服务器,请选择 shell。我们将在后续部分讨论 docker。

允许运行程序调用 InterSystems IRIS

将运行程序连接到 GitLab 后,我们需要允许运行程序与 InterSystems IRIS 交互,为此:

  1. gitlab-runner 用户应能够调用会话。 为此,请将该用户添加到 cacheusr 组:
    • usermod -a -G cacheusr gitlab-runner
  2. 在 InterSystems IRIS 中创建gitlab-runner 用户,并为其指定相应角色以执行 CD 任务(写入到数据库等)
  3. 允许进行操作系统级身份验证

对于 2 和 3,可以采用其他方式,例如传递用户名/密码,但我认为操作系统身份验证是更好的选择。

结论

在本部分中:

  • 我们安装了 GitLab
  • 将环境连接到 GitLab

后续内容

在下一部分中,我们将编写持续交付配置。

0
0 102
文章 Michael Lei · 九月 26, 2024 7m read

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • 持续交付
  • GitLab 安装和配置
  • GitLab CI/CD

上一篇文章中,我们介绍了 Git 基础知识、深度理解 Git 概念对现代软件开发至关重要的原因,以及如何使用 Git 开发软件。 我们的侧重点仍是软件开发的实现部分,但本部分会介绍:

  • GitLab 工作流 - 从想法到用户反馈的完整软件生命周期流程
  • 持续交付 – 软件工程方式,团队通过这种方式在短周期内制作软件,从而确保软件可以随时实现可靠发布。 它的目的是更快速、更频繁地构建、测试和发布软件。

GitLab 工作流


GitLab 工作流是软件开发流程整个生命周期中可能采取的操作的逻辑序列。

GitLab 工作流会考虑我们在上一篇文章中探讨的 GitLab 流程。 具体如下:

  1. 想法:每个新提议都始于一个想法。
  2. 问题:探讨想法最有效的方法是为它创建问题。 您的团队和协作者可以在问题跟踪器中帮助您完善和改进问题。
  3. 计划:在讨论达成一致意见后,就可以开始编码了。 但首先,我们需要将问题指定至里程碑和问题看板,以此确定工作流的优先级并进行组织。
  4. 编码:现在,一切安排就绪后,我们就可以编写代码了。
  5. 提交:对草稿满意后,我们便可将代码提交到具有版本控制的功能分支。 上一篇文章详细介绍了 GitLab 流程。
  6. 测试:使用 GitLab CI 运行我们的脚本,以构建并测试应用程序。
  7. 审查:在脚本能够正常运行且测试和构建成功后,我们便可以让代码接受审查并获得批准。
  8. 暂存:现在应该将代码部署到暂存环境,以检查一切是否按预期进行,或者我们是否仍需要进行调整。
  9. 生产:如果一切顺利,便可将代码部署到生产环境!
  10. 反馈:现在可以回顾之前的流程,并检查有哪些阶段的工作需要改进。

再次说明,流程本身不是新的(或者 GitLab 独有的),并且可以通过其他工具来实现。

我们来讨论一下其中的几个阶段以及这些阶段涉及的内容。 还提供文档

问题和计划

GitLab 工作流的开始阶段以问题为中心,问题是指一个功能、一个错误或其他在语义上独立的工作。

问题有多个目的,例如:

  • 管理:问题具有截止日期、指定人员、用时和估计等, 可以帮助跟踪问题解决情况。
  • 行政管理:问题是里程碑的一部分,我们可以通过看板跟踪软件从一个版本过渡到另一个版本的进展。
  • 开发:问题具有与之相关的讨论和提交。

在计划阶段,我们可以按问题的优先级、里程碑、看板将问题分组,并获得问题的概览。

开发在前一部分进行了讨论,只需按照您希望使用的任何 git 流程执行操作即可。 我们开发了新功能并将其合并到 master 分支后,接下来应怎样操作?

持续交付

持续交付是一种软件工程方式,团队通过这种方式在短周期内制作软件,从而确保软件可以随时实现可靠发布。 它的目的是更快速、更频繁地构建、测试和发布软件。 这种方式允许对生产中的应用程序进行更多增量更新,从而帮助缩减交付更改的成本、缩短时间,以及降低风险。 简单且可重复的部署过程对于持续交付非常重要。

GitLab 中的持续交付

在 GitLab 中,持续交付配置按仓库以 YAML 配置文件形式定义。

  • 持续交付配置是一系列连续的阶段
  • 每个阶段都有一个或多个并行执行的脚本

脚本定义了一个操作以及执行该操作需要满足的条件:

  • 要执行的操作(运行 OS 命令、运行容器)?
  • 何时运行脚本:
    • 触发脚本的条件(特定分支)?
    • 之前的阶段失败时是否运行?
  • 手动运行还是自动运行?
  • 脚本在什么环境中运行?
  • 执行脚本后保存哪些工件(这些工件会从环境上传到 GitLab,以便轻松访问)?

环境是配置好的服务器或容器,可用于运行脚本。

运行程序用于在特定环境中执行脚本。 运行程序连接到 GitLab,并根据需要执行脚本。

运行程序可以部署在服务器上、容器上,甚至部署在本地机器上。

持续交付是如何实现的?

  1. 新提交推送到仓库中。
  2. GitLab 检查持续交付配置。
  3. 持续交付配置包含适用于所有情况的全部脚本,因此要过滤出应针对这一特定提交运行的一组脚本(例如提交到 master 分支仅会触发与 master 分支相关的操作)。 这组脚本称为管道
  4. 管道是在目标环境中执行的,执行结果会保存并显示在 GitLab 中。

例如,下面是提交到 master 分支后执行的一个管道:

管道包含四个阶段,各阶段连续执行

  1. 加载阶段会将代码加载到服务器中
  2. 测试阶段会运行单元测试
  3. 封装阶段包含两个并行运行的脚本:
    • 构建客户端
    • 导出服务器代码(主要用于提供信息)
  4. 部署阶段会将构建的客户端移动到 Web 服务器目录中。

我们可以看到,每个脚本都成功运行,如果其中一个脚本运行失败,则默认不会运行后面的脚本(但我们可以更改此行为):

如果我们打开脚本,可以查看日志并确定脚本运行失败的原因:

Running with gitlab-runner 10.4.0 (857480b6)
 on test runner (ab34a8c5)
Using Shell executor...
Running on gitlab-test...
Fetching changes...
Removing diff.xml
Removing full.xml
Removing index.html
Removing tests.html
HEAD is now at a5bf3e8 Merge branch '4-versiya-1-0' into 'master'
From http://gitlab.eduard.win/test/testProject
 * [new branch] 5-versiya-1-1 -> origin/5-versiya-1-1
 a5bf3e8..442a4db master -> origin/master
 d28295a..42a10aa preprod -> origin/preprod
 3ac4b21..7edf7f4 prod -> origin/prod
Checking out 442a4db1 as master...Skipping Git submodules setup$ csession ensemble "##class(isc.git.GitLab).loadDiff()"

[2018-03-06 13:58:19.188] Importing dir /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/

[2018-03-06 13:58:19.188] Loading diff between a5bf3e8596d842c5cc3da7819409ed81e62c31e3 and 442a4db170aa58f2129e5889a4bb79261aa0cad0

[2018-03-06 13:58:19.192] Variable modified var=$lb("MyApp/Info.cls")

Load started on 03/06/2018 13:58:19 Loading file /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/MyApp/Info.cls as udl Load finished successfully.

[2018-03-06 13:58:19.241] Variable items var="MyApp.Info.cls" var("MyApp.Info.cls")=""

Compilation started on 03/06/2018 13:58:19 with qualifiers 'cuk /checkuptodate=expandedonly' Compiling class MyApp.Info Compiling routine MyApp.Info.1 ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1] TEXT: quit, "1.0" } Detected 1 errors during compilation in 0.010s.

[2018-03-06 13:58:19.252] ERROR #5475: Error compiling routine: MyApp.Info.1. Errors: ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1] > ERROR #5030: An error occurred while compiling class 'MyApp.Info' ERROR: Job failed: exit status 1

编译错误导致脚本失败。

结论

  • GitLab 支持软件开发的所有主要阶段。
  • 持续交付可以帮助您自动执行软件构建、测试和部署任务。

后续内容

在下一篇文章中,我们将:

  • 安装 GitLab。
  • 将它连接到多个安装了 InterSystems 产品的环境。
  • 编写持续交付配置。

我们来探讨一下持续交付的运作方式。

首先,我们需要多个环境以及与之对应的分支。 代码进入此分支,并交付到目标环境:

环境分支交付有权提交的角色有权合并的角色
测试master自动开发者、所有者开发者、所有者
preprod预生产自动所有者
prod生产半自动(按下按钮进行交付)

所有者

作为示例,我们将使用 GitLab 流程开发一个新功能,并使用 GitLab CD 进行交付。

  1. 在功能分支中开发功能。
  2. 对功能分支进行审查并将其合并到 master 分支中。
  3. 一段时间(合并了多个功能)后,将 master 分支合并到 preprod 分支中
  4. 一段时间(用户测试等)后,将 preprod 分支合并到 prod 分支中

具体如下图所示:

  1. 开发和测试
    • 开发者将新功能的代码提交到单独的功能分支中
    • 功能稳定后,开发者将功能分支合并到 master 分支中
    • 来自 master 分支的代码被交付到测试环境,在其中进行加载和测试
  2. 交付到预生产环境
    • 开发者创建从 master 分支到 preprod 分支的合并请求
    • 仓库所有者在一段时间后批准合并请求
    • 来自 preprod 分支的代码被交付到预生产环境
  3. 交付到生产环境
    • 开发者创建从 preprod 分支到 prod 分支的合并请求
    • 仓库所有者在一段时间后批准合并请求
    • 仓库所有者按下“部署”按钮
    • 来自 prod 分支的代码被交付到生产环境

也可以用示意图形式表示此流程:

 

0
0 98
文章 Michael Lei · 九月 26, 2024 6m read

大家都搭建了测试环境。

有些人很幸运,可以在完全独立的环境中运行生产。

-- 佚名

.

在这一系列文章中,我想向大家介绍并探讨使用 InterSystems 技术和 GitLab 进行软件开发可以采用的几种方式。 我将介绍以下主题:

  • Git 101
  • Git 流程(开发流程)
  • GitLab 安装
  • GitLab 工作流
  • GitLab CI/CD
  • 包含容器的 CI/CD

第一部分将介绍现代软件开发的基础 – Git 版本控制系统和各种 Git 流程。

Git 101

虽然我们将主要探讨软件开发的概况以及 GitLab 如何帮助我们实现这一目标,但 Git,或者说 Git 设计中的几个基础的高级概念对于更好地理解后面的概念非常重要。

也就是说,Git 是基于这些概念的版本控制系统(还有更多概念,但这几个概念最为重要):

  • 非线性开发意味着,虽然我们的软件是从版本 1 到版本 2、再到版本 3 相继发布的,但实际上从版本 1 到版本 2 的升级是并行完成的 – 多名开发者会同时开发许多功能/错误修复。
  • 分布式开发意味着开发者独立于一个中央服务器或其他开发者,可以轻松地在自己的环境中进行开发。
  • 合并 – 基于前面提到的两个概念,我们会发现很多不同的版本同时存在,我们需要将它们统一成一个完整的状态。

我的意思不是说 Git 发明了这些概念。 Git 并没有发明这些概念, 而是使这些概念变得简单、流行,并加入了多个相关创新概念,也就是说,架构即代码/容器化改变了软件开发。

核心 git 术语

仓库是存储数据以及关于数据的元信息的项目。

  • “从物理层面来讲”,仓库是磁盘上的目录。
  • 仓库用于存储文件和目录。
  • 仓库还会存储每个文件的完整变更历史。

仓库可以:

  • 存储在您自己的计算机本地
  • 远程存储在远程服务器上

但从 git 的角度来看,本地仓库与远程仓库之间没有特殊的区别。

提交是仓库的固定状态。 很显然,如果每次提交都存储仓库的完整状态,我们的仓库很快就会变得非常大。 因此,提交会存储差异,也就是当前提交与其父提交之间的差异。

不同的提交可以具有不同数量的父提交:

  • 0 个 – 仓库中的第一个提交没有父提交。
  • 1 个 – 一切如常 - 我们的提交改变了仓库中的某些内容,就像在父提交期间一样
  • 2 个 – 当我们有两个不同的仓库状态时,我们可以将它们合并成一个新状态。 该状态和该提交就会有 2 个父提交。
  • >2 个 – 当我们将 2 个以上的不同仓库状态合并为一个新状态时,就会有 2 个以上的父提交。 这一概念与我们的讨论并没有特别大的关系,但它确实存在。

现在,对于父提交,每个与之不同的提交都被称为子提交。 每个父提交可以有任意数量的子提交。

分支是对提交的引用(或指针),如下图所示:

该图像显示的仓库具有两个提交(灰色圆圈),第二个圆圈是 master 分支的头部。 在我们添加更多提交后,仓库开始变成下图所示的状态:

这是最简单的情况。 我们的开发者一次负责处理一个更改。 但通常会有很多开发者同时负责处理不同的功能,我们需要使用提交树显示仓库中的变化。

提交树

我们从相同的起始状态开始。 仓库具有两个提交:

但现在,两名开发者在同时工作,为了避免相互干扰,他们在单独的分支中工作:

一段时间后,他们需要合并所做的更改,为此,他们创建了合并请求(也叫拉取请求), 顾名思义,该请求可将两个不同的仓库状态(本例中,我们要将 develop 分支合并到 master 分支中)合并为一个新状态。 接受相应审查并获得批准后,仓库状态如下图所示:

开发继续进行:

Git 101 - 总结

主要概念:

  • Git 是一个非线性的分布式版本控制系统。
  • 仓库用于存储数据以及关于数据的元信息。
  • 提交是仓库的固定状态。
  • 分支是对提交的引用。
  • 合并请求(也叫拉取请求)是将两个不同的仓库状态合并为一个新状态的请求。

如果您想了解更多关于 Git 的信息,可以阅读相关书籍

Git 流程

现在,读者已熟悉基本的 Git 术语和概念,我们来探讨一下如何使用 Git 管理软件生命周期的开发部分。很多实践(称为流程)介绍了使用 Git 的开发流程,但我们只会探讨其中两个:

  • GitHub 流程
  • GitLab 流程

GitHub 流程

GitHub 流程非常简单。 具体如下:

  1. 从仓库创建一个分支。
  2. 将更改提交到新分支
  3. 从您的分支发送一个拉取请求,其中包含您提议的更改,以发起讨论。
  4. 根据需要在您的分支上提交更多更改。 您的拉取请求将自动更新。
  5. 在分支准备好合并后,立即合并拉取请求。

我们需要遵守几条规则:

  • master 分支始终可部署(并且可正常运行!)
  • 不直接在 master 分支中进行开发
  • 在功能分支中进行开发
  • master 分支 == 生产* 环境**
  • 需要尽可能频繁地部署到生产环境

* 不要与“Ensemble 生产”混淆,这里的“生产”是指正式。

** 环境是配置好的代码运行位置,可以是服务器、虚拟机,甚至可以是容器。

如下图所示:

有关 GitHub 流程的更多信息,请参阅此处。 我们还提供了图解指南

GitHub 流程非常适合小型项目,如果您刚开始使用 Git 流程,可以尝试一下。 不过,GitHub 也会使用 GitHub 流程,因此也可以在大型项目中使用 GitHub 流程。

GitLab 流程

如果您还没有准备好立即部署到生产环境,GitLab 流程提供 GitHub 流程 + 环境。 具体做法是:在功能分支中进行开发(与上例相同),合并到 master 分支中(与上例相同),但这里有一个不同之处: master 分支仅等同于测试环境。 除此之外,还有链接到可能存在的各种其他环境的“环境分支”。

通常存在三个环境(可以根据需要创建更多环境):

  • 测试环境 == master 分支
  • 预生产环境 == preprod 分支
  • 生产环境 == prod 分支

进入其中一个环境分支的代码应立即移至相应的环境中,此流程可通过以下方式完成:

  • 自动(我们将在第 2 部分和第 3 部分探讨)
  • 半自动(与自动方式相同,唯一的区别是应按下按钮授权部署)
  • 手动

完整的流程是:

  1. 在功能分支中开发功能。
  2. 对功能分支进行审查并将其合并到 master 分支中。
  3. 一段时间(合并了多个功能)后,将 master 分支合并到 preprod 分支中
  4. 一段时间(用户测试等)后,将 preprod 分支合并到 prod 分支中
  5. 在我们进行合并和测试时,多个新功能已开发完毕并合并到 master 分支中,因此转到  3。

具体如下图所示:

有关 GitLab 流程的更多信息,请参阅此处

结论

  • Git是一个非线性的分布式版本控制系统。
  • Git 流程可用作软件开发周期的准则,有多种 Git 流程可供选择。

链接

讨论问题

  • 您使用 Git 流程吗? 使用哪一种?
  • 您为普通项目使用多少个环境?

后续内容

在接下来的部分中,我们将:

  • 安装 GitLab。
  • 探讨一些建议的调整。
  • 讨论 GitLab 工作流(不要与 GitLab 流程混淆)。

敬请关注。

0
0 129
文章 Jingwei Wang · 三月 28, 2023 5m read

IRIS 配置和用户帐户包含需要跟踪的各种数据元素,许多人难以在 IRIS 实例之间复制或同步这些系统配置和用户帐户。那么如何简化这个过程呢?

在软件工程中,CI/CD 或 CICD 是持续集成 (CI) 和(更常见的)持续交付或(较少见的)持续部署 (CD) 的组合实践集。 CI/CD 能消除我们所有的挣扎吗?

我在一个开发和部署 IRIS 集群的团队工作。我们在 Red Hat OpenShift 容器平台上的容器中运行 IRIS。

如果您当前没有使用 Kubernetes,请不要停止阅读。即使您没有使用 Kubernetes 或在容器中运行 IRIS,您也可能会遇到与我和我的团队面临的挑战类似的挑战。

我们决定将代码与配置分开,并将它们放在不同的 GitHub 存储库中。每次在代码库中进行提交时,都会触发管道运行。结果,从代码库中的文件构建了一个新image。

我们通过将 YAML 文件和其他配置工件添加到部署 GitHub 存储库,将配置定义为以 GitOps 方式使用的代码。 GitOps 是一个软件开发框架,它使组织能够持续交付软件应用程序,同时使用 Git 作为单一事实来源有效地管理 IT 基础设施(以及更多)。 GitOps 的好处之一是能够轻松回滚。您所需要做的就是恢复到 Git 中的先前状态。

0
0 315
文章 Michael Lei · 十二月 9, 2022 7m read

在数量众多、形形色色的 SQL 数据库市场中,InterSystems IRIS 作为一个超越 SQL 的平台脱颖而出,它提供无缝的多模型体验,支持丰富的开发范式。 特别是,先进的对象-关系引擎已经帮助组织为其数据密集型工作负载的每个方面使用了最适合的开发方式,例如在通过对象获取数据并同时通过 SQL 查询数据。 持久类与 SQL 表相对应,其属性与表中的各列相对应,可以使用用户定义的函数或存储过程轻松访问业务逻辑。 在这篇文章中,我们将深入了解表面之下的一点底层技术,讨论它可能如何影响您的开发和部署方式。 这是我们计划发展和改进的产品领域,因此请不要犹豫,在下面的评论区分享您的观点和体验。

保存存储定义 {Saving the Storage Definition}

编写全新的业务逻辑很容易,而且假如您有定义明确的 API 和规范,那么调整或扩展通常也很容易。 但是,当它不仅仅是业务逻辑,还涉及持久化数据时,从初始版本更改的任何内容都将需要能够妥善处理通过早期版本获取的数据。

0
0 187
文章 Michael Lei · 十月 10, 2022 2m read

Kong提供了一个开源的配置管理工具(用Go语言编写),称为decK(即声明式Kong,declarative Kong)。

  • 通过deck ping检查deck是否能识别你的Kong Gateway安装
deck ping   
Successfully connected to Kong!
Kong version:  2.3.3.2-enterprise-edition
  • 通过deck dump把 Kong Gateway 配置导出到一个叫 "kong.yaml" 的文件
deck dump
  • 修改kong.yaml文件后, 通过 deck diff 显示区别
0
0 106
文章 Qiao Peng · 九月 22, 2022 25m read

        关注FHIR的大侠们估计都注意到了,FHIR更新了它支持的互操作范式,除了消息、文档、服务、API这4种,增加了2个:资源仓库、订阅。前面4个好理解,为什么资源仓库和订阅会成为FHIR的新的互操作范式?互操作与应用集成是什么关系?

        这里借FHIR的新互操作范式,聊聊应用集成,看看集成平台是什么?有什么样的集成方案?以及怎么评价不同的方案。

0
4 667
文章 Qiao Peng · 五月 15, 2022 9m read

     在软件开发和业务集成中,规则无处不在:会员折扣的计算规则、根据消息类型和内容将其路由到不同目标系统的路由规则。还有一个规则发挥重要作用的地方-  辅助决策规则,例如临床知识库和医疗质量指标规则。

规则经常需要随业务调整和知识积累进行调整,而规则的调整是业务和行业专家定的。如果规则是以代码硬编码的,这些调整需要程序员改动,一来不直观、需要业务专家与程序员大量的沟通成本,二来硬编码改动会对应用伤筋动骨,甚至带来风险,三来没法控制新规则生效的时间 – 总不能让程序员在新规则生效的那一刻去编译和部署吧。

InterSystems规则引擎可以帮助我们解决这些问题,于构建、执行和记录消息路由规则和普通的业务规则,带给应用和集成方案充分的灵活性和可用性。甚至业务专家和临床信息学家都可以通过低代码的、图形化的规则编辑器修改规则和指定规则生效和失效时间。

InterSystems规则引擎是InterSystems IRIS数据平台和Health Connect与Ensemble集成平台的组件。创建的规则可以被单独调用,也可以被业务流程调用。

本篇介绍规则的如何使用InterSystems规则编辑器创建规则和规则引擎执行规则的逻辑。

1. 规则基本概念

    在设计规则前,先了解一下规则的基本概念。

1.1 上下文

0
0 460
文章 Michael Lei · 四月 25, 2022 2m read

Kong提供了一个开源的配置管理工具(用Go语言编写),称为decK(代表声明式Kong)

  • 通过deck ping检查deck是否能识别你的Kong Gateway安装
deck ping   
Successfully connected to Kong!
Kong version:  2.3.3.2-enterprise-edition
  • 通过deck dump把 Kong Gateway配置倒出到 "kong.yaml" 文件 
deck dump
  • 修改 kong.yaml以后通过deck diff 通过把区别显示出来
0
0 383
文章 Qiao Peng · 四月 25, 2022 12m read

InterSystems流程自动化与工作流引擎

InterSystems工作流程引擎的主要功能    2

使用InterSystems工作流程引擎    3

场景描述    3

环境配置与测试    5

任务管理    15

任务API和自定义任务用户界面    16

展望    17

15

集成平台除了集成业务系统,打通数据与业务流程外,另一个核心的功能就是流程自动化(BPA)。

流程自动化涉及几个重要的特性:

  1. 流程建模
  2. 流程协同
  3. 决策自动化
  4. 低代码工作流程自动化
  5. 任务协同与任务管理

其中第4和5点都是和工作流程相关的。

什么是工作流程(Workflow)?它和业务流程(Business Process)有何区别?为何集成平台要涉及对工作流程的管理?

0
2 422
文章 Qiao Peng · 十二月 18, 2021 12m read

应用集成技术是市场上被广泛使用的,也是充斥着术语和概念的一个技术领域。集成平台、消息引擎、消息中间件、集成引擎、集成中间件、企业服务总线(ESB)、API网关、API管理… 很多概念与名词。到底它们是什么意思?有什么区别?哪种技术适合解决哪种集成问题?

业务集成的需求和技术的演进是紧随业务系统的软件架构发展而发展的。通过小结软件架构的发展,我们更容易梳理业务集成技术的演进、更容易看清楚各种集成架构的优势和未来发展方向。

0
0 853
文章 Johnny Wang · 十一月 21, 2021 5m read

    当我和加州大学戴维斯分校健康中心的同事着手简化提供者对基因组数据报告的访问时,我们希望这些信息能帮助临床医生提供更好、更个性化的护理。 我们的基因组数据没有操作界面,既不可搜索,也不与患者图表相关联。 如果我们可以在 FHIR(快速医疗互操作性资源)连接器上利用SMART原则在平台之间实现单点登录,我们的护理团队就可以更早地获得数据,患者将能够更好地得到照顾,并在与癌症的斗争中取得积极成果。

    而这也是正在实现的事情。 我们支持基因组数据报告的工作为临床医生带来了 50 个离散数据点,这意味着医生用于搜索报告的时间更少,也拥有了更多具有重要洞察力的离散数据,简化了对临床试验信息的访问,最终患者也得到了及时的护理。

    但我们并没有停下脚步,这不过是迈向更广阔数字领域的第一步。在 InterSystems 的帮助下,我们拥有了规模越来越大、类型更多样的数据集。

0
0 139
文章 Hao Ma · 三月 26, 2021 15m read

关键字:IRIS,IntegratedML,Flask,FastAPI,TensorFlow Serving,HAProxy,Docker,Covid-19

目的:

过去几个月里,我们提到了一些深度学习和机器学习的快速演示,包括一个简单的 Covid-19 X 射线图像分类器和一个用于可能的 ICU 入院的 Covid-19 实验室结果分类器。  我们还介绍了 ICU 分类器的 IntegratedML 演示实现。  虽然“数据科学”远足仍在继续,但从“数据工程”的角度来看,或许也是尝试一些 AI 服务部署的好时机 - 我们能否将目前所接触到的一切都封装成一套服务 API?  我们可以利用哪些常用的工具、组件和基础架构,以最简单的方式实现这样的服务堆栈?

0
0 1420
文章 Nicky Zhu · 一月 11, 2021 5m read

本文将描述通过ObjectScript包管理器(见https://openexchange.intersystems.com/package/ObjectScript-Package-Manager-2)运行单元测试的过程,包括测试覆盖率测量(见https://openexchange.intersystems.com/package/Test-Coverage-Tool)。

ObjectScript中的单元测试

关于在ObjectScript中编写单元测试,已经有很好的文档,因此我就不再赘述了。您可以在这里找到单元测试教程:https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=TUNT_preface

最好的做法是将单元测试代码单独放在源代码树中,无论它只是“/tests”还是其他名字。在InterSystems中,我们最终使用/internal/testing/unit_tests/作为我们事实上的标准,这是有意义的,因为测试是内部/非发布的,而且除了单元测试还有其他类型的测试,但这对于简单的开源项目来说可能有点复杂。您可以在我们的一些GitHub仓库中看到这种结构。

从工作流的角度来看,这在VSCode中非常简单,您只需创建目录并将类放在里面。对于较老的以服务器为中心的源代码控制方法(Studio中使用的方法),您需要正确地地映射这个包,使用的方法会因源代码控制程序而异。

从单元测试类命名的角度来看,我个人的偏好(以及我的团队的最佳实践)是:

UnitTest.<待测试的包/类>[.<待测试的方法/特性>] 例如,如果在类MyApplication.SomeClass 中对方法Foo进行单元测试,单元测试类将被命名为UnitTest.MyApplication.SomeClass.Foo;如果测试是针对整个类的,那么名字就是UnitTest.MyApplication.SomeClass。

ObjectScript 包管理器中的单元测试

让ObjectScript包管理器知道您的单元测试,很简单!只需按如下所示向module.xml中添加一行代码(来自https://github.com/timleavitt/ObjectScript-Math/blob/master/module.xml - 这是Open Exchange上的@Get Oxycodone Online Trusted Affordable & Fast Steiwer的出色数学扩展包,我以它作为简单的正面例子):

<Module> 
  ... 
  <UnitTest Name="tests" Package="UnitTest.Math" Phase="test"/> 
</Module> 

这些代码的意思是:

  • 单元测试位于模块根目录下的“tests”目录中。
  • 单元测试在“UnitTest.Math”包中。这样很直观,因为被测试的类就在“Math”包中。
  • 单元测试在包生命周期的“测试”阶段运行。(当然还有一个可以运行它们的“验证”阶段,这里不赘述。)

运行单元测试

对于上述定义的单元测试,包管理器提供了一些实用工具来运行它们。您仍然可以像平常使用%UnitTest.Manager那样设置^UnitTestRoot等,但下面的方法可能更简单,尤其在同一个环境下做几个项目的时候。

您可以克隆上述objectscript-math仓库,然后用 zpm "load /path/to/cloned/repo/" 加载,或者在您自己的包上使用包名(和测试名)替换“objectscript-math”来尝试所有这些方法。

重新加载模块,然后运行所有单元测试: zpm "objectscript-math test"

只运行单元测试(不重新加载): zpm "objectscript-math test -only"

只运行单元测试(不重新加载)并提供详细输出: zpm "objectscript-math test -only -verbose"

运行一个特定的测试套件(指一个测试目录 - 在本例中,是UnitTest/Math/Utils中的所有测试)而不重新加载,并提供详细输出: zpm "objectscript-math test -only -verbose -DUnitTest.Suite=UnitTest.Math.Utils"

运行一个特定的测试用例(在本例中,是UnitTest.Math.Utils.TestValidateRange)而不重新加载,并提供详细输出: zpm "objectscript-math test -only -verbose -DUnitTest.Case=UnitTest.Math.Utils.TestValidateRange"

如果您只是想解决单个测试方法中的小问题: zpm "objectscript-math test -only -verbose -DUnitTest.Case=UnitTest.Math.Utils.TestValidateRange -DUnitTest.Method=TestpValueNull"

通过ObjectScript包管理器进行测试覆盖率测量

怎样评估单元测试的质量?测量测试覆盖率虽然并不全面,但至少有参考意义。早在2018年的全球峰会上,我就展示过。 见 - https://youtu.be/nUSeGHwN5pc

首先需要安装“测试覆盖率”包: zpm "install testcoverage"

注意,并不需要ObjectScript包管理器才能安装/运行;可以在Open Exchange上了解更多信息:https://openexchange.intersystems.com/package/Test-Coverage-Tool

不过如果您已经在用ObjectScript包管理器,那么您可以更好地利用这个“测试覆盖率”工具。

运行测试前,需要指定测试所覆盖的类/routine宏。这一点很重要,因为在非常大的代码库中(例如,HealthShare),测试和收集项目中所有文件的测试覆盖率所需要的内存可能超出您的系统内存。(提一句,如果您感兴趣,可以使用逐行监视器的gmheap。)

文件列表在您的单元测试根目录下的coverage.list文件中;单元测试的不同子目录(套件)可以拥有它们自己的副本,以覆盖在测试套件运行时将跟踪的类/例程。

有关objectscript-math的简单示例,见:https://github.com/timleavitt/ObjectScript-Math/blob/master/tests/UnitTest/coverage.list;测试覆盖率工具用户指南有更详细的介绍。

要在启用测试覆盖率测量的情况下运行单元测试,只需再向命令添加一个参数,指定应使用TestCoverage.Manager而非%UnitTest.Manager 来运行测试: zpm "objectscript-math test -only -DUnitTest.ManagerClass=TestCoverage.Manager"

输出(即使是非详细模式)将包括一个URL,供您查看您的类/routine(宏)的哪些行被单元测试覆盖了,以及一些汇总统计信息。

接下来的步骤

这些能不能在CI中自动化?能不能报告单元测试的结果和覆盖率分数/差异?答案是:能!您可以使用Docker,Travis CI和codecov.io来试一下这个简单示例,见https://github.com/timleavitt/ObjectScript-Math;我打算以后写篇文章来详细讲讲,介绍几种不同的方法。

0
0 263
文章 Jeff Liu · 一月 7, 2021 5m read

非常高兴地宣布,InterSystems 容器注册表现在可以使用了。 这为客户访问基于容器的版本及预览提供了新的渠道。 所有的社区版像都可在公共存储库中找到,且无需登录。 所有完整发布的像(IRIS、IRIS for Health、Health Connect、System Alerting and Monitoring、InterSystems Cloud Manager)和实用程序镜像(例如,仲裁器、Web 网关和 PasswordHash)都需要登录令牌,该令牌从 WRC 帐户生成。WRC 发布网站暂时将继续以 tarball 方式提供已发布像。 不过,您现在可以配置 CI/CD 管道以直接从 InterSystems 容器注册表“docker pull”镜像。
可通过 https://containers.intersystems.com 访问该注册表。 有关完整的使用说明,请参阅下文或参阅文档(使用 InterSystems 容器注册表)。如果您遇到任何问题或有任何反馈要分享,请在下面的评论中告知我们,或联系 support@intersystems.com。
--------------------------------------------------------------

使用 InterSystems 容器注册表

本文档列出了 InterSystems 容器注册表 (ICR) 中可用的像,并提供了使用说明。该注册表位于 containers.intersystems.com 上。

可以使用 docker pull 命令下载 ICR 中的像,例如:

docker pull containers.intersystems.com/intersystems/iris-community:2020.3.0.221.0

本文档包含以下部分:

  • 公共像
  • 受限访问镜像
  • 对 ICR 进行身份验证
  • 列出 ICR 清单

公共镜像

以下 ICR 像是公开可用的,无需身份验证即可拉取:

InterSystems IRIS

IntegratedML

2020.3

containers.intersystems.com/intersystems/iris-ml-community:2020.3.0.302.0

Community Edition

2020.3

containers.intersystems.com/intersystems/iris-community:2020.3.0.221.0

2020.3 ARM64

containers.intersystems.com/intersystems/iris-community-arm64:2020.3.0.221.0

InterSystems IRIS for Health

IntegratedML2020.3containers.intersystems.com/intersystems/irishealth-ml-community:2020.3.0.302.0
Community Edition

2020.3

containers.intersystems.com/intersystems/irishealth-community:2020.3.0.221.0

2020.3 ARM64

containers.intersystems.com/intersystems/irishealth-community-arm64:2020.3.0.221.0

System Alerting and Monitoring

 

1.0

containers.intersystems.com/intersystems/sam:1.0.0.115

以下 ICR 镜像仅对经过身份验证的用户可用:受限访问镜像

以下 ICR 像是公开可用的,无需身份验证即可拉取:

Arbiter

2020.1

containers.intersystems.com/intersystems/arbiter:2020.1.0.215.0

2020.2

containers.intersystems.com/intersystems/arbiter:2020.2.0.211.0

2020.3

containers.intersystems.com/intersystems/arbiter:2020.3.0.210.0

Health Connect

2020.1

containers.intersystems.com/intersystems/healthconnect:2020.1.0.215.0

InterSystems Cloud Manager (ICM)

2020.1

containers.intersystems.com/intersystems/icm:2020.1.0.215.0

2020.2

containers.intersystems.com/intersystems/icm:2020.2.0.211.0

2020.3

containers.intersystems.com/intersystems/icm:2020.3.0.221

InterSystems IRIS

2020.1

containers.intersystems.com/intersystems/iris:2020.1.0.215.0

2020.2

containers.intersystems.com/intersystems/iris:2020.2.0.211.0

2020.3

containers.intersystems.com/intersystems/iris:2020.3.0.221.0

2020.1 ARM64

containers.intersystems.com/intersystems/iris-arm64:2020.1.0.215.0

2020.2 ARM64

containers.intersystems.com/intersystems/iris-arm64:2020.2.0.211.0

2020.3 ARM64

containers.intersystems.com/intersystems/iris-arm64:2020.3.0.221.0

2020.3 IntegratedMLcontainers.intersystems.com/intersystems/iris-ml:2020.3.0.302.0

InterSystems IRIS for Health

2020.1

containers.intersystems.com/intersystems/irishealth:2020.1.0.217.1

2020.2

containers.intersystems.com/intersystems/irishealth:2020.2.0.211.0

2020.3

containers.intersystems.com/intersystems/irishealth:2020.3.0.221.0

2020.1 ARM64

containers.intersystems.com/intersystems/irishealth-arm64:2020.1.0.217.1

2020.2 ARM64

containers.intersystems.com/intersystems/irishealth-arm64:2020.2.0.211.0

2020.3 ARM64

containers.intersystems.com/intersystems/irishealth-arm64:2020.3.0.221.0

2020.3 IntegratedMLcontainers.intersystems.com/intersystems/irishealth-ml:2020.3.0.302.0

PasswordHash

1.0

containers.intersystems.com/intersystems/passwordhash:1.0

Web Gateway

2020.2

containers.intersystems.com/intersystems/webgateway:2020.2.0.211.0

2020.3

containers.intersystems.com/intersystems/webgateway:2020.3.0.221.0


要登录至 ICR,请执行以下步骤:对 ICR 进行身份验证

  • 在您的浏览器中加载 https://containers.intersystems.com/,然后使用您的 InterSystems/WRC 凭据登录。
  • 检索您的 Docker 登录令牌或完整的登录命令。
  • 在 Docker 界面(例如,PowerShell 窗口或 Linux 命令行)中,使用提供的凭据对 ICR 进行身份验证。 您可以通过复制并粘贴显示的完整 docker login 命令来执行此操作,例如:
docker login -u="bbinstoc" -p="provided_password" containers.intersystems.com

但是,出于安全原因,您可能想要输入命令 docker login container.intersystems.com,然后在 Username 提示符下输入用户名并将密码粘贴到 Password: 提示符下。

注意:如果您登录到另一个 Docker 注册表,则 docker login 命令可能会导致错误;登录到 container.intersystems.com 之前,请先注销其他注册表。

  1. 现在,您可以从 ICR 中拉取镜像,例如:
docker pull containers.intersystems.com/intersystems/iris:2020.3.0.221.0

列出 ICR 清单

API 可用于列出 Docker 注册表中的镜像和标签。 可用于列出注册表清单的开源第三方实用程序的一个示例是 docker-ls ,其可从 https://github.com/mayflower/docker-ls 获取。

获取此实用程序的方法有几种。 你可以:

  • 下载用于各种平台的预编译 docker-ls 二进制文件
  • 直接在某些平台上安装该实用程序,例如,在 Linux 系统上使用以下命令进行安装:
sudo snap install docker-ls
  • 在 Linux 平台上拉取并运行镜carinadigital/docker-ls:latest 以安装该实用程序,例如:
docker run --rm carinadigital/docker-ls:latest

安装 docker-ls 后,您可以使用以下命令列出 ICR 中的存储库:

docker-ls repositories --registry https://containers.intersystems.com --user username --password password

注意:使用 --interactive-password 选项提示输入密码,不要在命令行中输入密码。

要仅列出公开可用的像,请为 -user --password 选项提供空字符串 ("") 作为参数, 例如,以下仅列出了公共 InterSystems IRIS for Health 镜像的标签:

docker-ls tags --registry https://containers.intersystems.com --user "" --password "" intersystems/irishealth-community

如果希望看到非公共像的完整列表,则无论是否登录 container.intersystems.com,都需要向该实用程序提供用户名和密码。

可访问 https://github.com/mayflower/docker-ls 了解其他示例。

0
0 178