Хранить кучу манифестов кубера и кусков кода в блокноте плохая идея. Непонятно, где тут источник правды?
Для этого используются Git репозитории. В этом гайде мы выполним установку Gitlab в кластер кубернетес. Но установка в кластер у которого есть выход в интернет мало похожа на продакшн задачу. Именно поэтому сначала мы загрузим все пакеты и helm чарты на компе или сервере с выходом в интернет, а уже после выполним установку в закрытом периметре. (air-gapped).
Собираю единомышленников в телеграм канале. Там же отвечаю на вопросы.
Присоединяйся

Настройка очень тонкая. Всё зависит от тех компонентов, которые у вас уже есть.
В данной инструкции я постарался максимально приблизиться к той установке, что работает в проде.

Шаг 1. Требования
На компе/сервере с выходом в инет установлены:
- Helm
- Docker
Закрытый контур установлены:
- Docker или Nerdctl
- Helm
- Harbor или Nexus
- Minio или Ceph
- Longhorn или NFS
План такой:
На машине с инетом:
- Скачать helm-чарт
- Вытащить имена образов
- Сделать тестовый values.yaml
- Скачать образы
- Перенести в закрытый контур
В закрытым контуре:
- Запушить в свой репозиторий образы
- Запушить в свой репозиторий chart
- Написать values.yaml файл
- Подключить (создать секреты) интеграций
- Запустить установку
Шаг 2. Подготовка образов
1
2
3
| helm repo add gitlab https://charts.gitlab.io
helm repo update
helm pull gitlab/gitlab --untar
|
Создаём файл:
Вставляем, изменяем переменные на свои:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| global:
edition: ce
hosts:
domain: k8s.domain.local
gitlab:
name: gitlab.k8s.domain.local
ingress:
enabled: true
configureCertmanager: false
provider: nginx
class: nginx
tls:
enabled: false
registry:
enabled: false
minio:
enabled: false
gitlab-runner:
install: false
prometheus:
install: false
grafana:
enabled: false
|
Я не использую tls сертификаты, так как это перегрузит инструкцию, поэтому настраиваю http соединение и tls выбираю false
Выгружаем список образов:
1
| helm template gitlab ./gitlab -f values.yaml > rendered.yaml
|
Выгружаем образы в файл:
1
2
| grep image: rendered.yaml | awk '{print $2}' | sort -u > git-images.txt
grep -oE 'image:\s*"?[^"]+' rendered.yaml | sed -E 's/^image:\s*"?//; s/"$//' | sort -u > git-images.txt
|
Я создал в своём Harbor проект с именем gitlab. Публичный. Далее я буду использовать его в переменных окружения.

Сейчас мы выкачаем образы и затегаем.
Задаём переменные окружения:
1
2
| export HARBOR="harbor.domain.local"
export PROJECT="gitlab"
|
Выполняем скрипт:
1
2
3
4
5
6
7
8
9
10
11
12
13
| cat git-images.txt | tr -d '\r' | while IFS= read -r img; do
img="$(echo "$img" | tr -d '"' | xargs)"
[ -z "$img" ] && continue
cleaned="$(echo "$img" | sed -E 's#^(docker\.io|registry\.gitlab\.com|quay\.io|gcr\.io|ghcr\.io|registry\.k8s\.io)/##')"
new="${HARBOR}/${PROJECT}/${cleaned}"
echo "PULL $img"
sudo docker pull "$img" || { echo "FAIL pull: $img"; exit 1; }
echo "TAG $img -> $new"
sudo docker tag "$img" "$new" || { echo "FAIL tag: $img -> $new"; exit 1; }
done
|
Сохранить имена в файл:
1
2
3
4
5
6
| cat git-images.txt | tr -d '\r' | while IFS= read -r img; do
img="$(echo "$img" | tr -d '"' | xargs)"
[ -z "$img" ] && continue
cleaned="$(echo "$img" | sed -E 's#^(docker\.io|registry\.gitlab\.com|quay\.io|gcr\.io|ghcr\.io|registry\.k8s\.io)/##')"
echo "${HARBOR}/${PROJECT}/${cleaned}"
done | sort -u > harbor-images.txt
|
Сохраняем в архив:
1
| sudo docker save -o gitlab-harbor-images.tar $(cat harbor-images.txt)
|
Также запаковываем чарт:
1
| tar -cvf gitlab-helm.tgz gitlab
|
Перенеси в закрытый контур:
1
2
3
| harbor-images.txt
gitlab-harbor-images.tar
gitlab-helm.tgz
|
Шаг 3. Распаковка образов
Внутри закрытого контура:
1
| sudo docker load -i gitlab-harbor-images.tar
|
Задаём переменные окружения:
1
2
| export HARBOR="harbor.domain.local"
export PROJECT="gitlab"
|
Логинимся:
Пушим все образы:
1
2
3
4
5
6
| cat harbor-images.txt | tr -d '\r' | while IFS= read -r img; do
img="$(echo "$img" | tr -d '"' | xargs)"
[ -z "$img" ] && continue
echo "PUSH $img"
sudo docker push "$img" || { echo "FAIL push: $img"; exit 1; }
done
|

