InfoGrab DocsInfoGrab Docs

저장소 미러링 문제 해결

요약

미러링이 실패하면 GitLab은 프로젝트 세부 정보 페이지에 경고를 표시합니다. 문제가 있는 리포지터리 옆에는 Error 배지가 표시됩니다. GitHub 리포지터리로 미러링할 때 다음 메시지가 표시되는 경우가 있습니다.

미러링이 실패하면 GitLab은 프로젝트 세부 정보 페이지에 경고를 표시합니다. 예를 들면 다음과 같습니다. [warning-solid] Pull mirroring failed 1 hour ago. 경고 문구를 선택하면 Mirroring repositories 설정으로 이동합니다.

문제가 있는 리포지터리 옆에는 Error 배지가 표시됩니다. 배지 위에 마우스를 올리면 오류 메시지를 볼 수 있습니다. 오류 메시지에는 인증 실패나 분기된 브랜치처럼 흔한 문제의 구체적인 정보가 담겨 있습니다. 그 밖의 오류는 Git 작업에서 직접 전달되기도 합니다.

GitHub 미러링 시 오류 코드 2와 함께 RST_STREAM 수신#

GitHub 리포지터리로 미러링할 때 다음 메시지가 표시되는 경우가 있습니다.

13:Received RST_STREAM with error code 2

원인은 다음 중 하나일 수 있습니다.

  1. 커밋에 사용된 이메일 주소를 노출하는 푸시를 차단하도록 GitHub 설정이 되어 있을 수 있습니다. 이 문제를 해결하려면 다음 중 하나를 수행합니다.
  2. 리포지터리가 GitHub의 파일 크기 제한인 100MB를 초과했을 수 있습니다. 이 문제를 해결하려면 GitHub에 설정된 파일 크기 제한을 확인하고, 큰 파일은 Git Large File Storage(LFS)로 관리하는 방안을 검토합니다.

기한 초과#

GitLab을 업그레이드하면 사용자 이름 표현 방식이 바뀌므로, 미러링 사용자 이름과 비밀번호에서 %40 문자가 @로 바뀌도록 값을 업데이트해야 합니다.

연결 차단됨: 서버가 공개 키 인증만 허용함#

GitLab과 원격 리포지터리 사이의 연결이 차단되었습니다. TCP 점검에 성공하더라도, GitLab에서 원격 서버로 가는 경로의 모든 네트워크 구성 요소에서 차단이 있는지 확인해야 합니다.

이 오류는 방화벽이 나가는 패킷에 Deep SSH Inspection을 수행할 때 발생할 수 있습니다.

사용자 이름을 읽을 수 없음: 터미널 프롬프트 비활성화됨#

외부 리포지터리용 GitLab CI/CD로 새 프로젝트를 만든 뒤 다음 오류가 표시되는 경우가 있습니다.

  • Bitbucket Cloud의 경우:

    "2:fetch remote: "fatal: could not read Username for 'https://bitbucket.org':
    terminal prompts disabled\n": exit status 128."
    
  • Bitbucket Server(자체 호스팅)의 경우:

    "2:fetch remote: "fatal: could not read Username for 'https://lab.example.com':
    terminal prompts disabled\n": exit status 128.
    

미러링하는 리포지터리 URL에 리포지터리 소유자가 지정되어 있는지 확인합니다.

  1. 상단 바에서 Search or go to를 선택하고 프로젝트를 찾습니다.

  2. 왼쪽 사이드바에서 Settings > Repository를 선택합니다.

  3. Mirroring repositories를 확장합니다.

  4. 리포지터리 소유자가 지정되어 있지 않다면 URL을 삭제한 뒤 다음 형식으로 다시 추가합니다. OWNER, ACCOUNTNAME, PATH_TO_REPO, REPONAME은 실제 값으로 바꿉니다.

    • Bitbucket Cloud의 경우:

      https://OWNER@bitbucket.org/ACCOUNTNAME/REPONAME.git
      
    • Bitbucket Server(자체 호스팅)의 경우:

      https://OWNER@lab.example.com/PATH_TO_REPO/REPONAME.git
      

미러링을 위해 Cloud 또는 자체 호스팅 Bitbucket 리포지터리에 연결할 때는 문자열에 리포지터리 소유자가 반드시 있어야 합니다.

푸시 미러: LFS objects are missing#

다음과 같은 오류가 발생할 수 있습니다.

GitLab: GitLab: LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".

이 문제는 푸시 미러링에 SSH 리포지터리 URL을 사용할 때 발생합니다. SSH로 LFS 파일을 전송하는 푸시 미러링은 지원하지 않습니다.

푸시 미러에 SSH 대신 HTTPS 리포지터리 URL을 사용하면 문제를 우회할 수 있습니다.

이 문제를 해결하기 위한 이슈 249587이 등록되어 있습니다.

오류: Committer is not a member of team#

다음과 같은 오류가 발생할 수 있습니다.

remote: GitLab: Committer 'noreply@example.com' is not a member of team

