- Secrets 파일 분실 시
- 백업에서 컨테이너 레지스트리 푸시 실패
- Gzip 오류로 백업이 완료되지 않는 경우
-
백업이
File name too long
오류로 실패 -
pg_stat_statements
가 이전에 활성화된 경우 데이터베이스 백업 복원에 실패하는 문제
GitLab 백업 문제 해결
GitLab을 백업할 때 다음과 같은 문제가 발생할 수 있습니다.
Secrets 파일 분실 시
만약 비밀 파일을 백업하지 않았다면, GitLab을 다시 올바르게 작동시키기 위해 몇 가지 단계를 완료해야 합니다.
Secrets 파일은 필수 민감 정보를 포함하는 열의 암호화 키를 저장하는 데 책임이 있습니다. 이 키가 분실되면 GitLab은 해당 열을 복호화할 수 없어 다음과 같은 항목에 대한 액세스를 방해합니다:
CI/CD 변수 및 러너 인증과 같은 경우 예기치 않은 동작을 경험할 수 있습니다. 예를 들어:
- 작업이 멈춤.
- 500 오류.
이 경우 CI/CD 변수와 러너 인증을 모두 재설정해야 하며, 이에 대한 자세한 내용은 다음 섹션에서 설명하겠습니다. 토큰을 재설정한 후에는 프로젝트를 방문하고 작업을 다시 시작할 수 있어야 합니다.
모든 값이 복호화될 수 있는지 확인
현재 Secrets를 사용하여 복호화할 수 있는 값을 데이터베이스에 포함되어 있는지를 결정하기 위해 레이크 태스크를 사용할 수 있습니다.
백업 수행
분실된 Secrets 파일을 해결하기 위해 GitLab 데이터를 직접 수정해야 합니다.
사용자 이중 요소 인증 (2FA) 비활성화
2FA가 활성화된 사용자는 GitLab에 로그인할 수 없습니다. 이 경우 모든 사용자의 2FA를 비활성화해야 합니다. 이후 사용자가 다시 2FA를 활성화해야 합니다.
CI/CD 변수 재설정
-
데이터베이스 콘솔에 들어갑니다:
Linux 패키지 (Omnibus) GitLab 14.1 이전의 경우:
sudo gitlab-rails dbconsole
Linux 패키지 (Omnibus) GitLab 14.2 이후의 경우:
sudo gitlab-rails dbconsole --database main
자체 컴파일된 설치의 경우, GitLab 14.1 이전:
sudo -u git -H bundle exec rails dbconsole -e production
자체 컴파일된 설치의 경우, GitLab 14.2 이후:
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;
-
특정 그룹 또는 프로젝트를 알고 있다면 해당 변수를 삭제하려면
WHERE
문을 포함하여DELETE
에 지정해야 합니다:DELETE FROM ci_group_variables WHERE group_id = <GROUPID>; DELETE FROM ci_variables WHERE project_id = <PROJECTID>;
변경 사항이 적용되도록 GitLab을 다시 구성하거나 다시 시작해야할 수 있습니다.
러너 등록 토큰 재설정
-
데이터베이스 콘솔에 들어갑니다:
Linux 패키지 (Omnibus) GitLab 14.1 이전의 경우:
sudo gitlab-rails dbconsole
Linux 패키지 (Omnibus) GitLab 14.2 이후의 경우:
sudo gitlab-rails dbconsole --database main
자체 컴파일된 설치의 경우, GitLab 14.1 이전:
sudo -u git -H bundle exec rails dbconsole -e production
자체 컴파일된 설치의 경우, GitLab 14.2 이후:
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) GitLab 14.1 이전의 경우:
sudo gitlab-rails dbconsole
Linux 패키지 (Omnibus) GitLab 14.2 이후의 경우:
sudo gitlab-rails dbconsole --database main
자체 컴파일된 설치의 경우, GitLab 14.1 이전:
sudo -u git -H bundle exec rails dbconsole -e production
자체 컴파일된 설치의 경우, GitLab 14.2 이후:
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을 다시 운영 가능한 상태로 복원하고 손실된 데이터를 매뉴얼으로 대체할 수 있습니다.
통합 및 웹훅 수정하기
만약 시크릿을 분실한 경우, 통합 설정 및 웹훅 설정 페이지에서는 500
오류 메시지가 표시될 수 있습니다. 시크릿 손실은 이전에 구성된 통합이나 웹훅이 있는 프로젝트의 리포지터리에 액세스하려고 할 때 또한 500
오류를 발생시킬 수 있습니다.
문제를 해결하려면 영향을 받는 테이블(암호화된 열을 포함하는 테이블)을 잘라내면 됩니다. 이렇게 함으로써 구성된 통합, 웹훅 및 관련 메타데이터가 모두 삭제됩니다. 어떤 데이터도 삭제하기 전에 시크릿이 꼭 원인인지 확인해야 합니다.
-
데이터베이스 콘솔에 입력하세요:
Linux 패키지(Omnibus) GitLab 14.1 및 이전:
sudo gitlab-rails dbconsole
Linux 패키지(Omnibus) GitLab 14.2 및 이후:
sudo gitlab-rails dbconsole --database main
자체 컴파일된 설치, GitLab 14.1 및 이전:
sudo -u git -H bundle exec rails dbconsole -e production
자체 컴파일된 설치, GitLab 14.2 및 이후:
sudo -u git -H bundle exec rails dbconsole -e production --database main
-
다음 테이블들을 잘라내세요:
-- web_hooks 테이블 잘라내기 TRUNCATE integrations, chat_names, issue_tracker_data, jira_tracker_data, slack_integrations, web_hooks, zentao_tracker_data, web_hook_logs CASCADE;
백업에서 컨테이너 레지스트리 푸시 실패
만약 리눅스 패키지(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
오류가 발생할 수 있습니다 (이슈 #354984). 예를 들면:
Problem: <class 'OSError: [Errno 36] File name too long:
이 문제로 백업 스크립트가 완료되지 않습니다. 이 문제를 해결하려면 문제를 일으키는 파일 이름을 잘라내야 합니다. 파일 확장자를 포함하여 최대 246자까지 허용됩니다.
오류를 해결하고 파일 이름을 줄이려면 다음 단계를 따릅니다:
- 데이터베이스에 추적되지 않는 원격 업로드된 파일을 정리합니다.
- 데이터베이스의 파일 이름을 줄입니다.
- 백업 작업을 다시 실행합니다.
원격 업로드 파일 정리
부모 리소스가 삭제된 후에도 객체 리포지터리 업로드가 남아있는 알려진 문제가 있었습니다 (이슈 #45425). 이 문제는 해결되었습니다.
이러한 파일을 수정하려면 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) GitLab 14.2 및 이후:
sudo gitlab-rails dbconsole --database main
Linux 패키지(Omnibus) GitLab 14.1 및 이전:
sudo gitlab-rails dbconsole
자체 컴파일된 설치, GitLab 14.2 및 이후:
sudo -u git -H bundle exec rails dbconsole -e production --database main
자체 컴파일된 설치, GitLab 14.1 및 이전:
sudo -u git -H bundle exec rails dbconsole -e production
-
246자보다 긴 파일 이름을 가진
업로드
레코드를 검색하세요:다음 쿼리는 0부터 10000까지 일괄 처리로 246자 이상의 파일 이름을 가진
업로드
레코드를 선택합니다. 이렇게 하면 수천 개의 레코드가 있는 큰 GitLab 인스턴스에서 성능이 향상됩니다.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 | loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelits.txt new_path | public/@hashed/loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelits.txt
여기서:
-
current_filename
: 현재 246자보다 긴 파일 이름 -
new_filename
: 246자로 잘랐을 때의 새 파일 이름 -
new_path
: 새 경로,new_filename
(잘라낸)을 고려한 것
일괄결과를 확인한 후, 다음 배치 결과를 변경하려면(
row_id
) 다음 순차 번호를 사용하세요(10000에서 20000까지).uploads
테이블의 마지막 레코드에 도달할 때까지 이러한 프로세스를 반복하세요. -
-
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;
일괄 업데이트 결과를 확인한 후, 다음 배치 크기(
row_id
)를 사용하여 변경하세요(10000부터 20000까지).uploads
테이블의 마지막 레코드에 도달할 때까지 이러한 프로세스를 반복하세요. -
이전 쿼리에서 새로운 파일 이름이 예상대로 되었는지 확인하세요. 이전 단계에서 찾은 레코드를 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 (중략)
백업 작업 다시 실행
이전 단계를 모두 수행한 후 백업 작업을 다시 실행합니다.
pg_stat_statements
가 이전에 활성화된 경우 데이터베이스 백업 복원에 실패하는 문제
PostgreSQL 데이터베이스의 GitLab 백업에는 이전에 데이터베이스에서 활성화된 익스텐션을 활성화하는 데 필요한 모든 SQL 문이 포함됩니다.
pg_stat_statements
익스텐션은 PostgreSQL 사용자 중 superuser
권한을 가진 사용자만이 활성화하거나 비활성화할 수 있습니다. 복원 프로세스는 권한이 제한된 데이터베이스 사용자를 사용하므로 다음과 같은 SQL 문을 실행할 수 없습니다.
DROP EXTENSION IF EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
pg_stats_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
로 백업 파일을 업데이트합니다.