Получился 21 образ в репозитории + возможно также потребуется перетегать postgres-exporter в postgresql-exporter, будет 22 образа.
Создаём репозиторий с именем helm в Harbor, если он ещё не создан. И пушим наш архив с Helm.

1
| helm push gitlab-helm.tgz oci://harbor.doman.local/helm
|
Образы готовы к установке. Переходим к конфигурированию значений Helm.
Шаг 4. Конфигурация HELM values для Gitlab
У меня будет ArgoCD, с помощью него я установлю мониторинг, поэтому я не буду устанавливать Grafana/Prometheus и отключу этот параметр.
За Ingress у меня отвечает NGINX в k8s поэтому он мне не нужен.
Также у меня есть кластер серверов MinIO, свой репозиторий на основе Harbor, и хранилище на Longhorn.
Сертификаты пока не настроены, поэтому cert-manager тоже долой.
Соответственно эти компоненты я ставлю disabled/false.
А вот redis, postgresql у меня нет, поэтому их я их установлю, по умолчанию все компоненты = enabled/true.
Создаём файл:
Содержимое:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
| installCertmanager: false
nginx-ingress:
enabled: false
gitlab-runner:
install: false
prometheus:
install: false
grafana:
install: false
global:
edition: ce
hosts:
domain: k8s.domain.local
https: false
gitlab:
name: gitlab.k8s.domain.local
image:
registry: harbor.domain.local
ingress:
enabled: true
configureCertmanager: false
class: nginx
tls:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
certificates:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/certificates
tag: "v18.8.4"
# Рутовый ЦА для доверия между gitlab и minio
customCAs:
- secret: gitlab-ca
kubectl:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/kubectl
tag: "v18.8.4"
gitlabBase:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-base
tag: "v18.8.4"
registry:
enabled: false
minio:
enabled: false
appConfig:
object_store:
# Эти хранилища (бакеты) я создаю в следующем шаге
enabled: true
connection:
secret: gitlab-storage
key: config
artifacts:
enabled: true
bucket: gitlab-artifacts
lfs:
enabled: true
bucket: gitlab-lfs
uploads:
enabled: true
bucket: gitlab-uploads
packages:
enabled: true
bucket: gitlab-packages
backups:
bucket: gitlab-backups
tmpBucket: tmp
gitlab:
toolbox:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-toolbox-ce
tag: "v18.8.4"
backups:
objectStorage:
config:
# Этот секрет я создаю в следующем шаге
secret: s3-credentials
key: config
webservice:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-webservice-ce
workhorse:
image: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-workhorse-ce
migrations:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-toolbox-ce
tag: "v18.8.4"
gitaly:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitaly
tag: "v18.8.4"
persistence:
size: 20Gi
storageClass: longhorn
kas:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-kas
tag: "v18.8.4"
sidekiq:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-sidekiq-ce
gitlab-shell:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-shell
gitlab-exporter:
image:
repository: harbor.domain.local/gitlab/gitlab-org/build/cng/gitlab-exporter
registry:
enabled: false
redis:
image:
registry: harbor.domain.local
repository: gitlab/bitnamilegacy/redis
pullPolicy: IfNotPresent
metrics:
image:
registry: harbor.domain.local
repository: gitlab/bitnamilegacy/redis-exporter
pullPolicy: IfNotPresent
postgresql:
image:
registry: harbor.domain.local
repository: gitlab/bitnamilegacy/postgresql
pullPolicy: IfNotPresent
metrics:
image:
registry: harbor.domain.local
repository: gitlab/bitnamilegacy/postgresql-exporter
pullPolicy: IfNotPresent
|
Каждая строчка конфига выверена и побеждена в отладке на ошибки. Поэтому за правильность секций можно быть уверенными.
Версии (теги) устанавливайте свои. На момент написания актуальная версия образов: v18.8.4.
Чтобы поды гитлаба доверяли MinIO и другим сервисам я помещаю рутовый сертификат своего CA внутрь контейнера.
1
| kubectl -n gitlab create secret generic gitlab-ca --from-file=root-ca.crt=/usr/local/share/ca-certificates/root-ca.crt
|
Шаг 5. Настройка бакетов в MinIO
Создать в MinIO бакеты. Или подключите существующие.
- gitlab-artifacts
- gitlab-backups
- gitlab-lfs
- gitlab-registry
- gitlab-uploads

