SREの林 aka もりはやです。
先日行った、GitLab-CE(以後は単にGitLabと表記)のバージョンアップ作業が意外と大変だったため、同じような境遇のどなたかに向けて気づいた点を共有します。
シーケンス図的なもので表すとGitLabのアップグレードのために以下を行いました。
- 前提
- TL;DR
- 詳細
- GitLabはEC2のみにインストールされたシンプルな構成
- GitLabのアップグレードによって更新された構成
- GitLabのアップグレードは段階的に行う必要があるため専用サイトで確認
- Amazon Linux 2023はGitLab16系からしか対応しないため、中継のAmazon Linux 2を利用した
- バージョン14系からはBackground Migrationと呼ばれる処理が裏側で走るため、WEB画面からの確認が必須
- 時間短縮のため、OSをまたぐための論理バックアップとリストアを行う際に、バックアップディレクトリにEBSをマウントして取得後に新OSのサーバへ付け替える
- 時間短縮のため、アップグレード時のGitLabバックアップ処理を sudo touch /etc/gitlab/skip-auto-backup で抑止する
- 時間短縮のため、EBSのスペックは高めにする
- デグレ防止のために、プライベートIPアドレスは既存と同じにする
- 終わりに
前提
当社はメインのソースコードの保管先としてGitHubの活用を進めており、プロダクション用途のGitHub ActionsのCI/CDが日々稼働していますが、今回対象となったGitLabはOisixサービスの初期から支えてきた歴史あるコア部分のリポジトリを持つSelf-HostedなGitLabです。
TL;DR
今回の作業の要点は以下の通りです。
- GitLabはEC2のみにインストールされたシンプルな構成
- GitLabのアップグレードを行い以下のようにリプレース
- OS: Amazon Linux 1 -> Amazon Linux 2023
- GitLab: 13.6.7 -> 16.8.2(作業当時の最新)
- GitLabのアップグレードは段階的に行う必要があるため専用サイトで確認
- Amazon Linux 2023はGitLab16系からしか対応しないため、アップグレード中継としてAmazon Linux 2を利用
- バージョン14系からはBackground Migrationと呼ばれる処理が裏側で走るため、WEB画面からの確認が必須
- 時間短縮のため、OSをまたぐための論理バックアップとリストアを行う際に、バックアップディレクトリにEBSをマウントして取得後に新OSのサーバへ付け替え
- 時間短縮のため、アップグレード時のGitLabバックアップ処理を
sudo touch /etc/gitlab/skip-auto-backup
で抑止 - 時間短縮のため、EBSのスペックは高めに設定
- デグレ防止のために、プライベートIPアドレスは変化させずに維持
詳細
以下からはTL;DRに書いた各項目について解説していきます。
GitLabはEC2のみにインストールされたシンプルな構成
GitLabはSaaSとしての提供はもちろん、Self-hostedする場合でもVMやDockerやKubernetesなど多様な構成を選択できます。詳細は以下のドキュメント”Reference architectures”が詳しいですが、本記事で扱うGitLabはVMであるEC2を利用した”Standalone (non-HA)”と呼ばれるシンプルな構成です。
”Standalone (non-HA)”はシンプルに1台のサーバにGitLabのコンポーネント一式をインストールする構成で、ドキュメントの一部を引用すると以下の説明があり、これは当社の利用状況に該当しています。*1
For environments serving 2,000 or fewer users, we generally recommend a standalone approach by deploying a non-highly available single or multi-node environment.
GitLabのアップグレードによって更新された構成
GitLabのアップグレードを行った結果、以下のようにOSのアップグレードも実施しました。
- OS: Amazon Linux 1 -> Amazon Linux 2023
- GitLab: 13.6.7 -> 16.8.2(作業当時の最新)
当時GitLabが動作していたOSのAmazon Linux 1はすでにEOLとなったOSです。
以下に一部を引用します。2023-12-31にEOL(end of life)と記載があります。
The Amazon Linux AMI (also called Amazon Linux 1) reached its end of life on December 31, 2023. Amazon Linux AMI will no longer receive any security updates or bug fixes.
今回GitLabのアップグレードを行う上で、OSについてもセキュリティリスクの観点からEOLとなったAmazon Linux 1を使い続ける選択はありませんでしたし、GitLabとしてもAmazon Linux 1をサポート対象から外しています。
GitLab Docs - Supported operating systems
GitLabドキュメントからOSのサポート状況を確認し、OSについては最新のAmazon Linux 2023へアップグレードすることにしました。
GitLabのアップグレードは段階的に行う必要があるため専用サイトで確認
ユーザとしては残念なことに、GitLabの複数バージョンを跨ったアップグレードは推奨されていません。パッケージインストールなどによるアップグレード作業自体は段階を踏まずに実行することはできますが、大抵の場合 504
などのエラーを見ることになります。
以下のドキュメントから引用します。
GitLab Docs - Upgrade to a specific version using the official repositories
Upgrading directly to the latest major version can be problematic for older GitLab versions that require a multi-stage upgrade path.
この段階的なアップグレードをチェックするのは本来手間ですが、GitLabからはアップグレードパスを確認するための便利なページが提供されているためそちらを利用しました。
上記のリンク先の通り、今回の私たちの 13.6.7 -> 16.8.3
までは13段階ものアップグレードが必須と判明しました。コマンドにすると以下です。
コマンドは yum install
を rpm -Uvh
に置き換えています。
yum install gitlab-ce-13.6.7 # Amazon Linux 2のみ実施 rpm -Uvh gitlab-ce-13.8.8 rpm -Uvh gitlab-ce-13.12.15 rpm -Uvh gitlab-ce-14.0.12 rpm -Uvh gitlab-ce-14.3.6 rpm -Uvh gitlab-ce-14.9.5 rpm -Uvh gitlab-ce-14.10.5 rpm -Uvh gitlab-ce-15.0.5 rpm -Uvh gitlab-ce-15.4.6 rpm -Uvh gitlab-ce-15.11.13 rpm -Uvh gitlab-ce-16.1.6 rpm -Uvh gitlab-ce-16.3.7 rpm -Uvh gitlab-ce-16.7.6 rpm -Uvh gitlab-ce-16.8.3 yum install gitlab-ce-16.8.3 # Amazon Linux 2023のみ実施
この時点で心が折れそうになりつつも、これまで塩漬けしてきたツケを払う時だと覚悟を決めてアップグレード準備を進めました。
Amazon Linux 2023はGitLab16系からしか対応しないため、中継のAmazon Linux 2を利用した
確認を進めると段階的なアップグレードだけでなく別の制限事項も発見しました。
上でも紹介した GitLab Docs - Supported operating systems
によると、Amazon Linux 2023が対応しているのは GitLab CE / GitLab EE 16.3.0
以降のバージョンであるとわかります。
一方Amazon Linux 2は以下の理由で幅広いバージョンに対応していると判断し、段階アップグレードの中継用の一時的なサーバとして利用することにしました。 *2
- 互換性の高いCentOS 7が
GitLab CE / GitLab EE 7.10.0
からの対応となっている - CentOS7向けのGitLab RPMパッケージは検証においても正常に動作した(ように見えた)
- あくまでアップグレードのためだけの一時的なサーバで、運用自体はAmazon Linux 2023で行う
- *今考えれば、おとなしくCentOS7を利用する選択の方がよかったかもしれません
Amazon Linux 2でのアップグレードについては、以下のようなコマンドで次々とアップグレードを実施しました。*3
PKG_NAME=gitlab-ce-14.9.5-ce.0.el7.x86_64.rpm wget https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/7/${PKG_NAME}/download.rpm -O ${PKG_NAME} date ;time rpm -Uvh ${PKG_NAME} ; echo $? ; date
GitLabのパッケージについては検索可能な以下サイトが提供されています。
バージョン14系からはBackground Migrationと呼ばれる処理が裏側で走るため、WEB画面からの確認が必須
段階的なアップグレードは慣れれば繰り返し作業になります。調子に乗った私たちは rpm -Uvh
が完了次第、次のバージョンの rpm -Uvh
を実行していきました。
そして 13.6.7
-> 14.0.12
までは順調にアップグレードが行われましたが 14.3.6
でエラー画面を拝むことになりました。
原因は GitLabのBackground Migrationと呼ばれる仕組みがバージョン14から導入されたためです。これは本来ダウンタイムを最小化するために、GitLab自体は早々に起動してアップグレード処理をバックグラウンドで実行するための嬉しい仕様のようです。
しかし今回の私たちのように最新化するために連続してアップグレードを行うようなケースでは、バックグラウンドの処理の完了をCUI上からは意識できず、バックグラウンド処理が終わっていない状況でアップグレードを行いデータが破損する状況が発生したのです。
これについてはドキュメントを読むことですぐに"Background Migration"が完了していないことのエラーと当たりがついたため、各バージョンのアップグレード完了のたびに取得していたAWS Backupからリストアを行うことで時間のロスを少なく作業継続を行うことができました。
その後、一度バージョン14でアップグレード失敗をしてからは必ずBackground Migrationの完了を待ってから作業を行い、その後エラーになることはありませんでした。
(2024-03-01追記)
SNSにてCLIからBackgroud Migrationの状況を確認する方法を教えていただきました!これはとても便利です(作業時に知っていればもうちょい楽ができたかも...
gitlab-psql -c "SELECT job_class_name, table_name, column_name, job_arguments FROM batched_background_migrations WHERE status <> 3;"
毎月まめに上げておくのが、実はいちばん楽だったりしますね。新しいBackground Migrationのおかげで、作業時間は30分程、実際止まるのはそのうち10分前後だし。
— Taisuke 'Jeff' Inoue 💉x6 💙💛#StopAggression (@jeffi7) 2024年2月29日
ちなみにBackground Migrationの完了はCLIでも確認できます。(gitlab-psql -c "SELECT ....")https://t.co/16PXmT8JaR#gitlabjp https://t.co/p05LqJMkAm
時間短縮のため、OSをまたぐための論理バックアップとリストアを行う際に、バックアップディレクトリにEBSをマウントして取得後に新OSのサーバへ付け替える
GitLabにはバックアップとリストアを行う機能が準備されています。
GitLabのバックアップファイルはデフォルトで /var/opt/gitlab/backups
へ圧縮された形で出力されますが、当社の場合は数十GBの巨大なファイルサイズになりました。
これを rsync
などで移動するのは時間もコストもかかります。そのため既存のAmazon Linux 1からアップグレード用のAmazon Linux 2にGitLabを移設する際、EBSをAmazon Linux 1のバックアップディレクトリに一時的にマウントし、それを移設先のAmazon Linux 2の同じパスへマウントしてリストアを実施しました。
具体的な手順としては
- バックアップデータの移行用の領域として使うEBSを作成
- 上述したようにThroughoutなどの数値は必要に応じて増やす
- 既存サーバの
/var/opt/gitlab/backups
へマウントするmkfs
などでファイルシステムとしてフォーマットmount /dev/hoge /var/opt/gitlab/backups
でマウントchown git:root /var/opt/gitlab/backups
で権限を適切なものへ
- GitLabのバックアップを取得
date; time /opt/gitlab/bin/gitlab-rake gitlab:backup:create ; echo $? ; date
- バックアップの待ち時間を利用して新サーバへ既存サーバと同じGitLabのバージョンをインストール
- 公式ドキュメントの通り GitLab Docs - Install self-managed GitLab
- バックアップ終了後、既存サーバの
/var/opt/gitlab/backups
にマウントしていたEBSを新サーバの同じディレクトリへマウントしなおす - 新サーバ側でリストアを実施する
date ; time sudo gitlab-backup restore BACKUP=hogehoge_YYYY_MM_DD_13.6.7 ; echo $?
これと同様の作業を、Amazon Linux 2からAmazon Linux 2023へリプレースする際にも行っています。
時間短縮のため、アップグレード時のGitLabバックアップ処理を sudo touch /etc/gitlab/skip-auto-backup
で抑止する
Amazon Linux 2へのリストア完了後は段階的アップグレードをひたすら実行していきますが、工夫するポイントとして”自動バックアップの抑止”があります。
通常の rpm -Uvh
によるアップグレードでは自動で /var/opt/gitlab/backups
へデータベースのバックアップが取得されますが、AWS BackupのようなVMのスナップショットのバックアップを取得する場合は不要かつ時間もストレージも圧迫する余分な処理となります。
GitLabが成熟したソフトウェアと感じる機能の一つとして、自動バックアップの回避オプションも用意されており、それが sudo touch /etc/gitlab/skip-auto-backup
です。
https://docs.gitlab.com/ee/update/package/#back-up-before-upgrading
から以下に引用します。
The GitLab database is backed up before installing a newer GitLab version. You may skip this automatic database backup by creating an empty file at /etc/gitlab/skip-auto-backup:
これによって1バージョンアップあたり数十分の時間短縮を行えたため、必須の作業といえます。
時間短縮のため、EBSのスペックは高めにする
GitLabのアップグレード処理の中の”DBのマイグレーションプロセス”を早めるために、EC2のストレージであるEBSのチューニングも効果的でした。
私たちの環境では、gp3のデフォルト値のThroughput 125 (MiB/s) ではメトリクスが張り付いたため、タイプをio2としIOPSを増強してEBSを利用しました。io2(正式名は"io2 Block Express")は"More than 64,000 IOPS or 1,000 MiB/s of throughput"の性能を誇る強いEBSです。その分価格も上昇しますがアップグレードのための一時サーバ用途としては時間の方が重要と判断しました。
Amazon EBS volume types - Amazon Elastic Compute Cloud
GitLabのバックアップやアップグレード実行時にどこがボトルネックになるかは、普段の業務同様にCloudwatchやオブザバリティツールなどのグラフを確認することで検知できました。
実測値としてデフォルトのgp3とIOPSを増やしたio2のバックアップに要した時間を time
コマンドで測った結果が以下の通りです。長い夜間・休日作業のなかで18minの差は決して少なくはありません。
Type | Time |
---|---|
gp3 | real 139m10.425s |
io2 | real 121m44.948s |
デグレ防止のために、プライベートIPアドレスは既存と同じにする
これは当社の特殊要件と言えるかもしれません。当社のGitLabでは一部のGitLab-CI Runnerやgitクライアントについては、プライベートネットワーク経由で通信を行うケースがあり、hostsにGitLabのIPをリテラルに直書きしている場合がありました。
本来は全てをDNSに対応するべきところでしたが、クライアント側の影響範囲の特定が難しいことから、今回のアップグレード作業ではプライベートIPは変化させずに維持することとしました。
言葉としては簡単ですが、AWSの仕様でVPC内に同じIPを持つEC2を作成することはできません。*4 そのため既存サーバについてはAWS Backupによるバックアップ取得を行い、Terminateによる完全削除を実施した上で、GItLabの最新バージョンになったEC2を同じIPで起動させることでリプレースを行いました。 *5
EC2を同じIPでリプレースした後は、TargetGroupの紐付けを実施することでユーザからは「メンテ後にそのままのURLでGitLabが最新になっていた」状況を提供することができました。
終わりに
上述したGitLabのアップグレードについては、複数のGitLabサーバがあり、作業手順も待ち時間も少なくないことからとても1回の夜間作業では終えることができず、期間を空けて2回の丸一日分の夜間・休日作業が必要でした。
今回の教訓として(当たり前のことですが)ソフトウェア等のアップグレード作業を一気に行うと大変です。アップグレードの放置はセキュリティリスクや新機能の恩恵を受けられないことの機会損失に繋がります。可能な限り”塩漬け”*6は最小限としライフサイクルポリシーを定義して定期的なソフトウェアメンテナンスを実施していきましょう。
また、多少割高になるとしてもSaaSを選定してサービス側に保守をお任せすることでより本来の業務に集中できる時間を増やすことを検討してください。
最後に、本記事がどこかの自前GitLabサーバの管理者の助けに少しでもなれば幸いです。共に頑張りましょう...!packages.gitlab.com