GitLab 백업 문제 해결

GitLab을 백업할 때 다음과 같은 문제에 직면할 수 있습니다.

비밀 파일이 손실된 경우

비밀 파일을 백업하지 않았다면, GitLab이 정상적으로 작동하도록 하기 위해 몇 가지 단계를 완료해야 합니다.

비밀 파일은 필수적인 민감한 정보가 포함된 열의 암호화 키를 저장하는 역할을 합니다. 키가 손실되면 GitLab은 해당 열을 복호화할 수 없어 다음 항목에 대한 접근이 차단됩니다:

CI/CD 변수 및 러너 인증과 같은 경우에는 다음과 같은 예기치 않은 행동을 경험할 수 있습니다:

  • 작업이 멈춤.
  • 500 오류.

이 경우 CI/CD 변수 및 러너 인증에 대한 모든 토큰을 재설정해야 하며, 이는 다음 섹션에서 더 자세히 설명되어 있습니다. 토큰을 재설정한 후에는 프로젝트를 방문하고 작업이 다시 실행되기 시작해야 합니다.

경고:
이 섹션의 단계는 위에 나열된 항목에서 데이터 손실을 초래할 수 있습니다.
프리미엄 또는 얼티밋 고객인 경우 지원 요청을 여는 것을 고려하세요.

모든 값이 복호화될 수 있는지 확인

Rake 작업을 사용하여 데이터베이스에 복호화할 수 없는 값이 포함되어 있는지 확인할 수 있습니다.

백업 받기

손실된 비밀 파일을 우회하기 위해 GitLab 데이터를 직접 수정해야 합니다.

경고:
변경을 시도하기 전에 전체 데이터베이스 백업을 반드시 생성하세요.

사용자 이중 인증(2FA) 비활성화

2FA가 활성화된 사용자는 GitLab에 로그인할 수 없습니다. 이 경우, 모든 사용자에 대해 2FA 비활성화해야 하며, 이후 사용자는 2FA를 다시 활성화해야 합니다.

CI/CD 변수 재설정

  1. 데이터베이스 콘솔에 입력합니다:

    Linux 패키지(Omnibus)의 경우:

    sudo gitlab-rails dbconsole --database main
    

    셀프 컴파일 설치의 경우:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. ci_group_variablesci_variables 테이블을 확인합니다:

    SELECT * FROM public."ci_group_variables";
    SELECT * FROM public."ci_variables";
    

    삭제해야 하는 변수들입니다.

  3. 모든 변수를 삭제합니다:

    DELETE FROM ci_group_variables;
    DELETE FROM ci_variables;
    
  4. 변수를 삭제하려는 특정 그룹 또는 프로젝트를 아는 경우, DELETEWHERE 문을 포함시킬 수 있습니다:

    DELETE FROM ci_group_variables WHERE group_id = <GROUPID>;
    DELETE FROM ci_variables WHERE project_id = <PROJECTID>;
    

변경 사항이 효과를 발휘하려면 GitLab을 재구성하거나 재시작해야 할 수 있습니다.

실행자 등록 토큰 재설정

  1. 데이터베이스 콘솔에 입력합니다:

    Linux 패키지(Omnibus)의 경우:

    sudo gitlab-rails dbconsole --database main
    

    자체 컴파일 설치의 경우:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 프로젝트, 그룹 및 전체 인스턴스에 대한 모든 토큰을 지웁니다:

    경고: 마지막 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;
    

대기 중인 파이프라인 작업 재설정

  1. 데이터베이스 콘솔에 입력합니다:

    Linux 패키지(Omnibus)의 경우:

    sudo gitlab-rails dbconsole --database main
    

    자체 컴파일 설치의 경우:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 대기 중인 작업에 대한 모든 토큰을 지웁니다:

    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 오류를 발생시킬 수 있습니다.

수정 방법은 영향을 받은 테이블(암호화된 열을 포함하는 테이블)을 트렁크하고 삭제하는 것입니다. 이렇게 하면 구성된 모든 통합, 웹훅 및 관련 메타데이터가 삭제됩니다. 데이터를 삭제하기 전에 비밀 키가 근본 원인인지 확인해야 합니다.

  1. 데이터베이스 콘솔에 입력합니다:

    Linux 패키지(Omnibus)의 경우:

    sudo gitlab-rails dbconsole --database main
    

    자체 컴파일 설치의 경우:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 다음 테이블을 트렁크합니다:

    -- web_hooks 테이블 트렁크
    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로 실행되면서 발생하며,

복원 과정 중 레지스트리 파일에 올바른 소유권을 할당할 수 없습니다.