Имена этих бакетов для хранения данных мы указали в values.yaml
Чтобы Gitlab умел авторизовать в Minio и не говорил ошибку 500, “нет прав на запись” ему нужно прописать ключи и доступы.
Сначала в Minio нужно провести работы по созданию пользователя, политики и токена. Приступим.
Cоздаём пользователя gitlab-s3
Создаём политику IAM Policies -> Create policy -> gitlab
И содержимое:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::gitlab-artifacts",
"arn:aws:s3:::gitlab-backups",
"arn:aws:s3:::gitlab-lfs",
"arn:aws:s3:::gitlab-uploads"
]
},
{
"Effect": "Allow",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::gitlab-artifacts/*",
"arn:aws:s3:::gitlab-backups/*",
"arn:aws:s3:::gitlab-lfs/*",
"arn:aws:s3:::gitlab-uploads/*"
]
}
]
}
|
И добавляем пользователя gitlab-s3 к политике.

Создать ключ к бакету gitlab-s3:
Minio -> Users -> Access Key -> Create
Получится примерно такое:
1
2
| TasdasdaspksOr1iwOPABR
mS9pitEsdfsdfsdfsdfsue
|
Создать секрет
В качестве Endpoint у меня стоит nginx, который слушает s3.minio.domain.local на 443 и перенаправляет его на 9000.
Инструкция по установке MinIO здесь: ТЫК
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| apiVersion: v1
kind: Secret
metadata:
name: gitlab-storage
namespace: gitlab
type: Opaque
stringData:
config: |
provider: AWS
region: us-east-1
endpoint: "https://s3.minio.domain.local"
path_style: true
aws_access_key_id: TasdasdaspksOr1iwOPABR
aws_secret_access_key: "mS9pitEsdfsdfsdfsdfsue"
|
Далее создаём второй файл с секретом, оба файла мы указали в values.yaml в ObjectStorage.
Если указать неверные данные, то Gitlab не сможет записывать и хранить данные в MinIO.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| apiVersion: v1
kind: Secret
metadata:
name: s3-credentials
namespace: gitlab
type: Opaque
stringData:
config: |
s3:
bucket: gitlab-registry-storage
accesskey: TasdasdaspksOr1iwOPABR
secretkey: "mS9pitEsdfsdfsdfsdfsue"
region: us-east-1
regionendpoint: "https://s3.minio.domain.local"
v4auth: true
|
Создаём namespace в кубере:
1
| kubectl create ns gitlab
|
И применяем секреты:
1
2
| kubectl apply -f gitlab-secret.yaml
kubectl apply -f s3.yaml
|
Шаг 6. Установка Gitlab из Helm
1
| helm install gitlab oci://harbor.domain.local/helm/gitlab -n gitlab -f values.yaml
|
После успешной установки, которую можно проверить так:
1
| kubectl get po -n gitlab
|

Открываем вебку по адресу, который указали в конфиге (Добавьте имя в DNS):
https://gitlab.domain.local

Пользователь для авторизации: root
Получить пароль для входа:
1
| kubectl get secret -n gitlab gitlab-gitlab-initial-root-password -o jsonpath="{.data.password}" | base64 -d
|
Поздравляю! Установка завершена