技术文章

在基于云的持续集成环境中执行虚拟测试和仿真

作者 : Felix Wempe 和 Anas Hamrouni,AGSOTEC


“MATLAB 和 Simulink 产品的一个特点是独立于平台,支持 Windows、Linux 和 macOS 操作系统。这种普适性非常有利于将其集成到云解决方案,因为用户可灵活地选择最合适的基础架构。”

随着汽车公司持续地使用软件为客户提供更多价值,工程团队越来越多地将持续集成 (CI) 管道纳入其工作流。同时,随着团队使用基于模型的设计在更紧的工期内交付更高质量的产品,对虚拟测试和仿真的需求也在增加。这两种趋势共同促使许多团队寻找能够根据需要自动扩展的基于云的 CI 解决方案。

基于云的解决方案在成本、可扩展性和灵活性方面具有显著优势。与硬件(需要花费时间和投资在本地采购、设置和维护)相比,云平台提供一系列服务,使 CI 团队只需根据需要为所需的计算能力付费即可。云平台支持多种基础架构组件和多种版本的服务器,因此可以灵活地适应不断变化的 CI 技术堆栈、配置和环境需求。

基于云的 CI 的优势显而易见,但实现之路却不那么明朗。团队需要作出各个关键决策,包括使用哪个云提供商、部署什么 CI 技术以及是使用容器还是虚拟机 (VM) 等。答案取决于每个团队的具体情况。有鉴于此,我在 AGSOTEC 的团队针对各种用例构建了基于云的 CI 概念验证实现,以便在考虑可用选项时更好地了解优缺点。

本文通过案例研究来测试一个自动紧急制动 (AEB) 系统的 Simulink® 模型(图 1)。此系统采用基于云的 CI 并使用多种技术,包括 GitHub®、GitLab® 和 Jenkins®,以及根据需要在云中启动的虚拟机和容器实例。演示结束后,文中总结了工程师可用于帮助其团队针对基于云的 CI 需求作出正确技术选择的关键要点。

注意:我们在 Microsoft® Azure® 和 Amazon® Web Services (AWS) 上都已实现本文中演示的示例。为简单起见,本文重点讲述在 AWS 上的情形。同样,尽管 MATLAB® 和 Simulink 产品支持多种操作系统,但为了简单起见,示例中仅显示 Linux® 虚拟机和容器。

注意:本文中的示例和建议基于截至 2023 冬季 - 2024 年的云服务和功能。随着云技术的发展和供应商产品的改进,满足具体团队需求的最佳云 CI 解决方案可能随时变化。

截图:不同场景下自动紧急制动系统测试中使用的 Simulink 模型。

图 1. 用于测试 AEB 系统的场景变体的 Simulink 模型。

为什么有些工具非常适合基于云的 CI?

在深入探究技术细节之前,需要注意的是,并非所有软件都适合与 CI 结合使用或在云环境中使用。

MATLAB 和 Simulink 产品的一个特点是独立于平台,支持 Windows®、Linux 和 macOS 操作系统。这种普适性非常有利于集成到云解决方案,因为用户可灵活地选择最合适的基础架构。这反过来又可以节省成本,因为 Linux 通常比其他选择更具成本效益。

您还可以使用 MATLAB 包管理器 (MPM) 简化在 Linux 虚拟机和容器(包括 Docker® 容器)上安装 MATLAB 和 Simulink 产品。

最后,MathWorks 为各种广泛使用的 CI 软件和服务提供插件和其他集成。这些软件和服务为工程团队提供抽象层,可以让那些并非选定 CI 平台专家的人员轻松使用 CI。MathWorks 还提供过程顾问,该 App 可以直接在 MATLAB 和 Simulink 中创建管道。这些管道通过 MATLAB 工程中工件的数字线索支持增量编译。此外,通过 MATLAB 能够以交互方式使用该 App,以便在提交前进行验证并在 CI 平台上以自动方式使用。在某些平台上,甚至可以根据 MATLAB 管道描述自动生成多级 CI 管道。过程顾问是 CI/CD Automation for Simulink Check™ 的一部分。

