AWS EC2에서 GitLab Runner의 자동 스케일링

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed

GitLab Runner의 가장 큰 장점 중 하나는 빌드가 즉시 처리되도록 VM을 자동으로 생성하고 제거할 수 있는 기능입니다. 이 기능은 사용 방법에 따라 24/7 러너를 사용하지 않는 상황에서 비용 효율적이고 확장 가능한 솔루션을 제공할 수 있습니다.

소개

이 튜토리얼에서는 AWS에서 GitLab Runner를 올바르게 구성하는 방법을 살펴봅니다. AWS의 인스턴스는 요구에 따라 새로운 도커 인스턴스를 생성하는 러너 관리자로 작동합니다. 이러한 인스턴스의 러너는 이 안내서에서 다루는 매개변수를 사용하며, 작성 후에 수동으로 구성할 필요가 없습니다.

또한 Amazon의 EC2 Spot 인스턴스를 사용하여 강력한 자동 스케일링 기계를 사용하면서도 GitLab Runner 인스턴스의 비용을 크게 줄일 수 있습니다.

전제 조건

AWS에 대한 이해가 필요합니다. 대부분의 구성은 여기에서 이루어집니다.

Docker Machine amazonec2 드라이버 문서를 빠르게 참조하여 나중에 이 문서에서 설정할 매개변수에 익숙해져 보세요.

GitLab Runner는 GitLab 인스턴스와 네트워크 상에서 통신해야 합니다. AWS 보안 그룹을 구성하거나 DNS 구성을 설정할 때 고려해야 할 사항입니다.

예를 들어, EC2 리소스를 공개 트래픽과 분리된 다른 VPC에 유지하여 네트워크 보안을 강화할 수 있습니다. 환경은 각기 다를 수 있으므로 상황에 가장 적합한 방법을 고려해보세요.

AWS 보안 그룹

Docker Machine은 Docker와 통신에 필요한 포트 2376 및 SSH 22의 규칙이 있는 기본 보안 그룹을 사용하려고 시도할 것입니다. Docker를 의존하는 대신 필요한 규칙이 있는 보안 그룹을 생성하고 이를 GitLab Runner 옵션에서 제공하여 이 문서에서 볼 것입니다. 이렇게 하면 네트워킹 환경에 기반하여 미리 원하는 대로 사용자 정의할 수 있습니다. 러너/머신 섹션에서 구성할 수 있습니다.

포트 237622러너 관리자 인스턴스에서 접근 가능한지 확인해야 합니다.

AWS 자격 증명

작업이 실행되는 데 새로운 EC2 인스턴스를 제공하려면 스케일(EC2) 및 캐시 업데이트(대상 S3) 권한이 있는 사용자에 연결된 AWS 액세스 키가 필요합니다. EC2 (AmazonEC2FullAccess) 및 S3에 대한 정책(AmazonS3FullAccess)이 있는 새 사용자를 만드세요. S3에 필요한 최소한의 권한에 대한 자세한 정보는 runners.cache.s3을 참조하세요. 더 안전하게 하려면 해당 사용자의 콘솔 로그인을 비활성화할 수 있습니다. 보안 자격 증명을 편집기에 보관하거나 탭을 열어두세요. 나중에 GitLab Runner 구성에서 사용합니다.

또한 EC2 인스턴스 프로필에 필요한 AmazonEC2FullAccessAmazonS3FullAccess 정책을 포함하여 만들 수 있습니다.

러너 관리자 EC2 인스턴스에 이 인스턴스 프로필을 부여하여 작업 실행을 위한 새 EC2 인스턴스를 프로비저닝합니다. 러너 머신이 인스턴스 프로필을 사용하는 경우 러너 관리자의 인스턴스 프로필에 iam:PassRole 작업을 포함하세요.

예시:

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

러너 관리자 인스턴스 준비

첫 번째 단계는 머신을 생성하는 새로운 머신 생성 러너 관리자 역할을 하는 EC2 인스턴스에 GitLab Runner를 설치하는 것입니다. Docker 및 GitLab Runner가 Ubuntu, Debian, CentOS 또는 RHEL과 같이 지원하는 배포용으로 선택합니다.

