GitLab은 협업 및 버전 관리를, CI/CD를 위한 강력한 플랫폼으로, 기업에서 소프트웨어 개발 및 관리를 위해 널리 사용되고 있다. GitLab Geo는 GitLab의 중요한 기능 중 하나로, 지리적으로 분산된 팀 및 사용자들 간의 협업을 향상시키기 위해 설계되었다. 이 글에서는 GitLab Geo가 무엇이며, 어떻게 안정성과 확장성을 향상시키는지 개념과 실제 구성을 통해 알아보려 한다.
GitLab Geo란?
GitLab Geo는 지리적으로 분산된 팀 및 사용자들 간에 repository의 안정성을 향상시키기 위해 설계된 기능이다. 이는 지리적으로 떨어진 다양한 지역에서 소프트웨어 개발 및 협업을 수행하는 조직에게 특히 유용하다. Geo는 주로 다음과 같은 이점을 제공한다.
1. 안정성 향상
GitLab Geo는 지리적으로 분산된 여러 노드 간에 repository를 자동으로 동기화하여 안정성을 향상시킨다. 이는 단일 장소에서의 장애나 네트워크 문제로 인한 데이터 손실을 방지하고, 전체 시스템의 신뢰성을 높인다.
2. 성능 최적화
지리적으로 가까운 repository 노드를 사용함으로써 사용자에게 빠른 속도로 repository에 액세스할 수 있도록 도와준다. 이는 개발자들의 개발속도를 향상시키는데 도움을 준다.
3. 재해 대응 및 비즈니스 연속성
자연 재해, 하드웨어 장애 또는 다른 예기치 못한 상황에서도 Geo는 데이터의 손실을 방지하고 비즈니스 연속성을 유지할 수 있도록 도와준다.
아래는 Geo의 기본 아키텍처이다.
그림 참고 : https://docs.gitlab.com/ee/administration/geo/
Geo Secondary Node에 git push가 이루어지면, Geo Primary Node로 리다이렉트 되어 데이터가 저장된다. 이후 저장된 데이터는 Geo Secondary Node로 복제된다.
Geo 구성에서는 Gitlab 구성 때 사용하지 않는 개념이 하나 등장하는데 "Geo Tracking Database"이다.
Geo Secondary Node는 Geo Tracking Database라는 별도의 PostgreSQL 데이터베이스를 필요로 하며, 이 데이터베이스는 지리적으로 분산된 GitLab 노드 간 데이터의 변경 사항을 추적하고 기록한다. 이 데이터베이스는 읽기 전용이며, Geo Secondary Node를 추가할 때마다 별도의 Tracking Database가 필요하다.
본 글의 실습은 이전에 작성된 GitLab 구축 글[Gitlab 고가용성 환경 구축하기]글의 실습 기반으로 하기 때문에 본 글의 Geo 구성 실습을 진행하려면 이전 글에서 다룬 GitLab HA 구성 환경이 필요하다.
위 실습에서 구축한 GitLab을 "Geo Primary Node"로 지정하고, 본 글에서는 "Geo Secondary Node"를 생성하는 과정을 포함하여 진행한다.
본 글에서 구성할 Geo Secondary Node 아키텍처는 다음과 같다.
구성 환경
- AWS EC2 Instance (18 EA)
- OS : Amazon Linux 2023
- Kernel : 6.1.61-85.141.amzn2023.x86_64
- Instance type : c5.large / c5.xlarge(Rails node)
전제 조건
- AWS S3 버킷
- AWS Certificate Manager(ACM) 인증서
- Gitlab 라이센스
- Gitlab Geo Primary Node (https://wlsdn3004.tistory.com/51)
설치 버전
- Gitlab 16.6.0-ee
실습 절차
1. Geo Primary Node 설정 및 등록1-1. Gitlab 라이센스 등록
1-2. Geo Primary Node 설정
1-3. Patroni 설정
1-4. Haproxy 설정
2. Geo Secondary Node 설정
2-1. Gitlab 패키지 설치 (공통)
2-2. Redis & Consul/Sentinel 구성
2-3. Postgresql & Patroni / Pgbouncer 구성
2-4. Praefect Postgresql 구성
2-5. Praefect 구성
2-6. Gitaly 구성
2-7. Internal Loadbalancer 구성
2-8. Tracking Postgresql & Pgbouncer & Consul 구성
2-9. Sidekiq & Rails 구성
2-10. External Loadbalancer 구성
3. Geo Secondary Node 등록
다음 목록은 각 역할에 대한 서버와 IP 주소이다.
- 10.10.1.66 : Redis master & Consul/Sentinel 1
- 10.10.1.95 : Redis slave & Consul/Sentinel 2
- 10.10.1.112 : Redis slave & Consul/Sentinel 3
- 10.10.1.155 : Postgresql Leader & Pgbouncer
- 10.10.1.201 : Postgresql Replica & Pgbouncer
- 10.10.1.80 : Praefect Postgresql
- 10.10.1.132 : Praefect 1
- 10.10.1.92 : Praefect 2
- 10.10.1.223 : Praefect 3
- 10.10.1.117 : Gitaly 1
- 10.10.1.22 : Gitaly 2
- 10.10.1.241 : Gitaly 3
- 10.10.1.182 : Internal Loadbalancer (Haproxy)
- 10.10.1.179 : Sidekiq & Rails 1
- 10.10.1.143 : Sidekiq & Rails 2
- 10.10.1.131 : Tracking Postgresql Leader & Pgbouncer & Consul 1
- 10.10.1.18 : Tracking Postgresql Replica & Consul 2
- 10.10.1.37 : Tracking Postgresql Replica & Consul 3
1. Geo Primary Node 설정 및 등록
앞서 미리 구축한 Gitlab이 Geo Primary Node 역할을 한다.
1-1. Gitlab 라이센스 등록
Geo 구성을 위해서는 라이센스가 필요하다.
발급받은 라이센스를 Geo Primary Node에 등록하기 위해 Gitlab에 접속 후 아래 절차대로 진행한다.
- Gitlab Login > Search or go to... > Admin Area
- Settings > General
Add License에서 Expand를 누르면 아래와 같이 나오는데, 발급받은 라이센스를 upload 하여 추가한다.
등록이 완료되었으면 "Admin Area > Overview > Dashboard"에서 등록된 라이센스 정보를 확인할 수 있다.
1-2. Geo Primary Node 설정
Primary Gitlab Rails 인스턴스에서 아래와 같이 설정파일에 내용을 추가하여 Geo Primary Node 이름을 설정한다.
## /etc/gitlab/gitlab.rb
...
gitlab_rails['geo_node_name'] = 'geo-primary'
...
reconfigure 명령을 통해 변경사항 적용 후 Geo Primary Node로 설정한다.
$ gitlab-ctl reconfigure
$ gitlab-ctl set-geo-primary-node
Saving primary Geo node with name geo-primary and URL https://test.co.kr/ ...
https://test.co.kr/ is now the primary Geo node
1-3. Patroni 설정
데이터베이스 복제를 위해 replication slot을 구성해야 한다. 여기서 Patroni는 Geo의 공식 복제 관리 솔루션으로 데이터베이스 복제 관리를 수행한다.
아래와 같이 Primary 인스턴스의 Postgresql & Pgbouncer 노드에서 설정파일에 내용을 추가한다.
## /etc/gitlab/gitlab.rb
...
patroni['replication_slots'] = {
'geo_secondary' => { 'type' => 'physical' }
}
patroni['postgresql']['max_replication_slots'] = 6 ## 2 patronis + 1 reserved Geo
patroni['postgresql']['max_wal_senders'] = 6 ## 2 patronis + 1 reserved Geo
patroni['allowlist'] = %w(10.10.1.0/24 127.0.0.1/32) ## Geo primary secondary 노드의 patroni 전부 포함되게 설정
postgresql['md5_auth_cidr_addresses'] = %w[ ## Geo primary secondary 노드의 patroni 전부 포함되게 설정
10.10.1.0/24
]
patroni['use_pg_rewind'] = true
patroni['replication_password'] = 'test3' ## PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD
...
reconfigure 명령어로 변경사항을 적용한다.
$ gitlab-ctl reconfigure
1-4. Haproxy 설정
Geo Primary 노드에서 새 Leader가 선출될 때마다 Geo Secondary 노드에서 Standby Leader가 재구성되려고 한다. 이를 방지하기 위해 Geo Primary 노드의 Internal Loadbalancer를 설정해야 한다.
Geo Secondary Node의 Patroni가 접근할 수 있게 엔드포인트를 추가한다.
## /etc/haproxy/haproxy.cfg
...
defaults
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
frontend internal-postgresql-tcp-in
bind *:5000
mode tcp
option tcplog
default_backend postgresql
backend postgresql
option httpchk
http-check expect status 200
server patroni1.internal 10.10.1.170:5432 maxconn 100 check port 8008
server patroni2.internal 10.10.1.189:5432 maxconn 100 check port 8008
...
변경사항을 적용한다.
$ systemctl restart haproxy
$ ss -anp | grep 5000
tcp LISTEN 0 4096 0.0.0.0:5000 0.0.0.0:* users:(("haproxy",pid=1690,fd=10))
tcp ESTAB 0 0 10.10.1.233:5000 10.10.1.155:43686 users:(("haproxy",pid=1690,fd=20))
2. Geo Secondary Node 설정
2-1. Gitlab 패키지 설치 (공통)
Haproxy가 설치되는 EC2 인스턴스를 제외한 모든 EC2 인스턴스에 Omnibus 패키지를 설치한다.
$ dnf install -y policycoreutils-python-utils openssh-server openssh-clients perl postfix
$ curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash
$ dnf install -y gitlab-ee-16.6.0
테라폼을 이용하면 쉽게 구성할 수 있다.
## ec2.tf
...
resource "aws_instance" "gitlab_geo" {
count = 16
ami = "ami-01123b84e2a4fba05"
instance_type = "c5.large"
subnet_id = module.vpc.private_subnets[0]
vpc_security_group_ids = [
aws_security_group.private.id,
]
root_block_device {
volume_size = 20
volume_type = "gp3"
}
user_data = <<-EOF
#!/bin/bash
sudo dnf install -y policycoreutils-python-utils openssh-server openssh-clients perl postfix &&
sudo curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash &&
sudo dnf install -y gitlab-ee-16.6.0
EOF
tags = {
Name = "${var.name}-${count.index}-gitlab-geo-ec2"
}
}
resource "aws_instance" "geo_rails" {
count = 2
ami = "ami-01123b84e2a4fba05"
instance_type = "c5.xlarge"
subnet_id = module.vpc.private_subnets[0]
vpc_security_group_ids = [
aws_security_group.private.id,
]
root_block_device {
volume_size = 20
volume_type = "gp3"
}
user_data = <<-EOF
#!/bin/bash
sudo dnf install -y policycoreutils-python-utils openssh-server openssh-clients perl nc postfix &&
sudo curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash &&
sudo dnf install -y gitlab-ee
EOF
tags = {
Name = "${var.name}-sidekiq & rails-geo-${count.index}"
}
}
...
Gitlab 패키지가 설치되면 gitlab 관련된 파일들이 생성되는데 구성을 위해 수정해야 할 설정파일 및 위치는 "/etc/gitlab/gitlab.rb" 이다.
또한, 최초 Geo Primary Node 구성으로 인해 생성된 "/etc/gitlab/gitlab-secrets.json" 파일 및 내용을 복사하여 모든 인스턴스에 동일하게 위치시킨다.
2-2. Redis & Consul/Sentinel 구성
Redis master & Consul/Sentinel 1 구성
여기서 redis는 master 역할을 할당받아야 하기 때문에 "redis_master_role"을 사용하여 구성한다.
## /etc/gitlab/gitlab.rb
external_url 'https://{외부에서 접속할 도메인 이름}'
roles ['redis_master_role', 'redis_sentinel_role', 'consul_role']
redis['bind'] = '10.10.1.66'
redis['port'] = 6379
redis['password'] = 'redis-password'
consul['monitoring_service_discovery'] = true
node_exporter['listen_address'] = '0.0.0.0:9100'
redis_exporter['listen_address'] = '0.0.0.0:9121'
redis_exporter['flags'] = {
'redis.addr' => 'redis://10.10.1.66:6379',
'redis.password' => 'redis-password',
}
gitlab_rails['auto_migrate'] = false
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password' ## redis_master_password
redis['master_ip'] = '10.10.1.66'
sentinel['bind'] = '10.10.1.66'
sentinel['quorum'] = 2
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
server: true,
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
Redis slave & Consul/Sentinel 2 구성
여기서 redis는 slave 역할을 할당받아야 하기 때문에 redis_replica_role을 사용하여 구성한다.
## /etc/gitlab/gitlab.rb
external_url 'https://{외부에서 접속할 도메인 이름}'
roles ['redis_replica_role', 'redis_sentinel_role', 'consul_role']
redis['bind'] = '10.10.1.95'
redis['port'] = 6379
redis['password'] = 'redis-password'
consul['monitoring_service_discovery'] = true
node_exporter['listen_address'] = '0.0.0.0:9100'
redis_exporter['listen_address'] = '0.0.0.0:9121'
redis_exporter['flags'] = {
'redis.addr' => 'redis://10.10.1.95:6379',
'redis.password' => 'redis-password',
}
gitlab_rails['auto_migrate'] = false
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password' ## redis_master_password
redis['master_ip'] = '10.10.1.66'
sentinel['bind'] = '10.10.1.95'
sentinel['quorum'] = 2
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
server: true,
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
Redis slave & Consul/Sentinel 3 구성
여기서도 redis는 slave 역할을 할당받아야 하기 때문에 redis_replica_role을 사용하여 구성한다.
## /etc/gitlab/gitlab.rb
external_url 'https://{외부에서 접속할 도메인 이름}'
roles ['redis_replica_role', 'redis_sentinel_role', 'consul_role']
redis['bind'] = '10.10.1.112'
redis['port'] = 6379
redis['password'] = 'redis-password'
consul['monitoring_service_discovery'] = true
node_exporter['listen_address'] = '0.0.0.0:9100'
redis_exporter['listen_address'] = '0.0.0.0:9121'
redis_exporter['flags'] = {
'redis.addr' => 'redis://10.10.1.112:6379',
'redis.password' => 'redis-password',
}
gitlab_rails['auto_migrate'] = false
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password' ## redis_master_password
redis['master_ip'] = '10.10.1.66'
sentinel['bind'] = '10.10.1.112'
sentinel['quorum'] = 2
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
server: true,
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
위 3개의 redis, redis_sentinel, consul이 구성되었으면 아래 명령어를 통해 정상인지 확인한다.
Redis 확인
$ /opt/gitlab/embedded/bin/redis-cli -h 10.10.1.66 -a 'redis-password' info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.10.1.95,port=6379,state=online,offset=8310568611,lag=0
slave1:ip=10.10.1.112,port=6379,state=online,offset=8310568597,lag=1
master_failover_state:no-failover
master_replid:a389276d51730edfb56223d890ed78b934598cea
master_replid2:75620b09490579c382c7c55a3c6c2d06a4a4ec23
master_repl_offset:8310569062
second_repl_offset:7701886887
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:8309506159
repl_backlog_histlen:1062904
Consul 확인
$ /opt/gitlab/embedded/bin/consul members
Node Address Status Type Build Protocol DC Partition Segment
ip-10-10-1-112 10.10.1.112:8301 alive server 1.16.3 2 gitlab_consul default <all>
ip-10-10-1-66 10.10.1.66:8301 alive server 1.16.3 2 gitlab_consul default <all>
ip-10-10-1-95 10.10.1.95:8301 alive server 1.16.3 2 gitlab_consul default <all>
2-3. Postgresql & Patroni / Pgbouncer 구성
"Postgresql Leader & Pgbouncer", " Postgresql Replica & Pgbouncer" 2개 노드 전부 아래와 같이 설정한다.
## /etc/gitlab/gitlab.rb
roles(['consul_role', 'patroni_role', 'pgbouncer_role'])
postgresql['listen_address'] = '0.0.0.0'
patroni['postgresql']['max_replication_slots'] = 5 ## 최소3개 복제본당 2개 추가
patroni['postgresql']['max_wal_senders'] = 5 ## 최소3개 복제본당 2개 추가
patroni['standby_cluster']['enable'] = true
patroni['standby_cluster']['host'] = '10.10.1.233' ## INTERNAL_LOAD_BALANCER_PRIMARY_IP
patroni['standby_cluster']['port'] = 5000 ## INTERNAL_LOAD_BALANCER_PRIMARY_PORT
patroni['standby_cluster']['primary_slot_name'] = 'geo_secondary'
patroni['replication_password'] = 'test3' ## PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD
patroni['use_pg_rewind'] = true
patroni['username'] = 'test'
patroni['password'] = 'test'
patroni['allowlist'] = %w(10.10.1.0/24 127.0.0.1/32)
postgresql['pgbouncer_user_password'] = 'a46d71dedc6fd700d42bb91318d1cfc6' ## PGBOUNCER_PASSWORD_HASH
postgresql['sql_replication_password'] = 'ee3a10e38c1e16210c39f63c26988cd5' ## POSTGRESQL_REPLICATION_PASSWORD_HASH
postgresql['sql_user_password'] = '8ea51d17cfa0fe1bb503ada4f7f1c68f' ## POSTGRESQL_PASSWORD_HASH
postgresql['trust_auth_cidr_addresses'] = %w(10.10.1.0/24 127.0.0.1/32)
postgresql['md5_auth_cidr_addresses'] = [
'10.10.1.0/24',
]
node_exporter['listen_address'] = '0.0.0.0:9100'
postgres_exporter['listen_address'] = '0.0.0.0:9187'
pgbouncer_exporter['listen_address'] = '0.0.0.0:9188'
consul['enable'] = true
consul['watchers'] = %w(postgresql)
consul['configuration'] = {
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
consul['services'] = %w(postgresql)
consul['monitoring_service_discovery'] = true
pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
pgbouncer['databases'] = {
gitlabhq_production: {
host: "127.0.0.1",
user: "pgbouncer",
password: 'a46d71dedc6fd700d42bb91318d1cfc6'
}
}
pgbouncer['users'] = {
'gitlab-consul': {
password: '2deabc6a0997166c753c1537890f27c1' ## GITLAB_CONSUL_PASSWORD_HASH
},
'pgbouncer': {
password: 'a46d71dedc6fd700d42bb91318d1cfc6' ## PGBOUNCER_PASSWORD_HASH
}
}
gitlab_rails['db_password'] = 'test1' ## POSTGRESQL_PASSWORD
gitlab_rails['enable'] = true
gitlab_rails['auto_migrate'] = false
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
Consul이 Pgbouncer를 다시 로드할 수 있도록 아래와 같이 설정하여 .pgpass 파일을 생성한다.
$ gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
## test2 입력
$ gitlab-ctl hup pgbouncer
아래 명령을 통해 Postgresql ha 구성이 정상으로 되었는지 확인한다.
여기서 Postgresql Leader는 Primary Node의 Postgresql의 읽기 전용 복제본으로, "Standby Leader" Role 상태로 구동 중인지 확인한다.
$ gitlab-ctl patroni members
+ Cluster: postgresql-ha (7307436727722722598) -+---------+----+-----------+-----------------+
| Member | Host | Role | State | TL | Lag in MB | Pending restart |
+----------------+-------------+----------------+---------+----+-----------+-----------------+
| ip-10-10-1-155 | 10.10.1.155 | Standby Leader | running | 1 | | * |
| ip-10-10-1-201 | 10.10.1.201 | Replica | running | 1 | 0 | * |
+----------------+-------------+----------------+---------+----+-----------+-----------------+
2-4. Praefect Postgresql 구성
Praefect Postgresql에서 사용할 username/password 쌍에 대한 해시를 생성한다.
$ gitlab-ctl pg-password-md5 praefect
## testtest 입력
fab517b76c0579c2da57406331819f4c
Praefect에서 사용할 Postgresql을 구성을 위해 아래와 같이 설정파일을 수정한다.
## /etc/gitlab/gitlab.rb
roles(['postgres_role', 'consul_role'])
postgresql['listen_address'] = '0.0.0.0'
gitlab_rails['auto_migrate'] = false
consul['monitoring_service_discovery'] = true
postgresql['sql_user_password'] = "fab517b76c0579c2da57406331819f4c"
postgresql['trust_auth_cidr_addresses'] = %w(10.10.1.0/24 127.0.0.1/32)
node_exporter['listen_address'] = '0.0.0.0:9100'
postgres_exporter['listen_address'] = '0.0.0.0:9187'
consul['configuration'] = {
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
praefect가 사용할 데이터베이스와 사용자를 구성한다.
$ /opt/gitlab/embedded/bin/psql -U gitlab-psql -d template1 -h 10.10.1.80
template1=# CREATE ROLE praefect WITH LOGIN CREATEDB PASSWORD 'testtest';
CREATE ROLE
template1=# \q
위에서 생성한 praefect 사용자로 postgresql에 접근하여 "praefect_production"라는 데이터베이스를 생성한다.
$ /opt/gitlab/embedded/bin/psql -U praefect -d template1 -h 10.10.1.84
template1=> CREATE DATABASE praefect_production WITH ENCODING=UTF8;
CREATE DATABASE
template1=> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
---------------------+-------------+----------+---------+---------+---------------------------------
gitlabhq_production | gitlab | UTF8 | C.UTF-8 | C.UTF-8 |
postgres | gitlab-psql | UTF8 | C.UTF-8 | C.UTF-8 |
praefect_production | praefect | UTF8 | C.UTF-8 | C.UTF-8 |
template0 | gitlab-psql | UTF8 | C.UTF-8 | C.UTF-8 | =c/"gitlab-psql" +
| | | | | "gitlab-psql"=CTc/"gitlab-psql"
template1 | gitlab-psql | UTF8 | C.UTF-8 | C.UTF-8 | =c/"gitlab-psql" +
| | | | | "gitlab-psql"=CTc/"gitlab-psql"
(5 rows)
template1=> \q
2-5. Praefect 구성
Praefect 구성을 위해 아래와 같이 설정파일을 수정한다.
Praefect 1,2,3 구성
external_url 'https://{외부에서 접속할 도메인 이름}'
gitaly['enable'] = false
postgresql['enable'] = false
redis['enable'] = false
nginx['enable'] = false
puma['enable'] = false
sidekiq['enable'] = false
gitlab_workhorse['enable'] = false
prometheus['enable'] = false
alertmanager['enable'] = false
gitlab_exporter['enable'] = false
gitlab_kas['enable'] = false
praefect['enable'] = true ## praefect 2, 3은 false로 설정
praefect['auto_migrate'] = true
gitlab_rails['auto_migrate'] = false
consul['enable'] = true
consul['monitoring_service_discovery'] = true
praefect['configuration'] = {
listen_addr: '0.0.0.0:2305',
auth: {
token: 'test123', ## praefect_external_token
},
database: {
host: '10.10.1.80', ## praefect_postgresql IP
port: 5432,
user: 'praefect', ## praefect_postgresql에서 생성한 유저
password: 'testtest', ## praefect_postgresql_password
dbname: 'praefect_production',
session_pooled: {
host: '10.10.1.80',
port: 5432,
dbname: 'praefect_production', ## praefect postgresql에서 생성한 데이터베이스
user: 'praefect', ## praefect_postgresql에서 생성한 유저
password: 'testtest', ## praefect_postgresql_password
},
},
virtual_storage: [
{
name: 'default',
node: [
{
storage: 'gitaly-1',
address: 'tcp://10.10.1.117:8075', ## Gitaly 1 IP
token: 'test456' ## praefect_internal_token
},
{
storage: 'gitaly-2',
address: 'tcp://10.10.1.22:8075', ## Gitaly 2 IP
token: 'test456' ## praefect_internal_token
},
{
storage: 'gitaly-3',
address: 'tcp://10.10.1.241:8075', ## Gitaly 3 IP
token: 'test456' ## praefect_internal_token
},
],
},
],
prometheus_listen_addr: '0.0.0.0:9652',
}
node_exporter['listen_address'] = '0.0.0.0:9100'
consul['configuration'] = {
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
첫 번째 praefect 인스턴스만 데이터베이스 마이그레이션 작업을 해야 하는데, 데이터베이스 마이그레이션이 재구성 중에만 실행되고 업그레이드 시 자동으로 실행되지 않도록 하기 위해 다음 명령어 실행 후 적용한다.
$ touch /etc/gitlab/skip-auto-reconfigure
$ gitlab-ctl reconfigure
이후 두 번째 세 번째 노드도 gitlab-ctl reconfigure 명령으로 변경사항을 적용한다.
2-6. Gitaly 구성
Gitaly 구성을 위해 아래와 같이 설정파일을 수정한다.
Gitaly 1, 2, 3 구성
postgresql['enable'] = false
redis['enable'] = false
nginx['enable'] = false
puma['enable'] = false
sidekiq['enable'] = false
gitlab_workhorse['enable'] = false
prometheus['enable'] = false
alertmanager['enable'] = false
gitlab_exporter['enable'] = false
gitlab_kas['enable'] = false
gitlab_rails['auto_migrate'] = false
gitaly['enable'] = true
gitlab_rails['internal_api_url'] = 'http://10.10.1.182' ## internal L/B IP
consul['enable'] = true
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
node_exporter['listen_address'] = '0.0.0.0:9100'
gitaly['configuration'] = {
listen_addr: '0.0.0.0:8075',
prometheus_listen_addr: '0.0.0.0:9236',
auth: {
token: 'test456', ## praefect_internal_token
},
pack_objects_cache: {
enabled: true,
},
storage: [
{
name: 'gitaly-1', ## 각 gitaly마다 알맞게 설정(gitaly-1, gitaly-2, gitaly-3)
path: '/var/opt/gitlab/git-data',
},
],
}
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
위 설정 완료 후 praefect, gitaly에서 error가 보이지 않는지 확인한다.
만약, error 메세지가 보인다면 설정을 다시 한번 확인해야 한다.
$ gitlab-ctl tail
2-7. Internal Loadbalancer 구성
내부 통신을 위한 Loadbalancer를 구성한다. 해당 글에서는 Haproxy를 사용한다.
패키지를 설치한다.
$ dnf install -y haproxy
아래와 같이 haproxy 설정파일을 편집한다.
- 위치 : /etc/haproxy/haproxy.cfg
global
log /dev/log local0
log localhost local1 notice
log stdout format raw local0
defaults
log global
default-server inter 10s fall 3 rise 2
balance leastconn
timeout server 10s
timeout client 10s
timeout connect 5s
frontend internal-pgbouncer-tcp-in
bind *:6432
mode tcp
option tcplog
default_backend pgbouncer
frontend internal-praefect-tcp-in
bind *:2305
mode tcp
option tcplog
option clitcpka
default_backend praefect
frontend internal-rails-tcp-in
bind *:80
mode tcp
option tcplog
default_backend internalrails
backend pgbouncer
mode tcp
option tcp-check
server pgbouncer1 10.10.1.155:6432 check
server pgbouncer2 10.10.1.201:6432 check
backend praefect
mode tcp
option tcp-check
option srvtcpka
server praefect1 10.10.1.132:2305 check
server praefect2 10.10.1.92:2305 check
server praefect3 10.10.1.223:2305 check
backend internalrails
mode tcp
option tcp-check
server rails1 10.10.1.179:80 check
server rails2 10.10.1.143:80 check
haproxy 구동 후 문제가 없는지 확인한다.
아직 gitlab application 서버인 rails가 구동되지 않았기 때문에 해당 ip에 대한 오류만 보일 것이다.
$ systemctl start haproxy ; systemctl enable haproxy
$ systemctl status haproxy
2-8. Tracking Postgresql & Pgbouncer & Consul 구성
데이터베이스 추적을 위한 Geo Tracking Database 구성한다.
Tracking Postgresql에서 사용할 username/password 쌍에 대한 해시를 생성한다.
$ gitlab-ctl pg-password-md5 gitlab_geo (test5)
1bd0783f0a5325c5cd1f46d9f3614858
아래와 같이 설정파일을 수정한다.
Tracking Postgresql Leader
## /etc/gitlab/gitlab.rb
roles(['pgbouncer_role', 'patroni_role', 'consul_role'])
pgbouncer['enable'] = true
pgbouncer['users'] = {
'pgbouncer': {
password: 'a46d71dedc6fd700d42bb91318d1cfc6' ## PGBOUNCER_PASSWORD_HASH
}
}
pgbouncer['databases'] = {
gitlabhq_geo_production: {
host: '127.0.0.1',
user: 'pgbouncer',
password: 'a46d71dedc6fd700d42bb91318d1cfc6' ## PGBOUNCER_PASSWORD_HASH
}
}
pgbouncer['admin_users'] = %w(gitlab-psql gitlab-psql pgbouncer)
consul['watchers'] = %w(postgresql)
consul['services'] = %w(postgresql)
consul['configuration'] = {
server: true,
retry_join: %w[10.10.1.131 10.10.1.18 10.10.1.37]
}
consul['monitoring_service_discovery'] = true
postgresql['listen_address'] = '0.0.0.0'
postgresql['hot_standby'] = 'on'
postgresql['wal_level'] = 'replica'
postgresql['pgbouncer_user_password'] = 'a46d71dedc6fd700d42bb91318d1cfc6' ## PGBOUNCER_PASSWORD_HASH
postgresql['sql_replication_password'] = 'ee3a10e38c1e16210c39f63c26988cd5' ## POSTGRESQL_REPLICATION_PASSWORD_HASH
postgresql['sql_user_password'] = '1bd0783f0a5325c5cd1f46d9f3614858' ## POSTGRESQL_PASSWORD_HASH
postgresql['md5_auth_cidr_addresses'] = [
'10.10.1.0/24', '127.0.0.1/32'
]
postgresql['sql_user'] = "gitlab_geo"
patroni['allowlist'] = %w[
127.0.0.1/32
10.10.1.0/24
]
patroni['username'] = 'test'
patroni['password'] = 'test'
patroni['replication_password'] = 'test3' ## PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD
patroni['postgresql']['max_wal_senders'] = 7 ## 복제본 1개당 최소 3개, 추가 복제본당 2개
gitlab_rails['db_database'] = 'gitlabhq_geo_production'
gitlab_rails['db_username'] = 'gitlab_geo'
gitlab_rails['enable'] = true
gitlab_rails['auto_migrate'] = false
Tracking Postgresql Replica 1, 2
## /etc/gitlab/gitlab.rb
roles(['patroni_role', 'consul_role'])
consul['services'] = %w(postgresql)
consul['configuration'] = {
server: true,
retry_join: %w[10.10.1.131 10.10.1.18 10.10.1.37]
}
consul['monitoring_service_discovery'] = true
postgresql['listen_address'] = '0.0.0.0'
postgresql['hot_standby'] = 'on'
postgresql['wal_level'] = 'replica'
postgresql['pgbouncer_user_password'] = 'a46d71dedc6fd700d42bb91318d1cfc6' ## PGBOUNCER_PASSWORD_HASH
postgresql['sql_replication_password'] = 'ee3a10e38c1e16210c39f63c26988cd5' ## POSTGRESQL_REPLICATION_PASSWORD_HASH
postgresql['sql_user_password'] = '1bd0783f0a5325c5cd1f46d9f3614858' ## POSTGRESQL_PASSWORD_HASH
postgresql['md5_auth_cidr_addresses'] = [
'10.10.1.0/24', '127.0.0.1/32'
]
postgresql['sql_user'] = "gitlab_geo"
patroni['allowlist'] = %w[
127.0.0.1/32
10.10.1.0/24
]
patroni['username'] = 'test'
patroni['password'] = 'test'
patroni['replication_password'] = 'test3' ## PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD
patroni['postgresql']['max_wal_senders'] = 7 ## 복제본 1개당 최소 3개, 추가 복제본당 2개
gitlab_rails['db_database'] = 'gitlabhq_geo_production'
gitlab_rails['db_username'] = 'gitlab_geo'
gitlab_rails['enable'] = true
gitlab_rails['auto_migrate'] = false
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
Postgresql Leader 인스턴스에서 Consul이 Pgbouncer를 다시 로드할 수 있도록 아래와 같이 설정하여 .pgpass 파일을 생성한다.
$ gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
## test2 입력
$ gitlab-ctl restart pgbouncer
각 노드가 master와 통신하는지 확인한다. 아래와 유사하게 나오면 정상이다.
$ gitlab-ctl pgb-console
Password for user pgbouncer: ## test2 입력
pgbouncer=# show databases ; show clients ;
name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
gitlabhq_geo_production | MASTER_HOST | 5432 | gitlabhq_geo_production | | 20 | 0 | | 0 | 0
pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
(2 rows)
type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
C | pgbouncer | pgbouncer | active | 127.0.0.1 | 46304 | 127.0.0.1 | 6432 | 2023-11-28 18:09:59 | 2023-11-28 18:10:48 | 0x22b3880 | | 0 |
(2 rows)
아래 명령을 통해 "gitlabhq_geo_production" 데이터베이스에 접근하여 확인한다.
$ gitlab-psql gitlab-psql
gitlabhq_geo_production=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-------------------------+-------------+----------+---------+---------+---------------------------------
gitlabhq_geo_production | gitlab_geo | UTF8 | C | C.UTF-8 |
postgres | gitlab-psql | UTF8 | C | C.UTF-8 |
template0 | gitlab-psql | UTF8 | C | C.UTF-8 | =c/"gitlab-psql" +
| | | | | "gitlab-psql"=CTc/"gitlab-psql"
template1 | gitlab-psql | UTF8 | C | C.UTF-8 | =c/"gitlab-psql" +
| | | | | "gitlab-psql"=CTc/"gitlab-psql"
(4 rows)
2-9. Sidekiq & Rails 구성
아래와 같이 설정파일을 수정한다.
Sidekiq & Rails 1, 2 설정
external_url 'https://{외부에서 접속할 도메인 이름}'
gitlab_rails['enable'] = true
roles(['sidekiq_role', 'application_role'])
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password' ## redis_master_password
git_data_dirs({
"default" => {
"gitaly_address" => "tcp://10.10.1.182:2305", ## internal load balancer IP
"gitaly_token" => 'test123' ## praefect_external_token
}
})
gitlab_rails['db_host'] = '10.10.1.182' ## internal load balancer IP
gitlab_rails['db_port'] = 6432
gitlab_rails['db_password'] = 'test1' ## postgresql_user_password
gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.10.1.155', '10.10.1.201'] }
gitlab_rails['auto_migrate'] = false
sidekiq['enable'] = true
sidekiq['listen_address'] = "0.0.0.0"
sidekiq['queue_groups'] = ['*'] * 2 ## cpu core수 넘지 않게 설정
sidekiq['max_concurrency'] = 10
gitlab_rails['object_store']['enabled'] = true
gitlab_rails['object_store']['connection'] = {
'provider' => 'AWS',
'aws_access_key_id' => '{aws_access_key_id}',
'aws_secret_access_key' => '{aws_secret_access_key}',
'region' => '{region}'
}
gitlab_rails['object_store']['objects']['artifacts']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['lfs']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['uploads']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['packages']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['object_store']['objects']['pages']['bucket'] = '각 환경에 맞는 버킷 이름'
gitlab_rails['geo_node_name'] = 'geo-secondary'
gitlab_rails['redis_sentinels'] = [
{'host' => '10.10.1.66', 'port' => 26379},
{'host' => '10.10.1.95', 'port' => 26379},
{'host' => '10.10.1.112', 'port' => 26379},
]
node_exporter['listen_address'] = '0.0.0.0:9100'
gitlab_workhorse['prometheus_listen_addr'] = '0.0.0.0:9229'
puma['listen'] = '0.0.0.0'
puma['min_threads'] = 2
puma['max_threads'] = 2
consul['enable'] = true
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
retry_join: %w(10.10.1.66 10.10.1.95 10.10.1.112),
}
gitaly['enable'] = false
nginx['enable'] = true
nginx['listen_port'] = 80
nginx['listen_https'] = false
postgresql['enable'] = false
geo_secondary['enable'] = true
geo_secondary['db_username'] = 'gitlab_geo' ## tracking postgresql_user
geo_secondary['db_password'] = 'test5' ## tracking postgresql_user_password
geo_secondary['db_database'] = 'gitlabhq_geo_production'
geo_secondary['db_host'] = '10.10.1.131' ## Tracking Pgbouncer IP
geo_secondary['db_port'] = 6432 ## Tracking Pgbouncer Port
geo_secondary['auto_migrate'] = false
geo_postgresql['enable'] = false
geo_logcursor['enable'] = true
- 외부 로드밸런서에서 접근은 https, 내부는 통신은 http로 하기 위해 "nginx.[linten_port]"를 80으로 설정하였다.
로드 밸런싱된 Rails 노드에 도달할 때 호스트 불일치 오류가 발생하지 않게 하기 위해 첫 번째 EC2 인스턴스의 "/etc/ssh/ssh_host_*_key*"를 복사하여 rails 노드에 추가 및 수정해야 한다.
여기서는 첫 번째로 구성한 Redis & Consul 인스턴스의 아래 4개의 파일을 복사하여 rails 인스턴스에 덮어씌웠다.
- ssh_host_ecdsa_key
- ssh_host_ecdsa_key.pub
- ssh_host_ed25519_key
- ssh_host_ed25519_key.pub
첫 번째 Rails 인스턴스에서 변경사항을 적용한다.
$ touch /etc/gitlab/skip-auto-reconfigure
$ gitlab-ctl reconfigure
아래 명령을 통해 첫 번째 Rails 인스턴스에서 Tracking 데이터베이스 마이그레이션을 진행한다.
$ gitlab-rake db:migrate:geo
위 절차대로 첫 번째 Sidekiq & Rails 인스턴스가 구성되었으면 두 번째 sidekiq & rails 인스턴스도 변경사항을 적용한다.
$ gitlab-ctl reconfigure
이후 두 개의 Rails 인스턴스에서 gitlab에 git 유저가 ssh로 접근할 수 있게 sshd_config 파일을 아래와 같이 수정한다.
- 파일 위치 : /etc/ssh/sshd_config
...
Match User git
AuthorizedKeysCommand /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-keys-check git %u %k
AuthorizedKeysCommandUser git
Match all
...
sshd_config 변경사항이 적용되게 systemctl restart를 진행한다.
$ systemctl restart sshd
2-10. External Loadbalancer 구성
해당 절차는 Geo Primary Node 구성할 때 했던 방법과 동일하게 생성한다.
3. Geo Secondary Node 등록
Geo Primary Node로 접근하여 Geo Secondary Node를 등록한다.
- Search or go to... > Admin Area
- Geo > Sites
Add site를 클릭한다.
Geo Secondary Node의 이름과 구성에 맞게 External URL / Internal URL을 입력하고 생성한다.
아래와 같이 geo-secondary 노드가 등록되어 정상적으로 동작하는 걸 확인할 수 있다.
이후 Geo Primary Node에서 Project를 생성하고 wiki, issues 등 생성하면 Geo Secondary Node에서도 정상적으로 복제되어 생성되는 걸 확인할 수 있다.
데이터가 복제되면 Data type별 Sync 상태를 확인할 수 있다.
마무리
GitLab Geo는 지리적으로 분산된 팀이나 조직에게 안정성과 확장성을 제공하여 협업 및 개발 프로세스를 향상시키는 강력한 도구이다. 이를 통해 여러 팀이 협업하고 안전하게 데이터를 공유하며, GitLab의 강력한 기능을 최대한 활용할 수 있다.
자세한 내용은 공식 홈페이지에서 확인할 수 있다. (https://docs.gitlab.com/ee/administration/geo/)
'CICD > Gitlab' 카테고리의 다른 글
Gitlab Backup & Restore (Omnibus, Multi-node) (0) | 2023.12.12 |
---|---|
Gitlab Geo 환경 장애 복구 시나리오 실습 (0) | 2023.12.11 |
Gitlab 고가용성 환경 구성하기 (Omnibus, Multi-node) (0) | 2023.12.04 |
댓글