이 문제는 소스 프로젝트가 GitLab UI에서 만든 커밋에 서명하고, 대상 프로젝트에서 Check whether the commit author is a GitLab user 푸시 규칙이 켜져 있을 때 발생합니다. GitLab 이 서명한 커밋은 커미터가 인스턴스 이메일 주소이며, 이 주소는 GitLab 사용자에 속하지 않습니다.

GitLab 19.3 이상에서는 이 문제가 해결되었습니다. 푸시 규칙이 GitLab 이 서명한 커밋의 커미터 이메일 주소를 더 이상 검사하지 않습니다. 이전 버전에서는 대상 프로젝트의 해당 푸시 규칙을 끄는 방법으로 우회합니다.

풀 미러에 LFS 파일이 누락됨#

풀 미러링이 LFS 파일을 전송하지 않는 경우가 있습니다. 이 문제는 SSH 리포지터리 URL을 사용할 때 발생합니다.

대신 HTTPS 리포지터리 URL을 사용하면 문제를 우회할 수 있습니다.

풀 미러링이 파이프라인을 트리거하지 않음#

파이프라인이 실행되지 않는 원인은 여러 가지일 수 있습니다.

The repository is being updated 상태에서 실패도 성공도 드러나지 않음#

드물게 Redis의 미러링 슬롯이 모두 소진될 수 있습니다. 메모리 부족(OoM) 때문에 Sidekiq 워커가 회수되는 경우가 원인일 수 있습니다. 이 경우 미러링 job 이 시작된 뒤 곧바로 끝나지만 실패도 성공도 하지 않습니다. 명확한 로그도 남지 않습니다. 이 문제를 확인하려면 다음을 수행합니다.

  1. Rails 콘솔에 들어가 Redis 미러링 용량을 확인합니다.

    current = Gitlab::Redis::SharedState.with { |redis| redis.scard('MIRROR_PULL_CAPACITY') }.to_i
    maximum = Gitlab::CurrentSettings.mirror_max_capacity
    available = maximum - current
    
  2. 미러링 용량이 0 이거나 매우 낮다면 다음으로 멈춘 job을 모두 비울 수 있습니다.

    Gitlab::Redis::SharedState.with { |redis| redis.smembers('MIRROR_PULL_CAPACITY') }.each do |pid|
      Gitlab::Redis::SharedState.with { |redis| redis.srem('MIRROR_PULL_CAPACITY', pid) }
    end
    
  3. 명령을 실행한 뒤에는 백그라운드 job 페이지에 새 미러링 job 이 예약되는 것이 보여야 합니다. 특히 수동으로 트리거했을 때 그렇습니다.

유효하지 않은 URL#

SSH로 미러링을 설정할 때 이 오류가 발생하면 URL 형식이 올바른지 확인합니다.

미러링은 호스트와 프로젝트 경로를 :로 구분하는 git@gitlab.com:gitlab-org/gitlab.git 형태의 SCP 유사 클론 URL을 지원하지 않습니다. ssh://git@gitlab.com/gitlab-org/gitlab.git처럼 ssh:// 프로토콜을 포함한 표준 URL이 필요합니다.

호스트 키 확인 실패#

이 오류는 대상 호스트의 공개 SSH 키가 바뀌었을 때 반환됩니다. 공개 SSH 키는 거의 바뀌지 않습니다. 호스트 키 확인에 실패했지만 키가 여전히 유효하다고 판단된다면 리포지터리 미러를 삭제한 뒤 다시 만들어야 합니다. 자세한 내용은 리포지터리 미러 생성을 참고합니다.

미러 사용자가 삭제되어 리포지터리 미러링이 비활성화됨#

다음과 비슷한 이메일 알림을 받을 수 있습니다.

Repository mirroring on <project_path> was disabled because the mirror user <username> was deleted.

To re-enable mirroring, update your repository mirroring settings.

이 문제는 각 미러가 그것을 설정한 사용자에 묶여 있기 때문에 발생합니다. 해당 사용자의 계정이 삭제되면 GitLab은 미러를 자동으로 비활성화합니다. 미러를 만드는 데 사용한 그룹 액세스 토큰이나 프로젝트 액세스 토큰을 폐기해도 연결된 봇 사용자가 함께 삭제되므로 같은 동작이 일어납니다.

미러를 다른 사용자에게 재할당할 수는 없습니다. 이 문제를 해결하려면 다른 사용자로 미러를 다시 설정합니다.

자세한 내용은 이슈 488449를 참고합니다.

미러 사용자와 토큰을 단일 서비스 계정으로 이전#

이 작업에는 GitLab Rails 콘솔 접근 권한이 필요합니다.

활용 사례: 여러 사용자가 각자의 GitHub 자격 증명으로 리포지터리 미러링을 설정해 두면, 그 사용자가 퇴사할 때 미러링이 끊깁니다. 다음 스크립트로 흩어진 미러링 사용자와 토큰을 단일 서비스 계정으로 이전합니다.

Warning

데이터를 변경하는 명령은 올바르게 실행하지 않거나 적절한 조건이 아닐 때 실행하면 손상을 일으킬 수 있습니다. 항상 테스트 환경에서 먼저 실행하고, 복원할 백업 인스턴스를 준비해 둡니다.

