GitLab: Helm-чарт values, зависимости и деплой в Kubernetes с AWS S3

Автор: | 04/02/2023

Продолжаем сетапить GitLab в Kubernetes. Первая часть – GitLab: компоненты, архитектура, инфраструктура и запуск из Helm-чарта в Minikube, теперь давайте готовиться деплоить в AWS Elastic Kubernetes Service.

Что будем делать и где:

  • деплоим в AWS из Helm-чарта, для начала какой-то “test env”
  • Kubernetes – AWS EKS
  • object store – AWS S3
  • PostgreSQL – из operator
  • Redis – пока используем дефолтный, позже переедем на KeyDB, который тоже разворачивается оператором
  • Gitaly – попробуем в кластере, возможно, на отдельной ноде – у нас всего 150-200 пользователей, нагрузки большой не должно быть, скейлинг и тем более Praefik не нужны

GitLab Operator выглядит в целом интересно, но в документации через слово Experimental и Beta, так что пока не трогаем – используем чарт.

Helm chart prerequisites

По чарту: для начала надо пройтись по доступным параметрам чарта, и посмотреть что нам вообще надо будет из внешних ресурсов (лоад-балансеры, корзины S3, PostgreSQL), и что ещё можно настроить через чарт.

Не забываем посмотреть GitLab chart prerequisites, из которого нам пока интересны:

  • PostgreSQL: мы будем использовать оператор, и развернём свой кластер с блекджеком и базами
  • Redis: у нас есть KeyDB-оператор, потом будем через него, пока дефолтный из чарта
  • Networking and DNS: используем AWS ALB Contoller, создадим ACM сертификат, DNS в Route53, записи создаются через External DNS
  • Persistence: интересный нюанс, возможно, надо будет настроить reclaimPolicy to Retain, см. Configuring Cluster Storage
  • Prometheus: тоже поинт на подумать – у нас есть Kube Prometheus Stack со своим Prometheus и Alertmanager, надо будет подумать – отключать ли встроенный в GitLab, или оставлять
  • Outgoing email: пока не будем настраивать, но потом надо будет подумать. В принципе – есть поддержка AWS SES, где-то в документации встречал, так что нормально
  • RBAC: у нас есть, поддерживается, так что оставляем по-умолчанию, т.е. включаем

Учитываем, что чарт включает в себя пачку зависимостей, и полезно пройтись и посмотреть что там ещё деплоится и с какими параметрами.

Структура Helm-чарта и его values.yaml

У GitLab Helm-чарта достаточно сложная структура его values, так как чарт включает в себя набор сабчартов и зависимостей, см. GitLab Helm subcharts.

Что бы представить себе структуру его values – можно посмотреть структуру каталога charts с дочерними чартами:

[simterm]

$ tree -d gitlab/charts/
gitlab/charts/
|-- certmanager-issuer
|   `-- templates
|-- gitlab
|   |-- charts
|   |   |-- geo-logcursor
|   |   |   `-- templates
|   |   |-- gitaly
|   |   |   `-- templates
|   |   |-- gitlab-exporter
|   |   |   `-- templates
|   |   |-- gitlab-grafana
|   |   |   `-- templates
|   |   |-- gitlab-pages
|   |   |   `-- templates
|   |   |-- gitlab-shell
|   |   |   `-- templates
|   |   |-- kas
|   |   |   `-- templates
|   |   |-- mailroom
|   |   |   `-- templates
|   |   |-- migrations
|   |   |   `-- templates
|   |   |-- praefect
|   |   |   `-- templates
|   |   |-- sidekiq
|   |   |   `-- templates
|   |   |-- spamcheck
|   |   |   `-- templates
|   |   |-- toolbox
|   |   |   `-- templates
|   |   `-- webservice
|   |       `-- templates
|   |           `-- tests
|   `-- templates
|-- minio
|   `-- templates
|-- nginx-ingress
|   `-- templates
|       `-- admission-webhooks
|           `-- job-patch
`-- registry
    `-- templates

[/simterm]

Кроме того, есть набор внешних зависимостей, см. файл requirements.yaml.

Соотвественно, в values основного чарта параметры разбиты на globals (см. Subcharts and Global Values), которые используются дочерними чартами, и параметры для конкретных чартов. См. Configure charts using globals, т.е. наш values.yaml будет выглядеть так:

