Amazon EC2にGitalyをインストールしてみた

Amazon EC2にGitalyをインストールしてみた

Clock Icon2025.03.06

Gitalyとは

Gitデータの読み取りと書き込みを行う、リポジトリストレージサービスです。

GitLabを構成する1つのサービスです。

https://docs.gitlab.com/ee/administration/gitaly/

通常はGitLabインストール時に一緒にインストールされます。

アクティブユーザー数が1000ユーザーを超えるリファレンスアーキテクチャでは、GitalyサーバーをGitLabサーバーと分ける構成になっています。

Amazon EFSはサポートされていない

For repository data, only local storage is supported for Gitaly and Gitaly Cluster for performance and consistency reasons. Alternatives such as NFS or cloud-based file systems are not supported.

Disk requirements | Gitaly and Gitaly Cluster | GitLab Docsから引用

リポジトリストレージは容量が大きくなる可能性があるため、Amazon EFSの使用を検討する方もいるかもしれません。

パフォーマンスと一貫性の理由から、Gitalyではローカルストレージのみがサポートされています。

Amazon EFSやNFSは利用できません。

Gitaly Cluster

通常の1台構成では、Gitalyサーバーが単一障害点になります。

冗長性を確保した、Gitaly Clusterというオプションもあります。

アクティブユーザー数が1000ユーザーを超えるリファレンスアーキテクチャ、Gitaly Clusterを使った構成になっていました。

採用するかどうかは要件次第ですが、以下のデメリットがあることに注意してください。

  • 構成が複雑
  • コストが掛かる(最低3台必要)
  • スナップショットバックアップができない

PraefectやPraefect用のPostgreSQLが必要だったり、シングル構成と比べてコンポーネントが多くなります。

また、ノードが最低3台必要なため、コストがかかります。

Praefect DBとディスクストレージが同期しなくなる可能性があるため、スナップショットバックアップをサポートしていません。GitLab公式の手順でバックアップを行う必要があります。(Rakeタスク実行)

詳細やその他制限事項は以下をご確認ください。

https://docs.gitlab.com/administration/gitaly/

ちなみに、Praefectは現時点では、KubernetesやAmazon ECSをサポートしていません。

Limitations when running in Kubernetes, Amazon ECS, or similar
Praefect (Gitaly Cluster) is not supported and Gitaly has known limitations.

Known Issues | Gitaly and Gitaly Cluster | GitLab Docs

本記事ではシングル構成の手順を説明します。

EC2インスタンスの作成

以下のTerraformコードでEC2を作成します。

今回はUbuntuのAMIを使って、EC2を作成します。

まずは、networkの関連部分です。

network/main.tf(SecurityGroup関連のみ抜粋)
resource "aws_security_group" "gitlab_internal_networking" {
  name_prefix = "${var.prefix}-internal-networking"
  vpc_id      = module.vpc.vpc_id

  description = "${var.prefix} - Internal Networking Security Group"

  tags = {
    Name = "${var.prefix}-internal-networking"
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_vpc_security_group_ingress_rule" "gitlab_internal_networking" {
  security_group_id = aws_security_group.gitlab_internal_networking.id

  description = "Open internal networking for VMs and Node Groups"
  ip_protocol = "-1"

  referenced_security_group_id = aws_security_group.gitlab_internal_networking.id

  tags = {
    Name = "${var.prefix}-internal-networking"
  }
}

resource "aws_vpc_security_group_egress_rule" "gitlab_internal_networking_internet" {
  security_group_id = aws_security_group.gitlab_internal_networking.id

  description = "Open internet access for VMs"
  ip_protocol = "-1"

  cidr_ipv4 = "0.0.0.0/0"

  tags = {
    Name = "${var.prefix}-internal-networking-internet"
  }
}

output "gitlab_internal_networking_security_group_id" {
  value = aws_security_group.gitlab_internal_networking.id
}

networkとgitalyでState fileを分けました。

HCP Terraformを利用しており、それぞれ以下のWorkspaceを作成しています。

  • Network Workspace名: gitlab-eks-network
  • Gitaly Workspace名: gitlab-eks-gitaly

networkの一部の値は、Gitalyで使います。

そのために、gitlab-eks-networkRemote state sharingで共有先にgitlab-eks-gitalyを指定しています。

ここからはGitaly部分です。

gitaly/variables.tf
variable "prefix" {
  type        = string
  description = "The prefix to use for all resources"
  default     = "test-gitlab"
}

variable "region" {
  type        = string
  description = "The AWS region to deploy to"
  default     = "us-east-2"
}

variable "hcp_tf_organization" {
  type        = string
  description = "The name of the HCP Terraform organization"
}

variable "hcp_tf_network_workspace" {
  type        = string
  description = "The name of the HCP Terraform workspace for the network"
  default     = "gitlab-eks-network"
}

variable "key_pair_name" {
  type        = string
  description = "The name of the key pair to use for the GitLab instance"
  default     = "test-gitlab"
}

variable "route53_zone_name" {
  type        = string
  description = "The name of the Route 53 zone to use for the GitLab instance"
}

本記事では、検証用の構成を想定しているため、GitLab公式の推奨値より小さいサイズのインスタンスを使用しています。

実運用では、GitLab公式の推奨値に従うことをお勧めします。

可能な限りGitLab公式の推奨値はコメントで残しています。ご参考までに。

gitaly/main.tf
locals {
  tags = {
    project    = var.prefix
    managed_by = "Terraform"
  }
}

data "tfe_outputs" "network" {
  organization = var.hcp_tf_organization
  workspace    = var.hcp_tf_network_workspace
}

data "aws_ami" "ubuntu_default" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["amazon"]
}

resource "aws_instance" "gitaly" {
  # 推奨値 m5.xlarge 2k USER https://docs.gitlab.com/administration/reference_architectures/2k_users/
  instance_type        = "t3.medium"
  ami                  = data.aws_ami.ubuntu_default.id
  key_name             = var.key_pair_name
  iam_instance_profile = aws_iam_instance_profile.gitaly.name
  vpc_security_group_ids = [
    data.tfe_outputs.network.values.gitlab_internal_networking_security_group_id,
  ]

  subnet_id = data.tfe_outputs.network.values.private_subnets[0]

  root_block_device {
    volume_type = "gp3"
    # https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/terraform/modules/gitlab_ref_arch_aws/variables.tf?ref_type=heads
    volume_size = "50" # 推奨値 500
    # iops        = "8000" # 推奨値

    encrypted = true

    delete_on_termination = true
  }

  # Enforce IMDSv2 - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#metadata-options
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"
    http_put_response_hop_limit = 1
    instance_metadata_tags      = "enabled"
  }
  tags = merge({
    Name = "${var.prefix}-gitaly"
    },
  local.tags)

  lifecycle {
    ignore_changes = [
      ami
    ]
  }
}

resource "aws_iam_instance_profile" "gitaly" {
  name = "${var.prefix}-gitaly"
  role = aws_iam_role.gitaly.name
}

data "aws_iam_policy_document" "assume_role" {
  statement {
    effect = "Allow"

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }

    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role" "gitaly" {
  name               = "${var.prefix}-gitaly"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
  tags               = local.tags
}

# セッションマネージャー接続用
resource "aws_iam_role_policy_attachment" "ssm" {
  role       = aws_iam_role.gitaly.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_route53_zone" "internal" {
  name = "gitlab.internal"
  vpc {
    vpc_id = data.tfe_outputs.network.values.vpc_id
  }
}

resource "aws_route53_record" "gitaly" {
  zone_id = aws_route53_zone.internal.zone_id
  name    = "gitaly.gitlab.internal"
  type    = "A"
  ttl     = 300
  records = [aws_instance.gitaly.private_ip]
}

今回は同一VPC内にGitLabサーバーがあると想定しています。

GitalyサーバーはGitLabサーバーからのアクセスを受けれたら良いので、Private Subnetに配置します。

Route53 プライベートホストゾーンを利用して、GitLabサーバーとGitalyサーバー間の名前解決を実装します。

GitLabからGitalyへの疎通は8075ポートで行われます。GitalyからGitLabへの疎通は「80 / 443」ポートで行われます。(※1)

Security Groupで通信を許可します。

今回は「同一セキュリティグループ内のリソース間で全ての通信を許可」を用意して、GitalyとGitLabにそれぞれアタッチします。

terraform init
terraform plan
terraform apply

※1 Configure Gitaly | GitLab Docs

GitLabのインストール

作成したEC2にセッションマネージャーで接続します。

aws ssm start-session --target <インスタンスID>
cd /home/ssm-user

Gitaly単体で動かす場合でも、GitLabのパッケージをインストールする必要があります。

必要なパッケージをインストールします。

sudo apt-get update
sudo apt-get install -y curl openssh-server ca-certificates tzdata perl

GitLabをインストールします。

通常はEXTERNAL_URLの設定が必要ですが、Gitaly単体で動かす場合は不要です。

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
sudo apt-get install gitlab-ee
# 通常は以下のように`EXTERNAL_URL`を指定
# sudo EXTERNAL_URL="https://gitlab.example.com" apt-get install gitlab-ee

https://about.gitlab.com/install/#ubuntu

Gitalyの設定

Gitaly用のトークンを用意します。

Gitaly接続用のため、接続元のGitLabサーバーにも保存する必要があります。

openssl rand -base64 64 | tr -d '\n'

GitLab設定ファイルを開きます。

sudo vi /etc/gitlab/gitlab.rb

以下の内容で設定ファイルを修正します。

<GitLabサーバーURL>は、Gitalyに接続するGitLabサーバーのURLを設定してください。

<トークン>は前の手順で作ったトークンを設定してください。

/etc/gitlab/gitlab.rb

# Avoid running unnecessary services on the Gitaly server
postgresql['enable'] = false
redis['enable'] = false
nginx['enable'] = false
puma['enable'] = false
sidekiq['enable'] = false
gitlab_workhorse['enable'] = false
gitlab_exporter['enable'] = false
gitlab_kas['enable'] = false

# If you run a separate monitoring node you can disable these services
prometheus['enable'] = false
alertmanager['enable'] = false

# If you don't run a separate monitoring node you can
# enable Prometheus access & disable these extra services.
# This makes Prometheus listen on all interfaces. You must use firewalls to restrict access to this address/port.
# prometheus['listen_address'] = '0.0.0.0:9090'
# prometheus['monitor_kubernetes'] = false

# If you don't want to run monitoring services uncomment the following (not recommended)
# node_exporter['enable'] = false

# Prevent database connections during 'gitlab-ctl reconfigure'
gitlab_rails['auto_migrate'] = false

# Configure the gitlab-shell API callback URL. Without this, `git push` will
# fail. This can be your 'front door' GitLab URL or an internal load
# balancer.
# Don't forget to copy `/etc/gitlab/gitlab-secrets.json` from Gitaly client to Gitaly server.
gitlab_rails['internal_api_url'] = '<GitLabサーバーURL>' # 例)https://gitlab.example.com

gitaly['configuration'] = {
   # ...
   #
   # Make Gitaly accept connections on all network interfaces. You must use
   # firewalls to restrict access to this address/port.
   # Comment out following line if you only want to support TLS connections
   listen_addr: '0.0.0.0:8075',
   auth: {
     # ...
     #
     # Authentication token to ensure only authorized servers can communicate with
     # Gitaly server
     token: '<トークン>',
   },
}

以下のコマンドを実行して、GitLabを再構成します。

sudo gitlab-ctl reconfigure

動作確認

以下のコマンドでステータスを確認します。

sudo gitlab-ctl status

gitaly logrotate node-exporterの3つが動いていればOKです。

出力例
run: gitaly: (pid 18061) 24s; run: log: (pid 17996) 38s
run: logrotate: (pid 17960) 45s; run: log: (pid 17968) 44s
run: node-exporter: (pid 18053) 25s; run: log: (pid 18038) 27s

おわりに

GitalyをEC2にインストールしてみました。

インストールだけでそれなりに長くなってしまったので、GitLabサーバーからの接続方法は別記事にて紹介する予定です。

以上、AWS事業本部の佐藤(@chari7311)でした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.