svc_user = User.find_by(username: 'ourServiceUser')
token = 'githubAccessToken'

Project.where(mirror: true).each do |project|
  import_url = project.unsafe_import_url

  # The expected url output is https://token@project/path.git
  repo_url = if import_url.include?('@')
               # Case 1: The url is something like https://23423432@project/path.git
               import_url.split('@').last
             elsif import_url.include?('//')
               # Case 2: The url is something like https://project/path.git
               import_url.split('//').last
             end

  next unless repo_url

  final_url = "https://#{token}@#{repo_url}"

  project.mirror_user = svc_user
  project.import_url = final_url
  project.username_only_import_url = final_url
  project.save
end

The requested URL returned error: 301#

http:// 또는 https:// 프로토콜로 미러링할 때는 리포지터리의 정확한 URL을 지정해야 합니다. 예를 들면 https://gitlab.example.com/group/project.git 입니다.

HTTP 리다이렉트는 따라가지 않으므로 .git을 빠뜨리면 301 오류가 발생할 수 있습니다.

13:fetch remote: "fatal: unable to access 'https://gitlab.com/group/project': The requested URL returned error: 301\n": exit status 128.

GitLab 인스턴스에서 Geo 보조 노드로 푸시 미러 실패#

대상이 Geo 보조 노드이면 푸시 요청이 Geo 기본 노드로 프록시되기 때문에 HTTP 또는 HTTPS 프로토콜을 사용하는 GitLab 리포지터리 푸시 미러링이 실패하고, 다음 오류가 표시됩니다.

13:get remote references: create git ls-remote: exit status 128, stderr: "fatal: unable to access 'https://gitlab.example.com/group/destination.git/': The requested URL returned error: 302".

이 현상은 Geo 통합 URL 이 설정되어 있고 대상 호스트 이름이 보조 노드의 IP 주소로 확인될 때 발생합니다.

다음 방법으로 이 오류를 피할 수 있습니다.

  • 푸시 미러가 SSH 프로토콜을 사용하도록 설정합니다. 다만 LFS 오브젝트는 항상 HTTP 또는 HTTPS로 전송되어 여전히 리다이렉트되므로, 리포지터리에 LFS 오브젝트가 없어야 합니다.
  • 리버스 프록시로 소스 인스턴스의 모든 요청을 Geo 기본 노드로 보냅니다.
  • 소스에 로컬 hosts 파일 항목을 추가해 대상 호스트 이름이 Geo 기본 노드의 IP 주소로 확인되도록 합니다.
  • 대신 대상에 풀 미러를 설정합니다.

풀 또는 푸시 미러 업데이트 실패: The project is not mirrored#

GitLab Silent Mode가 켜져 있으면 풀 미러와 푸시 미러가 업데이트되지 않습니다. 이 경우 UI에서 미러링을 허용하는 옵션이 비활성화됩니다.

관리자는 GitLab Silent Mode가 꺼져 있는지 확인할 수 있습니다.

Silent Mode 때문에 미러링이 실패할 때의 디버그 절차는 다음과 같습니다.

  • API로 미러를 트리거하면 The project is not mirrored가 표시됩니다.
  • 풀 또는 푸시 미러를 이미 설정했는데 미러링된 리포지터리에 더 이상 업데이트가 없다면, 프로젝트의 풀·푸시 미러 정보와 상태가 아래처럼 최신이 아닌지 확인합니다. 이는 미러링이 일시 중지되었다는 뜻이며, GitLab Silent Mode를 끄면 자동으로 다시 시작됩니다.

예를 들어 가져오기를 막고 있는 것이 Silent Mode 라면 출력은 다음과 비슷합니다.

"id": 1,
"update_status": "finished",
"url": "https://test.git"
"last_error": null,
"last_update_at": null,
"last_update_started_at": "2023-12-12T00:01:02.222Z",
"last_successful_update_at": null

초기 미러링 실패: Unable to pull mirror repo: Unable to get pack index#

다음과 비슷한 오류가 발생할 수 있습니다.

13:fetch remote: "error: Unable to open local file /var/opt/gitlab/git-data/repositories/+gitaly/tmp/quarantine-[OMITTED].idx.temp.temp\nerror: Unable to get pack index https://git.example.org/ebtables/objects/pack/pack-[OMITTED].idx\nerror: Unable to find fcde2b2edba56bf408601fb721fe9b5c338d10ee under https://git.example.org/ebtables
Cannot obtain needed object fcde2b2edba56bf408601fb721fe9b5c338d10ee
while processing commit 2c26b46b68ffc68ff99b453c1d30413413422d70.
error: fetch failed.\n": exit status 128.

이 문제는 Gitaly가 "dumb" HTTP 프로토콜을 통한 리포지터리 미러링이나 가져오기를 지원하지 않기 때문에 발생합니다.

서버가 "smart" 인지 "dumb" 인지 확인하려면 cURL로 git-upload-pack 서비스의 레퍼런스 디스커버리를 시작해 Git "smart" 클라이언트를 흉내 냅니다.

$GIT_URL="https://git.example.org/project"
curl --silent --dump-header - "$GIT_URL/info/refs?service=git-upload-pack"\
  -o /dev/null | grep -Ei "$content-type:"
  • "smart" 서버는 Content-Type 응답 헤더에 application/x-git-upload-pack-advertisement를 반환합니다.
  • "dumb" 서버는 Content-Type 응답 헤더에 text/plain을 반환합니다.

자세한 내용은 레퍼런스 디스커버리에 관한 Git 문서를 참고합니다.

이 문제를 해결하려면 다음 중 하나를 수행합니다.

  • 소스 리포지터리를 "smart" 서버로 이전합니다.
  • SSH 프로토콜로 리포지터리를 미러링합니다(인증이 필요합니다).

풀 미러링이 Could not update main과 함께 실패함#

다음과 비슷한 오류가 발생할 수 있습니다.

  • 9:Could not update main. Please refresh and try again.

이 현상은 대상 리포지터리가 완전히 비어 있지 않을 때 최초 풀 미러 업데이트에서 발생할 수 있습니다.

예를 들어 대상 리포지터리에 다음이 이미 있을 수 있습니다.

  • 최초 README 커밋입니다.
  • 라이선스 또는 .gitignore 파일입니다.
  • 기존 브랜치 또는 태그입니다.

이 문제를 해결하려면 다음을 수행합니다.

  1. 대상 리포지터리를 빈 리포지터리로 다시 만듭니다.
  2. 리포지터리가 소스 리포지터리와 같은 오브젝트 형식을 사용하는지 확인합니다.
  3. README, 라이선스, .gitignore로 리포지터리를 초기화하지 않습니다.
  4. 풀 미러링을 다시 설정하고 Update now를 실행합니다.

기존 대상 리포지터리를 그대로 유지해야 한다면, Overwrite diverged branches를 켜고 미러링 사용자가 대상 브랜치를 덮어쓸 권한이 있는지 확인하는 방법으로 우회합니다.

오류: mismatched algorithms#

다음과 비슷한 오류가 발생할 수 있습니다.

13:fetch remote: "fatal: mismatched algorithms: client sha1; server sha256": exit status 128.

이 오류는 소스 리포지터리와 대상 리포지터리가 서로 다른 오브젝트 형식을 사용할 때 발생하며, 오브젝트 형식이 다른 리포지터리 사이의 풀 미러링은 지원하지 않습니다.

예를 들면 다음과 같습니다.

  • 소스 리포지터리는 SHA-1을 사용합니다.
  • 대상 리포지터리는 SHA-256을 사용하거나 그 반대입니다.

이 문제를 해결하려면 대상 리포지터리를 소스 리포지터리와 같은 오브젝트 형식으로 다시 만든 다음 미러링을 다시 설정합니다.

오류: File directory conflict#

다음과 비슷한 오류가 발생할 수 있습니다.

13:preparing reference update: file directory conflict

이 오류는 소스 리포지터리와 미러 리포지터리 사이에 태그 또는 브랜치 이름 충돌이 있을 때 발생합니다. 예를 들면 다음과 같습니다.

  • 미러 리포지터리에 x/y라는 태그나 브랜치가 있습니다.
  • 소스 리포지터리에 x라는 태그나 브랜치가 있습니다.

이 문제를 해결하려면 충돌하는 태그나 브랜치를 삭제합니다. 충돌하는 태그나 브랜치를 특정할 수 없다면 미러 리포지터리의 태그를 모두 삭제합니다. 분기된 브랜치 덮어쓰기도 대안이 될 수 있습니다.

Note

태그를 삭제하면 미러 리포지터리에서 수행한 작업이 손상될 수 있습니다.

미러 리포지터리의 태그를 모두 삭제하고 제거하려면 다음을 수행합니다.

  1. 미러링된 리포지터리의 로컬 사본에서 다음을 실행합니다.

    git tag -l | xargs -n 1 git push --delete origin
    
  2. 왼쪽 사이드바에서 Settings > Repository를 선택합니다.

  3. Mirroring repositories를 확장합니다.

  4. Update now ([retry])를 선택합니다.

대용량 LFS 파일로 푸시 미러링이 멈춤#

대용량 LFS 오브젝트가 포함된 프로젝트를 푸시 미러링하면 타임아웃 문제가 발생할 수 있습니다. 이 문제는 Git LFS 작업이 기본 활동 타임아웃을 초과할 때 발생합니다. 미러링 로그에는 다음 오류가 표시됩니다.

push to mirror: git push: exit status 1, stderr: "remote: GitLab: LFS objects are missing. Ensure LFS is properly set up or try a manual \"git lfs push --all\""

이 문제를 해결하려면 미러를 설정하기 전에 LFS 활동 타임아웃 값을 늘립니다.

git config lfs.activitytimeout 240

이 명령은 타임아웃을 240 초로 설정합니다. 파일 크기와 네트워크 상황에 맞춰 값을 조정할 수 있습니다.

