在 AWS EC2 上弹性伸缩极狐GitLab Runner

极狐GitLab Runner 的一个很大的优势是能够自动启动或关闭虚拟机,立即处理您的构建。这是一个很好的功能,当您不一直使用 Runner,需要划算且可扩展的解决方案时,这个功能非常有用。

简介

本次我们将介绍如何在 AWS 中合理配置极狐GitLab Runner。AWS 中的实例作为 Runner Manager,按需生成 Docker 实例。 这些实例中的 Runner 是自动生成的。他们使用本指南中的参数,且创建后无需手动配置。

此外,我们还会使用 Amazon EC2 Spot 实例, 在使用强大的弹性伸缩机器的同时,大幅降低极狐GitLab Runner 实例的成本。

先决条件

大多数配置都在 AWS 上进行,所以需要熟悉 AWS。

我们建议您阅读 Docker Machine amazonec2 驱动文档,熟悉本文后续要配置的参数。

您的极狐GitLab Runner 需要和您的极狐GitLab 实例通过网络进行通信,您需要考虑配置 AWS 安全组和 DNS 的时间。

例如,您可以把 EC2 资源从公共流量上分离到一个不同的 VPC上,以更好保障您的网络安全。您需要根据您的具体环境做相应调整。

AWS 安全组

Docker Machine 会使用默认安全组, 规定端口 2376 和 SSH 22 的规则,这是与 Docker Daemon 通信的必要条件。不依赖 Docker,您也可以创建包括您所需规则的安全组,并且在极狐GitLab Runner 选项中提供安全组,如下所示。 这样您就可以基于您的组网环境提前进行自定义。 您需要确定Runner Manager 实例可以访问端口 237622

AWS 凭据

您需要将 AWS Access Key 与有权限扩缩 (EC2) 和更新缓存 (通过 S3) 的用户进行绑定。 使用 EC2 (AmazonEC2FullAccess) 和 S3 (AmazonS3FullAccess) 的政策创建新用户。 为提高安全性,您可以为用户禁用控制台登录。 不要关闭选项卡或者在编辑器中复制粘贴安全证书,因为后续要在极狐GitLab Runner 配置中继续使用。

您还可以使用所需的 AmazonEC2FullAccessAmazonS3FullAccess 策略创建 EC2 实例配置文件

为作业执行创建新的 EC2 实例,将此实例的配置文件附着到 Runner Manager 实例。如果 Runner 机器使用实例配置文件,您需要在 Runner Manager 实例的实例配置文件中包含 iam:PassRole 操作。

示例:

{
    "Statement": [
        {
            "Action": "iam:PassRole",
            "Effect": "Allow",
            "Resource": "arn:aws:iam:::role/instance-profile-of-runner-machine"
        }
    ],
    "Version": "2012-10-17"
}

准备 Runner Manager 实例

第一步需要在 EC2 实例中安装极狐GitLab Runner,该实例被作用 Runner Manager,可以创建新机器。选择一个 Docker 和 Runner 都支持的分布,如 Ubuntu、 Debian、 CentOS 或 RHEL。

这个机器不需要多强大,因为 Runner Manager 实例本身并不运行作业。 您可以使用小实例进行初始配置。这个机器应该是个专属主机,因为它要一直开机运行。因此,它是唯一一个有持续基线成本的主机。

先决条件:

  1. 登录您的服务器
  2. 从官方极狐GitLab 仓库安装 Runner
  3. 安装 Docker
  4. 从极狐GitLab 派生安装 Docker Machine (Docker 已经弃用 Docker Machine)

安装成功之后需要注册。

注册极狐GitLab Runner

配置极狐GitLab Runner 之前,需要注册才能连接到极狐GitLab 实例。

  1. 获取 Runner 令牌
  2. 注册 Runner
  3. 执行器类型输入 docker+machine

现在可以开始配置 Runner。

note 如果您想让您实例中的每个用户都能使用弹性伸缩 Runner,您需要将其配置为共享 Runner。

配置 Runner

Runner 注册成功后,您需要编辑它的配置文件,为 AWS machine 驱动添加必选项。

