GitLab トラブル事例紹介

Toshiki Shimomura

2023.5.16

本ドキュメントでは、トラブルシューティングの事例を取り上げ、それぞれのトラブルの原因調査の流れと判明した原因、解決方法について記載します。

また、トラブルシューティングを円滑に進められるように心がけていることをご紹介します。

事例1:GitLab バックアップ復元スクリプトでオプションが効かない

背景

あるお客様では東京リージョンで GitLab をセルフホストしており
ディザスタリカバリのため、大阪リージョンにも構築することになりました。

東京リージョンの日次のバックアップを復元することでミラーリングし、リポジトリを参照できるようにします。なお、Web からログインしてのリポジトリの参照および git clone ができればよく、それ以外の機能は無効でよいというものです。

状況

GitLab の Helm Chart にはバックアップ用の CronJob が含まれており、そこで使われている backup-utility というスクリプトは復元にも使えます。
ただ、復元対象から不要なものをオプションで指定しましたが、それが反映されず、対象外のものを復元しようとしてエラーになりました。

$ cp /etc/gitlab/.s3cfg $HOME/.s3cfg && rm -f /srv/gitlab/tmp/backups/0_gitlab_backup.tar && echo yes | backup-utility \\
  --restore \\
  --skip registry \\
  --skip uploads \\
  --skip artifacts \\
  --skip lfs \\
  --skip packages \\
  --skip external_diffs \\
  --skip terraform_state \\
  --skip pages \\
  -t $(aws s3api list-objects-v2 --bucket sample-bucket --query 'sort_by(Contents, &Key)[-1].Key' --output text | sed -E 's/^([0-9]{10}_[0-9]{4}_[0-9]{2}_[0-9]{2}_.+)_gitlab_backup.tar$/\\1/g')
...
2022-11-15 02:33:10 +0000 -- done
Restoring pages ...
sync existing of pages failed
[Error] WARNING: Ignoring invalid line in '/home/git/.s3cfg': provider: AWS

WARNING: Ignoring invalid line in '/home/git/.s3cfg': region: ap-northeast-3

WARNING: Ignoring invalid line in '/home/git/.s3cfg': aws_access_key_id: ???

WARNING: Ignoring invalid line in '/home/git/.s3cfg': aws_secret_access_key: ???
ERROR: S3 error: 404 (NoSuchBucket): The specified bucket does not exist

試行錯誤

コマンドヘルプ

コマンドの使い方を確認してみます。

Usage: backup-utility [--restore|--cleanup] [-f URL] [-t TIMESTAMP] [--skip COMPONENT] [--backend BACKEND] [--s3config CONFIG]

Options:
  -h, --help                             Show this help message and exit.
  --restore [-t TIMESTAMP | -f URL]      When specified, utility restores from an existing backup specified
                                         as url or timestamp in object storage.
  -f URL                                 http(s):/ftp:/file: URL with backup location. Use with --restore.
  -t TIMESTAMP                           Timestamp (part before '_gitlab_backup.tar' in archive name),
                                         can be used to specify backup source or target name.
  --rsyncable                            Pass the '--rsyncable' parameter to gzip for artifact compression.
  --skip COMPONENT                       When specified, utility will skip the backup or restore of COMPONENT.
                                         May be defined multiple times. Valid values for COMPONENT are
                                         db, repositories, and any of the object storages (e.g. 'lfs').
...

--skip オプションは列挙でき、使い方は合っているように見受けられます。

処理順列挙

backup-utility の中身を読んで処理を追ったところ処理される関数・コマンドは次のようでした。

  • fetch_remote_backup
  • unpack_backup
  • gitlab-rake gitlab:backup:db:restore
  • gitlab-rake gitlab:backup:repo:restore
  • gitlab-rake gitlab:backup:builds:restore はスキップ)
  • object-storage-restore pages はスキップのはず)
  • gitlab-rake cache:clear
  • cleanup

object-storage-restore pages を手元で実行したところ同じエラーになったので、ここが実行されているとわかりました。

git@gitlab-toolbox-5f5f845954-lcvzk:/$ object-storage-restore pages /srv/gitlab/tmp/backups/pages.tar.gz 
Restoring pages ...
[Error] WARNING: Ignoring invalid line in '/home/git/.s3cfg': provider: AWS

WARNING: Ignoring invalid line in '/home/git/.s3cfg': region: ap-northeast-3