(issue #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

Backup failed

이런 경우, 다음을 확인하십시오:

  • Gzip 작업을 위한 충분한 디스크 여유 공간이 있는지 확인하세요.

백업 생성 중에 기본 전략을 사용하는 백업은

인스턴스 크기의 절반의 여유 디스크 공간을 요구하는 것이 드물지 않습니다.

  • NFS가 사용되고 있는 경우,

마운트 옵션 timeout이 설정되어 있는지 확인하세요. 기본값은 600이며,

이를 더 작은 값으로 변경하면 이 오류가 발생합니다.

파일 이름이 너무 깁니다 오류로 인한 백업 실패

백업 중에 파일 이름이 너무 깁니다 오류가 발생할 수 있습니다.

(issue #354984). 예를 들어:

문제: <class 'OSError: [Errno 36] 파일 이름이 너무 깁니다:

이 문제는 백업 스크립트의 완료를 중단시킵니다.

이 문제를 해결하려면 문제를 일으키는 파일 이름을 잘라야 합니다.

파일 확장자를 포함하여 최대 246자의 제한이 허용됩니다.

경고: 이 섹션의 단계는 데이터 손실을 초래할 수 있습니다. 모든 단계는 주어진 순서대로 엄격하게 따라야 합니다.

프리미엄 또는 얼티밋 고객인 경우, 지원 요청을 여는 것을 고려하세요.

오류를 해결하기 위해 파일 이름을 잘라내는 과정에는 다음이 포함됩니다:

  • 데이터베이스에서 추적되지 않는 원격 업로드 파일을 정리합니다.

  • 데이터베이스에서 파일 이름을 잘라냅니다.

  • 백업 작업을 다시 실행합니다.

원격 업로드 파일 정리

알려진 문제로 인해

부모 리소스가 삭제된 후에도 객체 저장소 업로드가 남아 있었습니다.

이 문제는 해결되었습니다.

이 파일을 수정하려면 저장소에 있지만 uploads 데이터베이스 테이블에서 추적되지 않는 모든 원격 업로드 파일을 정리해야 합니다.

  1. GitLab 데이터베이스에 존재하지 않는 경우

잃어버린 파일 및 찾기 디렉터리로 이동할 수 있는 모든 객체 저장소 업로드 파일을 나열하세요:

   bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production
  1. 이러한 파일을 삭제하고 모든 비참조 업로드 파일을 제거하려는 경우,

다음과 같이 실행하세요:

경고: 다음 작업은 되돌릴 수 없습니다.

   bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production DRY_RUN=false

데이터베이스에 참조된 파일 이름 자르기

문제를 일으키는 데이터베이스에 참조된 파일을 잘라야 합니다. 데이터베이스에 참조된 파일 이름은 다음에 저장되어 있습니다:

  • uploads 테이블에.
  • 발견된 참조에. 다른 데이터베이스 테이블과 열에서 발견된 참조.
  • 파일 시스템에서.

uploads 테이블에서 파일 이름 자르기:

  1. 데이터베이스 콘솔에 들어가기:

    리눅스 패키지(Omnibus)용:

    sudo gitlab-rails dbconsole --database main
    

    자가 컴파일한 설치의 경우:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 246자보다 긴 파일 이름에 대해 uploads 테이블을 검색합니다.

    다음 쿼리는 246자보다 긴 파일 이름을 가진 uploads 레코드를 0부터 10000까지 배치로 선택합니다. 이는 수천 개의 레코드를 가진 큰 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 테이블의 마지막 레코드에 도달할 때까지 반복합니다.

  3. 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 테이블의 마지막 레코드에 도달할 때까지 반복합니다.

  4. 이전 쿼리에서 새로운 파일 이름이 예상한 것인지 확인합니다. 이전 단계에서 찾은 레코드를 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;
    

    배치 업데이트가 완료된 후, 배치 크기(updatable_uploads.row_id)를 다음 숫자 시퀀스를 사용하여 변경해야 합니다(10000에서 20000). 이 과정을 uploads 테이블의 마지막 레코드에 도달할 때까지 반복합니다.

찾은 참조에서 파일 이름 자르기:

  1. 이러한 레코드가 어디선가 참조되고 있는지 확인합니다. 이를 확인하는 한 가지 방법은 데이터베이스를 덤프하고 상위 디렉토리 이름과 파일 이름을 검색하는 것입니다:

    1. 데이터베이스를 덤프하려면 다음 명령을 사용할 수 있습니다:

      pg_dump -h /var/opt/gitlab/postgresql/ -d gitlabhq_production > gitlab-dump.tmp
      
    2. 그런 다음 grep 명령을 사용하여 참조를 검색할 수 있습니다. 상위 디렉토리와 파일 이름을 결합하는 것이 좋습니다. 예를 들면:

      grep public/alongfilenamehere.txt gitlab-dump.tmp
      
  2. 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_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에 대한 참조 제거하기

기존 백업 파일을 수정하려면 다음 변경 사항을 수행하십시오:

  1. 백업에서 다음 파일을 추출합니다: db/database.sql.gz.

  2. 파일을 압축 해제하거나 압축된 파일을 처리할 수 있는 편집기를 사용합니다.

  3. 다음 줄 또는 유사한 줄을 제거합니다:

    CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
    
    COMMENT ON EXTENSION pg_stat_statements IS '모든 실행된 SQL 문장의 계획 및 실행 통계를 추적합니다';
    
  4. 변경 사항을 저장하고 파일을 다시 압축합니다.

  5. 수정된 db/database.sql.gz로 백업 파일을 업데이트합니다.