下面我们分部分介绍。

全局部分

在全局部分中,您可以指定可以在所有 Runner 上并发运行的作业的限制 (concurrent)。这很大程度上取决于您的需求,比如 Runner 要支持多少个用户,构建需要多长时间等。您可以先设置成 10,后续再根据情况增减。

check_interval 选项定义 Runner 检查极狐GitLab 中新作业的频率,单位为秒。

例如:

concurrent = 10
check_interval = 0

runners 部分

[[runners]] 部分中,最重要的是把 executor 设置成 docker+machine。这些设置大部分需要在您第一次注册 Runner 的时候完成。

limit 设置 Runner 可以创建的机器 (运行和闲置)的最大数量。

例如:

[[runners]]
  name = "gitlab-aws-autoscaler"
  url = "<URL of your GitLab instance>"
  token = "<Runner's token>"
  executor = "docker+machine"
  limit = 20

[[runners]] 下的 其他选项也可用。

runners.docker 部分

[runners.docker] 部分中,如果 .gitlab-ci.yml 中没有指定子 Runner 使用的默认 Docker Image,您可以进行指定。

通过使用 privileged = true,所有的 Runner 都可以运行 Docker in Docker。 这对于您通过极狐GitLab CI/CD 构建自己的 Docker Image 很有用。

我们会使用下面描述中的分布式缓存模式,所以下面我们使用 disable_cache = true 禁用 Docker 执行器的内部缓存机制。

例如:

  [runners.docker]
    image = "alpine"
    privileged = true
    disable_cache = true

runners.cache 部分

为了加快您的作业,极狐GitLab Runner 提供了一种缓存机制, 将目录和/或文件在后续作业之间进行保存和共享。我们虽然不强制,但是建议您使用极狐GitLab Runner 提供的分布式缓存机制。 因为新实例会被按需创建,所以必须有一个共同的地方存储缓存。

在下面的例子中我们使用 Amazon S3 举例:

  [runners.cache]
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      ServerAddress = "s3.amazonaws.com"
      AccessKey = "<your AWS Access Key ID>"
      SecretKey = "<your AWS Secret Access Key>"
      BucketName = "<the bucket where your cache should be kept>"
      BucketLocation = "us-east-1"

下面是缓存机制的更多信息:

runners.machine 部分

这是配置中最重要的部分,也指导极狐GitLab Runner 怎样和何时创建新 Docker Machine 实例或者移除旧 Docker Machine 实例。

我们关注 AWS Machine 选项,其余的设置请阅读以下内容:

runners.machine 部分的示例:

  [runners.machine]
    IdleCount = 1
    IdleTime = 1800
    MaxBuilds = 10
    MachineDriver = "amazonec2"
    MachineName = "gitlab-docker-machine-%s"
    MachineOptions = [
      "amazonec2-access-key=XXXX",
      "amazonec2-secret-key=XXXX",
      "amazonec2-region=us-central-1",
      "amazonec2-vpc-id=vpc-xxxxx",
      "amazonec2-subnet-id=subnet-xxxxx",
      "amazonec2-zone=x",
      "amazonec2-use-private-address=true",
      "amazonec2-tags=runner-manager-name,gitlab-aws-autoscaler,gitlab,true,gitlab-runner-autoscale,true",
      "amazonec2-security-group=xxxxx",
      "amazonec2-instance-type=m4.2xlarge",
    ]
    [[runners.machine.autoscaling]]
      Periods = ["* * 9-17 * * mon-fri *"]
      IdleCount = 50
      IdleTime = 3600
      Timezone = "UTC"
    [[runners.machine.autoscaling]]
      Periods = ["* * * * * sat,sun *"]
      IdleCount = 5
      IdleTime = 60
      Timezone = "UTC"

设置 Docker Machine 驱动为 amazonec2,Machine 名字有标准的前缀,后面是 %s (必填) ,且由子 Runner 的 ID gitlab-docker-machine-%s 代替。

根据您的 AWS 基础架构,您可以在 MachineOptions 下设置多个选项。下表中是最常见的一些选项。

