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 instances를 활용하여 높은 성능의 자동 확장 기계를 사용하면서도 GitLab Runner 인스턴스의 비용을 크게 줄일 수 있습니다.

사전 요구 사항

AWS에 대한 이해는 필수입니다. 대부분의 구성은 여기에서 이루어집니다.

Docker 머신 amazonec2 driver documentation을 빠르게 살펴보시기를 권장합니다. 나중에 이 기사에서 설정할 매개변수에 익숙해지도록 도와줄 것입니다.

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

예를 들어, EC2 자원을 공개 트래픽에서 분할하여 네트워크 보안을 강화할 수 있는 다른 VPC에 유지할 수 있습니다. 환경에 따라 가장 적합한 방법을 고려하십시오.

AWS 보안 그룹

Docker 머신은 포트 2376 및 SSH 22에 대한 규칙을 사용할 시도합니다. 이는 Docker와 의사소통하는 데 필요하므로 Docker를 신뢰하는 대신 필요한 규칙을 사용하는 보안 그룹을 생성하고 이를 GitLab Runner 옵션에서 제공할 수 있습니다. 이렇게 하면 네트워킹 환경에 따라 규칙을 미리 사용자 정의할 수 있습니다. 러너 관리자 인스턴스에 의해 알아볼 수 있습니다. 단, 포트 237622이 Runner Manager 인스턴스에 의해 접근 가능한지 확인해야 합니다.

AWS 자격 증명

(EC2)을 확장하고 캐시를 업데이트할 수 있는 사용자에 연결된 AWS Access Key가 필요합니다. EC2 (AmazonEC2FullAccess) 및 S3의 정책으로 새 사용자를 생성하십시오. 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를 설치하는 것입니다. Ubuntu, Debian, CentOS 또는 RHEL과 같이 Docker와 GitLab Runner가 지원하는 배포판을 선택하세요.

러너 관리자 인스턴스는 작업 자체를 실행하지 않기 때문에 강력한 머신일 필요는 없습니다. 초기 구성을 위해 작은 인스턴스로 시작할 수 있습니다. 이 머신은 항상 켜져 있어야 하므로, 초기 비용이 들 수 있습니다. 그러므로 전용 호스트가 될 수 있습니다.

사전 요구 사항 설치:

  1. 서버에 로그인
  2. 공식 GitLab 리포지터리에서 GitLab Runner 설치
  3. Docker 설치
  4. GitLab 포크의 Docker Machine 설치 (Docker는 Docker Machine을 사용하지 않음)

이제 Runner가 설치되었으므로 등록할 시간입니다.

GitLab Runner 등록

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

  1. 러너 토큰 획득
  2. 러너 등록
  3. 실행기 유형을 요청받으면 docker+machine으로 입력하세요

이제 가장 중요한 부분인 GitLab Runner를 구성할 차례입니다.

note
모든 사용자가 자동으로 확장되는 러너를 사용할 수 있기를 원한다면, 공유된 러너로 등록하십시오.

러너 구성

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

먼저 조각별로 나누어보겠습니다.

글로벌 섹션

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

check_interval 옵션은 러너가 GitLab을 확인하는 빈도(초)를 정의합니다.

예:

concurrent = 10
check_interval = 0

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

runners 섹션

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

limit은 이 러너가 스폰할 수 있는 최대 머신(실행 중 및 유휴)의 수를 설정합니다. 자세한 정보는 relationship between limit, concurrent and 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를 사용하여, 모든 러너가 Docker in Docker를 실행할 수 있게 됩니다. 이는 GitLab CI/CD를 통해 자체 Docker 이미지를 빌드할 계획이 있는 경우 유용합니다.

그 다음, 우리는 내부 캐시 메커니즘을 비활성화하기 위해 disable_cache = true를 사용합니다. 이후 섹션에서 설명되는 분산 캐시 모드를 사용할 것이기 때문에 필요합니다.

예시:

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

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

runners.cache 섹션

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

다음 예시에서 우리는 아마존 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 러너에게 언제 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"

도커 머신 드라이버는 amazonec2로 설정되어 있으며, 머신 이름은 표준 접두사로 시작하여 %s (필수)로 끝납니다. 이는 자식 러너의 ID가 대체됩니다: gitlab-docker-machine-%s.

이제, AWS 인프라에 따라 MachineOptions 아래에서 여러 옵션을 설정할 수 있습니다. 가장 일반적인 옵션들을 아래에서 확인할 수 있습니다.

머신 옵션 설명
amazonec2-access-key=XXXX EC2 인스턴스를 생성할 권한이 있는 사용자의 AWS 액세스 키, 자세한 내용은 AWS credentials를 참조하세요.
amazonec2-secret-key=XXXX EC2 인스턴스를 생성할 권한이 있는 사용자의 AWS 비밀 액세스 키, 자세한 내용은 AWS credentials를 참조하세요.
amazonec2-region=eu-central-1 인스턴스를 시작할 때 사용할 리전, 이를 완전히 생략할 수 있으며 기본값으로 us-east-1이 사용됩니다.
amazonec2-vpc-id=vpc-xxxxx 인스턴스를 시작할 VPC ID](https://gitlab.com/gitlab-org/ci-cd/docker-machine/-/blob/main/docs/drivers/aws.md#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 머신의 개인 IP 주소를 사용하되 public IP 주소를 생성합니다. 내부 트래픽을 유지하고 추가 비용을 피하는 데 유용합니다.
amazonec2-tags=runner-manager-name,gitlab-aws-autoscaler,gitlab,true,gitlab-runner-autoscale,true 추가적인 태그 키-값 쌍으로, AWS 콘솔에서 인스턴스를 식별하는 데 유용합니다. “Name” tag은 기본적으로 머신 이름으로 설정됩니다. [[runners]]에서 설정한 러너 이름과 일치하도록 “runner-manager-name”을 설정하여 특정 매니저 설정에 의해 생성된 모든 EC2 인스턴스를 필터링할 수 있습니다.
amazonec2-security-group=xxxx AWS VPC 보안 그룹 이름입니다. 보안 그룹 ID가 아닙니다. AWS security groups를 참조하세요.
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 러너 AMI ID입니다.
amazonec2-request-spot-instance=true 온디맨드 가격보다 저렴한 EC2 용량을 사용합니다.
amazonec2-spot-price=xxxx_runner_machine_spot_price=x.xx Spot 인스턴스 입찰 가격 (미국 달러). --amazonec2-request-spot-instance flagtrue로 설정해야 합니다. amazonec2-spot-price를 생략하면 Docker Machine은 기본값으로 시간당 $0.50의 최대 가격을 설정합니다.
amazonec2-security-group-readonly=true 보안 그룹을 읽기 전용으로 설정합니다.
amazonec2-userdata=xxxx_runner_machine_userdata_path 러너 머신의 userdata 경로를 지정합니다.
amazonec2-root-size=XX 인스턴스의 루트 디스크 크기(GB)입니다.
note
  • MachineOptions 아래에서는 사용하는 인프라 구성에 따라 다른 옵션이 적용될 수 있으므로, AWS Docker Machine driver가 지원하는 어떤 것이라도 추가할 수 있습니다. Docker의 문서를 꼭 참고해보시기를 권장합니다.
  • 자식 인스턴스는 기본적으로 Ubuntu 16.04를 사용하며, amazonec2-ami를 설정하여 다른 AMI ID를 선택하지 않는 한 기본값을 사용합니다. Docker Machine에서 지원하는 기본 운영 체제만 설정하세요.
  • 여러분이 머신 옵션 중 하나로 amazonec2-private-address-only=true를 지정하면 EC2 인스턴스에 공개 IP가 할당되지 않습니다. VPC가 올바르게 구성되어 있으며 라우팅이 정상적이라면 괜찮지만, 보다 복잡한 구성일 경우도 고려해야 합니다. Docker 문서에서 VPC 연결에 대해 자세히 읽어보세요.

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 Spot 인스턴스를 사용하여 비용 줄이기

Amazon의 설명에 따르면:

Amazon EC2 Spot 인스턴스를 사용하면 예비 Amazon EC2 컴퓨팅 용량에 입찰을 할 수 있습니다. Spot 인스턴스는 종종 온디맨드 가격에 비해 할인된 가격으로 제공되기 때문에 애플리케이션 실행 비용을 크게 낮출 수 있고 동일한 예산으로 애플리케이션의 컴퓨팅 용량과 처리량을 증가시키며 새로운 유형의 클라우드 컴퓨팅 애플리케이션을 활성화할 수 있습니다.

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

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

amazonec2-spot-price로 이 구성을 사용하면 AWS가 Spot 인스턴스에 대한 입찰 가격을 해당 인스턴스 클래스의 기본 온디맨드 가격으로 설정합니다. amazonec2-spot-price를 완전히 생략하면 Docker Machine은 최대 가격을 기본 값 $0.50 per hour로 설정합니다.

더 나아가서 Spot 인스턴스 요청을 더 특별하게 사용할 수 있습니다:

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

이 구성을 사용하면 Spot 인스턴스를 사용하여 Docker Machines가 $0.03 per hour의 최대 Spot 요청 가격 및 Spot 인스턴스의 지속 시간을 60분으로 생성됩니다. 위에서 언급된 0.03 숫자는 예시일뿐이므로 선택한 지역의 현재 요금을 확인해야 합니다.

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

Spot 인스턴스의 주의사항

Spot 인스턴스는 사용되지 않는 리소스를 사용하고 인프라 비용을 최소화하는 좋은 방법이지만, 함의를 알고 있어야 합니다.

Spot 인스턴스에서 CI 작업을 실행하면 Spot 인스턴스의 가격 모델로 인해 실패 비율이 증가할 수 있습니다. 지정한 최대 Spot 가격이 현재 Spot 가격을 초과하면 요청한 용량을 얻을 수 없습니다. Spot 가격은 시간당 기준으로 조정됩니다. 현재 최대 가격이 수정된 Spot 인스턴스 가격보다 낮은 기존 Spot 인스턴스는 2분 이내에 종료되며 Spot 호스트 상의 모든 작업이 실패합니다.

따라서 오토스케일 러너가 새로운 머신을 생성하지 못할 수도 있지만 계속하여 인스턴스를 요청할 것입니다. 이렇게 하면 결국 60번의 요청을 만들고 그 후로는 AWS가 더 이상 요청을 받지 않습니다. 이 상황에서는 Spot 가격이 적절하게 사용될 때까지 잠시 이용하지 못할 수 있습니다.

이 경우에 대처하기 위해 러너 매니저 머신에서 다음 명령어를 사용하여 Docker Machines 상태를 확인할 수 있습니다:

docker-machine ls -q --filter state=Error --format "{{.NAME}}"
note
GitLab 러너가 Spot 가격 변경을 정상적으로 처리하는 데 문제가 있고, docker-machine이 계속해서 Docker Machine을 제거하려고 시도하는 보고가 있습니다. GitLab은 이에 대한 패치를 제공했습니다. 더 자세한 정보는 아래의 이슈를 참조하세요. #2771#2772.

GitLab 포크에서는 AWS EC2 플릿과 Spot 인스턴스의 지원을 제공하지 않습니다. 대신 Continuous Kernel Integration Project의 다운스트림 포크를 사용할 수 있습니다.

결론

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

GitLab Runner의 autoscale 기능을 사용하면 시간과 비용을 절약할 수 있습니다. AWS가 제공하는 Spot 인스턴스를 사용하면 더 많은 비용을 절약할 수 있지만, 이에 대한 영향을 인지해야 합니다. 입찰액이 충분히 높다면 문제가 되지 않아야 합니다.

이 튜토리얼에서 영향을 많이 받은 다음 사용 사례를 읽어볼 수 있습니다: