Amazon EC2にGitalyをインストールしてみた
Gitalyとは
Gitデータの読み取りと書き込みを行う、リポジトリストレージサービスです。
GitLabを構成する1つのサービスです。
通常は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タスク実行)
詳細やその他制限事項は以下をご確認ください。
ちなみに、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の関連部分です。
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-network
のRemote state sharing
で共有先にgitlab-eks-gitaly
を指定しています。
ここからはGitaly部分です。
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公式の推奨値はコメントで残しています。ご参考までに。
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
Gitalyの設定
Gitaly用のトークンを用意します。
Gitaly接続用のため、接続元のGitLabサーバーにも保存する必要があります。
openssl rand -base64 64 | tr -d '\n'
GitLab設定ファイルを開きます。
sudo vi /etc/gitlab/gitlab.rb
以下の内容で設定ファイルを修正します。
<GitLabサーバーURL>
は、Gitalyに接続するGitLabサーバーのURLを設定してください。
<トークン>
は前の手順で作ったトークンを設定してください。
# 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)でした。