저장소 미러링 문제 해결

GitLab v19.5
Tier: Free, Premium, Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
원문 보기

요약

미러링이 실패하면 GitLab은 프로젝트 세부 정보 페이지에 경고를 표시합니다. 문제가 있는 리포지터리 옆에는 Error 배지가 표시됩니다. GitHub 리포지터리로 미러링할 때 다음 메시지가 표시되는 경우가 있습니다.

미러링이 실패하면 GitLab은 프로젝트 세부 정보 페이지에 경고를 표시합니다. 예를 들면 다음과 같습니다. [warning-solid] Pull mirroring failed 1 hour ago. 경고 문구를 선택하면 Mirroring repositories 설정으로 이동합니다.

문제가 있는 리포지터리 옆에는 Error 배지가 표시됩니다. 배지 위에 마우스를 올리면 오류 메시지를 볼 수 있습니다. 오류 메시지에는 인증 실패나 분기된 브랜치처럼 흔한 문제의 구체적인 정보가 담겨 있습니다. 그 밖의 오류는 Git 작업에서 직접 전달되기도 합니다.

GitHub 미러링 시 오류 코드 2와 함께 RST_STREAM 수신#

GitHub 리포지터리로 미러링할 때 다음 메시지가 표시되는 경우가 있습니다.

13:Received RST_STREAM with error code 2

원인은 다음 중 하나일 수 있습니다.

  1. 커밋에 사용된 이메일 주소를 노출하는 푸시를 차단하도록 GitHub 설정이 되어 있을 수 있습니다. 이 문제를 해결하려면 다음 중 하나를 수행합니다.
  2. 리포지터리가 GitHub의 파일 크기 제한인 100MB를 초과했을 수 있습니다. 이 문제를 해결하려면 GitHub에 설정된 파일 크기 제한을 확인하고, 큰 파일은 Git Large File Storage(LFS)로 관리하는 방안을 검토합니다.

기한 초과#

GitLab을 업그레이드하면 사용자 이름 표현 방식이 바뀌므로, 미러링 사용자 이름과 비밀번호에서 %40 문자가 @로 바뀌도록 값을 업데이트해야 합니다.

연결 차단됨: 서버가 공개 키 인증만 허용함#

GitLab과 원격 리포지터리 사이의 연결이 차단되었습니다. TCP 점검에 성공하더라도, GitLab에서 원격 서버로 가는 경로의 모든 네트워크 구성 요소에서 차단이 있는지 확인해야 합니다.

이 오류는 방화벽이 나가는 패킷에 Deep SSH Inspection을 수행할 때 발생할 수 있습니다.

사용자 이름을 읽을 수 없음: 터미널 프롬프트 비활성화됨#

외부 리포지터리용 GitLab CI/CD로 새 프로젝트를 만든 뒤 다음 오류가 표시되는 경우가 있습니다.

  • Bitbucket Cloud의 경우:

    "2:fetch remote: "fatal: could not read Username for 'https://bitbucket.org':
    terminal prompts disabled\n": exit status 128."
    
  • Bitbucket Server(자체 호스팅)의 경우:

    "2:fetch remote: "fatal: could not read Username for 'https://lab.example.com':
    terminal prompts disabled\n": exit status 128.
    

미러링하는 리포지터리 URL에 리포지터리 소유자가 지정되어 있는지 확인합니다.

  1. 상단 바에서 Search or go to를 선택하고 프로젝트를 찾습니다.

  2. 왼쪽 사이드바에서 Settings > Repository를 선택합니다.

  3. Mirroring repositories를 확장합니다.

  4. 리포지터리 소유자가 지정되어 있지 않다면 URL을 삭제한 뒤 다음 형식으로 다시 추가합니다. OWNER, ACCOUNTNAME, PATH_TO_REPO, REPONAME은 실제 값으로 바꿉니다.

    • Bitbucket Cloud의 경우:

      https://OWNER@bitbucket.org/ACCOUNTNAME/REPONAME.git
      
    • Bitbucket Server(자체 호스팅)의 경우:

      https://OWNER@lab.example.com/PATH_TO_REPO/REPONAME.git
      

미러링을 위해 Cloud 또는 자체 호스팅 Bitbucket 리포지터리에 연결할 때는 문자열에 리포지터리 소유자가 반드시 있어야 합니다.

푸시 미러: LFS objects are missing#

다음과 같은 오류가 발생할 수 있습니다.

GitLab: GitLab: LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".

이 문제는 푸시 미러링에 SSH 리포지터리 URL을 사용할 때 발생합니다. SSH로 LFS 파일을 전송하는 푸시 미러링은 지원하지 않습니다.

푸시 미러에 SSH 대신 HTTPS 리포지터리 URL을 사용하면 문제를 우회할 수 있습니다.

이 문제를 해결하기 위한 이슈 249587이 등록되어 있습니다.

오류: Committer is not a member of team#

다음과 같은 오류가 발생할 수 있습니다.

remote: GitLab: Committer 'noreply@example.com' is not a member of team

이 문제는 소스 프로젝트가 GitLab UI에서 만든 커밋에 서명하고, 대상 프로젝트에서 Check whether the commit author is a GitLab user 푸시 규칙이 켜져 있을 때 발생합니다. GitLab 이 서명한 커밋은 커미터가 인스턴스 이메일 주소이며, 이 주소는 GitLab 사용자에 속하지 않습니다.

GitLab 19.3 이상에서는 이 문제가 해결되었습니다. 푸시 규칙이 GitLab 이 서명한 커밋의 커미터 이메일 주소를 더 이상 검사하지 않습니다. 이전 버전에서는 대상 프로젝트의 해당 푸시 규칙을 끄는 방법으로 우회합니다.

풀 미러에 LFS 파일이 누락됨#

풀 미러링이 LFS 파일을 전송하지 않는 경우가 있습니다. 이 문제는 SSH 리포지터리 URL을 사용할 때 발생합니다.

대신 HTTPS 리포지터리 URL을 사용하면 문제를 우회할 수 있습니다.

풀 미러링이 파이프라인을 트리거하지 않음#

파이프라인이 실행되지 않는 원인은 여러 가지일 수 있습니다.

The repository is being updated 상태에서 실패도 성공도 드러나지 않음#

드물게 Redis의 미러링 슬롯이 모두 소진될 수 있습니다. 메모리 부족(OoM) 때문에 Sidekiq 워커가 회수되는 경우가 원인일 수 있습니다. 이 경우 미러링 job 이 시작된 뒤 곧바로 끝나지만 실패도 성공도 하지 않습니다. 명확한 로그도 남지 않습니다. 이 문제를 확인하려면 다음을 수행합니다.

  1. Rails 콘솔에 들어가 Redis 미러링 용량을 확인합니다.

    current = Gitlab::Redis::SharedState.with { |redis| redis.scard('MIRROR_PULL_CAPACITY') }.to_i
    maximum = Gitlab::CurrentSettings.mirror_max_capacity
    available = maximum - current
    
  2. 미러링 용량이 0 이거나 매우 낮다면 다음으로 멈춘 job을 모두 비울 수 있습니다.

    Gitlab::Redis::SharedState.with { |redis| redis.smembers('MIRROR_PULL_CAPACITY') }.each do |pid|
      Gitlab::Redis::SharedState.with { |redis| redis.srem('MIRROR_PULL_CAPACITY', pid) }
    end
    
  3. 명령을 실행한 뒤에는 백그라운드 job 페이지에 새 미러링 job 이 예약되는 것이 보여야 합니다. 특히 수동으로 트리거했을 때 그렇습니다.

유효하지 않은 URL#

SSH로 미러링을 설정할 때 이 오류가 발생하면 URL 형식이 올바른지 확인합니다.

미러링은 호스트와 프로젝트 경로를 :로 구분하는 git@gitlab.com:gitlab-org/gitlab.git 형태의 SCP 유사 클론 URL을 지원하지 않습니다. ssh://git@gitlab.com/gitlab-org/gitlab.git처럼 ssh:// 프로토콜을 포함한 표준 URL이 필요합니다.

호스트 키 확인 실패#

이 오류는 대상 호스트의 공개 SSH 키가 바뀌었을 때 반환됩니다. 공개 SSH 키는 거의 바뀌지 않습니다. 호스트 키 확인에 실패했지만 키가 여전히 유효하다고 판단된다면 리포지터리 미러를 삭제한 뒤 다시 만들어야 합니다. 자세한 내용은 리포지터리 미러 생성을 참고합니다.

미러 사용자가 삭제되어 리포지터리 미러링이 비활성화됨#

다음과 비슷한 이메일 알림을 받을 수 있습니다.

Repository mirroring on <project_path> was disabled because the mirror user <username> was deleted.

To re-enable mirroring, update your repository mirroring settings.

이 문제는 각 미러가 그것을 설정한 사용자에 묶여 있기 때문에 발생합니다. 해당 사용자의 계정이 삭제되면 GitLab은 미러를 자동으로 비활성화합니다. 미러를 만드는 데 사용한 그룹 액세스 토큰이나 프로젝트 액세스 토큰을 폐기해도 연결된 봇 사용자가 함께 삭제되므로 같은 동작이 일어납니다.

미러를 다른 사용자에게 재할당할 수는 없습니다. 이 문제를 해결하려면 다른 사용자로 미러를 다시 설정합니다.

자세한 내용은 이슈 488449를 참고합니다.

미러 사용자와 토큰을 단일 서비스 계정으로 이전#

이 작업에는 GitLab Rails 콘솔 접근 권한이 필요합니다.

활용 사례: 여러 사용자가 각자의 GitHub 자격 증명으로 리포지터리 미러링을 설정해 두면, 그 사용자가 퇴사할 때 미러링이 끊깁니다. 다음 스크립트로 흩어진 미러링 사용자와 토큰을 단일 서비스 계정으로 이전합니다.

Warning

데이터를 변경하는 명령은 올바르게 실행하지 않거나 적절한 조건이 아닐 때 실행하면 손상을 일으킬 수 있습니다. 항상 테스트 환경에서 먼저 실행하고, 복원할 백업 인스턴스를 준비해 둡니다.