Machine 选项 描述
amazonec2-access-key=XXXX 有权限创建 EC2 实例的用户的 AWS Access Key,详情请参见 AWS 证书
amazonec2-secret-key=XXXX 有权限创建 EC2 实例的用户的 AWS Secret Key,详情请参见 AWS 证书
amazonec2-region=eu-central-1 启动实例所使用的区域,您可以完全删除它,使用默认的 us-east-1
amazonec2-vpc-id=vpc-xxxxx 启动实例的 VPC ID
amazonec2-subnet-id=subnet-xxxx AWS VPC 子网 ID
amazonec2-zone=x 如果没有指定,是可用区域是 a。需要与指定子网的可用区域相同。例如,区域是 eu-west-1b 时,它必须是 amazonec2-zone=b
amazonec2-use-private-address=true 使用 Docker Machine 的私有 IP 地址,但是也会创建一个公网 IP 地址。对将流量锁在内部和避免额外成本十分有用
amazonec2-tags=runner-manager-name,gitlab-aws-autoscaler,gitlab,true,gitlab-runner-autoscale,true AWS 额外标签键值对,对识别 AWS 控制台上的实例十分有用。 “名字” 标签 默认设置为 machine 的名字。我们将名字设置为 “runner-manager-name”,用以匹配 [[runners]] 中设置的 Runner 名称,因此我们可以过滤所有特定 manager 设置创建的 EC2 实例
amazonec2-security-group=xxxx AWS VPC 安全组名称,不是安全组 ID。详情请参见 AWS 安全组
amazonec2-instance-type=m4.2xlarge 运行子 Runner 的实例类型
amazonec2-ssh-user=xxxx 能够进行实例 SSH 访问的用户
amazonec2-iam-instance-profile=xxxx_runner_machine_inst_profile_name 为 Runner Machine 所使用的 IAM 实例配置文件
amazonec2-ami=xxxx_runner_machine_ami_id 特定图像的极狐GitLab Runner AMI ID
amazonec2-request-spot-instance=true 使用额外的 EC2 容量,且比按需价格更低
amazonec2-spot-price=xxxx_runner_machine_spot_price=x.xx Spot 实例投标价格(美元)。需要将 --amazonec2-request-spot-instance flag 设置为 true。如果您删除 amazonec2-spot-price,Docker Machine 会将最高价钱设置为默认的每小时 $0.50
amazonec2-security-group-readonly=true 将安全组设置为只读
amazonec2-userdata=xxxx_runner_machine_userdata_path 指定 Runner Machine 的 userdata 路径
amazonec2-root-size=XX 实例的根磁盘大小(GB)

注意:

  • 您可以在 MachineOptions 下添加 AWS Docker Machine 驱动支持的任何东西。 我们非常鼓励您阅读 Docker 文档,因为您的基础架构设置可能会用到不同的选项。
  • 子实例默认使用 Ubuntu 16.04,除非您通过设置 amazonec2-ami 选择其他 AMI ID。仅设置 Docker Machine 支持的基本操作系统
  • 如果您将 amazonec2-private-address-only=true 指定为一个机器选项,则不会为您的 EC2 实例分配公共 IP。 如果您的 VPC 正确配置了互联网网关(IGW)且路由正确,这种情况是可以的。但是如果您的配置比较复杂,就需要考虑这个问题。

[runners.machine] 下的其他选项也可用。

整合

以下是 /etc/gitlab-runner/config.toml的完整例子:

concurrent = 10
check_interval = 0