用于虚拟测试管道的虚拟机和容器

基于云的 CI 管道的一个关键组成部分是运行虚拟测试的环境。一种选择是创建一个虚拟机镜像,然后配置管道以自动启动此虚拟机的实例来执行排队作业。另一种方法是使用容器替代虚拟机,并集成无服务器容器执行服务。该服务会将预配置的容器从注册表导入 CI 管道中。

为虚拟测试管道创建虚拟机镜像

AWS 提供许多支持虚拟机镜像,称为 Amazon 机器镜像 (AMI),用户可以对其进行修改以创建自定义 AMI。

要创建在 CI 管道中使用的自定义 AMI,请执行以下步骤:

  1. 启动一个具有所需配置的现有 AMI 实例。
  2. 连接到该实例并安装所有需要的软件。

您可以使用 MPM 在 Linux 环境中安装 MATLAB、Simulink 和任何所需的工具箱。在 MathWorks MATLAB 依存关系 GitHub 仓库中的相关依赖项文件中提供了具体 MATLAB 版本的所有依存关系。

例如,以下命令安装 MATLAB R2022b 及其依赖项:

apt-get update 

# 下载依赖项 

apt-get install --no-install-recommends -y ca-certificates unzip libasound2 libc6 libcairo2 libcairo-gobject2 libcap2 libcrypt1 libcrypt-dev libcups2 libdrm2 libdw1 libgbm1 libgdk-pixbuf2.0-0 libgl1 libglib2.0-0 libgomp1 libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 libgtk-3-0 libice6 libnspr4 libnss3 libodbc1 libpam0g libpango-1.0-0 libpangocairo-1.0-0 libpangoft2-1.0-0 libsndfile1 libsystemd0 libuuid1 libwayland-client0 libxcomposite1 libxcursor1 libxdamage1 libxfixes3 libxft2 libxinerama1 libxrandr2 libxt6 libxtst6 libxxf86vm1 linux-libc-dev locales locales-all make net-tools odbcinst1debian2 procps sudo unzip wget zlib1g 

export DEBIAN_FRONTEND=noninteractive 

wget -q GIT HUB.com/mathworks/build-glibc-bz-19329-patch/releases/download/ubuntu-focal/all-packages.tar.gz 

tar -x -f all-packages.tar.gz --exclude glibc-*.deb --exclude libc6-dbg*.deb 

