- secrets 파일을 잃어버렸을 때
- 백업에서 복원 후 컨테이너 레지스트리 푸시 실패
- Gzip 오류로 백업이 완료되지 않음
-
File name too long
오류로 백업이 실패함 -
pg_stat_statements
가 이전에 활성화되었을 때 데이터베이스 백업 복원이 실패하는 경우
GitLab 백업 문제 해결
GitLab을 백업할 때 다음과 같은 문제가 발생할 수 있습니다.
secrets 파일을 잃어버렸을 때
만약 secrets 파일을 백업하지 않았다면, GitLab을 다시 제대로 작동시키기 위해 몇 가지 단계를 완료해야 합니다.
secrets 파일은 필요한 중요 정보를 포함하는 열의 암호화 키를 저장하는 데 책임이 있습니다. 키를 잃어버리면 GitLab은 해당 열을 해독할 수 없어 다음 항목에 액세스할 수 없게 됩니다:
CI/CD 변수 및 러너(runner) 인증과 같은 경우 예상치 못한 동작을 경험할 수 있습니다. 예를 들어 다음과 같습니다:
- 작업이 멈춤.
- 500 에러.
이 경우, CI/CD 변수와 러너(runner) 인증에 대한 모든 토큰을 재설정해야 하며, 이에 대해 더 자세히 설명된 내용은 다음 섹션에서 설명합니다. 토큰을 재설정한 후 프로젝트를 방문하고 작업을 다시 시작해야 합니다.
모든 값을 해독할 수 있는지 확인
현재의 secrets를 사용하여 데이터베이스에 해독할 수 없는 값이 있는지 확인할 수 있습니다.
Rake 작업을 사용하면 됩니다.
백업 만들기
잃어버린 secrets 파일을 대체하기 위해 GitLab 데이터를 직접 수정해야 합니다.
사용자 이중 인증 (2FA) 비활성화
2FA를 활성화한 사용자는 GitLab에 로그인할 수 없습니다. 이 경우 모든 사용자의 2FA를 비활성화해야 합니다. 그 후 사용자는 다시 2FA를 활성화해야 합니다.
CI/CD 변수 재설정
-
데이터베이스 콘솔에 입력합니다:
Linux 패키지(Omnibus)의 경우:
sudo gitlab-rails dbconsole --database main
직접 설치한 경우:
sudo -u git -H bundle exec rails dbconsole -e production --database main
-
ci_group_variables
및ci_variables
테이블을 확인합니다:SELECT * FROM public."ci_group_variables"; SELECT * FROM public."ci_variables";
삭제해야 할 변수입니다.
-
모든 변수를 삭제합니다:
DELETE FROM ci_group_variables; DELETE FROM ci_variables;
-
삭제할 특정 그룹 또는 프로젝트를 알고 있다면
DELETE
에서 해당 그룹 또는 프로젝트를 지정하기 위해WHERE
문을 포함할 수 있습니다:DELETE FROM ci_group_variables WHERE group_id = <GROUPID>; DELETE FROM ci_variables WHERE project_id = <PROJECTID>;
변경 사항이 적용되려면 GitLab을 다시 구성하거나 다시 시작해야 할 수 있습니다.
러너(runner) 등록 토큰 재설정
-
데이터베이스 콘솔에 입력합니다:
Linux 패키지(Omnibus)의 경우:
sudo gitlab-rails dbconsole --database main
직접 설치한 경우:
sudo -u git -H bundle exec rails dbconsole -e production --database main
-
프로젝트, 그룹 및 전체 인스턴스에 대한 모든 토큰을 지웁니다:
최종UPDATE
작업은 러너가 새 작업을 가져오지 못하게 합니다. 새로운 러너를 등록해야 합니다.-- 프로젝트 토큰 지우기 UPDATE projects SET runners_token = null, runners_token_encrypted = null; -- 그룹 토큰 지우기 UPDATE namespaces SET runners_token = null, runners_token_encrypted = null; -- 인스턴스 토큰 지우기 UPDATE application_settings SET runners_registration_token_encrypted = null; -- JWT 인증에 사용되는 키 지우기 -- 이는 $CI_JWT_TOKEN 작업 변수를 깰 수 있습니다: -- https://gitlab.com/gitlab-org/gitlab/-/issues/325965 UPDATE application_settings SET encrypted_ci_jwt_signing_key = null; -- 러너 토큰 지우기 UPDATE ci_runners SET token = null, token_encrypted = null;
보류 중인 파이프라인 작업 재설정
-
데이터베이스 콘솔에 입력합니다:
Linux 패키지(Omnibus)의 경우:
sudo gitlab-rails dbconsole --database main
직접 설치한 경우:
sudo -u git -H bundle exec rails dbconsole -e production --database main
-
보류 중인 작업에 대한 모든 토큰을 지웁니다:
GitLab 15.3 및 이전 버전의 경우:
-- 빌드 토큰 지우기 UPDATE ci_builds SET token = null, token_encrypted = null;
GitLab 15.4 이후 버전의 경우:
-- 빌드 토큰 지우기 UPDATE ci_builds SET token_encrypted = null;
나머지 기능에 대해서도 유사한 전략을 적용할 수 있습니다. 해독할 수 없는 데이터를 제거함으로써 GitLab을 다시 운영할 수 있으며, 손실된 데이터를 매뉴얼으로 대체할 수 있습니다.
통합 및 웹훅 수정
secrets를 잃어버렸다면, 통합 설정 및 웹훅 설정 페이지에 500
오류 메시지가 표시될 수 있습니다. 손실된 secrets로 이전에 구성된 통합 또는 웹훅이 있는 프로젝트의 리포지터리에 액세스하려고 시도하면 500
오류가 발생할 수 있습니다.
해결 방법은 영향을 받는 테이블(암호화된 열을 포함하는 테이블)을 잘라서 삭제하는 것입니다. 이로써 구성된 통합, 웹훅 및 관련된 메타데이터가 모두 삭제됩니다. 데이터를 삭제하기 전에 secrets가 원인인지 확인해야 합니다.
-
데이터베이스 콘솔에 입력합니다:
Linux 패키지(Omnibus)의 경우:
sudo gitlab-rails dbconsole --database main
직접 설치한 경우:
sudo -u git -H bundle exec rails dbconsole -e production --database main
-
다음 테이블을 잘라냅니다:
-- 웹훅 테이블 자르기 TRUNCATE integrations, chat_names, issue_tracker_data, jira_tracker_data, slack_integrations, web_hooks, zentao_tracker_data, web_hook_logs CASCADE;
백업에서 복원 후 컨테이너 레지스트리 푸시 실패
컨테이너 레지스트리를 사용하는 경우, 백업을 복원한 후 Linux 패키지(Omnibus) 인스턴스에서 레지스트리 데이터를 복원한 후 레지스트리로 푸시하는 것이 실패할 수 있습니다.
이러한 실패는 레지스트리 로그에서 권한 문제를 언급합니다. 예를 들어:
level=error
msg="response completed with error"
err.code=unknown
err.detail="filesystem: mkdir /var/opt/gitlab/gitlab-rails/shared/registry/docker/registry/v2/repositories/...: permission denied"
err.message="unknown error"
이 문제는 백업을 실행하는 미특권 사용자인 git
으로 인해 발생하며, 복원 프로세스 중에 레지스트리 파일에 올바른 소유권을 할당할 수 없는 것입니다 (이슈 #62759).
레지스트리를 다시 작동시키려면 다음을 수행합니다:
sudo chown -R registry:registry /var/opt/gitlab/gitlab-rails/shared/registry/docker
기본 파일 시스템 위치를 변경했다면, /var/opt/gitlab/gitlab-rails/shared/registry/docker
대신 사용자 정의 위치에 대해 chown
명령을 실행합니다.
Gzip 오류로 백업이 완료되지 않음
백업을 실행할 때 Gzip 오류 메시지를 받을 수 있습니다:
sudo /opt/gitlab/bin/gitlab-backup create
...
Dumping ...
...
gzip: stdout: Input/output error
백업 실패
이런 경우 다음을 확인하세요:
- Gzip 작업을 위한 충분한 디스크 공간이 있는지 확인합니다. 백업 생성 중에는 기본 전략을 사용하는 백업에서 인스턴스 크기의 절반에 해당하는 무료 디스크 공간이 필요할 수 있습니다.
- NFS를 사용 중인 경우,
timeout
마운트 옵션이 설정되어 있는지 확인하세요. 기본값은600
이며, 이 값을 더 작은 값으로 변경하면 이 오류가 발생할 수 있습니다.
File name too long
오류로 백업이 실패함
백업 중에 File name too long
오류(issue #354984)를 만날 수 있습니다. 예를 들면:
문제: <class 'OSError: [Errno 36] File name too long:
이 문제로 백업 스크립트가 완료되지 못합니다. 이 문제를 해결하려면 문제를 유발하는 파일 이름을 줄여야 합니다. 파일 확장자를 포함하여 최대 246자까지 허용됩니다.
오류를 해결하기 위해 파일 이름을 줄이는 단계는 다음과 같습니다:
- 데이터베이스에서 추적되지 않는 원격 업로드된 파일을 정리합니다.
- 데이터베이스 내의 파일 이름을 줄입니다.
- 백업 작업을 다시 실행합니다.
추적되지 않는 원격 업로드된 파일 정리
알려진 문제로 인해 부모 리소스가 삭제된 후에도 객체 리포지터리 업로드가 남아있는 경우가 있습니다. 이 문제는 해결되었습니다.
이러한 파일들을 해결하려면, uploads
데이터베이스 테이블에 추적되지 않는 모든 원격 업로드된 파일을 정리해야 합니다.
-
GitLab 데이터베이스에 존재하지 않는 파일을 잃어버린 물품 디렉터리로 옮길 수 있는 모든 객체 리포지터리 업로드 파일을 나열합니다:
bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production
-
이러한 파일을 삭제하고 모든 참조되지 않은 업로드된 파일을 제거하려면 다음을 실행합니다:
다음 작업은 취소할 수 없습니다.bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production DRY_RUN=false
데이터베이스에서 참조된 파일 이름 줄이기
문제를 일으키는 데이터베이스에서 참조된 파일 이름을 줄여야 합니다. 데이터베이스에서 참조된 파일 이름은 다음에 저장됩니다:
-
uploads
테이블. - 찾은 참조. 기타 데이터베이스 테이블과 열에서 찾은 참조.
- 파일 시스템.
uploads
테이블에서 파일 이름을 줄입니다:
-
데이터베이스 콘솔로 이동합니다:
Linux 패키지(Omnibus)의 경우:
sudo gitlab-rails dbconsole --database main
직접 컴파일된 설치의 경우:
sudo -u git -H bundle exec rails dbconsole -e production --database main
-
246자보다 긴 파일 이름을 가진
uploads
레코드를 검색합니다:다음 쿼리는 큰 GitLab 인스턴스에서 성능을 향상시키기 위해 0에서 10000까지의 배치로 246자보다 긴 파일 이름을 가진
uploads
레코드를 선택합니다.CREATE TEMP TABLE uploads_with_long_filenames AS SELECT ROW_NUMBER() OVER(ORDER BY id) row_id, id, path FROM uploads AS u WHERE LENGTH((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1]) > 246; CREATE INDEX ON uploads_with_long_filenames(row_id); SELECT u.id, u.path, -- 현재 파일 이름 (regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1] AS current_filename, -- 새 파일 이름 CONCAT( LEFT(SPLIT_PART((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242), COALESCE(SUBSTRING((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$')) ) AS new_filename, -- 새 경로 CONCAT( COALESCE((regexp_match(u.path, '(.*\/).*'))[1], ''), CONCAT( LEFT(SPLIT_PART((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242), COALESCE(SUBSTRING((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$')) ) ) AS new_path FROM uploads_with_long_filenames AS u WHERE u.row_id > 0 AND u.row_id <= 10000;
출력 예시:
-[ RECORD 1 ]----+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- id | 34 path | public/@hashed/loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisit.txt current_filename | loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisit.txt new_filename | loremipsumdolorsitametconsecteturadipisicingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipisicingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelits.txt new_path | public/@hashed/loremipsumdolorsitametconsecteturadipisicingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipisicingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelits.txt
여기서:
-
current_filename
: 현재 246자보다 긴 파일 이름. -
new_filename
: 246자로 줄여진 새 파일 이름. -
new_path
:new_filename
(줄인)을 고려한 새 경로.
배치 결과를 확인한 후,
uploads
테이블의 마지막 레코드에 도달할 때까지 다음 숫자 시퀀스(10000에서 20000까지)로 배치 크기(row_id
)를 변경해야 합니다. -
-
uploads
테이블에서 찾은 파일 이름을 새로 줄인 파일 이름으로 변경합니다. 다음 쿼리는 변경 사항을 안전하게 검사할 수 있도록 업데이트를 롤백합니다:CREATE TEMP TABLE uploads_with_long_filenames AS SELECT ROW_NUMBER() OVER(ORDER BY id) row_id, path, id FROM uploads AS u WHERE LENGTH((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1]) > 246; CREATE INDEX ON uploads_with_long_filenames(row_id); BEGIN; WITH updated_uploads AS ( UPDATE uploads SET path = CONCAT( COALESCE((regexp_match(updatable_uploads.path, '(.*\/).*'))[1], ''), CONCAT( LEFT(SPLIT_PART((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242), COALESCE(SUBSTRING((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$')) ) ) FROM uploads_with_long_filenames AS updatable_uploads WHERE uploads.id = updatable_uploads.id AND updatable_uploads.row_id > 0 AND updatable_uploads.row_id <= 10000 RETURNING uploads.* ) SELECT id, path FROM updated_uploads; ROLLBACK;
배치 업데이트 결과를 확인한 후,
uploads
테이블의 마지막 레코드에 도달할 때까지 다음 숫자 시퀀스(10000에서 20000까지)로 배치 크기(row_id
)를 변경해야 합니다. -
이전 쿼리에서 얻은 새 파일 이름이 예상대로 246자로 줄어들었는지 확인합니다. 이전 단계에서 찾은 레코드를 246자로 줄이고자 하는 경우 다음을 실행합니다:
다음 작업은 취소할 수 없습니다.CREATE TEMP TABLE uploads_with_long_filenames AS SELECT ROW_NUMBER() OVER(ORDER BY id) row_id, path, id FROM uploads AS u WHERE LENGTH((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1]) > 246; CREATE INDEX ON uploads_with_long_filenames(row_id); UPDATE uploads SET path = CONCAT( COALESCE((regexp_match(updatable_uploads.path, '(.*\/).*'))[1], ''), CONCAT( LEFT(SPLIT_PART((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242), COALESCE(SUBSTRING((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$')) ) ) FROM uploads_with_long_filenames AS updatable_uploads WHERE uploads.id = updatable_uploads.id AND updatable_uploads.row_id > 0 AND updatable_uploads.row_id <= 10000;
배치 업데이트를 마치면,
uploads
테이블의 마지막 레코드에 도달할 때까지 다음 숫자 시퀀스(updatable_uploads.row_id
)로 배치 크기를 변경해야 합니다.
데이터베이스에서 참조된 파일 이름을 줄입니다:
-
이러한 레코드가 어디에서든 참조되는지 확인합니다. 이를 위한 한 가지 방법은 데이터베이스를 덤프하여 부모 디렉터리 이름과 파일 이름을 검색하는 것입니다:
-
데이터베이스를 덤프하려면 다음과 같은 명령을 사용할 수 있습니다(예시로):
pg_dump -h /var/opt/gitlab/postgresql/ -d gitlabhq_production > gitlab-dump.tmp
-
다음으로
grep
명령을 사용하여 참조를 검색할 수 있습니다. 부모 디렉터리와 파일 이름을 결합하는 것이 좋은 아이디어가 될 수 있습니다. 예를 들어:grep public/alongfilenamehere.txt gitlab-dump.tmp
-
-
uploads
테이블에서 얻은 새 파일 이름을 사용하여 읽기 전용 덤프에 참조를 검색하세요.
파일 시스템에서 파일 이름을 줄입니다. uploads
테이블에서 얻은 새 파일 이름을 사용하여 파일 시스템에서 파일 이름을 매뉴얼으로 바꿔야 합니다.
백업 작업 다시 실행
이전 단계를 모두 따른 후, 백업 작업을 다시 실행합니다.
pg_stat_statements
가 이전에 활성화되었을 때 데이터베이스 백업 복원이 실패하는 경우
PostgreSQL 데이터베이스의 GitLab 백업에는 이전에 활성화되었던 확장기능을 활성화하는 데 필요한 모든 SQL 문이 포함됩니다.
pg_stat_statements
확장 기능은 superuser
역할을 가진 PostgreSQL 사용자만이 활성화 또는 비활성화할 수 있습니다.
복원 프로세스는 권한이 제한된 데이터베이스 사용자를 사용하기 때문에 다음의 SQL 문을 실행할 수 없습니다:
DROP EXTENSION IF EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
pg_stat_statements
확장 기능이 없는 PostgreSQL 인스턴스에서 백업을 복원하려고 시도하면 다음의 오류 메시지가 표시됩니다:
ERROR: permission denied to create extension "pg_stat_statements"
HINT: Must be superuser to create this extension.
ERROR: extension "pg_stat_statements" does not exist
pg_stats_statements
확장 기능이 활성화된 인스턴스에서 복원을 시도하면 정리 단계에서 다음과 유사한 오류 메시지로 실패합니다:
rake aborted!
ActiveRecord::StatementInvalid: PG::InsufficientPrivilege: ERROR: must be owner of view pg_stat_statements
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:42:in `block (4 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `each'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/backup.rake:71:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'
Caused by:
PG::InsufficientPrivilege: ERROR: must be owner of view pg_stat_statements
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:42:in `block (4 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `each'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/backup.rake:71:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'
Tasks: TOP => gitlab:db:drop_tables
(See full trace by running task with --trace)
덤프 파일에 pg_stat_statements
포함 방지
백업 번들의 일부인 PostgreSQL 덤프 파일에 확장 기능을 포함시키지 않으려면, public
스키마 이외의 스키마에서 확장 기능을 활성화합니다:
CREATE SCHEMA adm;
CREATE EXTENSION pg_stat_statements SCHEMA adm;
확장 기능이 이전에 public
스키마에서 활성화된 경우, 새로운 스키마로 이동시킵니다:
CREATE SCHEMA adm;
ALTER EXTENSION pg_stat_statements SET SCHEMA adm;
스키마를 변경한 후 pg_stat_statements
데이터를 조회하려면, 새로운 스키마로 뷰 이름에 접두사를 붙입니다:
SELECT * FROM adm.pg_stat_statements limit 0;
public
스키마에서 활성화되어 있는 것으로 예상하는 써드파티 모니터링 솔루션과 호환성을 유지하려면, search_path
에 포함시켜야 합니다:
set search_path to public,adm;
pg_stat_statements
에 대한 참조를 제거하여 기존 덤프 파일 수정
기존 백업 파일을 수정하려면 다음과 같은 변경을 수행합니다:
- 백업에서 다음 파일을 추출합니다:
db/database.sql.gz
. - 파일을 압축 해제하거나 압축 파일을 처리할 수 있는 편집기를 사용합니다.
-
다음 라인 또는 유사한 라인을 제거합니다:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed';
- 변경사항을 저장하고 파일을 다시 압축합니다.
- 수정된
db/database.sql.gz
로 백업 파일을 업데이트합니다.