https://gitlab.com/gitlab-org/build/CNG/-/blob/v14.9.5/gitlab-toolbox/scripts/bin/backup-utility#L312

スキップ対象の処理

スキップ対象が , 区切りで入っている変数 SKIPPEDecho したところ、スキップ対象のはずの pages が入っていませんでした。

builds,registry,uploads,artifacts,lfs,packages,external_diffs,terraform_state,registry

skipped_via_flag は配列ですが、この書き方だと先頭の要素しか出力されません。

export SKIPPED=$(echo ${skipped_line#:skipped:},${skipped_via_flag})

検証用スクリプト options.sh

#!/usr/bin/env bash

skipped_via_flag=()

while [[ $# -gt 0 ]]
do
  key="$1"
  case $key in
    --skip)
      skipped_via_flag+=( "$2" )
      shift
      shift
      ;;
    *)
      echo "Unexpected parameter: $key"
      exit 1
      ;;
  esac
done

skipped_line=registry
echo ${skipped_line},${skipped_via_flag}
echo ${skipped_line},$(IFS=,; echo "${skipped_via_flag[*]}")
./options.sh --skip terraform_state --skip pages
registry,terraform_state
registry,terraform_state,pages

原因

backup-utility のオプション指定の不具合でした。

対処

--skip オプションのうち、先頭に指定したものは実際にスキップされるので、--skip pages を先頭に指定して回避しました。
また GitLab 公式に MR を送り、次のリリースで正しく機能するようにしました。
https://gitlab.com/gitlab-org/build/CNG/-/merge_requests/1210

事例2:CI の config を変えたら go mod download でリポジトリへの接続に失敗するようになった

状況

GitLab のリポジトリ名を hoge-grpc-proto から hogehoge-grpc-proto 変更したところ、CI ジョブで go mod download する途中にリポジトリへの接続に失敗しました。

$ GOPRIVATE=${GOPRIVATE} go mod download
go: gitlab.example.com/application/hogehoge-grpc-proto.git@v0.9.0: invalid version: git ls-remote -q origin in /builds/application/hogehoge-backend/.go/pkg/mod/cache/vcs/88fd5b99149db552bc9bdee05ebe7995c96689713f73961826c83633a5752ee1: exit status 128:
	fatal: unable to connect to gitlab.example.com:
	gitlab.example.com[0: 52.69.45.177]: errno=Connection timed out
	gitlab.example.com[1: 13.231.92.164]: errno=Connection timed out
	gitlab.example.com[2: 54.199.161.138]: errno=Connection timed out

.gitlab-ci.yml

variables:
  IMAGE_NAME: hogehoge-backend
  AWS_ECR: $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  GOPRIVATE: gitlab.example.com/*

stages:
  - lint
  - build_and_push

lint:
  stage: lint
  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/golangci/golangci-lint:latest
  only:
    changes:
      - internal/**/*
      - injector/**/*
      - internal/**/*
      - pkg/**/*
      - .gitlab-ci.yml
      - go.mod
      - go.sum
      - main.go
  cache:
    key: $CI_COMMIT_REF_SLUG
    paths:
      - .go/pkg/mod
  variables:
    GOPATH: $CI_PROJECT_DIR/.go
  before_script:
    - mkdir -p .go
    - git config --global url."https://hogehoge-grpc-proto:$TOKEN1@gitlab.example.com/application/hogehoge-grpc-proto".insteadOf "https://gitlab.example.com/application/hogehoge-grpc-proto"
    - git config --global url."https://gopkg:$TOKEN2@gitlab.example.com/application/gopkg".insteadOf "https://gitlab.example.com/application/gopkg"
    - GOPRIVATE=${GOPRIVATE} go mod download
  script:
    - golangci-lint run --fix --fast --timeout 5m ./...

プライベートモジュールの認証情報を git config で登録しておき、go mod download でダウンロードします。

go.mod

module gitlab.example.com/application/hogehoge-backend

go 1.16

require (
        /* 省略 */
	gitlab.example.com/application/gopkg v0.0.4
	gitlab.example.com/application/hogehoge-grpc-proto v0.9.0
)

replace (
	gitlab.example.com/application/gopkg => gitlab.example.com/application/gopkg.git v0.0.4
	gitlab.example.com/application/hogehoge-grpc-proto => gitlab.example.com/application/hogehoge-grpc-proto.git v0.9.0
)

URL のパス中のリポジトリ名を特定するために .git を付与しています。

試行錯誤

GitLab Runner から GitLab リポジトリへの接続確認

GitLab を管理するための Toolbox Pod からリポジトリに HTTPS で接続したところ、成功しました。

git@gitlab-toolbox-5689dd6c97-chfhh:/$ curl https://gitlab.example.com
<html><body>You are being <a href="https://gitlab.example.com/users/sign_in">redirected</a>.</body></html>

ALB のセキュリティグループ

リポジトリ側の ALB のセキュリティグループを見直したところ、インバウンドの 80番・443 番ポートは許可されていました。

類似 CI のジョブ

他の類似プロジェクトでも同じくプライベートモジュールを使って構成しているので、そちらで CI ジョブが正常に実行できるか確かめたところ、問題なく完了しました。

同等環境を立ち上げて再現

golangci-lint の Pod を立ち上げて CI ジョブのコマンドを逐次実行します。

go mod download には -x オプションがあり、内部で実行しているコマンドを表示します。

$ kubectl run golanci-lint --image=golangci/golangci-lint:latest -it --rm --restart=Never -n gitlab --overrides='{"spec":{"nodeSelector":{"workload/gitlab-runner":"true"}}}' --command -- /bin/bash
$ cd /root
$ cat <<EOF > go.mod
module gitlab.example.com/application/hogehoge-backend

go 1.16

require (
        /* 省略 */
	gitlab.example.com/application/gopkg v0.0.4
	gitlab.example.com/application/hogehoge-grpc-proto v0.9.0
)

replace (
	gitlab.example.com/application/gopkg => gitlab.example.com/application/gopkg.git v0.0.4
	gitlab.example.com/application/hogehoge-grpc-proto => gitlab.example.com/application/hogehoge-grpc-proto.git v0.9.0
)
EOF
$ git config --global url."https://hogehoge-grpc-proto:$TOKEN1@gitlab.example.com/application/hogehoge-grpc-proto".insteadOf "https://gitlab.example.com/application/hogehoge-grpc-proto"
$ git config --global url."https://gopkg:$TOKEN2@gitlab.example.com/application/gopkg".insteadOf "https://gitlab.example.com/application/gopkg"
$ GOPRIVATE=gitlab.example.com/* go mod download -x
cd /go/pkg/mod
git ls-remote https://gitlab.example.com/application/hogehoge-grpc-proto
cd /go/pkg/mod
git ls-remote git+ssh://gitlab.example.com/application/hogehoge-grpc-proto
^C
$ git ls-remote https://gitlab.example.com/application/hogehoge-grpc-proto
remote: HTTP Basic: Access denied
fatal: Authentication failed for 'https://gitlab.example.com/application/hogehoge-grpc-proto/'

原因

プライベートモジュールが入ったリポジトリのデプロイトークンが間違っていました。

デプロイトークン名も hoge-grpc-proto から hogehoge-grpc-proto に変えたが、新しい方を作成していませんでした。
そのため、go mod download において HTTPS でリポジトリに接続を試みるも、認証に失敗して SSH で再試行しようとして接続拒否されました。

Config ファイルの文字列を一斉に置換した時点で満足してしまい、認証情報の変更を忘れるのはありがちだなと思いました。

対処

プライベートモジュールのリポジトリ omatome-grpc-proto でデプロイトークンを再生成し,利用プロジェクト omatome-backend の CI 設定で環境変数の値を差し替えました。

トラブルシューティングの際に心がけていること

トラブルシューティングを何度か経験してきて,円滑に進めるために心がけていることを挙げてみました。

  • ログを素直に解釈する
    • 余計な推測で遠回りにならないようにする
  • 原因の仮説を(可能性が低いものも含めて)網羅的に列挙する
  • 類似プロダクトでも発生しているか確認する
    • 発生していれば、共通の根本原因がある
    • この場合、想定より影響範囲が大きいことになる
    • 発生していなければ、原因は類似プロダクトとの差分にある
  • 同等環境で再現・検証してみる
    • コマンドに verbose debug オプションがあれば有効にして情報を得る
  • 前も同じものがあったかもバイアスを排除する
    • 同じだったら無意識にスキップしてしまうのに気をつける
  • そもそもログを適切に出力する
    • ソースコード上でエラーを握りつぶしている箇所を疑う

まとめ

いかかでしたでしょうか。
今回は2つの事例を紹介しつつ、調査の流れや個人的に意識していることをまとめてみました。
他にもよいプラクティスがあると思いますが、参考になれば幸いです。

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