이 머신은 작업을 실행하지는 않지만 항상 실행되어야 하는 러너 관리자 인스턴스라서 강력한 머신이 아니어도 됩니다. 초기 구성에서는 더 작은 인스턴스로 시작할 수 있습니다. 이 기계는 지속적인 기본 비용이 있는 유일한 호스트이기 때문에 전용 호스트입니다.

준비 사항:

  1. 서버에 로그인
  2. 공식 GitLab 저장소에서 GitLab Runner 설치
  3. Docker 설치
  4. GitLab 분기에서 Docker Machine 설치 (Docker는 Docker Machine을 폐기했습니다)

러너가 설치되었으므로 이제 등록할 때입니다.

GitLab Runner 등록하기

GitLab Runner를 구성하기 전에 먼저 등록하여 GitLab 인스턴스와 연결해야 합니다.

  1. 러너 토큰 얻기
  2. 러너 등록하기
  3. 실행기 타입을 묻힐 때 docker+machine을 입력하세요.

이제 가장 중요한 부분인 GitLab Runner 구성으로 넘어갈 수 있습니다.

참고: 모든 사용자가 오토스케일 러너를 사용할 수 있도록 하려면, 러너를 공유로 등록하세요.

러너 구성하기

이제 러너가 등록되었으므로 구성 파일을 편집하여 AWS 머신 드라이버에 필요한 옵션을 추가해야 합니다.

먼저 조각조각 나누어 봅시다.

전역 섹션

전역 섹션에서 모든 러너 전체에서 동시에 실행될 수 있는 작업의 제한(concurrent)을 정의할 수 있습니다. 이는 GitLab Runner가 수용할 사용자 수, 빌드에 걸리는 시간 등과 같은 요구사항에 크게 의존합니다. 10과 같이 낮은 값으로 시작하여 이후에 값을 증가시키거나 감소시킬 수 있습니다.

check_interval 옵션은 러너가 GitLab에서 새로운 작업을 확인해야 하는 빈도를 초 단위로 정의합니다.

예시:

concurrent = 10
check_interval = 0

기타 옵션 도 사용할 수 있습니다.

runners 섹션

[[runners]] 섹션에서 가장 중요한 부분은 executor가 반드시 docker+machine으로 설정되어야 한다는 점입니다. 대부분의 설정은 처음에 러너를 등록할 때 처리됩니다.

limit는 이 러너가 생성할 수 있는 최대 머신(동작 중 및 유휴 상태) 수를 설정합니다. 자세한 내용은 limit, concurrent, 및 IdleCount 간의 관계 를 확인하십시오.

예시:

[[runners]]
  name = "gitlab-aws-autoscaler"
  url = "<당신의 GitLab 인스턴스의 URL>"
  token = "<러너의 토큰>"
  executor = "docker+machine"
  limit = 20

[[runners]] 아래의 기타 옵션들도 사용할 수 있습니다.

runners.docker 섹션

[runners.docker] 섹션에서는 .gitlab-ci.yml에 정의되지 않은 경우 모든 자식 러너에서 사용할 기본 Docker 이미지를 정의할 수 있습니다. privileged = true를 사용하면 모든 러너가 GitLab CI/CD를 통해 Docker 이미지를 빌드 할 수 있습니다.

다음으로 분산 캐시 모드를 사용할 것이기 때문에 내부 캐시 메커니즘을 비활성화하기 위해 disable_cache = true를 사용합니다.

예시:

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

[runners.docker] 아래의 기타 옵션들도 사용할 수 있습니다.

runners.cache 섹션

작업을 빠르게 하기 위해 선택한 디렉터리 및/또는 파일을 저장하고 후속 작업 간에 공유하는 캐시 메커니즘을 제공하는데 GitLab Runner가 제공합니다. 이 설정은 이번 설치에는 필요하지는 않지만, GitLab Runner가 제공하는 분산 캐시 메커니즘을 사용하는 것이 좋습니다. 새로운 인스턴스가 필요한 경우 캐시가 저장된 공통 위치가 필수적입니다.