global: 

  hosts:
    domain:
    hostSuffix: # используется в ./charts/gitlab/charts/webservice, ./charts/gitlab/charts/toolbox/, ./charts/registry/, etc
  ...

gitlab:
  kas:
    enabled: # используется ./charts/gitlab/charts/kas/
...

nginx-ingress:
  enabled: # используется для ./gitlab/charts/nginx-ingress
...

postgresql:
  install: # используется при установке внешнего чарта postgresql

Теперь посмотрим на сами values, и какие из них нам могут быть полезны.

Helm chart values

“Стартовая точка” для работы с чартом – Installing GitLab by using Helm.

Ещё хорошо пройтись по дефолтному values.yaml – там в комментариях есть ссылки на сервисы/параметры.

Собственно, параметры, которые нам могут быть интересны, см. все в Configure charts using globals и GitLab Helm chart deployment options:

  • global.hosts:
    • domain: указываем корневой домен, в котором будут создавать записи для GitLab
    • externalIP: он вроде обязательный, но так как у нас будет AWS ALB, то не используем
    • hostSuffix: test – добавим к создаваемым субдоменам, получим домен вида gitlab-test.example.com
    • httpstrue, будет ALB с AWS Certificate Manager
    • ssh:  тут надо будет задать отдельный субдомен для доступа к GitLab Shell
  • global.ingress: так как у нас AWS ALB, то см. пример в alb-full.yaml
    • annotations.*annotation-key*: добавим аннотации для ALB
    • configureCertmanager: надо будет отключить, т.к. SSL терминейтим на лоад-балансере
    • tls.secretName: в нашем случае не нужен, т.к. SSL терминейтим на лоад-балансере
    • path: для ALB нужен будет /*
    • provider: для ALB == aws
  • global.gitlabVersion: думал тут задать версию Gitlab, которую будем деплоить, но оказалось что нет – правильнее через версию чарта, см. GitLab Version
  • global.psql: параметры для подключения к PostgreSQL сервисами GitLab, см. Configure PostgreSQL settings
    • host: адрес PosgtreSQL Service
    • database, username: понятно
    • password:
      • useSecret: пароль будем хранить в Secret
      • secret: имя секрета
      • key: ключ/поле в Секрете, по которому получаем пароль
  • global.redis: вообще будет внешний, KeyDB, и тут надо будет указать параметры доступа к нему, но для начала оставим дефолтный, см. Configure Redis settings,
  • global.grafana.enabled: включаем – посмотрим, какие там есть дашборды (нету, надо будет настраивать отдельно, см. Grafana JSON Dashboards)
  • global.registry: см. Using the Container Registry
    • bucket: надо будет создать корзину, этот параметр используется в /charts/gitlab/charts/toolbox/ для бекапов, сам GitLab Registry настраивается через отдельные параметры, рассмотрим ниже
  • global.gitaly: пока используем из чарта, несмотря на рекомендации – будем деплоить в виде Kubernetes Pod в кластер, оставляем по-умолчанию, но имеем ввиду
  • global.minio: отключим – используем сразу AWS S3
  • global.appConfig: вот тут прям много всего, см. Configure appConfig settings – содержит общие параметры для чартов WebserviceSidekiq и Gitaly
    • cdnHost: вообще-то хорошая бы вроде вещь, но посмотрим, как его использовать, пока не трогаем
    • contentSecurityPolicy: тоже полезная штука, но настроим потом, см. Content Security Policy
    • enableUsagePing: “телеметрия” для самого GitLab Inc, не вижу смысла, отключим
    • enableSeatLink: не понял, что это, ссылка seat link support ведёт в “никуда” – на странице инфы про seat не нашёл, но видимо это что-то связанное с кол-вом юзеров в лицензии, а так как мы её не покупаем – то можно отключить
    • object_store: общие параметры для работы с корзинами типа ключей доступа и proxy, см. Consolidated object storage:
      • connection: тут надо будет создать секрет, в котором описываются настройки подключения к корзинам, в том числе ACCESS/SECRET ключи, но мы вместо ключей используем ServiceAccount и IAM role
    • Specify buckets: какие корзины нужны, есть набор дефолтных имён типа gitlab-artifacts, но для dev- и test- имеет смысл переопределить
      • storage_options: шифрование для корзин, имеет смысл менять если используется AWS KMS, но мы скорее всго оставим дефолтное шифрование
    • LFS, Artifacts, Uploads, Packages, External MR diffs, and Dependency Proxy – настройки корзин для различных сервисов, пока не будем трогать, посмотрим по ходу дела
    • gitlab_kas: настройки для GitLab Agent for Kubernetes, пока оставим по-умолчанию, не уверен, что он нам понадобится
    • omniauth: настройки для SSO, как-нибудь потом, вообще будем подключать аутентификацию через Google, см. OmniAuth
  • global.serviceAccounts: вместо ACCESS/SECRET будем использовать Kubernetes ServiceAccounts с IAM Role для подов:
    • create: включаем создание SA для сервисов
    • annotations: а тут укажем ARN IAM-роли
  • global.nodeSelector: полезно, потом будем на выделенных нодах
  • global.affinity && global.antiAffinity: плюс можно настроить Affinity
  • global.common.labels: зададим тут всякие environment, team, etc
  • global.tracing: пока не трогаем, но имеем ввиду на будущее – потом допилим какой-то Jaeger/Grafana Tempo
  • global.priorityClassName: возможно полезно или даже нужно, потом потыкаем, см. Pod Priority and Preemption

Из глобальных переменных это вроде всё, кроме них нам надо будет:

  • отключить certmanager – используем AWS Certificate Manager на AWS LoadBalncer
  • отключить postgresql – используем внешний
  • отключить gitlab-runner – у нас уже есть запущенные раннеры, потом попробуем прикрутить их к этому инстансу GitLab
  • отключить nginx-ingress – у нас AWS ALB Controller и AWS ALB балансеры
  • настроить Services для webservice и gitlab-shell
  • настроить registry

Теперь, перед деплоем чарта, надо подготовить внешние сервисы:

  • кластер и база PostgreSQL
  • корзины AWS S3
  • сертификат в AWS Certificate Manager для лоад-балансера

Подготовка – создание внешних ресурсов

PostgreSQL

У нас используется PostgreSQL Operator – описываем ему создание кластера.

Параметры по коннектам, памяти, диску и реквестам-лимитам пока дефолтные – посмотрим по ходу дела, как сильно будет нагружаться база.

Описываем создание базы gitlabhq_test и дефолтных юзеров – defaultUsers=true:

kind: postgresql
apiVersion: acid.zalan.do/v1
metadata:
  name: gitlab-cluster-test-psql
  namespace: gitlab-cluster-test
  labels:
    team: devops
    environment: test
spec:
  teamId: devops
  postgresql:
    version: "14"
    parameters:
      max_connections: '100'
      shared_buffers: 256MB
      work_mem: 32MB
  numberOfInstances: 3
  preparedDatabases:
    gitlabhq_test:
      defaultUsers: true
      schemas:
        public:
          defaultRoles: false
  enableMasterLoadBalancer: false
  enableReplicaLoadBalancer: false
  enableConnectionPooler: false
  enableReplicaConnectionPooler: false
  volume:
    size: 10Gi
    storageClass: encrypted
  resources:
    requests:
      cpu: "100m"
      memory: 100Mi
    limits:
      memory: 1024Mi
  enableLogicalBackup: true
  logicalBackupSchedule: "0 1 * * *"
  sidecars:
    - name: exporter
      image: quay.io/prometheuscommunity/postgres-exporter:v0.11.1
      ports:
        - name: exporter
          containerPort: 9187
          protocol: TCP
      resources:
        limits:
          memory: 50M
        requests:
          cpu: 50m
          memory: 50M
      env:
      - name: DATA_SOURCE_URI
        value: localhost/postgres?sslmode=disable
      - name: DATA_SOURCE_USER
        value: "$(POSTGRES_USER)"
      - name: DATA_SOURCE_PASS
        value: "$(POSTGRES_PASSWORD)"
      - name: PG_EXPORTER_AUTO_DISCOVER_DATABASES
        value: "true"

Создаём неймспейс:

[simterm]

$ kk create ns gitlab-cluster-test

[/simterm]

Деплоим кластер:

[simterm]

$ kk apply -f postgresql.yaml 
postgresql.acid.zalan.do/gitlab-cluster-test-psql created

[/simterm]

Проверяем поды:

[simterm]

$ kk -n gitlab-cluster-test get pod
NAME                                READY   STATUS              RESTARTS   AGE
devops-gitlab-cluster-test-psql-0   2/2     Running             0          24s
devops-gitlab-cluster-test-psql-1   0/2     ContainerCreating   0          4s

[/simterm]

Окей, создаются.

Проверяем Секреты – потом используем секрет пользователя postgres в конфигах GitLab:

[simterm]

$ kk -n gitlab-cluster-test get secret
NAME                                                                                             TYPE                                  DATA   AGE
default-token-2z2ct                                                                              kubernetes.io/service-account-token   3      4m12s
gitlabhq-test-owner-user.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do    Opaque                                2      65s
gitlabhq-test-reader-user.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do   Opaque                                2      65s
gitlabhq-test-writer-user.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do   Opaque                                2      66s
postgres-pod-token-p7b4g                                                                         kubernetes.io/service-account-token   3      66s
postgres.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do                    Opaque                                2      66s
standby.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do                     Opaque                                2      66s

[/simterm]

Вроде ОК.

AWS S3

Теперь создадим корзины.

Нам будут нужны:

К именам корзин добавляем or как свой “идентификатор”, ибо имена корзин общие, что бы не было ошибки BucketAlreadyExists, и имя окружения – test, т.е. список получается такой:

  • or-gitlab-registry-test
  • or-gitlab-artifacts-test
  • or-git-lfs-test
  • or-gitlab-packages-test
  • or-gitlab-uploads-test
  • or-gitlab-mr-diffs-test
  • or-gitlab-terraform-state-test
  • or-gitlab-ci-secure-files-test
  • or-gitlab-dependency-proxy-test
  • or-gitlab-pages-test
  • or-gitlab-backups-test
  • or-gitlab-tmp-test

Не факт, что понадобятся все, посмотрим по ходу настройки GitLab и его фич, но пока создадим.

Создаём с AWS CLI:

[simterm]

$ aws --profile internal s3 mb s3://or-gitlab-registry-test
make_bucket: or-gitlab-registry-test

[/simterm]

Повторяем для всех остальных.

AWS Certificate Manager

Для Ingress нам нужен TLS-сертификат, см. Requesting a public certificate, а для получения сертификата – очевидно, что домен.

В нашем случае используем internal.example.com, для которого создадим wildcard-сертификат в ACM:

В FQDN указываем *.internal.example.com, что бы включить все субдомены. Затем для GitLab используем параметры global.hosts.domain: iinternal.example.com и hostSuffix: test, что в результате создаст несколько Ingress и Services, которые через ExternalDNS создадут необходимые записи в Route53.

В Validation Method выбираем DNS – самый простой, тем более что доменная зона хостится в Route53 – всё создаётся в пару кликов:

Переходим к сертификату – он сейчас в Pending validation, кликаем Create records in Route53:

Теперь статус Issued, запоминаем его ARN – он нам понадобится в values:

AWS IAM Policy и IAM Role для ServiceAccount

Для ServiceAccount, который должен будет давать доступ к AWS к корзинам, надо создать IAM Policy и IAM Role. Детально см. Kubernetes ServiceAccounts с IAM Role для подов, тут быстренько.

Создаём полиси:

{
    "Statement": [
        {
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:ListMultipartUploadParts",
                "s3:AbortMultipartUpload"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::or-gitlab-registry-test/*",
                "arn:aws:s3:::or-gitlab-artifacts-test/*",
                "arn:aws:s3:::or-git-lfs-test/*",
                "arn:aws:s3:::or-gitlab-packages-test/*",
                "arn:aws:s3:::or-gitlab-uploads-test/*",
                "arn:aws:s3:::or-gitlab-mr-diffs-test/*",
                "arn:aws:s3:::or-gitlab-terraform-state-test/*",
                "arn:aws:s3:::or-gitlab-ci-secure-files-test/*",
                "arn:aws:s3:::or-gitlab-dependency-proxy-test/*",
                "arn:aws:s3:::or-gitlab-backups-test/*",
                "arn:aws:s3:::or-gitlab-tmp-test/*"
            ]
        },
        {
            "Action": [
                "s3:ListBucket",
                "s3:ListAllMyBuckets",
                "s3:GetBucketLocation",
                "s3:ListBucketMultipartUploads"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::or-gitlab-registry-test",
                "arn:aws:s3:::or-gitlab-artifacts-test",
                "arn:aws:s3:::or-git-lfs-test",
                "arn:aws:s3:::or-gitlab-packages-test",
                "arn:aws:s3:::or-gitlab-uploads-test",
                "arn:aws:s3:::or-gitlab-mr-diffs-test",
                "arn:aws:s3:::or-gitlab-terraform-state-test",
                "arn:aws:s3:::or-gitlab-ci-secure-files-test",
                "arn:aws:s3:::or-gitlab-dependency-proxy-test",
                "arn:aws:s3:::or-gitlab-backups-test",
                "arn:aws:s3:::or-gitlab-tmp-test"
            ]
        }
    ],
    "Version": "2012-10-17"
}

Создаём IAM Role – выбираем Web identity:

Подключаем созданную выше Policy:

Вроде всё? Можно писать values.

Деплой Gitlab Helm

Создание values.yaml

Все дефолтные values тут>>>,  можно использовать как пример, но не стоит полностью копировать – в свой values пишем только то, что у нас отличается от дефолтного.

global

hosts

Configure Host settings

Указываем домен, используя который чарт пропишет значения в Ingress и Services, которые будет создавать – получим набор доменов вида gitlab.internal.example.com, registry.internal.example.com и т.д.

Для SSH в примере для Ingress указан отдельный субдомен, так как под SSH будет создавать отдельный Service с Network Load Balancer для доступа к 22 TCP.

hostSuffix добавит суффикс к создаваемым записям, т.е. в результате будут субдомены вида gitlab-test.internal.example.com и registry-test.internal.example.com.

Но к SSH hostSuffix не применится, поэтому указываем сразу с суффиксом.

Получается так:

global:
  hosts:
    domain: internal.example.com
    hostSuffix: test
    ssh: gitlab-shell-test.internal.example.com

ingress

Configure Ingress settings

Берём для примера alb-full.yaml, от себя добавим только load_balancing.algorithm.type=least_outstanding_requests:

ingress:
  # Common annotations used by kas, registry, and webservice
  annotations:
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:eu-central-1:514***799:certificate/7227a8fa-1124-441c-81d7-ec168180190d
    alb.ingress.kubernetes.io/group.name: gitlab
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-group-attributes: load_balancing.algorithm.type=least_outstanding_request
    alb.ingress.kubernetes.io/target-type: ip
    kubernetes.io/ingress.class: alb
    nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
  class: none
  configureCertmanager: false
  enabled: true
  path: /*
  pathType: ImplementationSpecific
  provider: aws
  tls:
    enabled: false

Если использовать KAS, то ему нужен будет отдельный LoadBalancer, настроим его чуть позже в блоке gitlab.kas.

psql

Configure PostgreSQL settings

Находим имя Service, который был создан во время деплоя PostgreSQL кластера:

[simterm]

$ kk -n gitlab-cluster-test get svc
NAME                                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
devops-gitlab-cluster-test-psql          ClusterIP   172.20.67.135    <none>        5432/TCP   63m
devops-gitlab-cluster-test-psql-config   ClusterIP   None             <none>        <none>     63m
devops-gitlab-cluster-test-psql-repl     ClusterIP   172.20.165.249   <none>        5432/TCP   63m

[/simterm]

Пользователя пока возьмём дефолтного postgress, из готового секрета:

[simterm]

$ kk -n gitlab-cluster-test get secret postgres.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do        
NAME                                                                            TYPE     DATA   AGE
postgres.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do   Opaque   2      67m

[/simterm]

Для production таки надо будет делать отдельного, ибо postgress == root.

Готовим конфиг:

psql:
  host: devops-gitlab-cluster-test-psql
  database: gitlabhq_test
  username: postgres
  password:
    useSecret: true    
    secret: postgres.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do
    key: password

redis

Configure Redis settings

Configure Redis chart-specific settings

Configure the GitLab chart with an external Redis

Пока оставим всё по-умолчанию – Redis используется только для кеширования, посмотрим, как он будет работать и что по ресурсам.

registry

Configure Registry settings

В globals для registry указываем только корзину:

registry:
  bucket: or-gitlab-registry-test

gitaly

Пока оставим, как есть. Потом подумаем, может вынесем на отдельную ноду.

Using the GitLab-Gitaly chart

Надо будет помониторить как он ресурсы использует, но в случае с нашими 150-200 пользователей вряд ли там будет необходимость сильно его допиливать или тем более разворачивать кластер Praefect.

minio

Отключаем, будем ходить сразу в AWS S3:

minio:
  enabled: false

grafana

Включим, посмотрим, что в ней есть (ничего 🙂 ):

grafana:
  enabled: true

appConfig

Список корзин есть в values.

object_store таки надо настраивать – добавить секрет, в котором будут указаны провайдер и регион корзин. Ключи не используем – будет ServiceAccount.

Пример возьмём из rails.s3.yaml, см. connection:

provider: AWS
region: eu-central-1

Создаём Secret:

[simterm]

$ kk -n gitlab-cluster-test create secret generic gitlab-rails-storage --from-file=connection=rails.s3.yaml

[/simterm]

И описываем appConfig, получается так:

appConfig:
  enableUsagePing: false
  enableSeatLink: true # disable?
  object_store:
    enabled: true
    proxy_download: true
    connection:  
      secret: gitlab-rails-storage
      key: connection
  artifacts:
    bucket: or-gitlab-artifacts-test
  lfs:
    bucket: or-git-lfs-test
  packages:
    bucket: or-gitlab-packages-test
  uploads:
    bucket: or-gitlab-uploads-test
  externalDiffs:
    bucket: or-gitlab-mr-diffs-test
  terraformState:
    bucket: or-gitlab-terraform-state-test
  ciSecureFiles:
    bucket: or-gitlab-ci-secure-files-test
  dependencyProxy:
    bucket: or-gitlab-dependency-proxy-test
  backups:
    bucket: or-gitlab-backups-test
    tmpBucket: or-gitlab-tmp-test

serviceAccount

См. Kubernetes: ServiceAccount з AWS IAM Role для Kubernetes Pod

Вроде можно без создания ServiceAccount, просто через аннотации к сервисам – IAM roles for AWS when using the GitLab chart.

Но мы будем использовать ServiceAccount, роль с политикой уже делали – добавляем:

serviceAccount:
  enabled: true
  create: true
  annotations: 
    eks.amazonaws.com/role-arn: arn:aws:iam::514***799:role/S3GitlabClusterTest

Так, из globals вроде всё.

registry

Defining the Registry Configuration

Тут надо настроить storage, который хранится в Secret, и в котором тоже необходимо указать имя корзины: параметр в globals.registry будет использоваться для бекапов, а параметр тут – самим сервисом Registry, см. Docker Registry images.

Для примера возьмём файл registry.s3.yaml, но без ключей, так как для Registry будет создан свой ServiceAccoumt с IAM Role:

s3:
  bucket: or-gitlab-registry-test
  region: eu-central-1
  v4auth: true

Создаём секрет:

[simterm]

$ kk -n gitlab-cluster-test create secret generic registry-storage --from-file=config=registry.s3.yaml

[/simterm]

Описываем конфиг:

registry:
  enabled: true
  service:
    type: NodePort
  storage:
    secret: registry-storage
    key: config

gitlab – Services

Отдельно описываем Services для kas, webservice и gitlab-shell, из того же примера alb-full.yaml.

Для gitlab-shell Service в аннотации external-dns.alpha.kubernetes.io/hostname указываем имя хоста:

gitlab:
  kas:
    enabled: true
    ingress:
      # Specific annotations needed for kas service to support websockets
      annotations:
        alb.ingress.kubernetes.io/healthcheck-path: /liveness
        alb.ingress.kubernetes.io/healthcheck-port: "8151"
        alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
        alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=4000,routing.http2.enabled=false
        alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=86400
        alb.ingress.kubernetes.io/target-type: ip
        kubernetes.io/tls-acme: "true"
        nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
        nginx.ingress.kubernetes.io/x-forwarded-prefix: "/path"
    # k8s services exposed via an ingress rule to an ELB need to be of type NodePort
    service:
      type: NodePort
  webservice:
    enabled: true
    service:
      type: NodePort
  # gitlab-shell (ssh) needs an NLB
  gitlab-shell:
    enabled: true
    service:
      annotations:
        external-dns.alpha.kubernetes.io/hostname: "gitlab-shell-test.internal.example.com"
        service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
        service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
        service.beta.kubernetes.io/aws-load-balancer-type: "external"
      type: LoadBalancer

Прочее

Отключаем ненужные нам сервисы:

certmanager:
  install: false

postgresql:
  install: false

gitlab-runner:
  install: false

nginx-ingress:
  enabled: true

Полный values.yaml

В результате получаем такой конфиг:

global:

  hosts:
    domain: internal.example.com
    hostSuffix: test
    ssh: gitlab-shell-test.internal.example.com

  ingress:
    # Common annotations used by kas, registry, and webservice
    annotations:
      alb.ingress.kubernetes.io/backend-protocol: HTTP
      alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:eu-central-1:514***799:certificate/7227a8fa-1124-441c-81d7-ec168180190d
      alb.ingress.kubernetes.io/group.name: gitlab
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-group-attributes: load_balancing.algorithm.type=least_outstanding_requests
      alb.ingress.kubernetes.io/target-type: ip
      kubernetes.io/ingress.class: alb
      nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
    class: none
    configureCertmanager: false
    enabled: true
    path: /*
    pathType: ImplementationSpecific
    provider: aws
    tls:
      enabled: false

  psql:
    host: devops-gitlab-cluster-test-psql
    database: gitlabhq_test
    username: postgres
    password:
      useSecret: true
      secret: postgres.devops-gitlab-cluster-test-psql.credentials.postgresql.acid.zalan.do
      key: password

  registry:
    bucket: or-gitlab-registry-test

  minio:
    enabled: false

  grafana:
    enabled: true

  appConfig:
    enableUsagePing: false
    enableSeatLink: true # disable?
    object_store:
      enabled: true
      proxy_download: true
      connection:
        secret: gitlab-rails-storage
        key: connection
    artifacts:
      bucket: or-gitlab-artifacts-test
    lfs:
      bucket: or-git-lfs-test
    packages:
      bucket: or-gitlab-packages-test
    uploads:
      bucket: or-gitlab-uploads-test
    externalDiffs:
      bucket: or-gitlab-mr-diffs-test
    terraformState:
      bucket: or-gitlab-terraform-state-test
    ciSecureFiles:
      bucket: or-gitlab-ci-secure-files-test
    dependencyProxy:
      bucket: or-gitlab-dependency-proxy-test
    backups:
      bucket: or-gitlab-backups-test
      tmpBucket: or-gitlab-tmp-test

  serviceAccount:
    enabled: true
    create: true
    annotations:
      eks.amazonaws.com/role-arn: arn:aws:iam::514***799:role/S3GitlabClusterTest

  common:
    labels:
      environment: test

gitlab:
  kas:
    enabled: true
    ingress:
      # Specific annotations needed for kas service to support websockets
      annotations:
        alb.ingress.kubernetes.io/healthcheck-path: /liveness
        alb.ingress.kubernetes.io/healthcheck-port: "8151"
        alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
        alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=4000,routing.http2.enabled=false
        alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=86400
        alb.ingress.kubernetes.io/target-type: ip
        kubernetes.io/tls-acme: "true"
        nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
        nginx.ingress.kubernetes.io/x-forwarded-prefix: "/path"
    # k8s services exposed via an ingress rule to an ELB need to be of type NodePort
    service:
      type: NodePort
  webservice:
    enabled: true
    service:
      type: NodePort
  # gitlab-shell (ssh) needs an NLB
  gitlab-shell:
    enabled: true
    service:
      annotations:
        external-dns.alpha.kubernetes.io/hostname: "gitlab-shell-test.internal.example.com"
        service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
        service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
        service.beta.kubernetes.io/aws-load-balancer-type: "external"
      type: LoadBalancer

registry:
  enabled: true
  service:
    type: NodePort
  storage:
    secret: registry-storage
    key: config

certmanager:
  install: false

postgresql:
  install: false

gitlab-runner:
  install: false

nginx-ingress:
  enabled: false

Gitlab deploy

Ну и вроде всё? Давайте деплоить:

[simterm]

$ helm repo add gitlab https://charts.gitlab.io/
$ helm repo update
$ helm upgrade --install --namespace gitlab-cluster-test gitlab gitlab/gitlab --timeout 600s -f gitlab-cluster-test-values.yaml

[/simterm]

Проверяем Ingresses:

[simterm]

$ kk -n gitlab-cluster-test get ingress
NAME                        CLASS    HOSTS                                ADDRESS                                                           PORTS   AGE
gitlab-grafana-app          <none>   gitlab-test.internal.example.com     k8s-***.eu-central-1.elb.amazonaws.com   80      117s
gitlab-kas                  <none>   kas-test.internal.example.com        k8s-***.eu-central-1.elb.amazonaws.com   80      117s
gitlab-registry             <none>   registry-test.internal.example.com   k8s-***.eu-central-1.elb.amazonaws.com   80      117s
gitlab-webservice-default   <none>   gitlab-test.internal.example.com     k8s-***.eu-central-1.elb.amazonaws.com   80      117s

[/simterm]

Открываем URL https://gitlab-test.internal.example.com:

Получаем рутовый пароль:

[simterm]

$ kubectl -n gitlab-cluster-test get secret gitlab-gitlab-initial-root-password -ojsonpath='{.data.password}' | base64 --decode ; echo
cV6***y1t

[/simterm]

Логинимся под root:

Проверка деплоя

Не верится мне, что с первого раза всё завелось…

Repository

Проверяем работу с репозиторием – работу сервисов Gitaly и GitLab Shell.

Создаём тестовый репозиторий:

Копируем адрес – с субдоменом gitlab-shell-test.internal.example.com, который указывали в конфигах:

Клонируем, подверждаем добавления сертификата в /home/setevoy/.ssh/known_hosts:

[simterm]

$ git clone [email protected]:gitlab-instance-da4355a9/test-repo.git
Cloning into 'test-repo'...
The authenticity of host 'gitlab-shell-test.internal.example.com (3.***.***.79)' can't be established.
ED25519 key fingerprint is SHA256:xhC1Q/lduNbg49kGljYUb21YlBBsxrG89xE+iCHD+xc.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'gitlab-shell-test.internal.example.com' (ED25519) to the list of known hosts.
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

[/simterm]

И содержимое:

[simterm]

$ ll test-repo/
total 8
-rw-r--r-- 1 setevoy setevoy 6274 Feb  4 13:24 README.md

[/simterm]

Попробуем пушнуть какие-то изменения обратно:

[simterm]

$ cd test-repo/
$ echo test > test.txt
$ git add test.txt
$ git commit -m "test"
$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 285 bytes | 285.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To gitlab-shell-test.internal.example.com:gitlab-instance-da4355a9/test-repo.git
   7217947..4eb84db  main -> main

[/simterm]

И в WebUI:

Окей, работает.

Container Registry

И проверим Registry – примеры команд есть в веб-интерфейсе > Container Registry:

Логинимся с тем же логином:паролем, которые использовали для логина в веб-интерфейс GitLab (пользователь root, пароль из секрета gitlab-gitlab-initial-root-password):

[simterm]

$ docker login registry-test.internal.example.com
Username: root
Password:
...
Login Succeeded

[/simterm]

Создаём Dockerfile:

FROM busybox
RUN echo "hello world"

Собираем образ, тегаем с именем нашего Registry:

[simterm]

$ docker build -t registry-test.internal.example.com/gitlab-instance-da4355a9/test-repo:1 .

[/simterm]

И пушим:

[simterm]

$ docker push registry-test.internal.example.com/gitlab-instance-da4355a9/test-repo:1  
The push refers to repository [registry-test.internal.example.com/gitlab-instance-da4355a9/test-repo]
b64792c17e4a: Mounted from gitlab-instance-da4355a9/test 
1: digest: sha256:eb45a54c2c0e3edbd6732b454c8f8691ad412b56dd10d777142ca4624e223c69 size: 528

[/simterm]

Проверяем корзину or-gitlab-registry-test:

[simterm]

$ aws --profile internal s3 ls or-gitlab-registry-test/docker/registry/v2/repositories/gitlab-instance-da4355a9/test-repo/_manifests/tags/1/
                           PRE current/
                           PRE index/

[/simterm]

Окей – Registry работает, доступ к S3 работает, с ServiceAccounts проблем вроде нет.

ServiceAccounts и S3 access

Но на всякий случай проверим в других сервисах, например из пода Toolbox:

[simterm]

$ kk -n gitlab-cluster-test exec -ti gitlab-toolbox-565889b874-vgqcb -- bash
git@gitlab-toolbox-565889b874-vgqcb:/$ aws s3 ls or-gitlab-registry-test
                           PRE docker/

[/simterm]

Окей – тут тоже всё работает.

Grafana

Проверяем Grafana – https://gitlab-test.internal.example.com/-/grafana/.

Логин root, пароль берём из секрета:

[simterm]

$ kubectl -n gitlab-cluster-test get secret gitlab-grafana-initial-password -ojsonpath='{.data.password}' | base64 --decode ; echo
Cqc***wFS

[/simterm]

Работает… Дашборды надо подключать отдельно, потом займёмся, как дойдём до мониторинга вообще, пока см. Grafana JSON Dashboards.

В общем, вроде всё.

Посмотрим, что дальше – администрирование, миграция проектов, мониторинг.