본문 바로가기
CICD/Gitlab

Gitlab 고가용성 환경 구성하기 (Omnibus, Multi-node)

by wlsdn3004 2023. 12. 4.
728x90
반응형

 

본 글은 gitlab 3000명의 사용자가 사용하기 위해 설계된 아키텍처 기반으로 작성한 글이다.

 

GitLab Omnibus는 GitLab 및 해당 종속성을 포함하는 종합적인 패키지를 의미한다. 이 패키지를 통해 GitLab과 필요한 모든 구성 요소를 설치할 수 있으며, 더 나아가 각각의 구성 요소를 분리하여 독립적으로 설치하는 것도 가능하다.

 

이러한 유연성은 관리자들에게 큰 이점을 제공한다. 왜냐하면 시스템 환경과 요구 사항에 맞게 각 구성요소를 조정(확장 및 축소)할 수 있기 때문이다. 

 

본 글에서는 Omnibus 패키지를 활용하여 고가용성 GitLab 환경을 구축하는 방법에 대해 다룬다.

 

본 글에서 구성할 아키텍처는 다음과 같다.

해당 구성을 통해 각 구성 요소를 분리하고 확장하여 여러 사용자가 사용하는 환경에 안정적으로 Gitlab을 제공할 수 있다.

 

 

구성요소의 역할은 다음과 같다.

  • Redis : GitLab에서 주로 캐싱 및 세션 데이터 저장에 사용
  • Consul : 서비스 디스커버리를 제공하여 서비스 위치와 상태 정보를 관리
  • Redis SentinelRedis 인스턴스들의 상태를 모니터링하고, 장애 발생 시 자동으로 장애 복구 수행
  • 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

 

 

다음 목록은 각 역할에 대한 서버와 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)
반응형

댓글