다음 예시에서 우리는 Amazon S3를 사용합니다:

  [runners.cache]
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      ServerAddress = "s3.amazonaws.com"
      AccessKey = "<당신의 AWS 액세스 키 ID>"
      SecretKey = "<당신의 AWS 비밀 액세스 키>"
      BucketName = "<캐시를 보관할 버킷>"
      BucketLocation = "us-east-1"

여기에 다른 정보를 참조하여 본 캐시 메커니즘을 더 탐구할 수 있습니다:

runners.machine 섹션

이것은 구성의 가장 중요한 부분이며, GitLab Runner에게 언제 어떻게 새로운 Docker Machine 인스턴스를 생성하거나 예전 인스턴스를 제거할지에 대해 알려주는 부분입니다.

여기서는 AWS 머신 옵션에 중점을 둘 것이며, 나머지 설정에 대해서는 - 자동 확장 알고리즘 및 그를 기반으로 하는 매개 변수 - 자동 확장 주기 에 대해 읽어보시기 바랍니다.

다음은 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로 설정했으며, 머신 이름에 표준 접두어와 %s(필수)가 있는 것으로 변경할 자식 러너의 ID에 의해 대체되는 형식입니다: gitlab-docker-machine-%s.

당신의 AWS 인프라에 따라 MachineOptions 아래에 많은 옵션을 설정할 수 있습니다.

머신 옵션 설명
amazonec2-access-key=XXXX EC2 인스턴스를 생성할 권한이 있는 사용자의 AWS 액세스 키, AWS 자격 증명을 참조하세요.
amazonec2-secret-key=XXXX EC2 인스턴스를 생성할 권한이 있는 사용자의 AWS 비밀 액세스 키, 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 콘솔에서 인스턴스를 식별하는 데 유용합니다. 기본적으로 “Name” 태그는 머신 이름으로 설정됩니다. [[runners]]에 설정된 러너 이름과 일치하는 “runner-manager-name”을 설정하여 특정 매니저 설정으로 생성된 모든 EC2 인스턴스를 필터링할 수 있습니다.
amazonec2-security-group=xxxx AWS VPC 보안 그룹 이름, 보안 그룹 ID가 아닙니다. AWS 보안 그룹을 확인하세요.
amazonec2-instance-type=m4.2xlarge 자식 러너가 실행될 인스턴스 유형
amazonec2-ssh-user=xxxx 인스턴스에 SSH 액세스 권한이있는 사용자
amazonec2-iam-instance-profile=xxxx_runner_machine_inst_profile_name 러너 머신을위한 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 스팟 인스턴스 입찰 가격 (미국 달러). --amazonec2-request-spot-instance 플래그를 true로 설정해야합니다. amazonec2-spot-price를 생략하면 Docker Machine은 기본값으로 1시간에 0.50 달러를 설정합니다.
amazonec2-security-group-readonly=true 보안 그룹을 읽기 전용으로 설정합니다.
amazonec2-userdata=xxxx_runner_machine_userdata_path 러너 머신 userdata 경로를 지정합니다.
amazonec2-root-size=XX 인스턴스의 루트 디스크 크기(GB).

참고:

[runners.machine] 아래의 기타 옵션들도 사용할 수 있습니다.

모두 함께 모아 보기

다음은 /etc/gitlab-runner/config.toml의 전체 예시입니다.

concurrent = 10
check_interval = 0

[[runners]]
  name = "gitlab-aws-autoscaler"
  url = "<당신의 GitLab 인스턴스 URL>"
  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 = "<당신의 AWS 액세스 키 ID>"
      SecretKey = "<당신의 AWS 비밀 액세스 키>"
      BucketName = "<캐시를 유지해야 하는 버킷>"
      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 스팟 인스턴스를 사용하여 비용 절감

Amazon의 설명에 따르면:

