GeekFactory

int128.hatenablog.com

Kubernetes上のGitLab Runnerでビルドキャッシュを利用する

GitLab CI/CDではビルドキャッシュがサポートされています.本稿では,KubernetesにデプロイしているGitLab Runnerでビルドキャッシュを利用する方法を紹介します.

ここでは以下を利用している前提とします.

  • AWS
  • Kubernetes 1.13 (EKS)
  • kube2iam
  • Terraform 0.12
  • Helmfile v0.80 or later

以下の流れでビルドキャッシュを構成していきます.

  1. ビルドキャッシュ用のS3バケットを作成する.
  2. GitLab RunnerのIAMロールを作成する.
  3. GitLab Runnerが生成するworkerのIAMロールを作成する.
  4. GitLab Runnerをデプロイする.
  5. ジョブを設定する.

GitLab RunnerとAWSリソースの構成図を以下に示します.

f:id:int128:20190925174057p:plain

1. ビルドキャッシュ用のS3バケットを作成する

GitLab Runnerはデフォルトではローカルにビルドキャッシュを保存しますが,GitLab Runnerを使い捨てにする場合はビルドキャッシュが消えてしまいます. S3バケットを利用すると複数のGitLab Runnerでビルドキャッシュを共有できます.

以下のようにTerraformでS3バケットを作成します.

# S3 bucket for cache of GitLab Runner
# https://gitlab.com/gitlab-org/gitlab-runner/blob/master/docs/configuration/autoscale.md#distributed-runners-caching
resource "aws_s3_bucket" "gitlab_runner_cache" {
  bucket = "${var.cluster_name}-gitlab-runner-cache"
  acl    = "private"
}

2. GitLab RunnerのIAMロールを作成する

GitLab Runnerはジョブの最初にビルドキャッシュを取得します.また,ジョブの最後にビルドキャッシュを保存します.そこで,ビルドキャッシュ用のS3バケットを読み書きできるIAMロールを作成し,GitLab Runnerにアタッチします.正確には,GitLab Runnerが生成するworkerではなく,GitLab Runnerの本体にIAMロールをアタッチする必要があります.

以下のようにTerraformでIAMロールとIAMポリシーを作成します.

# IAM role for GitLab Runner
resource "aws_iam_role" "gitlab_runner" {
  name               = "${var.cluster_name}-gitlab-runner"
  assume_role_policy = data.aws_iam_policy_document.kube2iam_assume_role.json
}

resource "aws_iam_role_policy" "gitlab_runner" {
  role   = aws_iam_role.gitlab_runner.name
  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "${aws_s3_bucket.gitlab_runner_cache.arn}/*",
                "${aws_s3_bucket.gitlab_runner_cache.arn}"
            ]
        }
    ]
}
EOF
}

data "aws_iam_policy_document" "kube2iam_assume_role" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
    principals {
      type        = "AWS"
      identifiers = [module.eks.worker_iam_role_arn]
    }
  }
}

3. GitLab Runnerが生成するworkerのIAMロールを作成する

GitLab Runnerはジョブを実行する時に新しいPodを生成します.ジョブで指定したイメージやスクリプトはPod内で実行されます.

以下のようにTerraformでIAMロールを作成します.

# IAM role for building app on GitLab Runner
resource "aws_iam_role" "gitlab_runner_app" {
  name               = "${var.cluster_name}-gitlab-runner-app"
  assume_role_policy = data.aws_iam_policy_document.kube2iam_assume_role.json
}

ここでは特に権限を割り当てていませんが,将来的に権限が必要になった場合はこのIAMロールに権限を割り当てます. 例えば,ECRにイメージを保存する必要が出てきた場合は以下のIAMポリシーをアタッチします.

resource "aws_iam_role_policy_attachment" "gitlab_runner_app_ecr" {
  role       = aws_iam_role.gitlab_runner_app.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser"
}

4. GitLab Runnerをデプロイする

GitLab RunnerにはHelm Chartが用意されています.

以下のようにHelmfileでChartをデプロイします.

releases:
  - name: gitlab-runner-app
    namespace: gitlab
    chart: gitlab/gitlab-runner
    values:
      - gitlabUrl: {{ .Environment.Values.gitlabURL }}
        runnerRegistrationToken: {{ .Environment.Values.gitlabRunnerRegistrationToken }}
        rbac:
          create: true
        podAnnotations:
          # IAM role for GitLab Runner manager pod
          iam.amazonaws.com/role: CLUSTER-gitlab-runner
        runners:
          tags: "app"
          cache:
            # Distributed runners caching
            # https://gitlab.com/gitlab-org/gitlab-runner/blob/master/docs/configuration/advanced-configuration.md#the-runnerscaches3-section
            cacheType: s3
            cacheShared: true
            s3ServerAddress: s3.amazonaws.com
            s3BucketName: {{ .Environment.Values.cacheBucketName }}
            s3BucketLocation: {{ .Environment.Values.cacheBucketRegion }}
          podAnnotations:
            # IAM role for building apps
            iam.amazonaws.com/role: CLUSTER-gitlab-runner-app

environments:
  default:
    values:
      - gitlabURL: https://gitlab.example.com
        gitlabRunnerRegistrationToken: YOUR_TOKEN
        cacheBucketName: CLUSTER-gitlab-runner-cache
        cacheBucketRegion: ap-northeast-1

ポイントは以下です.

  • podAnnotations でGitLab Runnerの本体に割り当てるIAMロールを指定します.
  • runners.podAnnotations でGitLab Runnerが生成するworkerに割り当てるIAMロールを指定します.
  • runners.cache.cacheType には s3 を指定します.これを忘れるとS3バケットが有効になりません.
  • runners.cache.cacheShared はtrueにします.これを忘れるとビルドキャッシュが別のGitLab Runnerに引き継がれません.

GitLab Runnerでビルドキャッシュを設定する方法は以下のドキュメントが参考になります.

5. ジョブを設定する

GitLabのリポジトリ.gitlab-ci.yml を作成します.WebIDEでファイルを作成すると,ビルドジョブの動作をすぐに確認できるので便利です.

build:
  cache:
    paths:
      - .terraform

ジョブのログに以下が出力されていればビルドキャッシュが有効になっています.

Checking cache for default...
Downloading cache.zip from https://NAME-gitlab-runner-cache.s3-ap-northeast-1.amazonaws.com/project/NUMBER/default 
Successfully extracted cache

...


Creating cache default...
.terraform: found 140 matching files
Downloading cache.zip from https://NAME-gitlab-runner-cache.s3-ap-northeast-1.amazonaws.com/project/NUMBER/default 
Created cache

トラブルシューティング

GitLab Runnerの本体がS3バケットにアクセスできない場合は,ジョブのログに以下が出力されます.

Checking cache for NAME...
No URL provided, cache will not be downloaded from shared cache server. Instead a local version of cache will be extracted. 

...

Creating cache NAME...
No URL provided, cache will be not uploaded to shared cache server. Cache will be stored only locally. 

また,GitLab RunnerのPodのログに以下が出力されます.

error while generating S3 pre-signed URL: No IAM roles attached to this EC2 service

Podに適切なIAMロールが割り当てられているか確認しましょう.GitLab Runnerが生成するworkerにS3アクセスのIAMポリシーを割り当てている場合もこれらログが出力されます.