Миграция данных из GitLab 14.0.12 в 17.8.z
Решаемая проблема
Версии 3.16 и 3.18 поддерживают версии GitLab, которые отстают от официальных версий. Обновление GitLab до последней официальной версии требует более 10 обновлений подряд. После обновления до версии 4.0 продукт не предоставляет автоматического обновления до 17.8.z. В этом решении описывается, как выполнить миграцию данных с версии 14.0.12 на 17.8.z.
Учитывая большой разрыв между версиями, для обновления используется подход миграции данных.
Терминология
Обзор процесса
Путь миграции данных
Согласно официальной документации по пути обновления, путь миграции данных следующий:
- 14.0.12 -> 14.3.6
- 14.3.6 -> 14.9.5
- 14.9.5 -> 14.10.5
- 14.10.5 -> 15.0.5
- 15.0.5 -> 15.4.6
- 15.4.6 -> 15.11.13
- 15.11.13 -> 16.3.8
- 16.3.8 -> 16.7.9
- 16.7.9 -> 16.11.10
- 16.11.10 -> 17.3.6
- 17.3.6 → 17.5.5
- 17.5.5 → 17.8.1
В конце необходимо сделать резервную копию данных из версии 17.8.1 и импортировать её в экземпляр platform-deployed версии 17.8.z для завершения миграции данных.
Процесс миграции данных
Сделайте резервную копию platform-deployed GitLab и восстановите её в GitLab, развернутом с использованием образа all-in-one, затем обновите до 17.8.1 с помощью образа all-in-one, и наконец восстановите резервную копию данных в экземпляр platform-deployed версии 17.8.z.
- Сделайте резервную копию экземпляра
platform-deployed GitLab 14.0.12.
- Восстановите резервную копию в развертывание
all-in-one GitLab 14.0.12.
- Выполните поэтапное обновление развертывания
all-in-one до GitLab 17.8.1.
- Сделайте резервную копию обновленного экземпляра
all-in-one GitLab 17.8.1.
- Восстановите резервную копию из
all-in-one 17.8.1 в экземпляр platform-deployed GitLab 17.8.z.
TIP
Для последних версий GitLab и оператора смотрите Release Note.
Длительность миграции
Процесс миграции включает операции резервного копирования и восстановления базы данных и репозиториев:
- Чем больше база данных, тем дольше миграция.
- Большое количество репозиториев или крупные отдельные репозитории значительно увеличивают время.
- Производительность хранилища также влияет на эффективность — рекомендуется использовать topolvm для лучшей производительности.
Тестовая конфигурация:
- Один большой репозиторий (~600 МБ), остальные — небольшие начальные репозитории
- Экземпляр GitLab: 6700 задач, 3600 merge requests
Предварительные требования
-
Установите kubectl (>=1.30.0) и yq (>=4.45.0) на хосте выполнения.
kubectl version --client
yq --version
# Вывод:
# Client Version: v1.30.0
# Kustomize Version: v5.4.2
# yq (https://github.com/mikefarah/yq/) version v4.45.4
-
Настройте переменные окружения:
# Замените на имя старого CR GitLab
export GITLAB_NAME=<old gitlab name>
export GITLAB_NAMESPACE=<gitlab deploy namespace>
-
Подготовьте PVC, необходимые для обновления. Они должны быть созданы в том же namespace, что и старый экземпляр GitLab:
TIP
Простой способ оценить необходимый размер хранилища для исходного экземпляра — сложить размеры PVC, используемых базой данных и компонентами Gitaly.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backup-pvc
namespace: ${GITLAB_NAMESPACE}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 40Gi
storageClassName: ceph
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: upgrade-pvc
namespace: ${GITLAB_NAMESPACE}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
storageClassName: ceph
volumeMode: Filesystem
- backup-pvc: Используется для хранения архивов резервных копий. Рекомендуемый объем — в два раза больше размера исходного экземпляра.
- Замените на имя storage class в вашем кластере.
- upgrade-pvc: Используется для хранения данных GitLab во время поэтапного обновления. Рекомендуется выделить объем, равный сумме общего размера исходного экземпляра и трёхкратного размера базы данных.
- Замените на имя storage class в вашем кластере.
-
Подготовьте образы, необходимые для обновления. Скачайте каждый образ из приложений и загрузите их в репозиторий образов, к которому имеет доступ кластер с развертыванием GitLab. Ссылки для скачивания образов:
# amd образы
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-0-12-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-3-6-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-9-5-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-10-5-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-15-0-5-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-15-4-6-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-15-11-13-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-16-3-8-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-16-7-9-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-16-11-10-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-17-3-6-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-17-5-5-amd.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-17-8-1-amd.tgz
# arm образы
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-0-12-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-3-6-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-9-5-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-14-10-5-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-15-0-5-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-15-4-6-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-15-11-13-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-16-3-8-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-16-7-9-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-16-11-10-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-17-3-6-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-17-5-5-arm.tgz
https://cloud.alauda.cn/attachments/knowledge/242090509/gitlab-ce-17-8-1-arm.tgz
# amd образы
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-0-12-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-3-6-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-9-5-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-10-5-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-15-0-5-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-15-4-6-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-15-11-13-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-16-3-8-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-16-7-9-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-16-11-10-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-17-3-6-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-17-5-5-amd.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-17-8-1-amd.tgz
# arm образы
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-0-12-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-3-6-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-9-5-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-14-10-5-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-15-0-5-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-15-4-6-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-15-11-13-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-16-3-8-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-16-7-9-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-16-11-10-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-17-3-6-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-17-5-5-arm.tgz
https://cloud.alauda.io/attachments/knowledge/242090509/gitlab-ce-17-8-1-arm.tgz
-
Подготовьте скрипты, необходимые для обновления.
Выполните следующую команду для генерации четырёх скриптов:
check_gitlab.sh: Проверка готовности pod GitLab.
finalize_migrations.sql: Завершение миграций.
monitor_gitlab.sh: Мониторинг pod GitLab для проверки завершения миграции данных.
rolling_upgrade.sh: Поэтапное обновление экземпляров GitLab один за другим.
cat << 'EOF' > check_gitlab.sh
#!/bin/bash
gitlab_pod=$1
port=${2:-30855}
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log "Starting monitoring script for GitLab pod: ${gitlab_pod} on port: ${port}"
while true; do
curl_result=$(kubectl -n $GITLAB_NAMESPACE exec ${gitlab_pod} -- curl -s localhost:${port} | grep "You are being")
if [[ -z "${curl_result}" ]]; then
log "HTTP not ready. Retrying in 10 seconds..."
sleep 10
continue
fi
log "GitLab is ready"
break
done
EOF
cat << 'EOF' > finalize_migrations.sql
select concat(
'gitlab-rake gitlab:background_migrations:finalize[',
job_class_name, ',',
table_name, ',',
column_name, $$,'$$,
REPLACE(cast(job_arguments as text), ',', $$\,$$),
$$']$$
)
from batched_background_migrations WHERE status NOT IN(3, 6);
EOF
cat << 'EOF' > monitor_gitlab.sh
#!/bin/bash
gitlab_pod=$1
port=${2:-30855}
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log "Starting monitoring script for GitLab pod: ${gitlab_pod} on port: ${port}"
while true; do
curl_result=$(kubectl -n $GITLAB_NAMESPACE exec ${gitlab_pod} -- curl -s localhost:${port} | grep "You are being")
if [[ -z "${curl_result}" ]]; then
log "HTTP check not ready. Retrying in 10 seconds..."
sleep 10
continue
fi
migration_result=$(kubectl -n $GITLAB_NAMESPACE exec ${gitlab_pod} -- gitlab-psql -c "SELECT job_class_name, table_name, column_name, job_arguments FROM batched_background_migrations WHERE status NOT IN(3, 6);" | grep "(0 rows)")
if [[ -z "${migration_result}" ]]; then
log "Database migration is running. Retrying in 10 seconds..."
sleep 10
continue
fi
log "GitLab is ready, all migrations are done"
break
done
EOF
cat << 'EOF' > rolling_upgrade.sh
#!/bin/bash
image=$(kubectl -n $GITLAB_NAMESPACE get deployment gitlab -o jsonpath='{.spec.template.spec.containers[?(@.name=="gitlab")].image} ')
versions=(
14.0.12-ce.0
14.3.6-ce.0
14.9.5-ce.0
14.10.5-ce.0
15.0.5-ce.0
15.4.6-ce.0
15.11.13-ce.0
16.3.8-ce.0
16.7.9-ce.0
16.11.10-ce.0
17.3.6-ce.0
17.5.5-ce.0
17.8.1-ce.0
)
STATE_FILE="upgrade_state.log"
touch "$STATE_FILE"
is_done() {
grep -Fxq "$1" "$STATE_FILE"
}
mark_done() {
echo "$1" >> "$STATE_FILE"
}
for version in "${versions[@]}"; do
if is_done "$version"; then
echo "Version ${version} already processed, skipping..."
continue
fi
echo "Upgrading to ${version}..."
new_image="${image%:*}:${version}"
kubectl -n $GITLAB_NAMESPACE set image deployment/gitlab gitlab=$new_image
echo "Waiting for the GitLab pod to start..."
sleep 10
kubectl -n $GITLAB_NAMESPACE wait deploy gitlab --for condition=available --timeout=3000s
sleep 10
kubectl -n $GITLAB_NAMESPACE wait pod -l deploy=gitlab --for=condition=Ready --timeout=3000s
new_pod_name=$(kubectl -n $GITLAB_NAMESPACE get po -l deploy=gitlab --sort-by=.metadata.creationTimestamp --field-selector=status.phase=Running -o jsonpath='{.items[-1].metadata.name}')
echo "Waiting for GitLab to be ready..."
bash check_gitlab.sh $new_pod_name
echo "Waiting for migrations to finish..."
kubectl -n $GITLAB_NAMESPACE cp finalize_migrations.sql $new_pod_name:/tmp/finalize_migrations.sql
for i in {1..10}; do
echo "Running migration tasks (attempt $i/10)..."
if kubectl -n $GITLAB_NAMESPACE exec -ti $new_pod_name -- bash -c "gitlab-psql -t -A -f /tmp/finalize_migrations.sql > /tmp/run_migration_tasks.sh && xargs -d \"\n\" -P 3 -I {} bash -c \"{}\" < /tmp/run_migration_tasks.sh"; then
echo "Migration tasks completed successfully"
break
fi
sleep 30
done
bash monitor_gitlab.sh $new_pod_name
echo "Upgraded to ${version} successfully"
mark_done "$version"
done
echo "All upgrades completed successfully!"
EOF
├── check_gitlab.sh
├── finalize_migrations.sql
├── monitor_gitlab.sh
└── rolling_upgrade.sh
-
Включите компонент task-runner в GitLab 14 для выполнения команд резервного копирования
# сформировать репозиторий образа и тег для task-runner на основе образа gitaly
image=$(kubectl get pod ${GITLAB_NAME}-gitaly-0 -n $GITLAB_NAMESPACE -o jsonpath='{.spec.containers[0].image}')
tag=${image##*:}
registry=${image%%/*}
echo "registry: ${registry}"
echo "tag: ${tag}"
kubectl patch gitlabofficials.operator.devops.alauda.io "${GITLAB_NAME}" \
-n "$GITLAB_NAMESPACE" \
--type=merge \
-p "$(cat <<EOF
{
"spec": {
"helmValues": {
"gitlab": {
"task-runner": {
"enabled": "true",
"image": {
"repository": "${registry}/devops/gitlab-org/build/cng/gitlab-task-runner-ce",
"tag": "${tag}"
},
"resources": {
"limits": {
"cpu": "2",
"memory": "4G"
}
}
}
}
}
}
}
EOF
)"
# Ожидание доступности развертывания task-runner
echo "Waiting for the task-runner deployment to be available..."
sleep 60
# Проверка существования развертывания ${GITLAB_NAME}-task-runner
kubectl wait deploy ${GITLAB_NAME}-task-runner -n $GITLAB_NAMESPACE --for condition=available --timeout=3000s
kubectl get deploy -n $GITLAB_NAMESPACE ${GITLAB_NAME}-task-runner
kubectl patch gitlabofficials.operator.devops.alauda.io ${GITLAB_NAME} -n $GITLAB_NAMESPACE --type='merge' -p='
{
"spec": {
"helmValues": {
"gitlab": {
"task-runner": {
"enabled": "true",
"resources": { "limits": { "cpu": "2", "memory": "4G" } }
}
}
}
}
}'
# Ожидание доступности развертывания task-runner
echo "Waiting for the task-runner deployment to be available..."
sleep 60
# Проверка существования развертывания ${GITLAB_NAME}-task-runner
kubectl wait deploy ${GITLAB_NAME}-task-runner -n $GITLAB_NAMESPACE --for condition=available --timeout=3000s
kubectl get deploy -n $GITLAB_NAMESPACE ${GITLAB_NAME}-task-runner
# gitlabofficial.operator.devops.alauda.io/xxx patched
# Waiting for the task-runner deployment to be available...
# deployment.apps/xxx-task-runner condition met
# NAME READY UP-TO-DATE AVAILABLE AGE
# high-sc-sso-ingress-https-task-runner 1/1 1 1 178m
-
Переведите GitLab 14 в режим только для чтения
kubectl patch deploy ${GITLAB_NAME}-webservice-default -n $GITLAB_NAMESPACE --type='merge' -p='{"metadata":{"annotations":{"skip-sync":"true"}}}'
kubectl patch deploy ${GITLAB_NAME}-sidekiq-all-in-1-v1 -n $GITLAB_NAMESPACE --type='merge' -p='{"metadata":{"annotations":{"skip-sync":"true"}}}'
kubectl scale deploy ${GITLAB_NAME}-webservice-default -n $GITLAB_NAMESPACE --replicas 0
kubectl scale deploy ${GITLAB_NAME}-sidekiq-all-in-1-v1 -n $GITLAB_NAMESPACE --replicas 0
# deployment.apps/gitlab-old-webservice-default patched
# deployment.apps/gitlab-old-sidekiq-all-in-1-v1 patched
# deployment.apps/gitlab-old-webservice-default scaled
# deployment.apps/gitlab-old-sidekiq-all-in-1-v1 scaled
-
Примените патч к backup PVC для task-runner GitLab 14. PVC должен называться backup-pvc.
kubectl patch deploy ${GITLAB_NAME}-task-runner -n $GITLAB_NAMESPACE --type='json' -p='
[
{
"op": "add",
"path": "/metadata/annotations/skip-sync",
"value": "true"
},
{
"op": "replace",
"path": "/spec/template/spec/volumes/1",
"value": {"name": "task-runner-tmp", "persistentVolumeClaim": {"claimName": "backup-pvc"}}
}
]'
echo "Waiting for the task-runner deployment to be available..."
sleep 60
# Проверка существования развертывания ${GITLAB_NAME}-task-runner
kubectl wait deploy ${GITLAB_NAME}-task-runner -n $GITLAB_NAMESPACE --for condition=available --timeout=3000s
kubectl get deploy -n $GITLAB_NAMESPACE ${GITLAB_NAME}-task-runner
# deployment.apps/xxx-task-runner patched
# Waiting for the task-runner deployment to be available...
# deployment.apps/gitlab-14-task-runner condition met
# NAME READY UP-TO-DATE AVAILABLE AGE
# gitlab-14-task-runner 1/1 1 1 4m6s
-
Сделайте резервную копию данных GitLab 14
pod_name=$(kubectl get po -n $GITLAB_NAMESPACE -l app=task-runner,release=${GITLAB_NAME} -o jsonpath='{.items[0].metadata.name}')
kubectl exec -ti -n "$GITLAB_NAMESPACE" $pod_name -- gitlab-rake gitlab:backup:create SKIP=repositories
# Добавьте права на файл резервной копии.
kubectl exec -ti -n "$GITLAB_NAMESPACE" $pod_name -- bash -c '
chmod 777 /srv/gitlab/tmp/backups/*_gitlab_backup.tar
ls -lh /srv/gitlab/tmp/backups
'
# 2024-11-01 06:16:06 +0000 -- Dumping database ...
# Dumping PostgreSQL database gitlabhq_production ... [DONE]
# 2024-11-01 06:16:18 +0000 -- done
# ....
# done
# Deleting old backups ... skipping
# Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data
# and are not included in this backup. You will need these files to restore a backup.
# Please back them up manually.
# Backup task is done.
# total 300K
# -rwxrwxrwx 1 git git 300K Sep 15 09:15 1757927758_2025_09_15_14.0.12_gitlab_backup.tar
Сделайте резервную копию rails-secret, выполнив следующую команду, которая сохранит rails-secret в файл gitlab14-rails-secret.yaml в текущем каталоге.
kubectl get secrets -n ${GITLAB_NAMESPACE} ${GITLAB_NAME}-rails-secret -o jsonpath="{.data['secrets\.yml']}" | base64 --decode | yq -o yaml > gitlab14-rails-secret.yaml
-
Удалите компонент task-runner после завершения резервного копирования.
kubectl patch gitlabofficials.operator.devops.alauda.io ${GITLAB_NAME} -n $GITLAB_NAMESPACE --type='merge' -p='
{
"spec": {
"helmValues": {
"gitlab": {
"task-runner": null
}
}
}
}'
echo "Waiting for the task-runner deployment to be removed..."
sleep 30
kubectl get po -n "$GITLAB_NAMESPACE" -l app=task-runner,release=${GITLAB_NAME}
# Waiting for the task-runner deployment to be removed...
# No resources found in xxxx namespace.
Восстановление резервной копии в развертывание all-in-one 14.0.12
-
Используйте образ all-in-one для развертывания GitLab 14.0.12.
Сначала установите переменную окружения NODE_IP для доступа к экземпляру all-in-one GitLab через этот IP и порт NodePort.
export NODE_IP=<node_ip>
export GITLAB_IMAGE=<registry-host>/gitlab/gitlab-ce:14.0.12-ce.0
Создайте экземпляр all-in-one GitLab.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: gitlab-http-nodeport
namespace: ${GITLAB_NAMESPACE}
spec:
ports:
- appProtocol: tcp
name: web
port: 30855
protocol: TCP
targetPort: 30855
nodePort: 30855
selector:
deploy: gitlab
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab
namespace: ${GITLAB_NAMESPACE}
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
deploy: gitlab
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
deploy: gitlab
spec:
affinity: {}
containers:
- env:
# Внешний адрес доступа к all-in-one gitlab, замените на IP и nodeport порта узла кластера
- name: GITLAB_OMNIBUS_CONFIG
value: external_url 'http://${NODE_IP}:30855'
image: ${GITLAB_IMAGE} # Можно заменить на доступный образ
imagePullPolicy: IfNotPresent
name: gitlab
ports:
- containerPort: 30855
name: http
protocol: TCP
- containerPort: 2424
name: ssh
protocol: TCP
resources:
limits:
cpu: "8"
memory: 8Gi
requests:
cpu: 4
memory: "4Gi"
securityContext:
privileged: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /dev/shm
name: dshm
- mountPath: /var/opt/gitlab/backups
name: backup
subPath: backups
- mountPath: /etc/gitlab
name: gitlab-upgrade-data
subPath: config
- mountPath: /var/log/gitlab
name: gitlab-upgrade-data
subPath: logs
- mountPath: /var/opt/gitlab
name: gitlab-upgrade-data
subPath: data
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 512Mi
- name: backup
persistentVolumeClaim:
claimName: backup-pvc
- name: gitlab-upgrade-data
persistentVolumeClaim:
claimName: upgrade-pvc
EOF
# service/gitlab-http-nodeport created
# deployment.apps/gitlab created
Проверьте успешный запуск экземпляра all-in-one GitLab.
pod_name=$(kubectl get po -l deploy=gitlab -n $GITLAB_NAMESPACE -o jsonpath='{.items[0].metadata.name}')
bash check_gitlab.sh $pod_name
# 2024-11-01 16:02:47 - Starting monitoring script for GitLab pod: gitlab-7ff8d674bd-m65nz on port: 30855
# 2024-11-01 16:12:59 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:09 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:19 - HTTP not ready. Retrying in 10 seconds...
# ...
# 2024-11-01 16:13:29 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:40 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:50 - GitLab is ready
-
Измените содержимое rails secret в all-in-one GitLab. Замените поля secret_key_base, otp_key_base, db_key_base, openid_connect_signing_key, ci_jwt_signing_key соответствующими значениями из сохранённого rails secret GitLab 14.
# Скопировать gitlab-secrets.json локально
pod_name=$(kubectl get po -l deploy=gitlab -n $GITLAB_NAMESPACE -o jsonpath='{.items[0].metadata.name}')
kubectl -n $GITLAB_NAMESPACE cp $pod_name:/etc/gitlab/gitlab-secrets.json all-in-one-gitlab-secrets.json
# Заменить данными из gitlab 14 rails-secret.
KEYS=(secret_key_base otp_key_base db_key_base openid_connect_signing_key ci_jwt_signing_key)
for key in "${KEYS[@]}"; do
echo "Replace ${key} ..."
export KEY_VALUE=$(yq eval -r --unwrapScalar=false ".production.${key}" gitlab14-rails-secret.yaml)
yq eval ".gitlab_rails.${key} = env(KEY_VALUE)" all-in-one-gitlab-secrets.json -i
done
# Скопировать gitlab-secrets.json обратно в pod
kubectl -n $GITLAB_NAMESPACE cp all-in-one-gitlab-secrets.json $pod_name:/etc/gitlab/gitlab-secrets.json
# tar: Removing leading `/' from member names
# Replace secret_key_base ...
# Replace otp_key_base ...
# Replace db_key_base ...
# Replace openid_connect_signing_key ...
# Replace ci_jwt_signing_key ...
Отключите ненужные компоненты.
cat <<EOF >> gitlab.rb
prometheus['enable'] = false
gitlab_kas['enable'] = false
redis_exporter['enable'] = false
gitlab_exporter['enable'] = false
postgres_exporter['enable'] = false
sidekiq['enable'] = false
EOF
# Скопировать gitlab.rb в pod
kubectl -n $GITLAB_NAMESPACE cp gitlab.rb $pod_name:/etc/gitlab/gitlab.rb
-
Синхронизируйте данные Gitaly с all-in-one GitLab
Tip
Этот шаг необходимо выполнять на узле, где запущен pod all-in-one GitLab.
Сначала определите пути монтирования на узле для PVC компонента Gitaly исходного GitLab (названного repo-data-${GITLAB_NAME}-gitaly-0) и PVC upgrade-pvc, используемого all-in-one GitLab.
Используйте команду rsync для эффективного копирования данных Gitaly с исходного PVC в upgrade-pvc экземпляра all-in-one GitLab.
Tip
Используйте rsync вместо scp — это значительно быстрее и эффективнее для такого типа передачи данных.
Получите тип хранилища исходного экземпляра
persistence_type=$(kubectl get GitlabOfficial.operator.devops.alauda.io -n $GITLAB_NAMESPACE $GITLAB_NAME -o jsonpath='{.spec.persistence.type}')
echo "gitlab persistence type: $persistence_type"
origin_gitaly_local_mount_path=$(kubectl get GitlabOfficial.operator.devops.alauda.io -n $GITLAB_NAMESPACE $GITLAB_NAME -o jsonpath='{.spec.persistence.location.path}')/gitlab
origin_gitaly_node_ip=<origin_gitaly_node_ip>
all_in_one_upgrade_pvc_mount_path=<all_in_one_gitaly_mount_path>
# Выполните эту команду на узле, где расположен pod all-in-one.
rsync -avh --chown=998:998 ${origin_gitaly_node_ip}:${origin_gitaly_local_mount_path}/ ${all_in_one_upgrade_pvc_mount_path}/data/git-data/repositories/
# Проверьте владельца данных репозитория.
ls -alh ${all_in_one_upgrade_pvc_mount_path}/data/git-data/repositories
origin_gitaly_pvc_mount_path=<origin_gitaly_mount_path>
origin_gitaly_node_ip=<origin_gitaly_node_ip>
all_in_one_upgrade_pvc_mount_path=<all_in_one_gitaly_mount_path>
# Выполните эту команду на узле, где расположен pod all-in-one.
rsync -avh --chown=998:998 ${origin_gitaly_node_ip}:${origin_gitaly_pvc_mount_path}/ ${all_in_one_upgrade_pvc_mount_path}/data/git-data/repositories/
# Проверьте владельца данных репозитория.
ls -alh ${all_in_one_upgrade_pvc_mount_path}/data/git-data/repositories
Перезапустите pod и дождитесь его запуска.
# Перезапуск pod
kubectl delete po -l deploy=gitlab -n $GITLAB_NAMESPACE
echo "Waiting for the pod to start..."
sleep 10
pod_name=$(kubectl get po -l deploy=gitlab -n $GITLAB_NAMESPACE -o jsonpath='{.items[0].metadata.name}')
bash check_gitlab.sh $pod_name
# pod "gitlab-xxxx" deleted
# Waiting for the pod to start...
# 2024-11-01 16:02:47 - Starting monitoring script for GitLab pod: gitlab-7ff8d674bd-m65nz on port: 30855
# 2024-11-01 16:12:59 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:09 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:19 - HTTP not ready. Retrying in 10 seconds...
# ...
# 2024-11-01 16:13:29 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:40 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:50 - GitLab is ready
-
Восстановите резервные данные.
TIP
В процессе восстановления могут возникать ошибки, связанные с базой данных, например:
ERROR: role "xxx" does not exist
ERROR: function xxx does not exist
ERROR: permission denied to create extension "pg_stat_statements"
ERROR: could not open extension control file
Эти ошибки можно игнорировать, подробности доступны по ссылке (https://gitlab.com/gitlab-org/gitlab/-/issues/266988).
# Перевести gitlab в режим только для чтения
pod_name=$(kubectl get po -l deploy=gitlab -n $GITLAB_NAMESPACE -o jsonpath='{.items[0].metadata.name}')
kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- gitlab-ctl stop puma
kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- gitlab-ctl stop sidekiq
# Подтвердить остановку puma и sidekiq
kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- gitlab-ctl status
# Выполнить восстановление
backup_id=$(kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- bash -c 'ls /var/opt/gitlab/backups/*14.0.12_gitlab_backup.tar | head -n1 | sed "s|.*/\([0-9_]*_14\.0\.12\)_gitlab_backup\.tar|\1|"')
echo "Backup ID: ${backup_id}"
echo "This step will prompt you multiple times to confirm whether to continue. \nPlease select \"yes\" each time to proceed."
kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- bash -c 'gitlab-backup restore BACKUP=${backup_id}'
# ok: down: puma: 666s, normally up
# ok: down: sidekiq: 660s, normally up
# ...
# run: prometheus: (pid 1142) 1043s; run: log: (pid 680) 1092s
# down: puma: 695s, normally up; run: log: (pid 536) 1128s
# down: sidekiq: 687s, normally up; run: log: (pid 551) 1124s
# run: sshd: (pid 42) 1171s; run: log: (pid 41) 1171s
# command terminated with exit code 6
# ...
# This task will now rebuild the authorized_keys file.
# You will lose any data stored in the authorized_keys file.
# Do you want to continue (yes/no)? yes
#
# Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data
# and are not included in this backup. You will need to restore these files manually.
# Restore task is done.
Перезапустите pod и дождитесь его запуска, затем выполните gitlab-rake gitlab:check SANITIZE=true для проверки целостности GitLab.
# Перезапуск pod
kubectl delete po -l deploy=gitlab -n $GITLAB_NAMESPACE
# Ожидание запуска pod
sleep 10
pod_name=$(kubectl get po -l deploy=gitlab -n $GITLAB_NAMESPACE -o jsonpath='{.items[0].metadata.name}')
bash check_gitlab.sh $pod_name
kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- gitlab-rake gitlab:check SANITIZE=true
# pod "gitlab-xxxx" deleted
# 2024-11-01 16:02:47 - Starting monitoring script for GitLab pod: gitlab-7ff8d674bd-m65nz on port: 30855
# 2024-11-01 16:12:59 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:09 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:19 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:29 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:40 - HTTP not ready. Retrying in 10 seconds...
# 2024-11-01 16:13:50 - GitLab is ready
# ...
# GitLab configured to store new projects in hashed storage? ... yes
# All projects are in hashed storage? ... yes
# Checking GitLab App ... Finished
# Checking GitLab subtasks ... Finished
Войдите в GitLab, чтобы проверить успешность восстановления экземпляра (пароль пользователя root остаётся прежним), и выполните следующую команду для получения адреса доступа к экземпляру all-in-one.
kubectl -n $GITLAB_NAMESPACE get pod -l deploy=gitlab -o yaml | grep external_url
# value: external_url 'http://192.168.xxx.xxx:30855'
Поэтапное обновление all-in-one GitLab до 17.8.1
Запустите скрипт rolling_upgrade.sh, который обновит экземпляр all-in-one поэтапно до версии 17.8.1 согласно пути обновления.
bash -x rolling_upgrade.sh
Tip
Во время поэтапного обновления база данных может автоматически перезапускаться. В результате скрипт может показывать ошибки вида:
connections on Unix domain socket "/var/opt/gitlab/postgresql/.s.PGSQL.5432"
Это ожидаемое поведение — скрипт автоматически повторит попытку и продолжит работу.
После завершения скрипта войдите в веб-интерфейс GitLab, чтобы проверить, что версия экземпляра — 17.8.1, и убедитесь, что данные целы, включая кодовые репозитории, пользователей, задачи, merge requests и т.д.
Резервное копирование данных из экземпляра all-in-one 17.8.1
Остановите сервис экземпляра all-in-one и сделайте резервную копию данных. Файлы резервных копий сохранятся в каталоге /var/opt/gitlab/backups/.
pod_name=$(kubectl get po -l deploy=gitlab -n $GITLAB_NAMESPACE -o jsonpath='{.items[0].metadata.name}')
kubectl -n $GITLAB_NAMESPACE exec -ti $pod_name -- bash -c '
gitlab-ctl stop puma
gitlab-ctl stop sidekiq
gitlab-ctl status
gitlab-rake gitlab:backup:create
'
# ok: down: puma: 0s, normally up
# ok: down: sidekiq: 0s, normally up
# ...
# 2025-09-15 03:24:37 UTC -- Deleting backups/tmp ...
# 2025-09-15 03:24:37 UTC -- Deleting backups/tmp ... done
# 2025-09-15 03:24:37 UTC -- Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data
# and are not included in this backup. You will need these files to restore a backup.
# Please back them up manually.
# 2025-09-15 03:24:37 UTC -- Backup 1757906670_2025_09_15_17.8.1 is done.
# 2025-09-15 03:24:37 UTC -- Deleting backup and restore PID file at [/opt/gitlab/embedded/service/gitlab-rails/tmp/backup_restore.pid] ... done
Остановите сервис экземпляра all-in-one.
kubectl scale deploy gitlab -n $GITLAB_NAMESPACE --replicas 0
# deployment.apps/gitlab scaled
Namespace нового экземпляра gitlab
Namespace нового экземпляра gitlab должен совпадать с namespace старого экземпляра.
-
Следуйте Руководству по развертыванию GitLab, чтобы развернуть новый экземпляр GitLab 17.8.z с помощью оператора.
Tip
- Новый экземпляр должен находиться в том же namespace, что и старый.
- Метод доступа к новому экземпляру не должен конфликтовать со старым, например, доменное имя, nodeport и т.д.
После развертывания нового экземпляра установите переменную окружения с именем нового экземпляра:
export NEW_GITLAB_NAME=<name of the new GitLab instance>
-
Включите компонент toolbox для нового экземпляра GitLab.
kubectl patch gitlabofficial.operator.alaudadevops.io ${NEW_GITLAB_NAME} -n $GITLAB_NAMESPACE --type='merge' -p='
{
"spec": {
"helmValues": {
"gitlab": {
"toolbox": {
"enabled": true,
"resources": {
"limits": {
"cpu": 2,
"memory": "4G"
}
}
}
}
}
}
}'
Дождитесь завершения повторного развертывания экземпляра.
kubectl get po -l app=toolbox,release=${NEW_GITLAB_NAME} -n $GITLAB_NAMESPACE
# NAME READY STATUS RESTARTS AGE
# xx-gitlab-toolbox-xxxx-xxxx-xxxx-xxxx 1/1 Running 0 178m
-
Восстановите резервную копию в GitLab 17.8.z, развернутый оператором
TIP
В процессе восстановления могут возникать ошибки, связанные с базой данных, например:
ERROR: role "xxx" does not exist
ERROR: function xxx does not exist
ERROR: must be owner of extension
Эти ошибки можно игнорировать, подробности доступны по ссылке (https://gitlab.com/gitlab-org/gitlab/-/issues/266988).
Закройте внешний доступ к новому экземпляру gitlab 17.8.z и примените патч к компоненту toolbox для монтирования backup pvc.
kubectl patch deploy ${NEW_GITLAB_NAME}-webservice-default -n $GITLAB_NAMESPACE --type='merge' -p='{"metadata":{"annotations":{"skip-sync":"true"}}}'
kubectl patch deploy ${NEW_GITLAB_NAME}-sidekiq-all-in-1-v2 -n $GITLAB_NAMESPACE --type='merge' -p='{"metadata":{"annotations":{"skip-sync":"true"}}}'
kubectl scale deploy ${NEW_GITLAB_NAME}-webservice-default -n $GITLAB_NAMESPACE --replicas 0
kubectl scale deploy ${NEW_GITLAB_NAME}-sidekiq-all-in-1-v2 -n $GITLAB_NAMESPACE --replicas 0
# Изменить toolbox, примонтировать backup pvc к toolbox
kubectl patch deploy ${NEW_GITLAB_NAME}-toolbox -n $GITLAB_NAMESPACE -p='{"metadata":{"annotations":{"skip-sync":"true"}}}'
kubectl patch deploy ${NEW_GITLAB_NAME}-toolbox -n $GITLAB_NAMESPACE --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/volumes/1", "value": {"name": "toolbox-tmp", "persistentVolumeClaim": {"claimName": "backup-pvc"}}}]'
# deployment.apps/xxxx-xxxx-toolbox patched
Дождитесь готовности pod toolbox, затем восстановите резервную копию в новый экземпляр GitLab.
toolbox_pod_name=$(kubectl get po -n $GITLAB_NAMESPACE -l release=$NEW_GITLAB_NAME,app=toolbox -o jsonpath='{.items[0].metadata.name}')
# Сделать резервную копию старого файла резервной копии
kubectl -n $GITLAB_NAMESPACE exec -ti $toolbox_pod_name -- bash -c '
mkdir -p /srv/gitlab/tmp/backup_tarballs
mv /srv/gitlab/tmp/backups/*gitlab_backup.tar /srv/gitlab/tmp/backup_tarballs || true
'
backup_file=$(kubectl -n $GITLAB_NAMESPACE exec -ti $toolbox_pod_name -- bash -c "ls /srv/gitlab/tmp/backup_tarballs/*17.8.1_gitlab_backup.tar | head -n1 | tr -d '\r\n'")
echo "Backup file of 17.8.1: ${backup_file}"
kubectl -n $GITLAB_NAMESPACE exec -ti $toolbox_pod_name -- backup-utility --restore --skip-restore-prompt -f "file://${backup_file}"
# Backup file of 17.8.1: 1757906670_2025_09_15_17.8.1_gitlab_backup.tar
# 2025-09-15 06:59:21 UTC -- Restoring repositories ... done
# 2025-09-15 06:59:21 UTC -- Deleting backup and restore PID file at [/srv/gitlab/tmp/backup_restore.pid] ... done
# 2025-09-15 06:59:41 UTC -- Restoring builds ...
# 2025-09-15 06:59:41 UTC -- Restoring builds ... done
# 2025-09-15 06:59:41 UTC -- Deleting backup and restore PID file at [/srv/gitlab/tmp/backup_restore.pid] ... done
# Backup tarball not from a Helm chart based installation. Not processing files in object storage.
-
Замените rails-secret для нового экземпляра GitLab.
kubectl get secrets -n ${GITLAB_NAMESPACE} ${NEW_GITLAB_NAME}-rails-secret -o jsonpath="{.data['secrets\.yml']}" | base64 --decode | yq -o yaml > gitlab17-rails-secret.yaml
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' gitlab17-rails-secret.yaml gitlab14-rails-secret.yaml > final-rails-secret.yml
kubectl create secret generic rails-secret -n ${GITLAB_NAMESPACE} --from-file=secrets.yml=final-rails-secret.yml
kubectl patch gitlabofficials.operator.alaudadevops.io ${NEW_GITLAB_NAME} -n $GITLAB_NAMESPACE --type='merge' -p='
{
"spec": {
"helmValues": {
"global": {
"railsSecrets": {
"secret": "rails-secret"
}
}
}
}
}'
kubectl patch deploy ${NEW_GITLAB_NAME}-webservice-default -n $GITLAB_NAMESPACE --type='json' -p='[{"op":"remove","path":"/metadata/annotations/skip-sync"}]'
kubectl patch deploy ${NEW_GITLAB_NAME}-sidekiq-all-in-1-v2 -n $GITLAB_NAMESPACE --type='json' -p='[{"op":"remove","path":"/metadata/annotations/skip-sync"}]'
kubectl patch deploy ${NEW_GITLAB_NAME}-toolbox -n $GITLAB_NAMESPACE --type='json' -p='[{"op":"remove","path":"/metadata/annotations/skip-sync"}]'
kubectl delete pod -lrelease=${NEW_GITLAB_NAME} -n $GITLAB_NAMESPACE
kubectl scale deploy ${NEW_GITLAB_NAME}-webservice-default -n $GITLAB_NAMESPACE --replicas 1
kubectl scale deploy ${NEW_GITLAB_NAME}-sidekiq-all-in-1-v2 -n $GITLAB_NAMESPACE --replicas 1
# secret/rails-secret created
# gitlabofficial.operator.alaudadevops.io/xxxx-xxxx patched
Оператор автоматически повторно развернет новый экземпляр GitLab. Дождитесь завершения повторного развертывания.
-
Проверьте корректность данных после обновления.
- Войдите в UI GitLab в режиме администратора и проверьте корректность репозиториев и данных пользователей.
- Выберите несколько репозиториев и проверьте работоспособность кода, веток и merge requests.
-
Очистите неиспользуемые ресурсы:
- При необходимости удалите старый экземпляр GitLab и старый оператор.
- При необходимости удалите экземпляр
all-in-one GitLab.
FAQ
Восстановление данных Uploads
Мигрированные данные не включают аватары и вложения, поэтому новый экземпляр GitLab не может отображать этот контент. Для решения этой проблемы можно заменить PVC uploads GitLab 17.8.z на PVC, используемый в 14.0.12.
Установите переменную окружения с именем PVC uploads:
export UPLOADS_PVC_NAME=<name of the uploads pvc>
Примените патч к новому экземпляру GitLab для использования PVC uploads:
kubectl patch gitlabofficials.operator.devops.alauda.io ${NEW_GITLAB_NAME} -n $GITLAB_NAMESPACE --type='json' -p='
{
"spec": {
"helmValues": {
"global": {
"uploads": {
"persistence": {
"enabled": true,
"existingClaim": "${UPLOADS_PVC_NAME}"
}
}
}
}
}
}'
Если ваш старый экземпляр GitLab использует хранилище HostPath, необходимо вручную скопировать данные из каталога uploads старого экземпляра в путь /srv/gitlab/public/uploads компонента webservice нового экземпляра.
Как найти путь монтирования PVC на узле
Выполните следующие шаги, чтобы определить путь монтирования PersistentVolumeClaim (PVC) на узле Kubernetes:
-
Получите IP узла, на котором запущен pod
kubectl -n $GITLAB_NAMESPACE get pod <pod-name> -o jsonpath='{.status.hostIP}'
# Вывод:
# 192.168.xxx.xxx
-
Получите имя тома, связанного с PVC
kubectl get pvc -n $GITLAB_NAMESPACE <pvc-name> -o jsonpath='{.spec.volumeName}'
# Вывод:
# pvc-359bb8d2-841e-406c-a5e7-e48bc701f610
-
Найдите путь монтирования PVC на узле
Войдите на узел (используя IP из шага 1), затем выполните:
df -h | grep <pv-name>
# Вывод:
# 192.168.xx.xx:6789:/volumes/csi/csi-vol-4661bd66-a68f-42f6-b995-53bd5c3f79a8/ede3ffa1-0f77-4f44-b0e6-41ec7384484f 10G 216M 9.8G 3% /var/lib/kubelet/pods/7772096f-9b81-4698-8369-34689e36fc3a/ volumes/kubernetes.io~csi/pvc-359bb8d2-841e-406c-a5e7-e48bc701f610/mount
Путь, указанный в последнем столбце, например:
/var/lib/kubelet/pods/7772096f-9b81-4698-8369-34689e36fc3a/volumes/kubernetes.io~csi/pvc-359bb8d2-841e-406c-a5e7-e48bc701f610/mount
— это директория монтирования вашего PVC на узле.