Amazon EC2 스팟 인스턴스를 사용하면 여분의 Amazon EC2 컴퓨팅 용량에 입찰할 수 있습니다. 스팟 인스턴스는 On-Demand 가격에 비해 할인된 가격으로 자주 제공되므로 응용 프로그램을 실행하는 비용을 크게 줄이고, 동일한 예산으로 응용 프로그램의 컴퓨팅 용량과 처리량을 확장하며, 새로운 유형의 클라우드 컴퓨팅 응용 프로그램을 활성화할 수 있습니다.

/etc/gitlab-runner/config.toml에서 MachineOptions 섹션 아래에 위에서 선택한 runners.machine 옵션에 추가로 다음을 추가하세요:

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

amazonec2-spot-price를 비워둔 이 구성에서, AWS는 스팟 인스턴스의 입찰 가격을 해당 인스턴스 클래스의 기본 온디맨드 가격으로 설정합니다. amazonec2-spot-price를 완전히 생략하면 Docker Machine은 최대 가격을 시간당 기본 값인 $0.50으로 설정합니다.

스팟 인스턴스 요청을 더 사용자 정의할 수 있습니다:

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

이 구성에서는 스팟 인스턴스로 Docker Machines가 생성되며, 스팟 요청 가격이 최대 시간당 $0.03으로 설정되고 스팟 인스턴스의 지속 시간이 60분으로 제한됩니다. 상기 언급된 0.03 숫자는 예시이므로 선택한 지역에 따른 현재 가격을 확인해야 합니다.

Amazon EC2 스팟 인스턴스에 대해 더 알아보려면 다음 링크를 방문하세요:

스팟 인스턴스의 주의사항

스팟 인스턴스를 사용하면 미사용 중인 리소스를 활용하여 인프라 비용을 최소화할 수 있는 좋은 방법이지만, 이에 따른 영향을 인식해야 합니다.

스팟 인스턴스에서 CI 작업을 실행하면 스팟 인스턴스 가격 모델 때문에 실패율이 증가할 수 있습니다. 지정한 최대 스팟 가격이 현재 스팟 가격을 초과하면 요청한 용량을 받지 못합니다. 스팟 가격은 시간당으로 수정됩니다. 현재 스팟 인스턴스의 최대 가격이 수정된 스팟 인스턴스 가격보다 낮은 경우 두 분 안에 해당 스팟 인스턴스가 종료되고 스팟 호스트에서의 모든 작업이 실패합니다.

이로 인해 자동 확장 러너는 새 머신을 생성하지 못하고 계속해서 새로운 인스턴스를 요청하게 될 것입니다. 이로 인해 60번의 요청이 발생하고, 이후에는 더 이상 AWS가 요청을 수락하지 않게 됩니다. 그 후에 스팟 가격이 받을만한 수준이 되면 요청 제한 때문에 잠시 활동이 제한됩니다.

위와 같은 경우를 만나게 되면, 러너 관리자 머신에서 다음 명령을 사용하여 Docker Machines 상태를 확인할 수 있습니다:

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

참고: GitLab 러너가 스팟 가격 변경을 안전하게 처리하도록 하는 데 관련된 몇 가지 문제가 있으며, docker-machine이 계속해서 Docker Machine을 제거하려고 시도하는 보고가 있습니다. GitLab은 상위 프로젝트에서 두 경우에 대한 패치를 제공했습니다. 자세한 정보는 이슈 #2771#2772를 참조하세요.

GitLab의 포크는 AWS EC2 플리트 및 스팟 인스턴스와의 사용을 지원하지 않습니다. 대신, Continuous Kernel Integration Project의 다운스트림 포크를 사용할 수 있습니다.

결론

본 가이드에서는 AWS의 autoscale 모드에서 GitLab Runner를 설치하고 구성하는 방법에 대해 배웠습니다.

GitLab Runner의 autoscale 기능을 사용하면 시간과 비용을 모두 절약할 수 있습니다. AWS에서 제공하는 Spot 인스턴스를 사용하면 더 많은 비용을 절약할 수 있지만 그에 따른 영향에 대해 인지해야 합니다. 충분히 높은 입찰을 하고 있다면 문제가 없어야 합니다.

본 자습서가 (크게) 영향을 받은 다음의 사용 사례를 읽어볼 수 있습니다: