1. Intro
GitLab 설치 문서에서 확인할 수 있는 Memory 최소량은 4GB(~사용자 500명)에서 8GB(~사용자 1,000명) 이다. 그러나 실제 내부 환경에서 GitLab을 구축하고 테스트 하는 과정에서, 실제로는 훨씬 더 많은 메모리를 소모하는 것을 확인할 수 있었다.
이런저런 작업을 하던 중 GitLab Web UI가 느려져서 서버 상태를 확인해보니 다음 사진과 같이 서버의 메모리를 거의 최대까지 잡아먹고 있는 것을 확인하였다. 처음엔 GitLab Runner쪽을 의심했지만 GitLab 서버 또한 만만치 않게 메모리를 잡아 먹고 있었다. 메모리 돼지는 JAVA인 줄알았는데 Ruby도 만만치 않은 메모리 돼지인것인가.. 다행히 GitLab 문서에 메모리 사용량을 줄일 수 있는 여러 설정들이 있었고, 그 내용을 참고하여 소모량을 절반 정도 줄여보았다. 그 내용을 정리해두고자 한다.
2. 개요
nginx, mysql 같은 다른 패키지들과 달리, GitLab을 구성할 시 단순히 GitLab이 설치 되는것이 아니라 GitLab Rails(Puma, Sidekiq, etc), Gitaly, Redis, PostgreSQL, Nginx, Promethus 같은 네트워크, 스토리지, 모니터링 관련 여러 구성요소가 한꺼번에 설치된다.(Gitlab ombinus, Reference Architectures 참고)
해당 구성요소들은 GitLab 서비스의 안정성을 위해 필요하지만, default 값의 설정이 과분하다고 느껴진다면 다음 문서의 설정을 참고하여 설정값을 변경할 수 있다. Running GitLab in a memory-constrained environment
문서의 하단부를 참조하면, 언급되는 모든 설정을 적용시 GitLab의 메모리 소모량이 약 2Gib/스왑1Gib 정도로 조정됨을 기대할 수 있다고 한다.
total used free shared buff/cache available
Mem: 1.9Gi 1.7Gi 151Mi 31Mi 132Mi 102Mi
Swap: 1.0Gi 153Mi 870Mi
2.1. GitLab Data Backup/Restore
작업을 시작하기전에, GitLab의 Data를 Backup을 해두고 시작하면 좋다. 설정 과정에서에러가 있어 초기화를 해야한다거나 했을때 백업 데이터가 없다면? 생각만해도 싫다.
깃랩 백업과 복원은 이번글에서 중요하게 다룰 사항은 아니라서 간단히 문서와 커맨드만 언급하고 넘어가겠다. 유의사항, 자세한 작업 사항들은 공식 문서를 참조 바람. Back up and restore GitLab
- Backup
- 사용중인 GitLab License와 버전 전체 확인 (버전이 다를 시 restore 불가)
- GitLab 설정파일 백업
- Linux Package
- /etc/gitlab/gitlab-secrets.json
- /etc/gitlab/gitlab.rb
- Docker
- /srv/gitlab/config
- Linux Package
- 백업 명령어
- Linux Package
- sudo gitlab-backup create
- Docker
- docker exec -t <container name> gitlab-backup create
- Linux Package
- Restore
2.2. Puma 최적화
Puma는 GitLab의 Web 서버를 담당하는 구성 요소이다.
Puma is a fast, multi-threaded, and highly concurrent HTTP 1.1 server for Ruby applications. It runs the core Rails application that provides the user-facing features of GitLab.
Configure the bundled Puma instance of the GitLab package
worker process
기본적으로 Puma는 동시연결을 처리하기 위해 클러스터 모드로, 기본 2개의 worker_process를 갖도록 구성된다. 필요하다면 단일 프로세스로 처리할 수 있도록 설정값을 변경할 수 있다.
# sudo vi /etc/gitlab/gitlab.rb
puma['worker_processes'] = 0
per_worker_max_memory_mb
또는 개별 worker의 최대 메모리 사용량을 다음 설정을 통해 제한할 수 있다. 디폴트는 1200Mb이다. Reducing memory use
# sudo vi /etc/gitlab/gitlab.rb
puma['per_worker_max_memory_mb'] = 1024 # 1GB
2.2. Sidekiq 최적화
Sidekiq은 Gitlab의 작업을 처리하기 위한 백그라운드 프로세스이다.
Sidekiq is the background job processor GitLab uses to asynchronously run tasks.
Troubleshooting Sidekiq
max_concurrency
기본적으로 sidekiq은 50개의 동시성(concurrency)를 갖고 구동된다. 다음 설정을 통해 그보다 적은 값으로 변경할 수 있다. 동시성이 줄어 백그라운드 작업이 queue에서 대기하는 시간이 길어지긴 하겠지만 알빠노
# sudo vi /etc/gitlab/gitlab.rb
sidekiq['max_concurrency'] = 10
sidekiq environment variables
puma와 유사하게, 숫자를 줄이는 것외에도 메모리 최대 값 등을 변경할 수도 있다. Configuring the limits 의 환경변수 참조.
2.3 Gitaly 최적화
Gitlay는 git repository data를 저장하는 서비스이다. NFS를 대신해서, gRPC를 통해 다수의 Node간 git data를 동기화 하는듯 하다.
Gitaly provides high-level RPC access to Git repositories. It is used by GitLab to read and write Git data.
Gitaly and Gitaly Cluster
Concurrency & Mem limit
gitlab.rb의 default값을 확인해보면 레포당 HTTP Receive 20, SSH Upload 5의 동시성을 확인할 수 있고 최대 메모리는 12GB(memory_bytes: 12884901888) 임을 확인할 수 있다.
이를 다음의 설정값으로 변경할 수 있다고 한다. 하지만 데이터부분은 쫄려서 안건드림
# sudo vi /etc/gitlab/gitlab.rb
gitaly['configuration'] = {
concurrency: [
{
'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
'max_per_repo' => 3,
}, {
'rpc' => "/gitaly.SSHService/SSHUploadPack",
'max_per_repo' => 3,
},
],
cgroups: {
repositories: {
count: 2,
},
mountpoint: '/sys/fs/cgroup',
hierarchy_root: 'gitaly',
memory_bytes: 500000,
cpu_shares: 512,
},
}
gitaly['env'] = {
'GITALY_COMMAND_SPAWN_MAX_PARALLEL' => '2'
}
2.4. 내장 Prometheus 끄기
GitLab은 자체 서비스 모니터링을 위한 Prometheus가 내장되어있다. 그러나 GitLab 자체에 대한 모니터링이 크게 필요하지않다면 내장 프로메테우스를 비활성활 수 있다.
# sudo vi /etc/gitlab/gitlab.rb
prometheus_monitoring['enable'] = false
2.5. GitLab Rails 메모리 할당 줄이기
GitLab Rails는 Ruby on Rails 프레임 워크를 사용하여 GitLab의 핵심 부분을 담당하는 웹 어플리케이션이다. 다만 그러면 Puma랑 뭐가 다른건지 궁금해지긴 하는데.. Ruby에 대해선 잘 모르고 깃랩개발자는 아니니까 넘어간다.
At the heart of GitLab is a web application built using the Ruby on Rails framework.
Rails console
memory allocator 조정
GitLab Rails는 jemalloc이라는 메모리 할당자(memory allocator)를 사용한다. jemalloc은 성능 향상을 위해 필요한 메모리보다 더 많이 사전에 할당받는다. 이를 다음의 설정으로 변경할 수 있다.
# sudo vi /etc/gitlab/gitlab.rb
gitlab_rails['env'] = {
'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000'
}
gitaly['env'] = {
'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000'
}
2.6. postgreSQL 최적화
GitLab은 레포지토리 데이터를 담당하는 Gitaly외에도, gitlab 자체의 유저 정보 및 프로젝트 등 데이터 관리를 위해 postgreSQL을 사용한다.
공식 문서 원문에는 나와있지 않지만, 관련 글들 구글링하던 중 High memory usage for Gitlab CE에서 제안되었다. shared_buffers는 PostgreSQL이 메모리에서 데이터를 읽고 쓸 때 사용하는 공유 메모리 영역의 크기를 제어하는 매개변수이다. 과다하게 낮추면 그것대로 작동을 느려지게 하므로 적정값으로 셋팅한다.
# sudo vi /etc/gitlab/gitlab.rb
postgresql['shared_buffers'] = "256MB"
3. 설정 변경 값 적용
위의 공식문서등에서 언급된 모든 변경 사항을 정리하면 다음과 같다. 모든 변경값을 적용하진 않았고 기본값과 아래 권장값 사이의 적당한 값으로 설정하였다.
# sudo vi /etc/gitlab/gitlab.rb
puma['worker_processes'] = 0
puma['per_worker_max_memory_mb'] = 1024
sidekiq['max_concurrency'] = 10
prometheus_monitoring['enable'] = false
gitlab_rails['env'] = {
'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000'
}
gitaly['configuration'] = {
concurrency: [
{
'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
'max_per_repo' => 3,
}, {
'rpc' => "/gitaly.SSHService/SSHUploadPack",
'max_per_repo' => 3,
},
],
cgroups: {
repositories: {
count: 2,
},
mountpoint: '/sys/fs/cgroup',
hierarchy_root: 'gitaly',
memory_bytes: 500000,
cpu_shares: 512,
},
}
gitaly['env'] = {
'MALLOC_CONF' => 'dirty_decay_ms:1000,muzzy_decay_ms:1000',
'GITALY_COMMAND_SPAWN_MAX_PARALLEL' => '2'
}
postgresql['shared_buffers'] = "256MB"
설정파일을 수정하였으면 서버 또는 컨테이너 쉘에서 다음 명령어를 통해 gitlab을 재시작하고 잘 작동하는지를 기도한다.
sudo gitlab-ctl reconfigure
설정 변경 결과 약 14GB 최대치까지 도달했던 GitLab 메모리 사용량을 7GB로 절반 정도로 줄일 수 있었다. 아직 문서의 권장치 만큼 줄이지는 않았기에 개선의 여지가 있지만 추이를 보고 결정할 것이다.
유사한 사례를 다룬 다른글 (gitlab 운영시 CPU 및 메모리 점유율 높아지는 현상 및 대처법)을 살펴보면 초기에는 낮게 구동되었어도 실제 사용하다보면 다시 리소스 사용량이 늘어난다고 한다. 설정을 통해 프로세스의 수를 제한해두긴 했지만 bundle (puma,sidekiq)의 프로세스가 제대로 종료되지 않는 것이 의심되고, 과거 글들에서 unicorn(puma 전신) 프로세스를 정리하는 unicorn-worker-killer 나 cron등을 통해 sidekiq 프로세스를 정리한다 는 표현을 봐선 번들 프로세스가 쌓이는지 추이를 보고 결정해야할 듯 하다.