svc_user = User.find_by(username: 'ourServiceUser')
token = 'githubAccessToken'

Project.where(mirror: true).each do |project|
  import_url = project.unsafe_import_url

  # The expected url output is https://token@project/path.git
  repo_url = if import_url.include?('@')
               # Case 1: The url is something like https://23423432@project/path.git
               import_url.split('@').last
             elsif import_url.include?('//')
               # Case 2: The url is something like https://project/path.git
               import_url.split('//').last
             end

  next unless repo_url

  final_url = "https://#{token}@#{repo_url}"

  project.mirror_user = svc_user
  project.import_url = final_url
  project.username_only_import_url = final_url
  project.save
end

The requested URL returned error: 301#

http:// 또는 https:// 프로토콜로 미러링할 때는 리포지터리의 정확한 URL을 지정해야 합니다. 예를 들면 https://gitlab.example.com/group/project.git 입니다.

HTTP 리다이렉트는 따라가지 않으므로 .git을 빠뜨리면 301 오류가 발생할 수 있습니다.

13:fetch remote: "fatal: unable to access 'https://gitlab.com/group/project': The requested URL returned error: 301\n": exit status 128.

GitLab 인스턴스에서 Geo 보조 노드로 푸시 미러 실패#

대상이 Geo 보조 노드이면 푸시 요청이 Geo 기본 노드로 프록시되기 때문에 HTTP 또는 HTTPS 프로토콜을 사용하는 GitLab 리포지터리 푸시 미러링이 실패하고, 다음 오류가 표시됩니다.

13:get remote references: create git ls-remote: exit status 128, stderr: "fatal: unable to access 'https://gitlab.example.com/group/destination.git/': The requested URL returned error: 302".

이 현상은 Geo 통합 URL 이 설정되어 있고 대상 호스트 이름이 보조 노드의 IP 주소로 확인될 때 발생합니다.

다음 방법으로 이 오류를 피할 수 있습니다.

  • 푸시 미러가 SSH 프로토콜을 사용하도록 설정합니다. 다만 LFS 오브젝트는 항상 HTTP 또는 HTTPS로 전송되어 여전히 리다이렉트되므로, 리포지터리에 LFS 오브젝트가 없어야 합니다.
  • 리버스 프록시로 소스 인스턴스의 모든 요청을 Geo 기본 노드로 보냅니다.
  • 소스에 로컬 hosts 파일 항목을 추가해 대상 호스트 이름이 Geo 기본 노드의 IP 주소로 확인되도록 합니다.
  • 대신 대상에 풀 미러를 설정합니다.

풀 또는 푸시 미러 업데이트 실패: The project is not mirrored#

GitLab Silent Mode가 켜져 있으면 풀 미러와 푸시 미러가 업데이트되지 않습니다. 이 경우 UI에서 미러링을 허용하는 옵션이 비활성화됩니다.

관리자는 GitLab Silent Mode가 꺼져 있는지 확인할 수 있습니다.

Silent Mode 때문에 미러링이 실패할 때의 디버그 절차는 다음과 같습니다.

  • API로 미러를 트리거하면 The project is not mirrored가 표시됩니다.
  • 풀 또는 푸시 미러를 이미 설정했는데 미러링된 리포지터리에 더 이상 업데이트가 없다면, 프로젝트의 풀·푸시 미러 정보와 상태가 아래처럼 최신이 아닌지 확인합니다. 이는 미러링이 일시 중지되었다는 뜻이며, GitLab Silent Mode를 끄면 자동으로 다시 시작됩니다.

예를 들어 가져오기를 막고 있는 것이 Silent Mode 라면 출력은 다음과 비슷합니다.

"id": 1,
"update_status": "finished",
"url": "https://test.git"
"last_error": null,
"last_update_at": null,
"last_update_started_at": "2023-12-12T00:01:02.222Z",
"last_successful_update_at": null

초기 미러링 실패: Unable to pull mirror repo: Unable to get pack index#

다음과 비슷한 오류가 발생할 수 있습니다.

13:fetch remote: "error: Unable to open local file /var/opt/gitlab/git-data/repositories/+gitaly/tmp/quarantine-[OMITTED].idx.temp.temp\nerror: Unable to get pack index https://git.example.org/ebtables/objects/pack/pack-[OMITTED].idx\nerror: Unable to find fcde2b2edba56bf408601fb721fe9b5c338d10ee under https://git.example.org/ebtables
Cannot obtain needed object fcde2b2edba56bf408601fb721fe9b5c338d10ee
while processing commit 2c26b46b68ffc68ff99b453c1d30413413422d70.
error: fetch failed.\n": exit status 128.

이 문제는 Gitaly가 "dumb" HTTP 프로토콜을 통한 리포지터리 미러링이나 가져오기를 지원하지 않기 때문에 발생합니다.

서버가 "smart" 인지 "dumb" 인지 확인하려면 cURL로 git-upload-pack 서비스의 레퍼런스 디스커버리를 시작해 Git "smart" 클라이언트를 흉내 냅니다.