[[runners]]
  name = "gitlab-aws-autoscaler"
  url = "<URL of your GitLab instance>"
  token = "<runner's token>"
  executor = "docker+machine"
  limit = 20
  [runners.docker]
    image = "alpine"
    privileged = true
    disable_cache = true
  [runners.cache]
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      ServerAddress = "s3.amazonaws.com"
      AccessKey = "<your AWS Access Key ID>"
      SecretKey = "<your AWS Secret Access Key>"
      BucketName = "<the bucket where your cache should be kept>"
      BucketLocation = "us-east-1"
  [runners.machine]
    IdleCount = 1
    IdleTime = 1800
    MaxBuilds = 100
    MachineDriver = "amazonec2"
    MachineName = "gitlab-docker-machine-%s"
    MachineOptions = [
      "amazonec2-access-key=XXXX",
      "amazonec2-secret-key=XXXX",
      "amazonec2-region=us-central-1",
      "amazonec2-vpc-id=vpc-xxxxx",
      "amazonec2-subnet-id=subnet-xxxxx",
      "amazonec2-use-private-address=true",
      "amazonec2-tags=runner-manager-name,gitlab-aws-autoscaler,gitlab,true,gitlab-runner-autoscale,true",
      "amazonec2-security-group=XXXX",
      "amazonec2-instance-type=m4.2xlarge",
    ]
    [[runners.machine.autoscaling]]
      Periods = ["* * 9-17 * * mon-fri *"]
      IdleCount = 50
      IdleTime = 3600
      Timezone = "UTC"
    [[runners.machine.autoscaling]]
      Periods = ["* * * * * sat,sun *"]
      IdleCount = 5
      IdleTime = 60
      Timezone = "UTC"

使用 Amazon EC2 Spot 实例降低成本

参见亚马逊的描述

Amazon EC2 Spot 实例允许您竞标备用 Amazon EC2 计算容量。 由于与按需实例相比,Spot 实例通常有折扣,可以显着降低运行应用程序的成本,以相同的预算增加应用程序的计算能力和吞吐量,并启用新型云计算应用程序。

除了您上面选择的 runners.machine 选项, 在 MachineOptions 部分的 /etc/gitlab-runner/config.toml 中添加以下内容:

    MachineOptions = [
      "amazonec2-request-spot-instance=true",
      "amazonec2-spot-price=",
    ]

amazonec2-spot-price 为空的配置中,AWS 将您的 Spot 实例的竞标价格设置为同等实例级别的默认按需价格。如果您没有设置 amazonec2-spot-price,Docker Machine 会将最高价格设置为默认的每小时 0.5 美元

您也可以进一步自定义您的 Spot 实例请求:

    MachineOptions = [
      "amazonec2-request-spot-instance=true",
      "amazonec2-spot-price=0.03",
      "amazonec2-block-duration-minutes=60"
    ]

此配置中,您可以使用 Spot 实例创建 Docker Machine,其中最高的 Spot 请求价格是每小时 0.03 美元,且 Spot 实例的时间上限为 60 分钟。 上面提到的 0.03 只是一个例子,您需要根据您选择的地区查看定价。

要了解有关 Amazon EC2 Spot 实例的更多信息,请访问以下链接:

Spot 实例的限制

虽然 Spot 实例是使用未使用资源并最小化您的基础架构成本的很好方式,但是您也必须了解其影响。

在 Spot 实例上运行 CI 作业可能会因为 Spot 实例定价模型而增加失败率。如果您指定的最高 Spot 价格超过当前 Spot 价格,您将无法获得所需的容量。Spot 定价每小时修订一次。最高价格低于修改后的 Spot 实例价格的任何现有 Spot 实例都将在两分钟内终止,并且Spot 主机上的所有作业都将失败。

结果,弹性伸缩的 Runner 将无法创建新机器,却继续请求新实例。最终将发出 60 个请求,之后 AWS 将不再接受。一旦 Spot 价格可以接受,您将会因为超出调用数量限制而被锁定一段时间。

如果遇到这种情况,可以在 Runner Manager Machine 中使用如下命令查看 Docker Machine 的状态:

docker-machine ls -q --filter state=Error --format "{{.NAME}}"

结论

在本指南中,我们学习了如何在 AWS 上以弹性伸缩的模式安装和配置极狐GitLab Runner。

使用极狐GitLab Runner 的弹性伸缩功能可以节省您的时间和金钱。 使用 AWS 提供的 Spot 实例可以为您节省更多,但您必须要注意其影响。但是只要您的出价足够高,这些都不是问题。