본 글은 gitlab 3000명의 사용자가 사용하기 위해 설계된 아키텍처 기반으로 작성한 글이다.
GitLab Omnibus는 GitLab 및 해당 종속성을 포함하는 종합적인 패키지를 의미한다. 이 패키지를 통해 GitLab과 필요한 모든 구성 요소를 설치할 수 있으며, 더 나아가 각각의 구성 요소를 분리하여 독립적으로 설치하는 것도 가능하다.
이러한 유연성은 관리자들에게 큰 이점을 제공한다. 왜냐하면 시스템 환경과 요구 사항에 맞게 각 구성요소를 조정(확장 및 축소)할 수 있기 때문이다.
본 글에서는 Omnibus 패키지를 활용하여 고가용성 GitLab 환경을 구축하는 방법에 대해 다룬다.
본 글에서 구성할 아키텍처는 다음과 같다.
해당 구성을 통해 각 구성 요소를 분리하고 확장하여 여러 사용자가 사용하는 환경에 안정적으로 Gitlab을 제공할 수 있다.
구성요소의 역할은 다음과 같다.
- Redis : GitLab에서 주로 캐싱 및 세션 데이터 저장에 사용
- Consul : 서비스 디스커버리를 제공하여 서비스 위치와 상태 정보를 관리
- Redis Sentinel : Redis 인스턴스들의 상태를 모니터링하고, 장애 발생 시 자동으로 장애 복구 수행
- Postgresql : GitLab의 주 데이터베이스로, 사용자 데이터 및 프로젝트 정보 등을 저장
- Patroni : PostgreSQL 데이터베이스 클러스터의 고가용성을 관리하며, 자동 장애 복구와 리더 선출을 담당
- Pgbouncer : 데이터베이스 connection pool 관리 및 failover 조치 수행
- Praefect : Git 클라이언트와 Gitaly 스토리지 노드 간 투명한 프록시 역할
- Gitaly : Git 저장소 데이터의 저장 및 접근을 관리하는 Git RPC 서비스
- Internal Loadbalancer : 내부 통신을 위한 로드밸런서
- External Loadbalancer : 외부 통신을 위한 로드밸런서
- Sidekiq : 비동기 작업을 처리하는 백그라운드 작업 큐
- Gitlab Rails(Puma) : 웹 인터페이스 및 API에 대한 요청 처리
- Gitlab Workhorse : GitLab의 리버스 프록시로, 대용량 파일 업로드, Git clone 및 push 등의 작업을 처리
구성 환경
- AWS EC2 Instance(15 EA)
- OS : Amazon Linux 2023
- Kernel : 6.1.61-85.141.amzn2023.x86_64
- Instance type : c5.large
전제 조건
- AWS S3 버킷
- AWS Certificate Manager(ACM) 인증서
설치 버전
- Gitlab 16.6.0-ee
실습 절차
1. Gitlab 패키지 설치 (공통)2. Redis & Consul/Sentinel 구성
3. Postgresql & Patroni / Pgbouncer 구성
4. Praefect Postgresql 구성
5. Praefect 구성
6. Gitaly 구성
7. Internal Loadbalancer 구성
8. Sidekiq & Rails 구성
9. External Loadbalancer 구성
10. Gitlab 접속
11. S3 upload 테스트 & 확인
다음 목록은 각 역할에 대한 서버와 IP 주소이다.
- 10.10.1.139 : Redis & Consul/Sentinel 1
- 10.10.1.137 : Redis & Consul/Sentinel 2
- 10.10.1.244 : Redis & Consul/Sentinel 3
- 10.10.1.120 : Postgresql Leader & Pgbouncer
- 10.10.1.167 : Postgresql Replica & Pgbouncer
- 10.10.1.84 : Praefect Postgresql
- 10.10.1.242 : Praefect 1
- 10.10.1.130 : Praefect 2
- 10.10.1.188 : Praefect 3
- 10.10.1.183 : Gitaly 1
- 10.10.1.21 : Gitaly 2
- 10.10.1.169 : Gitaly 3
- 10.10.1.233 : Internal Loadbalancer (Haproxy)
- 10.10.1.127 : Sidekiq & Rails 1
- 10.10.1.38 : Sidekiq & Rails 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 인스턴스를 생성하는 테라폼 코드 예시이다.
## ec2.tf
...
resource "aws_instance" "gitlab" {
count = 13
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-ec2"
}
}
resource "aws_instance" "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 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}-sidekiq & rails-${count.index}"
}
}
...
Gitlab 패키지가 설치되면 gitlab 관련된 파일들이 생성되는데 구성을 위해 수정해야 할 설정파일 및 위치는 "/etc/gitlab/gitlab.rb" 이다.
또한, 최초 구성으로 인해 생성된 "/etc/gitlab/gitlab-secrets.json" 파일 및 내용을 복사하여 모든 인스턴스에 동일하게 위치시켜야 한다.
2. Redis & Consul/Sentinel 환경 구성
Redis & 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.139'
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.139:6379',
'redis.password' => 'redis-password',
}
gitlab_rails['auto_migrate'] = false
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password'
redis['master_ip'] = '10.10.1.139'
sentinel['bind'] = '10.10.1.139'
sentinel['quorum'] = 2
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
server: true,
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
Redis & 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.137'
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.137:6379',
'redis.password' => 'redis-password',
}
gitlab_rails['auto_migrate'] = false
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password'
redis['master_ip'] = '10.10.1.139'
sentinel['bind'] = '10.10.1.137'
sentinel['quorum'] = 2
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
server: true,
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
Redis & 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.244'
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.244:6379',
'redis.password' => 'redis-password',
}
gitlab_rails['auto_migrate'] = false
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password'
redis['master_ip'] = '10.10.1.139'
sentinel['bind'] = '10.10.1.244'
sentinel['quorum'] = 2
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
server: true,
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
위 3개의 redis, redis_sentinel, consul이 구성되었으면 아래 명령을 통해 정상인지 확인한다.
Redis 확인
$ /opt/gitlab/embedded/bin/redis-cli -h 10.10.1.139 -a 'redis-password' info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.10.1.244,port=6379,state=online,offset=31314246264,lag=1
slave1:ip=10.10.1.137,port=6379,state=online,offset=31314104645,lag=1
master_failover_state:no-failover
master_replid:672bd7a58cb93b29d2ea73547d1f77bbf2200659
master_replid2:66fb58c1b092bffb993a3992bad8532278d74b87
master_repl_offset:31314319744
second_repl_offset:30504908818
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:31313262162
repl_backlog_histlen:1057583
Consul 확인
$ /opt/gitlab/embedded/bin/consul members
Node Address Status Type Build Protocol DC Partition Segment
ip-10-10-1-137 10.10.1.137:8301 alive server 1.16.3 2 gitlab_consul default <all>
ip-10-10-1-139 10.10.1.139:8301 alive server 1.16.3 2 gitlab_consul default <all>
ip-10-10-1-244 10.10.1.244:8301 alive server 1.16.3 2 gitlab_consul default <all>
3. Postgresql & Patroni / Pgbouncer 구성
먼저, Postgresql에서 사용할 username/password 쌍에 대한 해시를 생성한다.
$ gitlab-ctl pg-password-md5 gitlab
test1
8ea51d17cfa0fe1bb503ada4f7f1c68f
$ gitlab-ctl pg-password-md5 pgbouncer
test2
a46d71dedc6fd700d42bb91318d1cfc6
$ gitlab-ctl pg-password-md5 gitlab_replicator
test3
ee3a10e38c1e16210c39f63c26988cd5
$ gitlab-ctl pg-password-md5 gitlab-consul
test4
2deabc6a0997166c753c1537890f27c1
위에서 생성한 해시값을 이용하여 postgresql 및 pgbouncer를 구성한다.
Postgresql Leader 구성
## /etc/gitlab/gitlab.rb
roles(['patroni_role', 'pgbouncer_role'])
postgresql['listen_address'] = '0.0.0.0'
patroni['postgresql']['max_replication_slots'] = 4 ## 데이터베이스 노드 수 2배
patroni['postgresql']['max_wal_senders'] = 5 ## 데이터베이스 노드 수 2배 + 1
gitlab_rails['auto_migrate'] = false
consul['services'] = %w(postgresql)
consul['monitoring_service_discovery'] = true
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
patroni['username'] = 'test' ## patroni_api_username
patroni['password'] = 'test' ## patroni_api_username
patroni['allowlist'] = %w(10.10.1.0/24 127.0.0.1/32)
postgresql['trust_auth_cidr_addresses'] = %w(10.10.1.0/24 127.0.0.1/32)
pgbouncer['databases'] = {
gitlabhq_production: {
host: "127.0.0.1",
user: "pgbouncer",
password: 'a46d71dedc6fd700d42bb91318d1cfc6' ## pgbouncer_password_hash
}
}
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['watchers'] = %w(postgresql)
consul['configuration'] = {
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
pgbouncer['users'] = {
'gitlab-consul': {
password: '2deabc6a0997166c753c1537890f27c1' ## consul_password_hash
},
'pgbouncer': {
password: 'a46d71dedc6fd700d42bb91318d1cfc6' ## pgbouncer_password_hash
}
}
Postgresql Replica 구성
## /etc/gitlab/gitlab.rb
roles(['patroni_role', 'pgbouncer_role'])
postgresql['listen_address'] = '0.0.0.0'
patroni['postgresql']['max_replication_slots'] = 4
patroni['postgresql']['max_wal_senders'] = 5
gitlab_rails['auto_migrate'] = false
consul['services'] = %w(postgresql)
consul['monitoring_service_discovery'] = true
postgresql['pgbouncer_user_password'] = 'a46d71dedc6fd700d42bb91318d1cfc6'
postgresql['sql_replication_password'] = 'ee3a10e38c1e16210c39f63c26988cd5'
postgresql['sql_user_password'] = '8ea51d17cfa0fe1bb503ada4f7f1c68f'
patroni['username'] = 'test'
patroni['password'] = 'test'
patroni['allowlist'] = %w(10.10.1.0/24 127.0.0.1/32)
postgresql['trust_auth_cidr_addresses'] = %w(10.10.1.0/24 127.0.0.1/32)
pgbouncer['databases'] = {
gitlabhq_production: {
host: "127.0.0.1",
user: "pgbouncer",
password: 'a46d71dedc6fd700d42bb91318d1cfc6'
}
}
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['watchers'] = %w(postgresql)
consul['configuration'] = {
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
pgbouncer['users'] = {
'gitlab-consul': {
password: '2deabc6a0997166c753c1537890f27c1'
},
'pgbouncer': {
password: 'a46d71dedc6fd700d42bb91318d1cfc6'
}
}
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
$ gitlab-ctl restart
적용이 완료되었으면 postgresql 구성을 확인한다.
$ gitlab-ctl patroni members
+ Cluster: postgresql-ha (7307436727722722598) ----+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------------+-------------+---------+---------+----+-----------+
| ip-10-10-1-120 | 10.10.1.120 | Leader | running | 1 | |
| ip-10-10-1-167 | 10.10.1.167 | Replica | running | 1 | 0 |
+----------------+-------------+---------+---------+----+-----------+
Consul이 Pgbouncer를 다시 로드할 수 있도록 아래와 같이 설정하여 .pgpass 파일을 생성한다.
$ gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
## test2 입력
각 노드가 master와 통신하는지 확인한다. 아래와 유사하게 나오면 정상이다.
$ gitlab-ctl pgb-console
Password for user pgbouncer: ## test2 입력
psql (13.11, server 1.21.0/bouncer)
Type "help" for help.
pgbouncer=# show databases ; show clients ;
name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_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)
4. Praefect Postgresql 구성
먼저, Praefect Postgresql에서 사용할 username/password 쌍에 대한 해시를 생성한다.
$ gitlab-ctl pg-password-md5 praefect
## testtest 입력
fab517b76c0579c2da57406331819f4c
Praefect에서 사용할 Postgresql을 구성을 위해 아래와 같이 설정파일을 수정한다.
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" ## praefect_postgresql_password_hash
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.139 10.10.1.137 10.10.1.244),
}
아래 명령을 통해 변경사항을 적용한다.
$ gitlab-ctl reconfigure
praefect가 사용할 데이터베이스와 사용자를 구성한다.
기본적으로 gitlab-psql 사용자가 Superuser이며 template1이라는 데이터베이스가 기본으로 생성되어 있다.
gitlab omnibus패키지에 포함되어 있는 psql 명령어로 postgresql에 접근하여 praefect 사용자를 구성하면 된다.
$ /opt/gitlab/embedded/bin/psql -U gitlab-psql -d template1 -h 10.10.1.84
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
5. Praefect 구성
Praefect 구성을 위해 아래와 같이 설정파일을 수정한다.
Praefect 1,2,3 구성
## /etc/gitlab/gitlab.rb
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['auto_migrate'] = true ## praefect 2, 3은 false로 설정
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.84', ## praefect_postgresql IP
port: 5432,
user: 'praefect', ## praefect_postgresql에서 생성한 유저
password: 'testtest', ## praefect_postgresql_password
dbname: 'praefect_production',
session_pooled: {
host: '10.10.1.84',
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.183:8075', ## Gitaly 1 IP
token: 'test456' ## praefect_internal_token
},
{
storage: 'gitaly-2',
address: 'tcp://10.10.1.21:8075', ## Gitaly 2 IP
token: 'test456' ## praefect_internal_token
},
{
storage: 'gitaly-3',
address: 'tcp://10.10.1.169: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.139 10.10.1.137 10.10.1.244),
}
먼저 첫 번째 praefect 인스턴스만 데이터베이스 마이그레이션 작업을 해야 하는데, 데이터베이스 마이그레이션이 재구성 중에만 실행되고 업그레이드 시 자동으로 실행되지 않도록 하기 위해 다음 명령어 실행 후 적용한다.
$ touch /etc/gitlab/skip-auto-reconfigure
$ gitlab-ctl reconfigure
이후 두 번째 세 번째 노드도 gitlab-ctl reconfigure 명령으로 변경사항을 적용한다.
6. Gitaly 구성
Gitaly는 Git 저장소의 저장 및 접근을 관리하는 역할을 수행한다. 그렇기 때문에 입력 및 출력 속도가 중요하여 읽기는 8,000 IOPS 쓰기는 2,000 IOPS의 처리량을 갖는 SSD 사용을 권장한다.
Gitaly 구성을 위해 아래와 같이 설정파일을 수정한다.
Gitaly 1, 2, 3 구성
## /etc/gitlab/gitlab.rb
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.233' ## internal L/B IP
consul['enable'] = true
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
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
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 3s fall 3 rise 2 on-marked-down shutdown-sessions
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.120:6432 check
server pgbouncer2 10.10.1.167:6432 check
backend praefect
mode tcp
option tcp-check
option srvtcpka
server praefect1 10.10.1.242:2305 check
server praefect2 10.10.1.130:2305 check
server praefect3 10.10.1.188:2305 check
backend internalrails
mode tcp
option tcp-check
server rails1 10.10.1.127:80 check
server rails2 10.10.1.38:80 check
haproxy 구동 후 문제가 없는지 확인한다.
아직 gitlab application 서버인 rails가 구동되지 않았기 때문에 해당 ip에 대한 오류만 보일 것이다.
$ systemctl start haproxy ; systemctl enable haproxy
$ systemctl status haproxy
8. Sidekiq & Rails 구성
아래와 같이 설정파일을 수정한다.
Sidekiq & Rails 1, 2
## /etc/gitlab/gitlab.rb
external_url 'https://{외부에서 접속할 도메인 이름}'
roles(['sidekiq_role', 'application_role'])
redis['master_name'] = 'gitlab-redis'
redis['master_password'] = 'redis-password'
gitlab_rails['redis_sentinels'] = [
{'host' => '10.10.1.139', 'port' => 26379},
{'host' => '10.10.1.137', 'port' => 26379},
{'host' => '10.10.1.244', 'port' => 26379},
]
git_data_dirs({
"default" => {
"gitaly_address" => "tcp://10.10.1.233:2305", ## internal load balancer IP
"gitaly_token" => 'test123' ## praefect_external_token
}
})
gitlab_rails['db_host'] = '10.10.1.233' ## 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.170', '10.10.1.189'] }
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
consul['enable'] = true
consul['monitoring_service_discovery'] = true
consul['configuration'] = {
retry_join: %w(10.10.1.139 10.10.1.137 10.10.1.244),
}
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
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'] = '각 환경에 맞는 버킷 이름'
gitaly['enable'] = false
nginx['enable'] = true
nginx['listen_port'] = 80
nginx['listen_https'] = false
postgresql['enable'] = false
- 외부 로드밸런서에서 접근은 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
데이터베이스 마이그레이션이 재구성 중에만 실행되고 업그레이드 시 자동으로 실행되지 않도록 하기 위해 다음 명령어 실행한다. 이 또한 위 praefect에서 진행했던 것처럼 첫 번째 rails 노드에서만 진행해야 한다.
또한, 첫 번째 노드에서 데이터베이스 마이그레이션을 진행할 때 Pgbouncer가 아닌 Postgresql Leader 노드에 직접 연결되도록 수정해야 한다. (reconfigure 후 다시 Pgbouncer를 바라보게 수정해야 함)
## /etc/gitlab/gitlab.rb
...
gitlab_rails['db_host'] = '10.10.1.120' ## postgresql leader ip로 변경
gitlab_rails['db_port'] = 5432 ## postgresql port로 변경
...
변경사항을 적용한다.
$ touch /etc/gitlab/skip-auto-reconfigure
$ gitlab-ctl reconfigure
reconfigure 후 인스턴스가 gitaly에 접근할 수 있는지 확인한다.
$ gitlab-rake gitlab:gitaly:check
Checking Gitaly ...
Gitaly: ... default ... OK
Checking Gitaly ... Finished
아래 명령을 통해 첫 번째 rails 인스턴스에서 데이터베이스 마이그레이션을 진행한다.
$ gitlab-rake gitlab:db:configure
Running db:schema:load rake task
psql:/opt/gitlab/embedded/service/gitlab-rails/db/structure.sql:9: NOTICE: extension "btree_gist" already exists, skipping
psql:/opt/gitlab/embedded/service/gitlab-rails/db/structure.sql:11: NOTICE: extension "pg_trgm" already exists, skipping
INFO: analyzing "public.p_ci_runner_machine_builds" inheritance tree
INFO: analyzing "gitlab_partitions_dynamic.ci_runner_machine_builds_100"
INFO: "ci_runner_machine_builds_100": scanned 0 of 0 pages, containing 0 live rows and 0 dead rows; 0 rows in sample, 0 estimated total rows
INFO: analyzing "public.p_ci_job_annotations" inheritance tree
INFO: analyzing "gitlab_partitions_dynamic.ci_job_annotations_100"
INFO: "ci_job_annotations_100": scanned 0 of 0 pages, containing 0 live rows and 0 dead rows; 0 rows in sample, 0 estimated total rows
INFO: analyzing "public.p_ci_builds_metadata" inheritance tree
INFO: analyzing "public.ci_builds_metadata"
INFO: "ci_builds_metadata": scanned 0 of 0 pages, containing 0 live rows and 0 dead rows; 0 rows in sample, 0 estimated total rows
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/001_application_settings.rb
Creating the default ApplicationSetting record.
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/002_admin.rb
Administrator account created:
login: root
password: You'll be prompted to create one on your first visit.
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/003_create_base_work_item_types.rb
OK
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/004_add_security_training_providers.rb
OK
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/010_settings.rb
Saved CI JWT signing key
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb
OK
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/030_default_organization.rb
OK
== Seed from /opt/gitlab/embedded/service/gitlab-rails/db/fixtures/production/040_create_work_item_related_link_restrictions.rb
OK
== Seed from ee/db/fixtures/production/010_license.rb
== Seed from ee/db/fixtures/production/027_plans.rb
OK
마이그레이션이 잘 되었으면 gitlab.rb 파일에서 Pgbouncer를 바라보게 다시 수정한다.
## /etc/gitlab/gitlab.rb
...
gitlab_rails['db_host'] = '10.10.1.233' ## internal load balancer IP
gitlab_rails['db_port'] = 6432 ## pgbouncer port
...
변경사항을 적용한다.
$ gitlab-ctl reconfigure
위 절차대로 첫 번째 sidekiq & rails 인스턴스가 구성되었으면 두 번째 인스턴스도 reconfigure 진행한다.
$ 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
9. External Loadbalancer 구성
외부에서 gitlab에 접근할 External Loadbalancer를 구성한다. 여기서 External Loadbalancer는 AWS의 NLB를 사용하였다.
먼저 NLB에서 사용할 Target Group을 아래 정보를 이용해 생성한다.
- 애플리케이션에 접속할 포트는 80/TCP
- gitlab의 ssh 접근을 위한 포트는 22/TCP
HTTP target group 생성
- AWS Console > EC2 > 대상 그룹 > 대상 그룹 생성
- 대상 유형은 인스턴스로 선택한다
- 프로토콜은 TCP 80으로 선택한다
- 다음을 눌러 대상 인스턴스 선택하는 부분으로 이동한다
- gitlab rails 인스턴스 선택, 80 포트로 입력 후 "아래에 보류 중인 것으로 포함"을 선택 후 생성한다.
SSH target group 생성
- 위 HTTP target group 생성과 동일하게 진행하되 target group 이름과 마지막 80 포트 대신 22 포트 입력 후 생성한다.
Network Loadbalancer 생성
- AWS Console > EC2 > 로드 밸런서 > Network Load Balancer 생성
- 인터넷 경계로 선택한다
- 각 환경에 맞게 VPC, 가용영역, Security Group을 선택한다
- 프로토콜 TLS, 포트 443, 대상 그룹은 위에서 생성한 http 대상 그룹 선택
- 프로토콜 TCP, 포트 22, 대상 그룹은 위에서 생성한 ssh 대상 그룹 선택
- 각 환경에 존재하는 AWS Certificate Manager 인증서 선택
Route53 등록
- AWS Console > Route 53 > 호스팅 영역
각 환경에 맞게 위에서 생성한 Loadbalancer를 도메인 매핑시킨다.
10. Gitlab 접속
External Loadbalancer가 생성되었으면 브라우저에서 Gitlab 대시보드에 접근한다.
최초 접근하면 password를 변경하라고 나온다. 최초 Admin ID는 root이다. 변경 후 접속하면 정상 접속 되는 걸 확인할 수 있다.
11. S3 upload 테스트 & 확인
rails 인스턴스에서 지정했던 upload 파일이 S3로 정상 업로드 되는지 확인해 볼 것이다.
먼저 Projects를 생성한다.
- test1이라는 이름으로 Project를 생성한다.
Project가 생성되었으면 업로드를 위해 Issue를 생성한다.
- Plan > Issues
- New issue
아래 사진과 같이 Description에 사진을 붙여 넣었더니 uploads 경로 아래에 {고유번호}/imag.png 가 생성되었다.
해당 정보가 S3에도 생성되었는지 확인한다.
- 참고로 Create issue를 하지 않아도 이미 S3에 upload 된다.
아래 그림을 보면 S3와 Gitlab Issues에 업로드 한 고유 번호가 같은 걸 확인할 수 있다.
Postgresql 인스턴스로 들어가 아래 명령어를 통해 upload 된 테이블의 레코드 수를 확인할 수 있다.
$ gitlab-psql
gitlabhq_production=# SELECT count(*) AS total, sum(case when store = '1' then 1 else 0 end) AS filesystem, sum(case when store = '2' then 1 else 0 end) AS ob jectstg FROM uploads;
total | filesystem | objectstg
-------+------------+-----------
1 | 0 | 1
(1 row)
'CICD > Gitlab' 카테고리의 다른 글
Gitlab Backup & Restore (Omnibus, Multi-node) (0) | 2023.12.12 |
---|---|
Gitlab Geo 환경 장애 복구 시나리오 실습 (0) | 2023.12.11 |
Gitlab 고가용성 Geo 환경 구성하기 (Omnibus, Multi-node) (0) | 2023.12.05 |
댓글