$GIT_URL="https://git.example.org/project"
curl --silent --dump-header - "$GIT_URL/info/refs?service=git-upload-pack"\
  -o /dev/null | grep -Ei "$content-type:"
  • "smart" 서버는 Content-Type 응답 헤더에 application/x-git-upload-pack-advertisement를 반환합니다.
  • "dumb" 서버는 Content-Type 응답 헤더에 text/plain을 반환합니다.

자세한 내용은 레퍼런스 디스커버리에 관한 Git 문서를 참고합니다.

이 문제를 해결하려면 다음 중 하나를 수행합니다.

  • 소스 리포지터리를 "smart" 서버로 이전합니다.
  • SSH 프로토콜로 리포지터리를 미러링합니다(인증이 필요합니다).

풀 미러링이 Could not update main과 함께 실패함#

다음과 비슷한 오류가 발생할 수 있습니다.

  • 9:Could not update main. Please refresh and try again.

이 현상은 대상 리포지터리가 완전히 비어 있지 않을 때 최초 풀 미러 업데이트에서 발생할 수 있습니다.

예를 들어 대상 리포지터리에 다음이 이미 있을 수 있습니다.

  • 최초 README 커밋입니다.
  • 라이선스 또는 .gitignore 파일입니다.
  • 기존 브랜치 또는 태그입니다.

이 문제를 해결하려면 다음을 수행합니다.

  1. 대상 리포지터리를 빈 리포지터리로 다시 만듭니다.
  2. 리포지터리가 소스 리포지터리와 같은 오브젝트 형식을 사용하는지 확인합니다.
  3. README, 라이선스, .gitignore로 리포지터리를 초기화하지 않습니다.
  4. 풀 미러링을 다시 설정하고 Update now를 실행합니다.

기존 대상 리포지터리를 그대로 유지해야 한다면, Overwrite diverged branches를 켜고 미러링 사용자가 대상 브랜치를 덮어쓸 권한이 있는지 확인하는 방법으로 우회합니다.

오류: mismatched algorithms#

다음과 비슷한 오류가 발생할 수 있습니다.

13:fetch remote: "fatal: mismatched algorithms: client sha1; server sha256": exit status 128.

이 오류는 소스 리포지터리와 대상 리포지터리가 서로 다른 오브젝트 형식을 사용할 때 발생하며, 오브젝트 형식이 다른 리포지터리 사이의 풀 미러링은 지원하지 않습니다.

예를 들면 다음과 같습니다.

  • 소스 리포지터리는 SHA-1을 사용합니다.
  • 대상 리포지터리는 SHA-256을 사용하거나 그 반대입니다.

이 문제를 해결하려면 대상 리포지터리를 소스 리포지터리와 같은 오브젝트 형식으로 다시 만든 다음 미러링을 다시 설정합니다.

오류: File directory conflict#

다음과 비슷한 오류가 발생할 수 있습니다.

13:preparing reference update: file directory conflict

이 오류는 소스 리포지터리와 미러 리포지터리 사이에 태그 또는 브랜치 이름 충돌이 있을 때 발생합니다. 예를 들면 다음과 같습니다.

  • 미러 리포지터리에 x/y라는 태그나 브랜치가 있습니다.
  • 소스 리포지터리에 x라는 태그나 브랜치가 있습니다.

이 문제를 해결하려면 충돌하는 태그나 브랜치를 삭제합니다. 충돌하는 태그나 브랜치를 특정할 수 없다면 미러 리포지터리의 태그를 모두 삭제합니다. 분기된 브랜치 덮어쓰기도 대안이 될 수 있습니다.

Note

태그를 삭제하면 미러 리포지터리에서 수행한 작업이 손상될 수 있습니다.

미러 리포지터리의 태그를 모두 삭제하고 제거하려면 다음을 수행합니다.

  1. 미러링된 리포지터리의 로컬 사본에서 다음을 실행합니다.

    git tag -l | xargs -n 1 git push --delete origin
    
  2. 왼쪽 사이드바에서 Settings > Repository를 선택합니다.

  3. Mirroring repositories를 확장합니다.

  4. Update now ([retry])를 선택합니다.

대용량 LFS 파일로 푸시 미러링이 멈춤#

대용량 LFS 오브젝트가 포함된 프로젝트를 푸시 미러링하면 타임아웃 문제가 발생할 수 있습니다. 이 문제는 Git LFS 작업이 기본 활동 타임아웃을 초과할 때 발생합니다. 미러링 로그에는 다음 오류가 표시됩니다.

push to mirror: git push: exit status 1, stderr: "remote: GitLab: LFS objects are missing. Ensure LFS is properly set up or try a manual \"git lfs push --all\""

이 문제를 해결하려면 미러를 설정하기 전에 LFS 활동 타임아웃 값을 늘립니다.

git config lfs.activitytimeout 240

이 명령은 타임아웃을 240 초로 설정합니다. 파일 크기와 네트워크 상황에 맞춰 값을 조정할 수 있습니다.