こんにちは、インフラ部の id:sue445 です。
先日ピクシブ社内で利用しているGitLabをオンプレミス環境からGCPに移行しました。
とても長いので全3回にわけて紹介したいと思います。
全体の構成
- 前編:前置きとアーキテクチャ検討
- 中編:環境構築
- 後編:実際の移行作業とその前後の対応。移行後の所感など
今回の目次
- 全体の構成
- 今回の目次
- tl;dr;
- 移行の理由
- 筆者略歴
- GitLab移行の時系列
- やったこと1: アーキテクチャ検討
- 最後に
tl;dr;
- GitLabをオンプレミスの環境からGCPに移行した
- 単なる設置場所の変更にとどまらず、GCPで動かすにあたってアーキテクチャを全て刷新した
- アーキテクチャの検討、PoC環境の構築などの準備期間も含めると1年以上かかった
移行の理由
GitLabをはじめピクシブの各種開発環境のサーバは本番環境用とは別のオンプレミス環境で運用しているのですが、そこの契約更新の関係で移転の計画がありました。
GitLab以外の開発環境のサーバは移転先が決まっていたのですが、GitLabはGitLab本体とGitLab CIの実行環境(AWS)が物理的に離れていたことによる弊害がありました。(後述)
そのため、この機会にGitLab本体とGitLab CIの実行環境を全て同一のクラウド上で動かすためにクラウド移行を決めました。
そして、移行ついでに旧GitLabで抱えていた様々な問題を解決しようとしました。
筆者略歴
- 2018年7月: ピクシブ入社
- 2018年12月: AWSのオートスケールRunnerを構築
- 2019年8月: GitLabをリプレイス
- この時はGitLab本体を同一の自社サーバールーム内でサーバ移行した
- URL以外は全部変わった
- APサーバにベタにソースをインストールして動かしていたGitLabをDockeriseしてバージョンアップしやすくした
- MySQLからPostgreSQLに移行
- 2020年3月: SentryをオンプレからGKEに移行
- 2021年1月: WEBマンガ総選挙 を HerokuからECSに移行
- 2022年10月: GitLabをオンプレミスからGCPに移行
- URL含めて全部変わった
こうしてみると毎年式年遷宮してておかしいなw
GitLab移行の時系列
この時系列を見てもらえれば分かるように1年以上かけた一大プロジェクトです。
- 2021年7月〜2022年1月: アーキテクチャ検討
- 2021年8〜9月: AWS(GitLab本体 on EKS)でPoC環境を作成
- 2021年11月: GCP(GitLab本体 on GKE)でPoC環境を作成
- 2021年12月〜2022年1月: GCP(GitLab本体 on GCE)でPoC環境を構築
- 2022年1月: GitLab専用のGCPプロジェクトを作成するための稟議書を作成
- 2022年2月: 稟議が通ったのでGitLab専用のGCPプロジェクトを作成し、開発環境を作成
- 2022年6月: 本番環境作成、オープンβ開始
- 2022年10月16日(大安吉日): 旧環境のGitLabを完全に止めてGCPに完全移行
やったこと1: アーキテクチャ検討
ピクシブでは主にAWSとGCPを利用しています。
クラウドに移行するというのは決まっていたのですが、AWSとGCPのどちらに移行するのが良いかを決めるために構成図を書いたりPoC環境を作成して検証しました
構成図
動かす場所の他にGitLab本体をKubernetes上で動かすかどうかも検討材料の1つにありました。
そのため、(AWS or GCP) x (GitLab本体 on VM or GitLab本体 on Kubernetes)の計4パターンを検討するために下記のパターンでそれぞれ構成図を書いてアーキテクチャを検討しました
- AWS: GitLab本体 on EC2
- AWS: GitLab本体 on EKS
- GCP: GitLab本体 on GCE
- GCP: GitLab本体 on GKE
この中で「GitLab本体 on EC2」以外の3つは実際にPoC環境を作成しました。
GitLabをクラウドに移行するための障壁をまとめた
クラウド移行に関してとにかく障壁ややりたいことがいっぱいあったので、AWSとGCPのどっちが向いているかを1つずつ調査しました。
下記のようなことを比較検討した結果、最終的にGCPに移行することにしました。
とにかく考慮事項が多すぎて移行先とアーキテクチャを決めるだけで半年以上かかりました。
URLをどうするか
移行前後で既存のGitLabのURLを維持するかどうかを真っ先に考えました。
利用者側からするとURLが変わらない方が圧倒的にいいのですが、万が一移転作業に失敗して切り戻しが発生した時のことを考えてURLを完全に変えることにしました。(URLを分けていれば旧URLから新URLへのリダイレクトをしなければいいので)
今回のGitLab移行は控えめに言ってビッグバンリリースだったため移転作業時に必ずどこかしら致命的な不具合が出ると確信して安全策をとっていました。
しかし幸運にも今回は大きな問題が起きなくてよかったです。(大安吉日に移行してよかった...)
またピクシブ特有の事情ですが、旧GitLabで利用していたドメインはGitLab専用のものではなくGitLabを含む社内システム全般で利用されていました。
このドメインのゾーンはAWSのRoute53で管理しています。
もし移行後も同じURLを使い続けているとDNSだけこのRoute53を触る必要があって、運用がかなり面倒なことになることが予想されました。
GCP移行時はもちろんですが、AWSに仮に移行したとしてもGitLab用のAWSアカウントを開設することを考えていたため2アカウントでの作業が発生するため面倒なのは変わりません。*1
AWSのマネージド証明書であるAWS Certificate Manager(ACM)はRoute53とACMが同一アカウントにあることを想定しているため、それぞれが別アカウントだとそれなりにハマりや面倒事の発生もあると予想されました。
その辺の面倒事を回避するためにもURLを変えました。
Cloud IAP利用時の通信のオーバーヘッドをなくしたい
ピクシブのほぼ全ての社内システムには iap-connector というツールが導入されています。
iap-connectorはGCP以外で動いているシステムに対して手軽にCloud IAPを導入するためのツールで、Cloud IAPによりロードバランサーでGWSアカウントによる認証とIAMロールによる認可を行っています。
詳しくは下記エントリに書いています。
移行前のGitLabにもiap-connectorが導入されており移行後も(iap-connectorを使うにしろ使わないにしろ)Cloud IAPは利用する予定でした。
しかしこのiap-connectorはGCP以外で動いている社内システムで利用することを前提とした汎用的なツールなのでどうしても無駄がありました。
具体的には下記のような通信経路になっていました
しかしGitLabをクラウド移行した場合、2つあるgatewayサーバを通る必要がなくなるため別の構成を考える必要がありました。*2
GCPとAWSのどちらに移行しても下記のような構成にすれば旧GitLabにあった通信のオーバーヘッドがなくせると判断しました。
実際にGCP移行した後の構成
AWSでPoC環境を作った時の構成
AWSであればALBにOpenID Connect(OIDC)を接続し、IDプロバイダー (IdP)にOneLoginなどを使うのがいいでしょう。
LDAPからの依存をやめたい&複数のログイン方法を統一したい
歴史的経緯により旧GitLabでは下記の2種類のログイン方法が混在していました
- LDAPログイン: LDAPアカウントによるログイン(LDAPで認証)
- Standardログイン: GitLab上で直接作成したアカウントによるログイン(GitLabがパスワードを発行しているのでGitLabで認証)
しかしLDAPサーバは旧GitLabと同一ロケーション(自社サーバールーム)内のサーバにあり外部からアクセスできないため、GitLab移行時になんとかする必要がありました。
また、これら2つのログイン方法もアカウント発行時に
- この人はエンジニア職メンバーでsshするのでLDAPアカウントを発行してLDAPでGitLabにログインしてもらう
- この人はエンジニア以外の職種のメンバーなのでGitLabでアカウントを作成する
- この人はエンジニア職メンバーだけどサーバサイドではなくsshが不要そうなのでLDAPアカウントは発行せずStandardログインでいく
- この人はエンジニア職メンバーの内定者アルバイトで今はsshしないけど、新入社員として配属された時の配属先によってはsshが必要になりそうなので念の為LDAPアカウントを発行しておく
のような感じで、インフラ部の長年の経験と勘と予知能力で対応していました。
最初はssh不要なのでStandardログインで対応していたけど後にsshが必要になってLDAPログインに切り替えるケースもたまに個別対応してました。(年に1〜2回くらい)
そんなこんなでLDAPをやめるついでにこのようなカオスな運用も改善したいと思っていました。
GitLabではSAMLに対応しており *3 、社内ではOneLoginが広く使われていたためGitLab移行によりOneLogin SAMLによるログインに完全移行することにしました。
既存のLDAPログインやStandardログインのGitLabアカウントが移行後にSAMLログインのアカウントとして認識されるかが一番の懸念点でした。
旧GitLabの最新のバックアップをGCPに持っていってリストアして検証したところ、SAMLで利用してるメールアドレスとGitLabのデータベース上にあるメールアドレスが一致していれば旧GitLabと全く同じアカウントでログインできることが確認できました。
ssh接続時にあるLDAP依存を不要にしたい
前述の理由によりLDAPを廃止したかったのですが、実はGitLabへのssh接続でもLDAPに依存していました。
具体的には下記のgatewayサーバにssh接続する時にGitLabの手前のgatewayサーバでLDAP認証をしていました。
そのためここに関してもLDAPに代替するものを導入するか、GitLabで直接sshを受けるか判断する必要がありました。
余談
LDAPアカウントがあると社内の開発環境にだいたい入れてしまう関係で正社員以外には基本的に発行していませんでした。
そのため業務委託やインターンなどは下記のようにしてVPNごしにアクセスしてもらっていました。
雇用形態によってGitLabへのアクセス方法が異なっていた点も旧GitLabの運用で面倒だった点なのでクラウド移行で刷新したいと思っていました。(ネタバレになりますが実際に刷新しました)
AWS移行案
ここの構成は若干悩ましいのですが
- GitLabのAPサーバをプライベートサブネットに置いてsshはパブリックサブネットにある踏み台サーバで受ける
- GitLabのAPサーバをパブリックサブネットに置いてsshを直接受ける
の2パターンが想定されました。
GitLab APをプライベートサブネットに置くパターン
GitLab APをパブリックサブネットに置くパターン
セキュリティの観点でいくとGitLab本体はプライベートサブネットで動かしたいところです。
しかしGitLabにgitユーザでssh接続させるためのauthorized_keysはGitLabが管理しているため、それを踏み台サーバにも同期させるのは手間があってそこがネックになりそうです。
Amazon EFSを使うのが一番楽なんですが1ヶ月&1GB辺り0.36USD*4なのでauthorized_keys用途で使うにはちょっとお高いんですよね...(1MBとして月368.64USD)
さらに欲を言うとセキュリティの観点からそもそもsshを22番ポート以外であってもインターネットに開放したくありません。(過激派)
GCP移行案
前述の通りCloud IAPを利用することでロードバランサーでのGWSアカウントによる認証とIAMロールによる認可が可能になりますが、Tunneling SSH Connectionsを利用することによりIAMロールの権限を利用したsshログインが可能になります。
検証した結果GitLabでTunneling SSH Connectionsを利用することにより、IAMロールの権限でgitアクセスできたためGCP採用の決め手の1つになりました。
実際の構成は後の章で詳しく書きます。
GitLab Runnerからgit cloneする時に内部通信にしたい
冒頭に書いたように移行前の旧GitLabではGitLab本体(自社サーバールーム)とGitLab CIの実行環境(AWS)が物理的に離れていて、かつ両者間はインターネットごしに通信を行っていました。
旧GitLabではGitLab本体がGitLab CIからの通信を許可するためにNAT Gatewayで固定IPを利用していました。
しかし、社内でGitLab CIが頻繁に利用されるようになったり、社内に巨大なgitリポジトリがいくつもある関係でここの通信がコスト的にもパフォーマンス的にもボトルネックになっていました。
具体的にはNAT Gatewayだけで1ヶ月辺り約8.6TBの転送量が発生していました。
また、インターネットが不安定になったり自社サーバールーム自体の帯域の問題により、巨大なgitリポジトリのCIでgit cloneが失敗することもたまに発生していました。
そのためこの問題を解決するためにGitLab本体とGitLab CIの実行環境を全てクラウドに持っていくことに決めました。
CIだけであれば全てオンプレミスの環境に持っていくのも解決策の1つなのですが、スケールしづらくなるし他の理由も考慮してクラウド完全移行に踏み切りました
AWSとGCPのどっちに移行するにしてもPrivate DNSを作成してGitLab CIの環境からはそこ経由でアクセスさせることで解決できそうでした。
実際にGCPに移行した後の構成
実際にGCPに移行した後の構成は下記になります。
GCPのCloud DNSにはインターネットからアクセスされる一般公開ゾーンと限られたVPCからのみアクセス可能な限定公開ゾーンを作ることができます。
VPC内からのDNS解決で一般公開ゾーンと限定公開ゾーンで同名のDNSがあった場合限定公開ゾーンのものが参照されます。
そのため、一般公開ゾーンと限定公開ゾーンで同名のDNSを作ることによりインターネットからのアクセス時にはCloud IAPによるIAMロールによる認可やCloud ArmorによるIP制限を適用しつつ、GitLab CIの環境からは限定公開ゾーンのDNSにアクセスして認証を回避することが実現できました。
また移行前の旧GitLabでは外向けのDNS(ブラウザでアクセスする時のホスト)は old-gitlab.example.com
を利用して内向けのDNS(sshでアクセスする時のホスト)は old-gitlab.example.private
*5を利用していましたが、限定公開ゾーンをうまく使うことで外向けのDNSと内向けのDNSを gitlab.example.com
で統一できました。
Docker Machineからの脱却
GitLab CIでオートスケール構成のRunnerを構築する場合下記のドキュメントを参考にDocker Machine + EC2スポットインスタンスの構成にしているケースが多いと思います。
https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/
実際旧GitLabでもこの構成を採用していました。その当時のことは下記に詳しく書いています。
しかしDocker Machineは現在では非推奨*6であり https://github.com/docker/machine もアーカイブされています。
非推奨になってるものを新規システムで採用する理由はどこにもありません。
またDocker MachineはEC2のスポットインスタンスに対応していてもスポットフリートには対応していないという問題もありました。
GitLab Runnerの冗長構成のために複数のAZやインスタンスタイプを利用するためにはDocker Machine executorのRunnerを複数台起動する必要があり設定ファイルが冗長になっていました。
そのため色々比較検討した結果、移行後のGitLab RunnerではKubernetes executor を採用することにしました。
GitLab本体、VMに置くか、Kubernetesに置くか
GitLab RunnerをKubernetesに置くことに迷いはなかったのですが、GitLab本体をKubernetesの上で動かすかどうかでかなり迷いがありました。
GitLab本体 on Kubernetes
AWSであればEKSクラスタ、GCPであればGKEクラスタの上でGitLab本体を動かす構成です
GitLab公式がHelm chartを提供しているのでこれを使うのがよさそうです。
https://docs.gitlab.com/charts/
特徴
- サーバの管理をしなくていい
- 複数のAvailability Zone(AWS)やZone(GCP)で冗長構成を取りやすい
GitLab本体 on VM
AWSであればEC2インスタンス、GCPであればGCEインスタンスの上にGitLab本体を動かす構成です
Omnibus GitLab(GitLab公式のchefレシピ)を利用してVM上でGitLabを構築するか、GitLabの公式のDockerイメージ(Omnibus GitLabをDockeriseしたもの)を利用することになると思います。
特徴
- VMの管理が必要
EKSとGKEでPoC環境を作成
時代はKubernetesなので真っ先にEKSとGKEでGitLabのPoC環境を作成しました。
しかし下記のような懸念事項が見つかりKubernetes上でGitLabを動かすのは難しいという結論になりました。
前提: やりたかったこと
- [MUST] セキュリティのためにロードバランサー側で認証を行いたい
- AWS: ALB + OIDC + OneLogin
- GCP: Cloud Load Balancing + Cloud IAP
- 補足: GitLab単体でも十分堅牢なのですが、DDoS攻撃を受けるとGitLabの負荷が高まり業務に支障が出たり、GitLabの脆弱性で外部から攻撃される可能性もあったので、さらに堅牢にするために不正なリクエストがGitLabのサーバに到達する前に止めておきたいという意図があります
- [SHOULD] 可用性を高めるためにGitLab本体を複数のロケーションで動かしたい
- AWS: 東京リージョン内の複数のAvailability Zone
- GCP: 東京リージョン内の複数のZone
懸念点1: NFSを使わずにボリュームの冗長構成をとるのが難しい
AWSやGCPでGitLabを構築する場合、DBやRedisなどは下記のマネージドサービスを利用することでGitLab本体の実行環境の外に切り出すことができ、それにより可用性を高めることができます
- データベース(PostgreSQL)
- AWS: RDS
- GCP: Cloud SQL
- Redis
- AWS: ElastiCache
- GCP: Cloud Memorystore for Redis
- オブジェクトストレージ(キャッシュやファイルのアップロード先などで利用される)
- AWS: S3
- GCP: GCS
しかしGitLabがホスティングしているgitのリポジトリだけは外に出すことができないのでローカルのボリュームに置く必要があり、そこがSPOFになってしまいます。
ボリュームを複数のロケーションで冗長構成を取ると聞いて真っ先に選択肢に上がるのがNFSでしょう。
しかしGitLabではパフォーマンスの観点からNFSが非推奨とされています。https://docs.gitlab.com/ee/administration/nfs.html
そのためNFSを使わずになんとかしてボリュームの冗長構成を取る必要がありました。
またAWSでEKSクラスタを作るにはFargate(Nodeがマネージド)に載せるかEC2(Nodeがアンマネージド)に載せるかの選択肢があります。
GitLabのHelm ChartだとGitLab本体をStatefulSetで作成するのですが、FargateでStatefulSetを作成する場合PVCはAmazon EFSしか対応していない *7ためFargateは使えず、EC2上でEKSを動かす必要があります。
しかしこの場合でもEBSを複数のAvailability Zoneで共有させることができないため、GitLab本体は単一のAvailability Zoneで動かすしかなさそうです。
(Podを再起動してもStatefulSetのPVCがあるAvailability Zone以外では実行できなさそうなのでAZ障害で死ぬ)
GitLab本体の冗長構成をとるのは難しいように思えますが、GCPのマルチライターモードを使えば実現可能そうということが分かっています。
マルチライターモードについては次の回で詳しく書こうと思います。
余談ですがGitLabでの冗長構成に関しては下記も参照ください。
https://docs.gitlab.com/ee/administration/reference_architectures/
懸念点2: GitLabのHelm ChartにCloud IAPを入れるのが難しい
GitLabのHelm Chartではnginx Ingres Controller(L4ロードバランサー)を利用していて、そこでsshやhttpsを受けています。
「nginx Ingress Controllerでsshってマジ????」って思った人もいると思いますが、GitLabがforkしてsshが通るようにしているようです。(マジで...)
- https://docs.gitlab.com/ee/administration/load_balancer.html
- https://docs.gitlab.com/charts/charts/nginx/
しかしGKEでCloud IAPを利用するにはGKE Ingress(L7ロードバランサー)を使う必要があります。
L7とL4では処理するレイヤーが違うので、GKE Ingressでsshを受けることはできません。
Helm Chartのnginx Ingress Controllerの手前にGKE Ingressを置く構成も考えたのですが、GitLab公式のHelm Chartをだいぶ魔改造しないとCloud IAPを適用できる気がしなかったのでHelm Chartを使うのを諦めました。(公式のHelm Chart無しでEKSやGKEでGitLabを構築できる気がしなかった...)
ピクシブだとセキュリティのため社内システムは基本的に全部Cloud IAP(ないしはそれに準ずるもの)を入れる必要があったためこれがKubernetes不採用の決め手になりました。
しかし、このような事情が無い場合はGitLabが提供しているHelm Chartはすごい便利なので十分採用理由があると思います。(なにより公式がメンテしてるので安心感がある)
ちなみにこれを検証したのが1年以上前なので実は今はCloud IAPなどにも対応しているかもしれないです(未確認)
GitLab Pagesでマネージドなワイルドカード証明書を使いたい
GitLab Pagesは https://<GitLabのユーザ名やグループ名>.example.com
のようなURLが必要になるためワイルドカード証明書が必要になります。
旧GitLabではLet's Encryptで作ったワイルドカード証明書をサーバに置いてnginxをSSL終端にしていました。
しかしAWSやGCPを使う以上SSL証明書もマネージドにしたいと思うのが人情です。
AWSだとAWS Certificate Manager (ACM)がワイルドカード証明書に対応しています。
しかしGCPだと検証を開始した時点では GoogleマネージドSSL証明書 と Cloud Load Balancing がワイルドカード証明書に対応していませんでした。
GCPでフルマネージドなワイルドカード証明書が対応してなかったために、AWSを使うか、GitLab PagesのSSL終端のみGCEのnginxにするか真剣に悩んでいました。
そんな折にGCPから Certificate Manager が発表され、GCPでもめでたくフルマネージドなワイルドカード証明書が利用可能になり、GCP採用の決め手が1つ増えました。
最後に
今回はここまでです。次回は主に実際の構築についての話をしようと思います。
次回以降も今回と同じくらいのボリュームなのでお楽しみに!
*1:費用の監視や権限分掌の観点で既存のRoute53を管理しているAWSアカウントでGitLabを運用するという選択肢ははじめからなかったです
*2:gatewayサーバを2つ挟むのは無駄やろってツッコミがありそうなので補足ですが、iap-connectorは元々本番環境用のデータセンターで動いている社内システムにルーティングする目的で作っていて開発環境はそこに相乗りしてるためこういう構成になってます(本番環境用途の方が圧倒的に多い)
*3: https://docs.gitlab.com/ee/integration/saml.html
*4: https://aws.amazon.com/jp/efs/pricing/
*5:このDNSはイントラ環境内のBINDで管理
*6: https://github.com/docker/roadmap/issues/245
*7:https://aws.amazon.com/jp/blogs/news/running-stateful-workloads-with-amazon-eks-on-aws-fargate-using-amazon-efs/