apt-get install --yes --no-install-recommends ./*.deb 

# Download MATLAB Package Manager 

wget -q https://www.mathworks.com/mpm/glnxa64/mpm 

chmod +x mpm 

# 使用 MPM 下载 MATLAB, Simulink 以及模型需要的工具箱 

./mpm install --release=r2022b --destination=/opt/matlab --products MATLAB Simulink Simulink_Test Image_Processing_Toolbox Computer_Vision_Toolbox Automated_Driving_Toolbox Control_System_Toolbox Model_Predictive_Control_Toolbox 
  1. 使用 AWS 管理控制台停止该实例并基于它创建一个 AMI。
  2. 保存 AMI ID 供以后配置 CI 系统时使用。

创建自定义镜像后,可以将其部署到基于虚拟机的管道中,实现以下工作流(图 2):

  1. 当开发人员提交对源代码库的更改时,系统会触发测试管道。
  2. 系统启动基于自定义 AMI 的新虚拟机(使用保存的 AMI ID)。
  3. 工程文件是从基于 Web 的仓库(如 GitLab、GitHub 或其他云文件托管服务)克隆的。
  4. MATLAB 通过 -batch 参量执行,将测试场景加载到 Simulink Test™ 中并启动其执行。
  5. 系统会生成包含测试结果的报告并将其存储为工件,并终止虚拟机。
截图:基于虚拟机的测试管道,始于开发人员推送更改,终于根据测试结果生成报告。

图 2. 基于虚拟机的虚拟测试管道。

为虚拟测试管道创建容器

使用 AWS 实现基于容器的 CI 管道的准备工作分为两步。第一步是,在本地创建一个容器镜像,然后将该镜像上传到基于 Web 的注册表,例如 Amazon Elastic Container Registry (Amazon ECR)。

按照创建 MATLAB 容器镜像 GitHub 仓库中的说明进行操作可极大简化这第一个步骤。

第二步是将图像上传到 Amazon ECR。请按照以下步骤执行操作:

  1. 使用 AWS 管理控制台创建一个 ECR 镜像仓库。
  2. 为新创建的云仓库标记本地容器。

以下命令将 ID 为 e5be3y248h47 的本地容器镜像标记为 aws_account_id.dkr.ecr.us-west-2.amazonaws.com/my-repository:tag

docker tag e5be3y248h47 aws_account_id.dkr.ecr.eu-central-1.amazonaws.com/my-repository:tag

  1. 将新标记的容器镜像推送到仓库:

docker push aws_account_id.dkr.ecr.us-west-2.amazonaws.com/my-repository:tag

在创建容器镜像并将其推送到 Amazon ECR 仓库后,可以将其部署在基于容器的管道中,该管道实现以下工作流(图 3):

  1. 当开发人员提交对源代码库的更改时,系统会触发测试管道。
  2. 系统会通知 AWS 无服务器计算引擎 Fargate,并从容器镜像注册表 (ECR) 启动容器。
  3. 工程文件是从基于 Web 的仓库(如 GitLab、GitHub 或其他云文件托管服务)克隆的。
  4. MATLAB 通过 -batch 参量执行,将测试场景加载到 Simulink Test 中并启动其执行。
  5. 系统会生成包含测试结果的报告并将其存储为工件,并终止无服务器容器。
截图:基于容器的虚拟测试管道,始于开发人员推送更改,终于根据测试结果生成报告。

图 3. 基于容器的虚拟测试管道。

配置 CI 系统以使用虚拟测试管道

一旦团队有能力通过基于容器或基于虚拟机的管道在云中运行虚拟测试,就可以使用该功能自动扩展 CI 流程。GitHub、GitLab 和 Jenkins 可以与虚拟机或容器结合使用,但我们发现以下三个选项通常更易于配置和使用。

选项 A:使用虚拟机将 GitHub 配置为自动扩展

GitHub Marketplace 提供的各种 GitHub Action 旨在便于 GitHub 与云服务的集成。

为了将 GitHub 与我们的 AWS 虚拟测试管道集成,我的团队使用 GitHub Action 来简化任务,例如在 CI 流程中动态启动、停止和管理 AWS EC2 runner 实例。

为了配置此 GitHub Action,我们在 Github 仓库的 .github/workflows/directory 中创建了一个名为 workflow.yml 的新工作流文件。在此文件中,我们定义看 CI 工作流(图 4),包括要执行的操作、操作序列以及运行条件。

AWS 云截图:GitHub 和虚拟机的持续集成工作流。

图 4. GitHub 和虚拟机的 CI 工作流。

以下示例代码演示了一个典型 GitHub Action 配置,它与 GitHub 机密结合使用来加密凭据和敏感参数。它指定必要的 AWS 凭据,定义操作模式(启动 EC2 runner),并为流程提供必要的输入,包括 GitHub 个人访问令牌、镜像 ID (AMI ID) 和实例类型。

name:Create and start an EC2 runner 
    runs-on:Linux 
    outputs: 
      label: ${{ steps.start-ec2-runner.outputs.label }} 
      ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }} 
    steps: 
      - name:Configure AWS credentials 
        # 将 Access Key,secret Key 和 Token 定义为 secret,并用它们配置 AWS 凭证 
        uses: aws-actions/configure-aws-credentials@v1-node16 
        with: 
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 
          aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} 
          aws-region: ${{ secrets.AWS_REGION }} 
      - name:Start EC2 runner 
        id: start-ec2-runner 
       # 配置自动生成的实例设置,如 image-id 和 security-group-id 
       uses: machulav/ec2-github-runner@v2 
       with: 
         mode: start 
         github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
         ec2-image-id: ${{ secrets.IMAGE_ID }} 
         ec2-instance-type: m5.large 
         subnet-id: ${{ secrets.SUBNET_ID }} 
         security-group-id: ${{ secrets.SECURTITY_GROUP_ID }} 
         iam-role-name: ${{ secrets.ROLE_NAME }} # 可选,Role 名称可以让实例使用带有该 Role 名称的其他 AWS 资源 
         aws-resource-tags: > # 可选,给实例添加额外信息 
           [ 
             {"Key":"Name", "Value": "ec2-github-runner"}, 
             {"Key":"GitHubRepository", "Value": "${{ github.repository }}"} 

接下来,我们添加以下 GitHub 操作代码来执行 MATLAB 脚本,这将启动测试过程:

testing: 
  name:Start MATLAB 
  needs: start-runner # 必需,运行器就绪时启动主要作业 
  runs-on: ${{ needs.start-runner.outputs.label }} # 在新建的启动器上运行作业 
  steps:  
    - name: Execute MATLAB tests  
      run: | 
        export MLM_LICENSE_FILE=27000@ec2-35-156-32-190.eu-central-1.compute.amazonaws.com  
        matlab -batch "addpath(genpath(cd)); \ 
        testFile = sltest.testmanager.load('AutonomousEmergencyBrakingTests.mldatx'); \ 
        testSuite = getTestSuiteByName(testFile,'Test Scenarios'); \ 
        testCase = getTestCaseByName(testSuite,'scenario_25_AEB_PedestrianTurning_Nearside_10kph'); \ 
        resultObj = run(testCase); \ 
        sltest.testmanager.report(resultObj,'Report.pdf', Title='Autonomous Emergency Braking', IncludeMATLABFigures=true, IncludeErrorMessages=true, IncludeTestResults=0, LaunchReport=false);"

运行包含上述操作的 GitHub CI 作业将基于之前创建的 AMI 在云中启动新虚拟机,并将其用作执行 MATLAB 测试脚本的节点(图 4)。

选项 B:使用容器将 GitLab 配置为自动扩展

为了将 GitLab 与无服务器容器结合使用,我们将 GitLab Runner 配置为与 AWS Fargate 计算引擎一起工作。这分三步完成:

  1. 我们按如下所示编辑了 runner config.toml 文件:
  2. [[runners]] 
      name = “fargate-test”
      url = “https://gitlab.com/” 
      token = “__REDACTED__” 
      executor = “custom” 
      builds_dir = “/opt/gitlab-runner/builds” 
      cache_dir = “/opt/gitlab-runner/cache” 
      [runners.custom] 
        volumes = [“/cache”, “/path/to-ca-cert-dir/ca.crt:/etc/gitlab-runner/certs/ca.crt:ro”] 
        config_exec = “/opt/gitlab-runner/fargate” 
        config_args = [“—config”, “/etc/gitlab-runner/fargate.toml”, “custom”, “config”] 
        prepare_exec = “/opt/gitlab-runner/fargate” 
        prepare_args = [“—config”, “/etc/gitlab-runner/fargate.toml”, “custom”, “prepare”] 
        run_exec = “/opt/gitlab-runner/fargate” 
        run_args = [“—config”, “/etc/gitlab-runner/fargate.toml”, “custom”, “run”] 
        cleanup_exec = “/opt/gitlab-runner/fargate” 
        cleanup_args = [“—config”, “/etc/gitlab-runner/fargate.toml”, “custom”, “cleanup”] 
    
  3. 我们安装了 AWS Fargate 自定义执行程序驱动程序:
  4. sudo curl -Lo /opt/gitlab-runner/fargate “https://gitlab-runner-custom-fargate-downloads.s3.amazonaws.com/latest/fargate-linux-amd64” 
    sudo chmod +x /opt/gitlab-runner/fargate 
    
  5. 我们使用所需的 AWS 参数配置了驱动程序的 fargate.toml 文件:
[Fargate] 
   Cluster = “test-cluster” # 集群名 
   Region = “us-east-2” # 要部署的 AWS 区域 in it 
   Subnet = “subnet-xxxxxx” # VPC subnet 
   SecurityGroup = “sg-xxxxxxxxxxxxx” # 容器联网 
   TaskDefinition = “test-task:1” # 将在 AWS Fargate 中执行的任务名 
   EnablePublicIP = true # 通过分配公网 IP 允许外部连接容器 

[TaskMetadata] 
  Directory = “/opt/gitlab-runner/metadata” 

[SSH] 
  Username = “root” # 容器中使用的用户名 
  Port = 22 # 将 SSH 端口设为 22 

在配置 Fargate 执行程序并将 runner 连接到工程仓库后,任何 GitLab CI 作业都将自动在云中启动容器,并使用它来执行作业(图 5)。

AWS 云截图:GitLab 和容器的持续集成工作流。

图 5. GitLab 和容器的 CI 工作流。

以下 CI 阶段脚本用于执行 MATLAB 测试脚本:

Run MATLAB:  
  script:  
    - export MLM_LICENSE_FILE=27000@ec2-35-156-32-190.eu-central-1.compute.amazonaws.com  
    - > 
      matlab -batch "addpath(genpath(cd));  
      testFile = sltest.testmanager.load('AutonomousEmergencyBrakingTests.mldatx'); 
      testSuite = getTestSuiteByName(testFile,'Test Scenarios'); 
      testCase = getTestCaseByName(testSuite,'scenario_25_AEB_PedestrianTurning_Nearside_10kph'); 
      resultObj = run(testCase); 
      sltest.testmanager.report(resultObj,'Report.pdf', Title='Autonomous Emergency Braking', IncludeMATLABFigures=true, IncludeErrorMessages=true, IncludeTestResults=0, LaunchReport=false);"

选项 C:使用虚拟机将 Jenkins 配置为自动扩展

Amazon EC2 Jenkins 插件支持将 AWS EC2 实例集成到 Jenkins CI 管道中,并支持自动执行实例创建、管理和终止。为了配置 EC2 插件,我们按照以下步骤执行了操作:

  1. 通过 Jenkins 插件管理器安装 EC2 插件。
  2. 导航到 Manage Jenkins > Configure System > Cloud > Add a new cloud > Amazon EC2。
  3. 输入 AWS 凭据,选择区域,并指定用于该实例的 AMI ID(图 6)。
  4. 根据需要配置实例类型、安全组和其他设置。
  5. 配置自动缩放选项,如使用量和闲置终止时间。
  6. 保存配置。
截图:安装时为 Jenkins 配置 EC2 插件的选项。

图 6. 为 Jenkins 配置 Amazon EC2 插件。

通过此设置,我们分配了管道以使用新创建的云节点并自动创建 EC2 实例来执行排队作业(图 7)。

 AWS 云截图:Jenkins 和虚拟机的持续集成工作流。

图 7. Jenkins 和虚拟机的 CI 工作流。(徽标由 Jenkins 提供。)

对于定义我们的 Jenkins 管道的 Jenkinsfile,我们按如下所示指定了工作环境:

environment { 
    MLM_LICENSE_FILE= '27000@ec2-35-156-32-190.eu-central-1.compute.amazonaws.com' 
} 

此外,我们使用了以下阶段代码来启动 MATLAB 测试脚本:

stage('Build and Test') {  
    steps {  
        sh ''' 
matlab -batch "addpath(genpath(cd));\ 
testFile = sltest.testmanager.load('AutonomousEmergencyBrakingTests.mldatx');\ 
testSuite = getTestSuiteByName(testFile,'Test Scenarios');\ 
testCase = getTestCaseByName(testSuite,'scenario_25_AEB_PedestrianTurning_Nearside_10kph');\ 
resultObj = run(testCase);\ 
sltest.testmanager.report(resultObj,'Report.pdf', Title='Autonomous Emergency Braking', IncludeMATLABFigures=true, IncludeErrorMessages=true, IncludeTestResults=0, LaunchReport=false);"'  
        ''' 
    }  
} 

比较备选方案:关键要点

在为基于云的 CI 选择虚拟机或容器时,从多方面进行考虑很有帮助:

  • 设置。创建 Amazon 机器镜像 (AMI) 通常需要启动实例、连接到该实例、安装必要的软件并将其保存为 AMI,而容器是从 Dockerfile 脚本创建的,该脚本包含组合一个镜像所需的所有命令。也可以通过 Python® 或 Terraform 脚本创建 AMI,因此这两种方法都支持版本化的基础架构或代码形式的基础架构。对于需要单个镜像的小工程,使用 AMI 会更方便,因为它不需要掌握大量知识来撰写安装过程或配置机器。如果某些工具不支持容器且需要手动安装,而 MathWorks 未提供这些工具,则 AMI 也是唯一选择。
  • 维护。容器镜像和 AMI 都可以通过修改创建脚本来更新和管理。此外,您还可以连接并修改 AMI 或容器镜像,并将它们保存为新镜像。
  • 成本。容器镜像在成本方面有优势,因为它们通常比较小巧。
  • 可移植性。包括 AMI 在内的虚拟机镜像无法迁移到 Azure 或 Google 等其他云提供商。相反,容器镜像是独立于平台的,可以跨多个平台共享。
  • 操作系统支持。虚拟机镜像和容器镜像都支持各种操作系统,包括 Linux 和 Windows Server®。然而,Windows 容器不提供 GUI 支持,因此在其上执行的任何应用程序都必须能够在没有 GUI 的情况下运行。

综合考虑上述因素,我们通常更喜欢容器镜像,因为它们在成本效益、可移植性和灵活性方面具有优势。然而,如果是为了下载和执行容器镜像以运行测试而部署虚拟机实例,AMI 之类的虚拟机镜像可能是更合适的解决方案,尤其是当对 Windows 桌面的支持是必须的情况下。

在 GitHub、GitLab 和 Jenkins 之间进行选择就不那么显而易见,因为每个 CI 平台都有各自的优缺点。

  • GitHub Marketplace 提供了大量工作流操作,但其独到之处在于能够将所有实例配置和测试阶段集成到一个 YAML 文件中。这种集中化方法使 GitHub 成为从一个位置全面控制和管理单个测试用例的理想解决方案。然而,它的劣势是缺乏可扩展性控制选项,例如管理闲置实例和闲置时间。
  • GitLab 也提供各种执行程序,相当于操作和插件,这有助于与一系列云服务的集成。它还提供可扩展性控制,例如闲置时间和一天中特定时间可用的实例数量。然而,它的设置和配置过程较为复杂,团队必须访问 runner manager 实例来修改某些硬编码的配置。
  • 对我们来说,Jenkins 非常容易安装和管理。它可以使用 webhook 与 GitLab、GitHub 上托管的仓库或其他基于 Web 的 Git™ 仓库连接,并在推送代码时自动触发管道。Jenkins 提供大量插件以及通知和插件管理器系统。

总的来说,我们建议尽可能使用容器,并根据工程的具体需求或团队现有知识或使用的特定平台等因素来决定使用哪个 CI 平台。

2024 年发布

查看文章,了解相关行业